From 9b329e7355ce9e396e6b4b033b019a5d5a8cd378 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Tue, 15 Nov 2022 10:32:08 +0100
Subject: [PATCH 01/22] temporary log window without log

---
 CMakeLists.txt      |  3 +++
 include/logwindow.h | 24 ++++++++++++++++++++++++
 include/petrack.h   |  6 ++++++
 src/logwindow.cpp   | 18 ++++++++++++++++++
 src/petrack.cpp     | 13 +++++++++++++
 ui/control.ui       |  6 +++++-
 ui/logwindow.ui     | 32 ++++++++++++++++++++++++++++++++
 7 files changed, 101 insertions(+), 1 deletion(-)
 create mode 100644 include/logwindow.h
 create mode 100644 src/logwindow.cpp
 create mode 100644 ui/logwindow.ui

diff --git a/CMakeLists.txt b/CMakeLists.txt
index f2cf11403..bc9937a6b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -384,6 +384,7 @@ target_sources(petrack_core PRIVATE
     include/frameRange.h
     include/pdoublespinbox.h
     include/pspinbox.h
+        include/logwindow.h
     )
 
 target_sources(petrack_core PRIVATE
@@ -447,6 +448,7 @@ target_sources(petrack_core PRIVATE
     src/manualTrackpointMover.cpp
     src/pdoublespinbox.cpp
     src/pspinbox.cpp
+        src/logwindow.cpp
     ui/about.ui
     ui/codeMarker.ui
     ui/colorMarker.ui
@@ -456,6 +458,7 @@ target_sources(petrack_core PRIVATE
     ui/control.ui
     ui/openMoCapDialog.ui
     ui/moCapSelectionWidget.ui
+        ui/logwindow.ui
 )
 
 target_sources(petrack PRIVATE
diff --git a/include/logwindow.h b/include/logwindow.h
new file mode 100644
index 000000000..cb466302d
--- /dev/null
+++ b/include/logwindow.h
@@ -0,0 +1,24 @@
+#ifndef LOGWINDOW_H
+#define LOGWINDOW_H
+
+#include <QWidget>
+#include <petrack.h>
+
+namespace Ui
+{
+class LogWindow;
+}
+
+class LogWindow : public QWidget
+{
+    Q_OBJECT
+
+public:
+    LogWindow(QWidget *parent, Ui::LogWindow *mUi);
+
+private:
+    Ui::LogWindow *mUi;
+    Petrack       *mMainWindow;
+};
+
+#endif // LOGWINDOW_H
diff --git a/include/petrack.h b/include/petrack.h
index b679b42bc..39f644cae 100644
--- a/include/petrack.h
+++ b/include/petrack.h
@@ -37,6 +37,7 @@
 #include "brightContrastFilter.h"
 #include "coordItem.h"
 #include "extrCalibration.h"
+#include "logwindow.h"
 #include "manualTrackpointMover.h"
 #include "moCapController.h"
 #include "moCapPerson.h"
@@ -85,6 +86,7 @@ class ColorMarkerWidget;
 class CodeMarkerWidget;
 class MultiColorMarkerWidget;
 class ViewWidget;
+class LogWindow;
 class Player;
 class TrackerItem;
 class StereoItem;
@@ -127,6 +129,7 @@ private slots:
     void fitInROI();
     void commandLineOptions();
     void keyBindings();
+    void showLogWindow();
     void about();
     void onlineHelp();
     void setCamera();
@@ -222,6 +225,7 @@ public:
     inline ColorMarkerWidget      *getColorMarkerWidget() { return mColorMarkerWidget; }
     inline CodeMarkerWidget       *getCodeMarkerWidget() { return mCodeMarkerWidget; }
     inline MultiColorMarkerWidget *getMultiColorMarkerWidget() { return mMultiColorMarkerWidget; }
+    inline LogWindow              *getLogWindow() { return mLogWindow; }
     inline GraphicsView           *getView() { return mView; }
     inline QGraphicsScene         *getScene() { return mScene; }
     inline QImage                 *getImage() { return mImage; }
@@ -383,6 +387,7 @@ private:
     ColorMarkerWidget      *mColorMarkerWidget;
     CodeMarkerWidget       *mCodeMarkerWidget;
     MultiColorMarkerWidget *mMultiColorMarkerWidget;
+    LogWindow              *mLogWindow;
 
     QAction      *mOpenSeqAct;
     QAction      *mOpenCameraAct;
@@ -426,6 +431,7 @@ private:
     QAction      *mDelPartRoiAct;
     QAction      *mCommandAct;
     QAction      *mKeyAct;
+    QAction      *mShowLogWindowAct;
     QAction      *mAboutAct;
     QAction      *mOnlineHelpAct;
     QActionGroup *mCameraGroupView;
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
new file mode 100644
index 000000000..291c02246
--- /dev/null
+++ b/src/logwindow.cpp
@@ -0,0 +1,18 @@
+#include "logwindow.h"
+
+#include "ui_logwindow.h"
+
+LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
+{
+    mMainWindow = (class Petrack *) parent;
+    if(!ui)
+    {
+        mUi = new Ui::LogWindow();
+    }
+    else
+    {
+        mUi = ui;
+    }
+
+    mUi->setupUi(this);
+}
diff --git a/src/petrack.cpp b/src/petrack.cpp
index 3b10964cf..b4b624945 100644
--- a/src/petrack.cpp
+++ b/src/petrack.cpp
@@ -165,6 +165,10 @@ Petrack::Petrack() :
     connect(mView, &GraphicsView::mouseAltReleased, this, &Petrack::releaseTrackPoint);
     connect(mView, &GraphicsView::mouseCtrlWheel, this, &Petrack::scrollShowOnly);
 
+    mLogWindow = new LogWindow(this, nullptr);
+    mLogWindow->setWindowFlags(Qt::Window);
+    mLogWindow->setWindowTitle("Log");
+
     mPlayerWidget = new Player(mAnimation, this);
 
     QVBoxLayout *vLayout = new QVBoxLayout;
@@ -1681,6 +1685,10 @@ void Petrack::showHideControlWidget()
     // show | hide Control
     mViewWidget->hideControls(mHideControlsAct->isChecked());
 }
+void Petrack::showLogWindow()
+{
+    mLogWindow->show();
+}
 
 void Petrack::setCamera()
 {
@@ -1802,6 +1810,9 @@ void Petrack::createActions()
     connect(mHideControlsAct, SIGNAL(triggered()), this, SLOT(showHideControlWidget()));
     connect(mHideControlsAct, SIGNAL(changed()), this, SLOT(showHideControlWidget()));
 
+    mShowLogWindowAct = new QAction(tr("&Show log window"), this);
+    connect(mShowLogWindowAct, SIGNAL(triggered()), this, SLOT(showLogWindow()));
+
     mCropZoomViewAct = new QAction(tr("&Transform while saving"), this); // Crop and zoom while saving
     mCropZoomViewAct->setCheckable(true);
 
@@ -1960,6 +1971,8 @@ void Petrack::createMenus()
     mViewMenu->addAction(mFontAct);
     mViewMenu->addSeparator();
     mViewMenu->addAction(mHideControlsAct);
+    mViewMenu->addSeparator();
+    mViewMenu->addAction(mShowLogWindowAct);
 
     mDeleteMenu = new QMenu(tr("&Delete"), this);
     mDeleteMenu->addAction(mDelPastAct);
diff --git a/ui/control.ui b/ui/control.ui
index 8360fdc20..597a195ab 100644
--- a/ui/control.ui
+++ b/ui/control.ui
@@ -74,7 +74,7 @@
       <enum>Qt::LeftToRight</enum>
      </property>
      <property name="currentIndex">
-      <number>0</number>
+      <number>1</number>
      </property>
      <property name="tabsClosable">
       <bool>false</bool>
@@ -3643,6 +3643,7 @@
                 <widget class="QLabel" name="recoNumberNow">
                  <property name="font">
                   <font>
+                   <weight>75</weight>
                    <bold>true</bold>
                   </font>
                  </property>
@@ -4990,6 +4991,7 @@
               <widget class="QLabel" name="trackNumberNow">
                <property name="font">
                 <font>
+                 <weight>75</weight>
                  <bold>true</bold>
                 </font>
                </property>
@@ -5030,6 +5032,7 @@
               <widget class="QLabel" name="trackNumberVisible">
                <property name="font">
                 <font>
+                 <weight>75</weight>
                  <bold>true</bold>
                 </font>
                </property>
@@ -5045,6 +5048,7 @@
               <widget class="QLabel" name="trackNumberAll">
                <property name="font">
                 <font>
+                 <weight>75</weight>
                  <bold>true</bold>
                 </font>
                </property>
diff --git a/ui/logwindow.ui b/ui/logwindow.ui
new file mode 100644
index 000000000..2660d78c7
--- /dev/null
+++ b/ui/logwindow.ui
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LogWindow</class>
+ <widget class="QWidget" name="LogWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Log</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
-- 
GitLab


From d02442f8d8049537bb4841c5dfba723410b7c153 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Wed, 16 Nov 2022 10:13:30 +0100
Subject: [PATCH 02/22] tmp commit

---
 include/logwindow.h | 22 ++++++++++++++++++++++
 src/autoCalib.cpp   |  1 +
 src/logwindow.cpp   | 29 +++++++++++++++++++++++++++++
 src/petrack.cpp     |  1 +
 ui/logwindow.ui     | 13 ++++++++-----
 5 files changed, 61 insertions(+), 5 deletions(-)

diff --git a/include/logwindow.h b/include/logwindow.h
index cb466302d..93e2362c1 100644
--- a/include/logwindow.h
+++ b/include/logwindow.h
@@ -1,3 +1,21 @@
+/*
+* PeTrack - Software for tracking pedestrians movement in videos
+* Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
 #ifndef LOGWINDOW_H
 #define LOGWINDOW_H
 
@@ -16,6 +34,10 @@ class LogWindow : public QWidget
 public:
     LogWindow(QWidget *parent, Ui::LogWindow *mUi);
 
+public slots:
+    void appendLogText(const QString &msg);
+    void appendLogText();
+
 private:
     Ui::LogWindow *mUi;
     Petrack       *mMainWindow;
diff --git a/src/autoCalib.cpp b/src/autoCalib.cpp
index 83f3d315b..c96544c6e 100644
--- a/src/autoCalib.cpp
+++ b/src/autoCalib.cpp
@@ -158,6 +158,7 @@ void AutoCalib::autoCalib()
 
         debout << "Search for cheesboard pattern (" << board_size.width << "x" << board_size.height
                << ") with square size: " << square_size << "cm..." << std::endl;
+        mMainWindow->getLogWindow()->appendLogText(QString("Search for cheesboard pattern ("));
         bool min_one_pattern_found = false;
         // search for chessbord corners in every image
         for(int i = 0; i < mCalibFiles.size(); ++i)
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index 291c02246..05b6175a9 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -1,3 +1,21 @@
+/*
+* PeTrack - Software for tracking pedestrians movement in videos
+* Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
 #include "logwindow.h"
 
 #include "ui_logwindow.h"
@@ -15,4 +33,15 @@ LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
     }
 
     mUi->setupUi(this);
+    mUi->logText->setReadOnly(true);
+    connect(mUi->testLog, SIGNAL(clicked()),this, SLOT(appendLogText()));
+}
+
+void LogWindow::appendLogText(const QString &msg){
+    mUi->logText->appendPlainText(msg);
+    mUi->logText->verticalScrollBar()->setValue(mUi->logText->verticalScrollBar()->maximum());
+}
+void LogWindow::appendLogText(){
+    mUi->logText->appendPlainText(QString("testtestestestestestetstetsetsetsetstestetsetstestetstestetsetstetsetstetstetstetststsetset"));
+    mUi->logText->verticalScrollBar()->setValue(mUi->logText->verticalScrollBar()->maximum());
 }
diff --git a/src/petrack.cpp b/src/petrack.cpp
index b4b624945..7d938c703 100644
--- a/src/petrack.cpp
+++ b/src/petrack.cpp
@@ -169,6 +169,7 @@ Petrack::Petrack() :
     mLogWindow->setWindowFlags(Qt::Window);
     mLogWindow->setWindowTitle("Log");
 
+
     mPlayerWidget = new Player(mAnimation, this);
 
     QVBoxLayout *vLayout = new QVBoxLayout;
diff --git a/ui/logwindow.ui b/ui/logwindow.ui
index 2660d78c7..21043e1ab 100644
--- a/ui/logwindow.ui
+++ b/ui/logwindow.ui
@@ -14,14 +14,17 @@
    <string>Form</string>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QPushButton" name="testLog">
+     <property name="text">
+      <string>Push me</string>
+     </property>
+    </widget>
+   </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
-      <widget class="QLabel" name="label">
-       <property name="text">
-        <string>Log</string>
-       </property>
-      </widget>
+      <widget class="QPlainTextEdit" name="logText"/>
      </item>
     </layout>
    </item>
-- 
GitLab


From 3812cc1b96f70396af4f3cead74606bef64ec037 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Mon, 21 Nov 2022 16:59:10 +0100
Subject: [PATCH 03/22] log function finally works now

---
 include/helper.h    |  2 +-
 include/logwindow.h |  6 ++++--
 src/autoCalib.cpp   |  1 -
 src/helper.cpp      |  8 ++++++++
 src/logwindow.cpp   | 14 +++++++-------
 src/petrack.cpp     |  2 +-
 ui/logwindow.ui     |  7 -------
 7 files changed, 21 insertions(+), 19 deletions(-)

diff --git a/include/helper.h b/include/helper.h
index 3433d1cc4..d1b11153a 100644
--- a/include/helper.h
+++ b/include/helper.h
@@ -291,7 +291,7 @@ inline clock_t getElapsedTime()
     return diffTime;
 }
 bool newerThanVersion(const QString &q1, const QString &q2);
-
+std::string splitFileName(std::string fileName);
 
 /**
  * Computes the median of the values in a given vector.
diff --git a/include/logwindow.h b/include/logwindow.h
index 93e2362c1..9a42d78cb 100644
--- a/include/logwindow.h
+++ b/include/logwindow.h
@@ -21,6 +21,9 @@
 
 #include <QWidget>
 #include <petrack.h>
+#include <string_view>
+#include <experimental/source_location>
+#include <sstream>
 
 namespace Ui
 {
@@ -35,8 +38,7 @@ public:
     LogWindow(QWidget *parent, Ui::LogWindow *mUi);
 
 public slots:
-    void appendLogText(const QString &msg);
-    void appendLogText();
+    void appendLogText(std::string_view msg, const std::experimental::source_location location = std::experimental::source_location::current());
 
 private:
     Ui::LogWindow *mUi;
diff --git a/src/autoCalib.cpp b/src/autoCalib.cpp
index c96544c6e..83f3d315b 100644
--- a/src/autoCalib.cpp
+++ b/src/autoCalib.cpp
@@ -158,7 +158,6 @@ void AutoCalib::autoCalib()
 
         debout << "Search for cheesboard pattern (" << board_size.width << "x" << board_size.height
                << ") with square size: " << square_size << "cm..." << std::endl;
-        mMainWindow->getLogWindow()->appendLogText(QString("Search for cheesboard pattern ("));
         bool min_one_pattern_found = false;
         // search for chessbord corners in every image
         for(int i = 0; i < mCalibFiles.size(); ++i)
diff --git a/src/helper.cpp b/src/helper.cpp
index 31cc9e9c9..42456c35e 100644
--- a/src/helper.cpp
+++ b/src/helper.cpp
@@ -254,3 +254,11 @@ bool newerThanVersion(const QString &q1, const QString &q2)
     }
     return false;
 }
+std::string splitFileName(std::string fileName){
+    std::string delimiter = "/";
+    size_t pos = 0;
+    while ((pos = fileName.find(delimiter)) != std::string::npos) {
+        fileName.erase(0, pos + delimiter.length());
+    }
+    return fileName;
+}
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index 05b6175a9..ee49ea6e6 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -34,14 +34,14 @@ LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
 
     mUi->setupUi(this);
     mUi->logText->setReadOnly(true);
-    connect(mUi->testLog, SIGNAL(clicked()),this, SLOT(appendLogText()));
 }
 
-void LogWindow::appendLogText(const QString &msg){
-    mUi->logText->appendPlainText(msg);
-    mUi->logText->verticalScrollBar()->setValue(mUi->logText->verticalScrollBar()->maximum());
-}
-void LogWindow::appendLogText(){
-    mUi->logText->appendPlainText(QString("testtestestestestestetstetsetsetsetstestetsetstestetstestetsetstetsetstetstetstetststsetset"));
+void LogWindow::appendLogText(std::string_view msg, const std::experimental::source_location location){
+
+    std::stringstream result;
+    result << location.function_name() << " in " << splitFileName(location.file_name()) << " line " << location.line() << ": " << msg;
+    std::cout << result.str() << std::endl;
+    mUi->logText->appendPlainText(QString::fromStdString(result.str()));
     mUi->logText->verticalScrollBar()->setValue(mUi->logText->verticalScrollBar()->maximum());
 }
+
diff --git a/src/petrack.cpp b/src/petrack.cpp
index 7d938c703..8ab6a7ee0 100644
--- a/src/petrack.cpp
+++ b/src/petrack.cpp
@@ -168,7 +168,7 @@ Petrack::Petrack() :
     mLogWindow = new LogWindow(this, nullptr);
     mLogWindow->setWindowFlags(Qt::Window);
     mLogWindow->setWindowTitle("Log");
-
+    mLogWindow->appendLogText("Test");
 
     mPlayerWidget = new Player(mAnimation, this);
 
diff --git a/ui/logwindow.ui b/ui/logwindow.ui
index 21043e1ab..d82423554 100644
--- a/ui/logwindow.ui
+++ b/ui/logwindow.ui
@@ -14,13 +14,6 @@
    <string>Form</string>
   </property>
   <layout class="QVBoxLayout" name="verticalLayout">
-   <item>
-    <widget class="QPushButton" name="testLog">
-     <property name="text">
-      <string>Push me</string>
-     </property>
-    </widget>
-   </item>
    <item>
     <layout class="QHBoxLayout" name="horizontalLayout">
      <item>
-- 
GitLab


From 896a5befd2390b833f496e667b3b1444c94edbbf Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Mon, 21 Nov 2022 17:00:25 +0100
Subject: [PATCH 04/22] formatting

---
 include/helper.h    |  2 +-
 include/logwindow.h | 40 +++++++++++++++++++++-------------------
 src/helper.cpp      |  8 +++++---
 src/logwindow.cpp   | 40 ++++++++++++++++++++--------------------
 4 files changed, 47 insertions(+), 43 deletions(-)

diff --git a/include/helper.h b/include/helper.h
index d1b11153a..86b61ed0b 100644
--- a/include/helper.h
+++ b/include/helper.h
@@ -290,7 +290,7 @@ inline clock_t getElapsedTime()
     lastTime = clock();
     return diffTime;
 }
-bool newerThanVersion(const QString &q1, const QString &q2);
+bool        newerThanVersion(const QString &q1, const QString &q2);
 std::string splitFileName(std::string fileName);
 
 /**
diff --git a/include/logwindow.h b/include/logwindow.h
index 9a42d78cb..da5733864 100644
--- a/include/logwindow.h
+++ b/include/logwindow.h
@@ -1,29 +1,29 @@
 /*
-* PeTrack - Software for tracking pedestrians movement in videos
-* Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
-*
-* This program is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation, either version 3 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program.  If not, see <https://www.gnu.org/licenses/>.
-*/
+ * PeTrack - Software for tracking pedestrians movement in videos
+ * Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
 
 #ifndef LOGWINDOW_H
 #define LOGWINDOW_H
 
 #include <QWidget>
-#include <petrack.h>
-#include <string_view>
 #include <experimental/source_location>
+#include <petrack.h>
 #include <sstream>
+#include <string_view>
 
 namespace Ui
 {
@@ -38,7 +38,9 @@ public:
     LogWindow(QWidget *parent, Ui::LogWindow *mUi);
 
 public slots:
-    void appendLogText(std::string_view msg, const std::experimental::source_location location = std::experimental::source_location::current());
+    void appendLogText(
+        std::string_view                         msg,
+        const std::experimental::source_location location = std::experimental::source_location::current());
 
 private:
     Ui::LogWindow *mUi;
diff --git a/src/helper.cpp b/src/helper.cpp
index 42456c35e..26ab3fea1 100644
--- a/src/helper.cpp
+++ b/src/helper.cpp
@@ -254,10 +254,12 @@ bool newerThanVersion(const QString &q1, const QString &q2)
     }
     return false;
 }
-std::string splitFileName(std::string fileName){
+std::string splitFileName(std::string fileName)
+{
     std::string delimiter = "/";
-    size_t pos = 0;
-    while ((pos = fileName.find(delimiter)) != std::string::npos) {
+    size_t      pos       = 0;
+    while((pos = fileName.find(delimiter)) != std::string::npos)
+    {
         fileName.erase(0, pos + delimiter.length());
     }
     return fileName;
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index ee49ea6e6..201707d4c 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -1,20 +1,20 @@
 /*
-* PeTrack - Software for tracking pedestrians movement in videos
-* Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
-*
-* This program is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation, either version 3 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program.  If not, see <https://www.gnu.org/licenses/>.
-*/
+ * PeTrack - Software for tracking pedestrians movement in videos
+ * Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
 
 #include "logwindow.h"
 
@@ -36,12 +36,12 @@ LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
     mUi->logText->setReadOnly(true);
 }
 
-void LogWindow::appendLogText(std::string_view msg, const std::experimental::source_location location){
-
+void LogWindow::appendLogText(std::string_view msg, const std::experimental::source_location location)
+{
     std::stringstream result;
-    result << location.function_name() << " in " << splitFileName(location.file_name()) << " line " << location.line() << ": " << msg;
+    result << location.function_name() << " in " << splitFileName(location.file_name()) << " line " << location.line()
+           << ": " << msg;
     std::cout << result.str() << std::endl;
     mUi->logText->appendPlainText(QString::fromStdString(result.str()));
     mUi->logText->verticalScrollBar()->setValue(mUi->logText->verticalScrollBar()->maximum());
 }
-
-- 
GitLab


From e45495475a285e6689075de1a9e2dd3ea05ce121 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Mon, 28 Nov 2022 16:23:39 +0100
Subject: [PATCH 05/22] cleanup before rebase

---
 include/logwindow.h                           |   51 +-
 include/spdlog/async.h                        |   99 +
 include/spdlog/async_logger-inl.h             |   92 +
 include/spdlog/async_logger.h                 |   68 +
 include/spdlog/cfg/argv.h                     |   44 +
 include/spdlog/cfg/env.h                      |   38 +
 include/spdlog/cfg/helpers-inl.h              |  120 +
 include/spdlog/cfg/helpers.h                  |   29 +
 include/spdlog/common-inl.h                   |   82 +
 include/spdlog/common.h                       |  412 ++
 include/spdlog/details/backtracer-inl.h       |   69 +
 include/spdlog/details/backtracer.h           |   45 +
 include/spdlog/details/circular_q.h           |  146 +
 include/spdlog/details/console_globals.h      |   32 +
 include/spdlog/details/file_helper-inl.h      |  172 +
 include/spdlog/details/file_helper.h          |   61 +
 include/spdlog/details/fmt_helper.h           |  164 +
 include/spdlog/details/log_msg-inl.h          |   37 +
 include/spdlog/details/log_msg.h              |   37 +
 include/spdlog/details/log_msg_buffer-inl.h   |   58 +
 include/spdlog/details/log_msg_buffer.h       |   33 +
 include/spdlog/details/mpmc_blocking_q.h      |  132 +
 include/spdlog/details/null_mutex.h           |   45 +
 include/spdlog/details/os-inl.h               |  606 +++
 include/spdlog/details/os.h                   |  118 +
 include/spdlog/details/periodic_worker-inl.h  |   28 +
 include/spdlog/details/periodic_worker.h      |   60 +
 include/spdlog/details/registry-inl.h         |  306 ++
 include/spdlog/details/registry.h             |  121 +
 include/spdlog/details/synchronous_factory.h  |   24 +
 include/spdlog/details/tcp_client-windows.h   |  160 +
 include/spdlog/details/tcp_client.h           |  145 +
 include/spdlog/details/thread_pool-inl.h      |  141 +
 include/spdlog/details/thread_pool.h          |  122 +
 include/spdlog/details/udp_client-windows.h   |  111 +
 include/spdlog/details/udp_client.h           |   94 +
 include/spdlog/details/windows_include.h      |   11 +
 include/spdlog/fmt/bin_to_hex.h               |  248 +
 include/spdlog/fmt/bundled/args.h             |  234 +
 include/spdlog/fmt/bundled/chrono.h           | 2069 ++++++++
 include/spdlog/fmt/bundled/color.h            |  651 +++
 include/spdlog/fmt/bundled/compile.h          |  611 +++
 include/spdlog/fmt/bundled/core.h             | 3323 +++++++++++++
 include/spdlog/fmt/bundled/fmt.license.rst    |   27 +
 include/spdlog/fmt/bundled/format-inl.h       | 1723 +++++++
 include/spdlog/fmt/bundled/format.h           | 4217 +++++++++++++++++
 include/spdlog/fmt/bundled/locale.h           |    2 +
 include/spdlog/fmt/bundled/os.h               |  478 ++
 include/spdlog/fmt/bundled/ostream.h          |  237 +
 include/spdlog/fmt/bundled/printf.h           |  640 +++
 include/spdlog/fmt/bundled/ranges.h           |  722 +++
 include/spdlog/fmt/bundled/std.h              |  171 +
 include/spdlog/fmt/bundled/xchar.h            |  229 +
 include/spdlog/fmt/chrono.h                   |   22 +
 include/spdlog/fmt/compile.h                  |   22 +
 include/spdlog/fmt/fmt.h                      |   33 +
 include/spdlog/fmt/ostr.h                     |   22 +
 include/spdlog/fmt/ranges.h                   |   22 +
 include/spdlog/fmt/std.h                      |   23 +
 include/spdlog/fmt/xchar.h                    |   22 +
 include/spdlog/formatter.h                    |   18 +
 include/spdlog/fwd.h                          |   18 +
 include/spdlog/logger-inl.h                   |  257 +
 include/spdlog/logger.h                       |  428 ++
 include/spdlog/pattern_formatter-inl.h        | 1436 ++++++
 include/spdlog/pattern_formatter.h            |  128 +
 include/spdlog/sinks/android_sink.h           |  142 +
 include/spdlog/sinks/ansicolor_sink-inl.h     |  145 +
 include/spdlog/sinks/ansicolor_sink.h         |  118 +
 include/spdlog/sinks/base_sink-inl.h          |   63 +
 include/spdlog/sinks/base_sink.h              |   52 +
 include/spdlog/sinks/basic_file_sink-inl.h    |   44 +
 include/spdlog/sinks/basic_file_sink.h        |   60 +
 include/spdlog/sinks/daily_file_sink.h        |  297 ++
 include/spdlog/sinks/dist_sink.h              |   97 +
 include/spdlog/sinks/dup_filter_sink.h        |   94 +
 include/spdlog/sinks/hourly_file_sink.h       |  204 +
 include/spdlog/sinks/mongo_sink.h             |  107 +
 include/spdlog/sinks/msvc_sink.h              |   59 +
 include/spdlog/sinks/null_sink.h              |   44 +
 include/spdlog/sinks/ostream_sink.h           |   50 +
 include/spdlog/sinks/qt_sinks.h               |  102 +
 include/spdlog/sinks/ringbuffer_sink.h        |   74 +
 include/spdlog/sinks/rotating_file_sink-inl.h |  152 +
 include/spdlog/sinks/rotating_file_sink.h     |   81 +
 include/spdlog/sinks/sink-inl.h               |   25 +
 include/spdlog/sinks/sink.h                   |   35 +
 include/spdlog/sinks/stdout_color_sinks-inl.h |   38 +
 include/spdlog/sinks/stdout_color_sinks.h     |   45 +
 include/spdlog/sinks/stdout_sinks-inl.h       |  139 +
 include/spdlog/sinks/stdout_sinks.h           |   87 +
 include/spdlog/sinks/syslog_sink.h            |  109 +
 include/spdlog/sinks/systemd_sink.h           |  119 +
 include/spdlog/sinks/tcp_sink.h               |   81 +
 include/spdlog/sinks/udp_sink.h               |   74 +
 include/spdlog/sinks/win_eventlog_sink.h      |  289 ++
 include/spdlog/sinks/wincolor_sink-inl.h      |  175 +
 include/spdlog/sinks/wincolor_sink.h          |   85 +
 include/spdlog/spdlog-inl.h                   |  120 +
 include/spdlog/spdlog.h                       |  354 ++
 include/spdlog/stopwatch.h                    |   69 +
 include/spdlog/tweakme.h                      |  140 +
 include/spdlog/version.h                      |   10 +
 src/logwindow.cpp                             |   75 +-
 src/petrack.cpp                               |    5 +-
 105 files changed, 26140 insertions(+), 65 deletions(-)
 create mode 100644 include/spdlog/async.h
 create mode 100644 include/spdlog/async_logger-inl.h
 create mode 100644 include/spdlog/async_logger.h
 create mode 100644 include/spdlog/cfg/argv.h
 create mode 100644 include/spdlog/cfg/env.h
 create mode 100644 include/spdlog/cfg/helpers-inl.h
 create mode 100644 include/spdlog/cfg/helpers.h
 create mode 100644 include/spdlog/common-inl.h
 create mode 100644 include/spdlog/common.h
 create mode 100644 include/spdlog/details/backtracer-inl.h
 create mode 100644 include/spdlog/details/backtracer.h
 create mode 100644 include/spdlog/details/circular_q.h
 create mode 100644 include/spdlog/details/console_globals.h
 create mode 100644 include/spdlog/details/file_helper-inl.h
 create mode 100644 include/spdlog/details/file_helper.h
 create mode 100644 include/spdlog/details/fmt_helper.h
 create mode 100644 include/spdlog/details/log_msg-inl.h
 create mode 100644 include/spdlog/details/log_msg.h
 create mode 100644 include/spdlog/details/log_msg_buffer-inl.h
 create mode 100644 include/spdlog/details/log_msg_buffer.h
 create mode 100644 include/spdlog/details/mpmc_blocking_q.h
 create mode 100644 include/spdlog/details/null_mutex.h
 create mode 100644 include/spdlog/details/os-inl.h
 create mode 100644 include/spdlog/details/os.h
 create mode 100644 include/spdlog/details/periodic_worker-inl.h
 create mode 100644 include/spdlog/details/periodic_worker.h
 create mode 100644 include/spdlog/details/registry-inl.h
 create mode 100644 include/spdlog/details/registry.h
 create mode 100644 include/spdlog/details/synchronous_factory.h
 create mode 100644 include/spdlog/details/tcp_client-windows.h
 create mode 100644 include/spdlog/details/tcp_client.h
 create mode 100644 include/spdlog/details/thread_pool-inl.h
 create mode 100644 include/spdlog/details/thread_pool.h
 create mode 100644 include/spdlog/details/udp_client-windows.h
 create mode 100644 include/spdlog/details/udp_client.h
 create mode 100644 include/spdlog/details/windows_include.h
 create mode 100644 include/spdlog/fmt/bin_to_hex.h
 create mode 100644 include/spdlog/fmt/bundled/args.h
 create mode 100644 include/spdlog/fmt/bundled/chrono.h
 create mode 100644 include/spdlog/fmt/bundled/color.h
 create mode 100644 include/spdlog/fmt/bundled/compile.h
 create mode 100644 include/spdlog/fmt/bundled/core.h
 create mode 100644 include/spdlog/fmt/bundled/fmt.license.rst
 create mode 100644 include/spdlog/fmt/bundled/format-inl.h
 create mode 100644 include/spdlog/fmt/bundled/format.h
 create mode 100644 include/spdlog/fmt/bundled/locale.h
 create mode 100644 include/spdlog/fmt/bundled/os.h
 create mode 100644 include/spdlog/fmt/bundled/ostream.h
 create mode 100644 include/spdlog/fmt/bundled/printf.h
 create mode 100644 include/spdlog/fmt/bundled/ranges.h
 create mode 100644 include/spdlog/fmt/bundled/std.h
 create mode 100644 include/spdlog/fmt/bundled/xchar.h
 create mode 100644 include/spdlog/fmt/chrono.h
 create mode 100644 include/spdlog/fmt/compile.h
 create mode 100644 include/spdlog/fmt/fmt.h
 create mode 100644 include/spdlog/fmt/ostr.h
 create mode 100644 include/spdlog/fmt/ranges.h
 create mode 100644 include/spdlog/fmt/std.h
 create mode 100644 include/spdlog/fmt/xchar.h
 create mode 100644 include/spdlog/formatter.h
 create mode 100644 include/spdlog/fwd.h
 create mode 100644 include/spdlog/logger-inl.h
 create mode 100644 include/spdlog/logger.h
 create mode 100644 include/spdlog/pattern_formatter-inl.h
 create mode 100644 include/spdlog/pattern_formatter.h
 create mode 100644 include/spdlog/sinks/android_sink.h
 create mode 100644 include/spdlog/sinks/ansicolor_sink-inl.h
 create mode 100644 include/spdlog/sinks/ansicolor_sink.h
 create mode 100644 include/spdlog/sinks/base_sink-inl.h
 create mode 100644 include/spdlog/sinks/base_sink.h
 create mode 100644 include/spdlog/sinks/basic_file_sink-inl.h
 create mode 100644 include/spdlog/sinks/basic_file_sink.h
 create mode 100644 include/spdlog/sinks/daily_file_sink.h
 create mode 100644 include/spdlog/sinks/dist_sink.h
 create mode 100644 include/spdlog/sinks/dup_filter_sink.h
 create mode 100644 include/spdlog/sinks/hourly_file_sink.h
 create mode 100644 include/spdlog/sinks/mongo_sink.h
 create mode 100644 include/spdlog/sinks/msvc_sink.h
 create mode 100644 include/spdlog/sinks/null_sink.h
 create mode 100644 include/spdlog/sinks/ostream_sink.h
 create mode 100644 include/spdlog/sinks/qt_sinks.h
 create mode 100644 include/spdlog/sinks/ringbuffer_sink.h
 create mode 100644 include/spdlog/sinks/rotating_file_sink-inl.h
 create mode 100644 include/spdlog/sinks/rotating_file_sink.h
 create mode 100644 include/spdlog/sinks/sink-inl.h
 create mode 100644 include/spdlog/sinks/sink.h
 create mode 100644 include/spdlog/sinks/stdout_color_sinks-inl.h
 create mode 100644 include/spdlog/sinks/stdout_color_sinks.h
 create mode 100644 include/spdlog/sinks/stdout_sinks-inl.h
 create mode 100644 include/spdlog/sinks/stdout_sinks.h
 create mode 100644 include/spdlog/sinks/syslog_sink.h
 create mode 100644 include/spdlog/sinks/systemd_sink.h
 create mode 100644 include/spdlog/sinks/tcp_sink.h
 create mode 100644 include/spdlog/sinks/udp_sink.h
 create mode 100644 include/spdlog/sinks/win_eventlog_sink.h
 create mode 100644 include/spdlog/sinks/wincolor_sink-inl.h
 create mode 100644 include/spdlog/sinks/wincolor_sink.h
 create mode 100644 include/spdlog/spdlog-inl.h
 create mode 100644 include/spdlog/spdlog.h
 create mode 100644 include/spdlog/stopwatch.h
 create mode 100644 include/spdlog/tweakme.h
 create mode 100644 include/spdlog/version.h

diff --git a/include/logwindow.h b/include/logwindow.h
index da5733864..9b5350cea 100644
--- a/include/logwindow.h
+++ b/include/logwindow.h
@@ -1,29 +1,32 @@
 /*
- * PeTrack - Software for tracking pedestrians movement in videos
- * Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
+* PeTrack - Software for tracking pedestrians movement in videos
+* Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
 
 #ifndef LOGWINDOW_H
 #define LOGWINDOW_H
 
+#include "spdlog/logger.h"
+#include "spdlog/sinks/qt_sinks.h"
+#include "spdlog//spdlog.h"
+
 #include <QWidget>
 #include <experimental/source_location>
 #include <petrack.h>
 #include <sstream>
-#include <string_view>
 
 namespace Ui
 {
@@ -32,19 +35,15 @@ class LogWindow;
 
 class LogWindow : public QWidget
 {
-    Q_OBJECT
+   Q_OBJECT
 
 public:
-    LogWindow(QWidget *parent, Ui::LogWindow *mUi);
-
-public slots:
-    void appendLogText(
-        std::string_view                         msg,
-        const std::experimental::source_location location = std::experimental::source_location::current());
+   LogWindow(QWidget *parent, Ui::LogWindow *mUi);
 
 private:
-    Ui::LogWindow *mUi;
-    Petrack       *mMainWindow;
+   Ui::LogWindow *mUi;
+   Petrack       *mMainWindow;
+   std::shared_ptr<spdlog::logger> mLogger;
 };
 
 #endif // LOGWINDOW_H
diff --git a/include/spdlog/async.h b/include/spdlog/async.h
new file mode 100644
index 000000000..d6e213498
--- /dev/null
+++ b/include/spdlog/async.h
@@ -0,0 +1,99 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+//
+// Async logging using global thread pool
+// All loggers created here share same global thread pool.
+// Each log message is pushed to a queue along with a shared pointer to the
+// logger.
+// If a logger deleted while having pending messages in the queue, it's actual
+// destruction will defer
+// until all its messages are processed by the thread pool.
+// This is because each message in the queue holds a shared_ptr to the
+// originating logger.
+
+#include <spdlog/async_logger.h>
+#include <spdlog/details/registry.h>
+#include <spdlog/details/thread_pool.h>
+
+#include <memory>
+#include <mutex>
+#include <functional>
+
+namespace spdlog {
+
+namespace details {
+static const size_t default_async_q_size = 8192;
+}
+
+// async logger factory - creates async loggers backed with thread pool.
+// if a global thread pool doesn't already exist, create it with default queue
+// size of 8192 items and single thread.
+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(std::string logger_name, SinkArgs &&... args)
+    {
+        auto &registry_inst = details::registry::instance();
+
+        // create global thread pool if not already exists..
+
+        auto &mutex = registry_inst.tp_mutex();
+        std::lock_guard<std::recursive_mutex> tp_lock(mutex);
+        auto tp = registry_inst.get_tp();
+        if (tp == nullptr)
+        {
+            tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1U);
+            registry_inst.set_tp(tp);
+        }
+
+        auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
+        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;
+    }
+};
+
+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(std::string logger_name, 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(std::string logger_name, SinkArgs &&... sink_args)
+{
+    return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
+}
+
+// set global thread pool.
+inline void init_thread_pool(
+    size_t q_size, size_t thread_count, std::function<void()> on_thread_start, std::function<void()> on_thread_stop)
+{
+    auto tp = std::make_shared<details::thread_pool>(q_size, thread_count, on_thread_start, on_thread_stop);
+    details::registry::instance().set_tp(std::move(tp));
+}
+
+inline void init_thread_pool(size_t q_size, size_t thread_count, std::function<void()> on_thread_start)
+{
+    init_thread_pool(q_size, thread_count, on_thread_start, [] {});
+}
+
+inline void init_thread_pool(size_t q_size, size_t thread_count)
+{
+    init_thread_pool(
+        q_size, thread_count, [] {}, [] {});
+}
+
+// get the global thread pool.
+inline std::shared_ptr<spdlog::details::thread_pool> thread_pool()
+{
+    return details::registry::instance().get_tp();
+}
+} // namespace spdlog
diff --git a/include/spdlog/async_logger-inl.h b/include/spdlog/async_logger-inl.h
new file mode 100644
index 000000000..a1c27a59b
--- /dev/null
+++ b/include/spdlog/async_logger-inl.h
@@ -0,0 +1,92 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/async_logger.h>
+#endif
+
+#include <spdlog/sinks/sink.h>
+#include <spdlog/details/thread_pool.h>
+
+#include <memory>
+#include <string>
+
+SPDLOG_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(), std::move(tp), overflow_policy)
+{}
+
+SPDLOG_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), {std::move(single_sink)}, std::move(tp), overflow_policy)
+{}
+
+// send the log message to the thread pool
+SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg)
+{
+    if (auto pool_ptr = thread_pool_.lock())
+    {
+        pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
+    }
+    else
+    {
+        throw_spdlog_ex("async log: thread pool doesn't exist anymore");
+    }
+}
+
+// send flush request to the thread pool
+SPDLOG_INLINE void spdlog::async_logger::flush_()
+{
+    if (auto pool_ptr = thread_pool_.lock())
+    {
+        pool_ptr->post_flush(shared_from_this(), overflow_policy_);
+    }
+    else
+    {
+        throw_spdlog_ex("async flush: thread pool doesn't exist anymore");
+    }
+}
+
+//
+// backend functions - called from the thread pool to do the actual job
+//
+SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg)
+{
+    for (auto &sink : sinks_)
+    {
+        if (sink->should_log(msg.level))
+        {
+            SPDLOG_TRY
+            {
+                sink->log(msg);
+            }
+            SPDLOG_LOGGER_CATCH(msg.source)
+        }
+    }
+
+    if (should_flush_(msg))
+    {
+        backend_flush_();
+    }
+}
+
+SPDLOG_INLINE void spdlog::async_logger::backend_flush_()
+{
+    for (auto &sink : sinks_)
+    {
+        SPDLOG_TRY
+        {
+            sink->flush();
+        }
+        SPDLOG_LOGGER_CATCH(source_loc())
+    }
+}
+
+SPDLOG_INLINE std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name)
+{
+    auto cloned = std::make_shared<spdlog::async_logger>(*this);
+    cloned->name_ = std::move(new_name);
+    return cloned;
+}
diff --git a/include/spdlog/async_logger.h b/include/spdlog/async_logger.h
new file mode 100644
index 000000000..91a93fcbe
--- /dev/null
+++ b/include/spdlog/async_logger.h
@@ -0,0 +1,68 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+// Fast asynchronous logger.
+// Uses pre allocated queue.
+// Creates a single back thread to pop messages from the queue and log them.
+//
+// Upon each log write the logger:
+//    1. Checks if its log level is enough to log the message
+//    2. Push a new copy of the message to a queue (or block the caller until
+//    space is available in the queue)
+// Upon destruction, logs all remaining messages in the queue before
+// destructing..
+
+#include <spdlog/logger.h>
+
+namespace spdlog {
+
+// Async overflow policy - block by default.
+enum class async_overflow_policy
+{
+    block,         // Block until message can be enqueued
+    overrun_oldest // Discard oldest message in the queue if full when trying to
+                   // add new item.
+};
+
+namespace details {
+class thread_pool;
+}
+
+class SPDLOG_API async_logger final : public std::enable_shared_from_this<async_logger>, public logger
+{
+    friend class details::thread_pool;
+
+public:
+    template<typename It>
+    async_logger(std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp,
+        async_overflow_policy overflow_policy = async_overflow_policy::block)
+        : logger(std::move(logger_name), begin, end)
+        , thread_pool_(std::move(tp))
+        , overflow_policy_(overflow_policy)
+    {}
+
+    async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp,
+        async_overflow_policy overflow_policy = async_overflow_policy::block);
+
+    async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp,
+        async_overflow_policy overflow_policy = async_overflow_policy::block);
+
+    std::shared_ptr<logger> clone(std::string new_name) override;
+
+protected:
+    void sink_it_(const details::log_msg &msg) override;
+    void flush_() override;
+    void backend_sink_it_(const details::log_msg &incoming_log_msg);
+    void backend_flush_();
+
+private:
+    std::weak_ptr<details::thread_pool> thread_pool_;
+    async_overflow_policy overflow_policy_;
+};
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "async_logger-inl.h"
+#endif
diff --git a/include/spdlog/cfg/argv.h b/include/spdlog/cfg/argv.h
new file mode 100644
index 000000000..36d9f1c44
--- /dev/null
+++ b/include/spdlog/cfg/argv.h
@@ -0,0 +1,44 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+#include <spdlog/cfg/helpers.h>
+#include <spdlog/details/registry.h>
+
+//
+// Init log levels using each argv entry that starts with "SPDLOG_LEVEL="
+//
+// set all loggers to debug level:
+// example.exe "SPDLOG_LEVEL=debug"
+
+// set logger1 to trace level
+// example.exe "SPDLOG_LEVEL=logger1=trace"
+
+// turn off all logging except for logger1 and logger2:
+// example.exe "SPDLOG_LEVEL=off,logger1=debug,logger2=info"
+
+namespace spdlog {
+namespace cfg {
+
+// search for SPDLOG_LEVEL= in the args and use it to init the levels
+inline void load_argv_levels(int argc, const char **argv)
+{
+    const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
+    for (int i = 1; i < argc; i++)
+    {
+        std::string arg = argv[i];
+        if (arg.find(spdlog_level_prefix) == 0)
+        {
+            auto levels_string = arg.substr(spdlog_level_prefix.size());
+            helpers::load_levels(levels_string);
+        }
+    }
+}
+
+inline void load_argv_levels(int argc, char **argv)
+{
+    load_argv_levels(argc, const_cast<const char **>(argv));
+}
+
+} // namespace cfg
+} // namespace spdlog
diff --git a/include/spdlog/cfg/env.h b/include/spdlog/cfg/env.h
new file mode 100644
index 000000000..1f39ebbb2
--- /dev/null
+++ b/include/spdlog/cfg/env.h
@@ -0,0 +1,38 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+#include <spdlog/cfg/helpers.h>
+#include <spdlog/details/registry.h>
+#include <spdlog/details/os.h>
+
+//
+// Init levels and patterns from env variables SPDLOG_LEVEL
+// Inspired from Rust's "env_logger" crate (https://crates.io/crates/env_logger).
+// Note - fallback to "info" level on unrecognized levels
+//
+// Examples:
+//
+// set global level to debug:
+// export SPDLOG_LEVEL=debug
+//
+// turn off all logging except for logger1:
+// export SPDLOG_LEVEL="*=off,logger1=debug"
+//
+
+// turn off all logging except for logger1 and logger2:
+// export SPDLOG_LEVEL="off,logger1=debug,logger2=info"
+
+namespace spdlog {
+namespace cfg {
+inline void load_env_levels()
+{
+    auto env_val = details::os::getenv("SPDLOG_LEVEL");
+    if (!env_val.empty())
+    {
+        helpers::load_levels(env_val);
+    }
+}
+
+} // namespace cfg
+} // namespace spdlog
diff --git a/include/spdlog/cfg/helpers-inl.h b/include/spdlog/cfg/helpers-inl.h
new file mode 100644
index 000000000..675a13af1
--- /dev/null
+++ b/include/spdlog/cfg/helpers-inl.h
@@ -0,0 +1,120 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/cfg/helpers.h>
+#endif
+
+#include <spdlog/spdlog.h>
+#include <spdlog/details/os.h>
+#include <spdlog/details/registry.h>
+
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <sstream>
+
+namespace spdlog {
+namespace cfg {
+namespace helpers {
+
+// inplace convert to lowercase
+inline std::string &to_lower_(std::string &str)
+{
+    std::transform(
+        str.begin(), str.end(), str.begin(), [](char ch) { return static_cast<char>((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch); });
+    return str;
+}
+
+// inplace trim spaces
+inline std::string &trim_(std::string &str)
+{
+    const char *spaces = " \n\r\t";
+    str.erase(str.find_last_not_of(spaces) + 1);
+    str.erase(0, str.find_first_not_of(spaces));
+    return str;
+}
+
+// return (name,value) trimmed pair from given "name=value" string.
+// return empty string on missing parts
+// "key=val" => ("key", "val")
+// " key  =  val " => ("key", "val")
+// "key=" => ("key", "")
+// "val" => ("", "val")
+
+inline std::pair<std::string, std::string> extract_kv_(char sep, const std::string &str)
+{
+    auto n = str.find(sep);
+    std::string k, v;
+    if (n == std::string::npos)
+    {
+        v = str;
+    }
+    else
+    {
+        k = str.substr(0, n);
+        v = str.substr(n + 1);
+    }
+    return std::make_pair(trim_(k), trim_(v));
+}
+
+// return vector of key/value pairs from sequence of "K1=V1,K2=V2,.."
+// "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
+inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str)
+{
+    std::string token;
+    std::istringstream token_stream(str);
+    std::unordered_map<std::string, std::string> rv{};
+    while (std::getline(token_stream, token, ','))
+    {
+        if (token.empty())
+        {
+            continue;
+        }
+        auto kv = extract_kv_('=', token);
+        rv[kv.first] = kv.second;
+    }
+    return rv;
+}
+
+SPDLOG_INLINE void load_levels(const std::string &input)
+{
+    if (input.empty() || input.size() > 512)
+    {
+        return;
+    }
+
+    auto key_vals = extract_key_vals_(input);
+    std::unordered_map<std::string, level::level_enum> levels;
+    level::level_enum global_level = level::info;
+    bool global_level_found = false;
+
+    for (auto &name_level : key_vals)
+    {
+        auto &logger_name = name_level.first;
+        auto level_name = to_lower_(name_level.second);
+        auto level = level::from_str(level_name);
+        // ignore unrecognized level names
+        if (level == level::off && level_name != "off")
+        {
+            continue;
+        }
+        if (logger_name.empty()) // no logger name indicate global level
+        {
+            global_level_found = true;
+            global_level = level;
+        }
+        else
+        {
+            levels[logger_name] = level;
+        }
+    }
+
+    details::registry::instance().set_levels(std::move(levels), global_level_found ? &global_level : nullptr);
+}
+
+} // namespace helpers
+} // namespace cfg
+} // namespace spdlog
diff --git a/include/spdlog/cfg/helpers.h b/include/spdlog/cfg/helpers.h
new file mode 100644
index 000000000..ab7584e05
--- /dev/null
+++ b/include/spdlog/cfg/helpers.h
@@ -0,0 +1,29 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/common.h>
+#include <unordered_map>
+
+namespace spdlog {
+namespace cfg {
+namespace helpers {
+//
+// Init levels from given string
+//
+// Examples:
+//
+// set global level to debug: "debug"
+// turn off all logging except for logger1: "off,logger1=debug"
+// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
+//
+SPDLOG_API void load_levels(const std::string &txt);
+} // namespace helpers
+
+} // namespace cfg
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "helpers-inl.h"
+#endif // SPDLOG_HEADER_ONLY
diff --git a/include/spdlog/common-inl.h b/include/spdlog/common-inl.h
new file mode 100644
index 000000000..728f98317
--- /dev/null
+++ b/include/spdlog/common-inl.h
@@ -0,0 +1,82 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/common.h>
+#endif
+
+#include <algorithm>
+#include <iterator>
+
+namespace spdlog {
+namespace level {
+
+#if __cplusplus >= 201703L
+constexpr
+#endif
+    static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
+
+static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
+
+SPDLOG_INLINE const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
+{
+    return level_string_views[l];
+}
+
+SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
+{
+    return short_level_names[l];
+}
+
+SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
+{
+    auto it = std::find(std::begin(level_string_views), std::end(level_string_views), name);
+    if (it != std::end(level_string_views))
+        return static_cast<level::level_enum>(std::distance(std::begin(level_string_views), it));
+
+    // check also for "warn" and "err" before giving up..
+    if (name == "warn")
+    {
+        return level::warn;
+    }
+    if (name == "err")
+    {
+        return level::err;
+    }
+    return level::off;
+}
+} // namespace level
+
+SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg)
+    : msg_(std::move(msg))
+{}
+
+SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno)
+{
+#ifdef SPDLOG_USE_STD_FORMAT
+    msg_ = std::system_error(std::error_code(last_errno, std::generic_category()), msg).what();
+#else
+    memory_buf_t outbuf;
+    fmt::format_system_error(outbuf, last_errno, msg.c_str());
+    msg_ = fmt::to_string(outbuf);
+#endif
+}
+
+SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT
+{
+    return msg_.c_str();
+}
+
+SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno)
+{
+    SPDLOG_THROW(spdlog_ex(msg, last_errno));
+}
+
+SPDLOG_INLINE void throw_spdlog_ex(std::string msg)
+{
+    SPDLOG_THROW(spdlog_ex(std::move(msg)));
+}
+
+} // namespace spdlog
diff --git a/include/spdlog/common.h b/include/spdlog/common.h
new file mode 100644
index 000000000..e69201a81
--- /dev/null
+++ b/include/spdlog/common.h
@@ -0,0 +1,412 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/tweakme.h>
+#include <spdlog/details/null_mutex.h>
+
+#include <atomic>
+#include <chrono>
+#include <initializer_list>
+#include <memory>
+#include <exception>
+#include <string>
+#include <type_traits>
+#include <functional>
+#include <cstdio>
+
+#ifdef SPDLOG_USE_STD_FORMAT
+#    include <version>
+#    if __cpp_lib_format >= 202207L
+#        include <format>
+#    else
+#        include <string_view>
+#    endif
+#endif
+
+#ifdef SPDLOG_COMPILED_LIB
+#    undef SPDLOG_HEADER_ONLY
+#    if defined(SPDLOG_SHARED_LIB)
+#        if defined(_WIN32)
+#            ifdef spdlog_EXPORTS
+#                define SPDLOG_API __declspec(dllexport)
+#            else // !spdlog_EXPORTS
+#                define SPDLOG_API __declspec(dllimport)
+#            endif
+#        else // !defined(_WIN32)
+#            define SPDLOG_API __attribute__((visibility("default")))
+#        endif
+#    else // !defined(SPDLOG_SHARED_LIB)
+#        define SPDLOG_API
+#    endif
+#    define SPDLOG_INLINE
+#else // !defined(SPDLOG_COMPILED_LIB)
+#    define SPDLOG_API
+#    define SPDLOG_HEADER_ONLY
+#    define SPDLOG_INLINE inline
+#endif // #ifdef SPDLOG_COMPILED_LIB
+
+#include <spdlog/fmt/fmt.h>
+
+#if !defined(SPDLOG_USE_STD_FORMAT) && FMT_VERSION >= 80000 // backward compatibility with fmt versions older than 8
+#    define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string)
+#    define SPDLOG_FMT_STRING(format_string) FMT_STRING(format_string)
+#    if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
+#        include <spdlog/fmt/xchar.h>
+#    endif
+#else
+#    define SPDLOG_FMT_RUNTIME(format_string) format_string
+#    define SPDLOG_FMT_STRING(format_string) format_string
+#endif
+
+// visual studio up to 2013 does not support noexcept nor constexpr
+#if defined(_MSC_VER) && (_MSC_VER < 1900)
+#    define SPDLOG_NOEXCEPT _NOEXCEPT
+#    define SPDLOG_CONSTEXPR
+#    define SPDLOG_CONSTEXPR_FUNC inline
+#else
+#    define SPDLOG_NOEXCEPT noexcept
+#    define SPDLOG_CONSTEXPR constexpr
+#    if __cplusplus >= 201402L
+#        define SPDLOG_CONSTEXPR_FUNC constexpr
+#    else
+#        define SPDLOG_CONSTEXPR_FUNC inline
+#    endif
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#    define SPDLOG_DEPRECATED __attribute__((deprecated))
+#elif defined(_MSC_VER)
+#    define SPDLOG_DEPRECATED __declspec(deprecated)
+#else
+#    define SPDLOG_DEPRECATED
+#endif
+
+// 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
+
+#ifndef SPDLOG_FUNCTION
+#    define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__)
+#endif
+
+#ifdef SPDLOG_NO_EXCEPTIONS
+#    define SPDLOG_TRY
+#    define SPDLOG_THROW(ex)                                                                                                               \
+        do                                                                                                                                 \
+        {                                                                                                                                  \
+            printf("spdlog fatal error: %s\n", ex.what());                                                                                 \
+            std::abort();                                                                                                                  \
+        } while (0)
+#    define SPDLOG_CATCH_STD
+#else
+#    define SPDLOG_TRY try
+#    define SPDLOG_THROW(ex) throw(ex)
+#    define SPDLOG_CATCH_STD                                                                                                               \
+        catch (const std::exception &) {}
+#endif
+
+namespace spdlog {
+
+class formatter;
+
+namespace sinks {
+class sink;
+}
+
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+using filename_t = std::wstring;
+// allow macro expansion to occur in SPDLOG_FILENAME_T
+#    define SPDLOG_FILENAME_T_INNER(s) L##s
+#    define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s)
+#else
+using filename_t = std::string;
+#    define SPDLOG_FILENAME_T(s) s
+#endif
+
+using log_clock = std::chrono::system_clock;
+using sink_ptr = std::shared_ptr<sinks::sink>;
+using sinks_init_list = std::initializer_list<sink_ptr>;
+using err_handler = std::function<void(const std::string &err_msg)>;
+#ifdef SPDLOG_USE_STD_FORMAT
+namespace fmt_lib = std;
+
+using string_view_t = std::string_view;
+using memory_buf_t = std::string;
+
+template<typename... Args>
+#    if __cpp_lib_format >= 202207L
+using format_string_t = std::format_string<Args...>;
+#    else
+using format_string_t = std::string_view;
+#    endif
+
+template<class T, class Char = char>
+struct is_convertible_to_basic_format_string : std::integral_constant<bool, std::is_convertible<T, std::basic_string_view<Char>>::value>
+{};
+
+#    if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
+using wstring_view_t = std::wstring_view;
+using wmemory_buf_t = std::wstring;
+
+template<typename... Args>
+#        if __cpp_lib_format >= 202207L
+using wformat_string_t = std::wformat_string<Args...>;
+#        else
+using wformat_string_t = std::wstring_view;
+#        endif
+#    endif
+#    define SPDLOG_BUF_TO_STRING(x) x
+#else // use fmt lib instead of std::format
+namespace fmt_lib = fmt;
+
+using string_view_t = fmt::basic_string_view<char>;
+using memory_buf_t = fmt::basic_memory_buffer<char, 250>;
+
+template<typename... Args>
+using format_string_t = fmt::format_string<Args...>;
+
+template<class T>
+using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+// clang doesn't like SFINAE disabled constructor in std::is_convertible<> so have to repeat the condition from basic_format_string here,
+// in addition, fmt::basic_runtime<Char> is only convertible to basic_format_string<Char> but not basic_string_view<Char>
+template<class T, class Char = char>
+struct is_convertible_to_basic_format_string
+    : std::integral_constant<bool,
+          std::is_convertible<T, fmt::basic_string_view<Char>>::value || std::is_same<remove_cvref_t<T>, fmt::basic_runtime<Char>>::value>
+{};
+
+#    if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
+using wstring_view_t = fmt::basic_string_view<wchar_t>;
+using wmemory_buf_t = fmt::basic_memory_buffer<wchar_t, 250>;
+
+template<typename... Args>
+using wformat_string_t = fmt::wformat_string<Args...>;
+#    endif
+#    define SPDLOG_BUF_TO_STRING(x) fmt::to_string(x)
+#endif
+
+#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
+#    ifndef _WIN32
+#        error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
+#    endif // _WIN32
+#endif     // SPDLOG_WCHAR_TO_UTF8_SUPPORT
+
+template<class T>
+struct is_convertible_to_any_format_string : std::integral_constant<bool, is_convertible_to_basic_format_string<T, char>::value ||
+                                                                              is_convertible_to_basic_format_string<T, wchar_t>::value>
+{};
+
+#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 : int
+{
+    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,
+    n_levels
+};
+
+#define SPDLOG_LEVEL_NAME_TRACE spdlog::string_view_t("trace", 5)
+#define SPDLOG_LEVEL_NAME_DEBUG spdlog::string_view_t("debug", 5)
+#define SPDLOG_LEVEL_NAME_INFO spdlog::string_view_t("info", 4)
+#define SPDLOG_LEVEL_NAME_WARNING spdlog::string_view_t("warning", 7)
+#define SPDLOG_LEVEL_NAME_ERROR spdlog::string_view_t("error", 5)
+#define SPDLOG_LEVEL_NAME_CRITICAL spdlog::string_view_t("critical", 8)
+#define SPDLOG_LEVEL_NAME_OFF spdlog::string_view_t("off", 3)
+
+#if !defined(SPDLOG_LEVEL_NAMES)
+#    define SPDLOG_LEVEL_NAMES                                                                                                             \
+        {                                                                                                                                  \
+            SPDLOG_LEVEL_NAME_TRACE, SPDLOG_LEVEL_NAME_DEBUG, SPDLOG_LEVEL_NAME_INFO, SPDLOG_LEVEL_NAME_WARNING, SPDLOG_LEVEL_NAME_ERROR,  \
+                SPDLOG_LEVEL_NAME_CRITICAL, SPDLOG_LEVEL_NAME_OFF                                                                          \
+        }
+#endif
+
+#if !defined(SPDLOG_SHORT_LEVEL_NAMES)
+
+#    define SPDLOG_SHORT_LEVEL_NAMES                                                                                                       \
+        {                                                                                                                                  \
+            "T", "D", "I", "W", "E", "C", "O"                                                                                              \
+        }
+#endif
+
+SPDLOG_API const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
+SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
+SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
+
+} // namespace level
+
+//
+// Color mode used by sinks with color support.
+//
+enum class color_mode
+{
+    always,
+    automatic,
+    never
+};
+
+//
+// Pattern time - specific time getting to use for pattern_formatter.
+// local time by default
+//
+enum class pattern_time_type
+{
+    local, // log localtime
+    utc    // log utc
+};
+
+//
+// Log exception
+//
+class SPDLOG_API spdlog_ex : public std::exception
+{
+public:
+    explicit spdlog_ex(std::string msg);
+    spdlog_ex(const std::string &msg, int last_errno);
+    const char *what() const SPDLOG_NOEXCEPT override;
+
+private:
+    std::string msg_;
+};
+
+[[noreturn]] SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
+[[noreturn]] SPDLOG_API void throw_spdlog_ex(std::string msg);
+
+struct source_loc
+{
+    SPDLOG_CONSTEXPR source_loc() = default;
+    SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in)
+        : filename{filename_in}
+        , line{line_in}
+        , funcname{funcname_in}
+    {}
+
+    SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT
+    {
+        return line == 0;
+    }
+    const char *filename{nullptr};
+    int line{0};
+    const char *funcname{nullptr};
+};
+
+struct file_event_handlers
+{
+    file_event_handlers()
+        : before_open(nullptr)
+        , after_open(nullptr)
+        , before_close(nullptr)
+        , after_close(nullptr)
+    {}
+
+    std::function<void(const filename_t &filename)> before_open;
+    std::function<void(const filename_t &filename, std::FILE *file_stream)> after_open;
+    std::function<void(const filename_t &filename, std::FILE *file_stream)> before_close;
+    std::function<void(const filename_t &filename)> after_close;
+};
+
+namespace details {
+
+// to_string_view
+
+SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(const memory_buf_t &buf) SPDLOG_NOEXCEPT
+{
+    return spdlog::string_view_t{buf.data(), buf.size()};
+}
+
+SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(spdlog::string_view_t str) SPDLOG_NOEXCEPT
+{
+    return str;
+}
+
+#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
+SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(const wmemory_buf_t &buf) SPDLOG_NOEXCEPT
+{
+    return spdlog::wstring_view_t{buf.data(), buf.size()};
+}
+
+SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(spdlog::wstring_view_t str) SPDLOG_NOEXCEPT
+{
+    return str;
+}
+#endif
+
+#ifndef SPDLOG_USE_STD_FORMAT
+template<typename T, typename... Args>
+inline fmt::basic_string_view<T> to_string_view(fmt::basic_format_string<T, Args...> fmt)
+{
+    return fmt;
+}
+#elif __cpp_lib_format >= 202207L
+template<typename T, typename... Args>
+SPDLOG_CONSTEXPR_FUNC std::basic_string_view<T> to_string_view(std::basic_format_string<T, Args...> fmt) SPDLOG_NOEXCEPT
+{
+    return fmt.get();
+}
+#endif
+
+// make_unique support for pre c++14
+
+#if __cplusplus >= 201402L // C++14 and beyond
+using std::enable_if_t;
+using std::make_unique;
+#else
+template<bool B, class T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+template<typename T, typename... Args>
+std::unique_ptr<T> make_unique(Args &&... args)
+{
+    static_assert(!std::is_array<T>::value, "arrays not supported");
+    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+#endif
+
+// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
+template<typename T, typename U, enable_if_t<!std::is_same<T, U>::value, int> = 0>
+constexpr T conditional_static_cast(U value)
+{
+    return static_cast<T>(value);
+}
+
+template<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>
+constexpr T conditional_static_cast(U value)
+{
+    return value;
+}
+
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "common-inl.h"
+#endif
diff --git a/include/spdlog/details/backtracer-inl.h b/include/spdlog/details/backtracer-inl.h
new file mode 100644
index 000000000..2621c8f7d
--- /dev/null
+++ b/include/spdlog/details/backtracer-inl.h
@@ -0,0 +1,69 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/details/backtracer.h>
+#endif
+namespace spdlog {
+namespace details {
+SPDLOG_INLINE backtracer::backtracer(const backtracer &other)
+{
+    std::lock_guard<std::mutex> lock(other.mutex_);
+    enabled_ = other.enabled();
+    messages_ = other.messages_;
+}
+
+SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT
+{
+    std::lock_guard<std::mutex> lock(other.mutex_);
+    enabled_ = other.enabled();
+    messages_ = std::move(other.messages_);
+}
+
+SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other)
+{
+    std::lock_guard<std::mutex> lock(mutex_);
+    enabled_ = other.enabled();
+    messages_ = std::move(other.messages_);
+    return *this;
+}
+
+SPDLOG_INLINE void backtracer::enable(size_t size)
+{
+    std::lock_guard<std::mutex> lock{mutex_};
+    enabled_.store(true, std::memory_order_relaxed);
+    messages_ = circular_q<log_msg_buffer>{size};
+}
+
+SPDLOG_INLINE void backtracer::disable()
+{
+    std::lock_guard<std::mutex> lock{mutex_};
+    enabled_.store(false, std::memory_order_relaxed);
+}
+
+SPDLOG_INLINE bool backtracer::enabled() const
+{
+    return enabled_.load(std::memory_order_relaxed);
+}
+
+SPDLOG_INLINE void backtracer::push_back(const log_msg &msg)
+{
+    std::lock_guard<std::mutex> lock{mutex_};
+    messages_.push_back(log_msg_buffer{msg});
+}
+
+// pop all items in the q and apply the given fun on each of them.
+SPDLOG_INLINE void backtracer::foreach_pop(std::function<void(const details::log_msg &)> fun)
+{
+    std::lock_guard<std::mutex> lock{mutex_};
+    while (!messages_.empty())
+    {
+        auto &front_msg = messages_.front();
+        fun(front_msg);
+        messages_.pop_front();
+    }
+}
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/backtracer.h b/include/spdlog/details/backtracer.h
new file mode 100644
index 000000000..b336ee776
--- /dev/null
+++ b/include/spdlog/details/backtracer.h
@@ -0,0 +1,45 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/details/log_msg_buffer.h>
+#include <spdlog/details/circular_q.h>
+
+#include <atomic>
+#include <mutex>
+#include <functional>
+
+// Store log messages in circular buffer.
+// Useful for storing debug data in case of error/warning happens.
+
+namespace spdlog {
+namespace details {
+class SPDLOG_API backtracer
+{
+    mutable std::mutex mutex_;
+    std::atomic<bool> enabled_{false};
+    circular_q<log_msg_buffer> messages_;
+
+public:
+    backtracer() = default;
+    backtracer(const backtracer &other);
+
+    backtracer(backtracer &&other) SPDLOG_NOEXCEPT;
+    backtracer &operator=(backtracer other);
+
+    void enable(size_t size);
+    void disable();
+    bool enabled() const;
+    void push_back(const log_msg &msg);
+
+    // pop all items in the q and apply the given fun on each of them.
+    void foreach_pop(std::function<void(const details::log_msg &)> fun);
+};
+
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "backtracer-inl.h"
+#endif
diff --git a/include/spdlog/details/circular_q.h b/include/spdlog/details/circular_q.h
new file mode 100644
index 000000000..e4fd5fd4a
--- /dev/null
+++ b/include/spdlog/details/circular_q.h
@@ -0,0 +1,146 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+// circular q view of std::vector.
+#pragma once
+
+#include <vector>
+#include <cassert>
+
+namespace spdlog {
+namespace details {
+template<typename T>
+class circular_q
+{
+    size_t max_items_ = 0;
+    typename std::vector<T>::size_type head_ = 0;
+    typename std::vector<T>::size_type tail_ = 0;
+    size_t overrun_counter_ = 0;
+    std::vector<T> v_;
+
+public:
+    using value_type = T;
+
+    // empty ctor - create a disabled queue with no elements allocated at all
+    circular_q() = default;
+
+    explicit circular_q(size_t max_items)
+        : max_items_(max_items + 1) // one item is reserved as marker for full q
+        , v_(max_items_)
+    {}
+
+    circular_q(const circular_q &) = default;
+    circular_q &operator=(const circular_q &) = default;
+
+    // move cannot be default,
+    // since we need to reset head_, tail_, etc to zero in the moved object
+    circular_q(circular_q &&other) SPDLOG_NOEXCEPT
+    {
+        copy_moveable(std::move(other));
+    }
+
+    circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT
+    {
+        copy_moveable(std::move(other));
+        return *this;
+    }
+
+    // push back, overrun (oldest) item if no room left
+    void push_back(T &&item)
+    {
+        if (max_items_ > 0)
+        {
+            v_[tail_] = std::move(item);
+            tail_ = (tail_ + 1) % max_items_;
+
+            if (tail_ == head_) // overrun last item if full
+            {
+                head_ = (head_ + 1) % max_items_;
+                ++overrun_counter_;
+            }
+        }
+    }
+
+    // Return reference to the front item.
+    // If there are no elements in the container, the behavior is undefined.
+    const T &front() const
+    {
+        return v_[head_];
+    }
+
+    T &front()
+    {
+        return v_[head_];
+    }
+
+    // Return number of elements actually stored
+    size_t size() const
+    {
+        if (tail_ >= head_)
+        {
+            return tail_ - head_;
+        }
+        else
+        {
+            return max_items_ - (head_ - tail_);
+        }
+    }
+
+    // Return const reference to item by index.
+    // If index is out of range 0…size()-1, the behavior is undefined.
+    const T &at(size_t i) const
+    {
+        assert(i < size());
+        return v_[(head_ + i) % max_items_];
+    }
+
+    // Pop item from front.
+    // If there are no elements in the container, the behavior is undefined.
+    void pop_front()
+    {
+        head_ = (head_ + 1) % max_items_;
+    }
+
+    bool empty() const
+    {
+        return tail_ == head_;
+    }
+
+    bool full() const
+    {
+        // head is ahead of the tail by 1
+        if (max_items_ > 0)
+        {
+            return ((tail_ + 1) % max_items_) == head_;
+        }
+        return false;
+    }
+
+    size_t overrun_counter() const
+    {
+        return overrun_counter_;
+    }
+
+    void reset_overrun_counter()
+    {
+        overrun_counter_ = 0;
+    }
+
+private:
+    // copy from other&& and reset it to disabled state
+    void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT
+    {
+        max_items_ = other.max_items_;
+        head_ = other.head_;
+        tail_ = other.tail_;
+        overrun_counter_ = other.overrun_counter_;
+        v_ = std::move(other.v_);
+
+        // put &&other in disabled, but valid state
+        other.max_items_ = 0;
+        other.head_ = other.tail_ = 0;
+        other.overrun_counter_ = 0;
+    }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/console_globals.h b/include/spdlog/details/console_globals.h
new file mode 100644
index 000000000..665201dd2
--- /dev/null
+++ b/include/spdlog/details/console_globals.h
@@ -0,0 +1,32 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/details/null_mutex.h>
+#include <mutex>
+
+namespace spdlog {
+namespace details {
+
+struct console_mutex
+{
+    using mutex_t = std::mutex;
+    static mutex_t &mutex()
+    {
+        static mutex_t s_mutex;
+        return s_mutex;
+    }
+};
+
+struct console_nullmutex
+{
+    using mutex_t = null_mutex;
+    static mutex_t &mutex()
+    {
+        static mutex_t s_mutex;
+        return s_mutex;
+    }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/file_helper-inl.h b/include/spdlog/details/file_helper-inl.h
new file mode 100644
index 000000000..d4528711e
--- /dev/null
+++ b/include/spdlog/details/file_helper-inl.h
@@ -0,0 +1,172 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/details/file_helper.h>
+#endif
+
+#include <spdlog/details/os.h>
+#include <spdlog/common.h>
+
+#include <cerrno>
+#include <chrono>
+#include <cstdio>
+#include <string>
+#include <thread>
+#include <tuple>
+
+namespace spdlog {
+namespace details {
+
+SPDLOG_INLINE file_helper::file_helper(const file_event_handlers &event_handlers)
+    : event_handlers_(event_handlers)
+{}
+
+SPDLOG_INLINE file_helper::~file_helper()
+{
+    close();
+}
+
+SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
+{
+    close();
+    filename_ = fname;
+
+    auto *mode = SPDLOG_FILENAME_T("ab");
+    auto *trunc_mode = SPDLOG_FILENAME_T("wb");
+
+    if (event_handlers_.before_open)
+    {
+        event_handlers_.before_open(filename_);
+    }
+    for (int tries = 0; tries < open_tries_; ++tries)
+    {
+        // create containing folder if not exists already.
+        os::create_dir(os::dir_name(fname));
+        if (truncate)
+        {
+            // Truncate by opening-and-closing a tmp file in "wb" mode, always
+            // opening the actual log-we-write-to in "ab" mode, since that
+            // interacts more politely with eternal processes that might
+            // rotate/truncate the file underneath us.
+            std::FILE *tmp;
+            if (os::fopen_s(&tmp, fname, trunc_mode))
+            {
+                continue;
+            }
+            std::fclose(tmp);
+        }
+        if (!os::fopen_s(&fd_, fname, mode))
+        {
+            if (event_handlers_.after_open)
+            {
+                event_handlers_.after_open(filename_, fd_);
+            }
+            return;
+        }
+
+        details::os::sleep_for_millis(open_interval_);
+    }
+
+    throw_spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing", errno);
+}
+
+SPDLOG_INLINE void file_helper::reopen(bool truncate)
+{
+    if (filename_.empty())
+    {
+        throw_spdlog_ex("Failed re opening file - was not opened before");
+    }
+    this->open(filename_, truncate);
+}
+
+SPDLOG_INLINE void file_helper::flush()
+{
+    if (std::fflush(fd_) != 0)
+    {
+        throw_spdlog_ex("Failed flush to file " + os::filename_to_str(filename_), errno);
+    }
+}
+
+SPDLOG_INLINE void file_helper::close()
+{
+    if (fd_ != nullptr)
+    {
+        if (event_handlers_.before_close)
+        {
+            event_handlers_.before_close(filename_, fd_);
+        }
+
+        std::fclose(fd_);
+        fd_ = nullptr;
+
+        if (event_handlers_.after_close)
+        {
+            event_handlers_.after_close(filename_);
+        }
+    }
+}
+
+SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf)
+{
+    size_t msg_size = buf.size();
+    auto data = buf.data();
+    if (std::fwrite(data, 1, msg_size, fd_) != msg_size)
+    {
+        throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno);
+    }
+}
+
+SPDLOG_INLINE size_t file_helper::size() const
+{
+    if (fd_ == nullptr)
+    {
+        throw_spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_));
+    }
+    return os::filesize(fd_);
+}
+
+SPDLOG_INLINE const filename_t &file_helper::filename() const
+{
+    return filename_;
+}
+
+//
+// return file path and its extension:
+//
+// "mylog.txt" => ("mylog", ".txt")
+// "mylog" => ("mylog", "")
+// "mylog." => ("mylog.", "")
+// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
+//
+// the starting dot in filenames is ignored (hidden files):
+//
+// ".mylog" => (".mylog". "")
+// "my_folder/.mylog" => ("my_folder/.mylog", "")
+// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
+SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension(const filename_t &fname)
+{
+    auto ext_index = fname.rfind('.');
+
+    // no valid extension found - return whole path and empty string as
+    // extension
+    if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
+    {
+        return std::make_tuple(fname, filename_t());
+    }
+
+    // treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
+    auto folder_index = fname.find_last_of(details::os::folder_seps_filename);
+    if (folder_index != filename_t::npos && folder_index >= ext_index - 1)
+    {
+        return std::make_tuple(fname, filename_t());
+    }
+
+    // finally - return a valid base and extension tuple
+    return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
+}
+
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/file_helper.h b/include/spdlog/details/file_helper.h
new file mode 100644
index 000000000..0f5988b9e
--- /dev/null
+++ b/include/spdlog/details/file_helper.h
@@ -0,0 +1,61 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/common.h>
+#include <tuple>
+
+namespace spdlog {
+namespace details {
+
+// Helper class for file sinks.
+// When failing to open a file, retry several times(5) with a delay interval(10 ms).
+// Throw spdlog_ex exception on errors.
+
+class SPDLOG_API file_helper
+{
+public:
+    file_helper() = default;
+    explicit file_helper(const file_event_handlers &event_handlers);
+
+    file_helper(const file_helper &) = delete;
+    file_helper &operator=(const file_helper &) = delete;
+    ~file_helper();
+
+    void open(const filename_t &fname, bool truncate = false);
+    void reopen(bool truncate);
+    void flush();
+    void close();
+    void write(const memory_buf_t &buf);
+    size_t size() const;
+    const filename_t &filename() const;
+
+    //
+    // return file path and its extension:
+    //
+    // "mylog.txt" => ("mylog", ".txt")
+    // "mylog" => ("mylog", "")
+    // "mylog." => ("mylog.", "")
+    // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
+    //
+    // the starting dot in filenames is ignored (hidden files):
+    //
+    // ".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_extension(const filename_t &fname);
+
+private:
+    const int open_tries_ = 5;
+    const unsigned int open_interval_ = 10;
+    std::FILE *fd_{nullptr};
+    filename_t filename_;
+    file_event_handlers event_handlers_;
+};
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "file_helper-inl.h"
+#endif
diff --git a/include/spdlog/details/fmt_helper.h b/include/spdlog/details/fmt_helper.h
new file mode 100644
index 000000000..d98671808
--- /dev/null
+++ b/include/spdlog/details/fmt_helper.h
@@ -0,0 +1,164 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+#pragma once
+
+#include <chrono>
+#include <type_traits>
+#include <iterator>
+#include <spdlog/fmt/fmt.h>
+#include <spdlog/common.h>
+
+#ifdef SPDLOG_USE_STD_FORMAT
+#    include <charconv>
+#    include <limits>
+#endif
+
+// Some fmt helpers to efficiently format and pad ints and strings
+namespace spdlog {
+namespace details {
+namespace fmt_helper {
+
+inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest)
+{
+    auto *buf_ptr = view.data();
+    dest.append(buf_ptr, buf_ptr + view.size());
+}
+
+#ifdef SPDLOG_USE_STD_FORMAT
+template<typename T>
+inline void append_int(T n, memory_buf_t &dest)
+{
+    // Buffer should be large enough to hold all digits (digits10 + 1) and a sign
+    SPDLOG_CONSTEXPR const auto BUF_SIZE = std::numeric_limits<T>::digits10 + 2;
+    char buf[BUF_SIZE];
+
+    auto [ptr, ec] = std::to_chars(buf, buf + BUF_SIZE, n, 10);
+    if (ec == std::errc())
+    {
+        dest.append(buf, ptr);
+    }
+    else
+    {
+        throw_spdlog_ex("Failed to format int", static_cast<int>(ec));
+    }
+}
+#else
+template<typename T>
+inline void append_int(T n, memory_buf_t &dest)
+{
+    fmt::format_int i(n);
+    dest.append(i.data(), i.data() + i.size());
+}
+#endif
+
+template<typename T>
+SPDLOG_CONSTEXPR_FUNC unsigned int count_digits_fallback(T n)
+{
+    // taken from fmt: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/format.h#L899-L912
+    unsigned 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
+        // "Three Optimization Tips for C++". See speed-test for a comparison.
+        if (n < 10)
+            return count;
+        if (n < 100)
+            return count + 1;
+        if (n < 1000)
+            return count + 2;
+        if (n < 10000)
+            return count + 3;
+        n /= 10000u;
+        count += 4;
+    }
+}
+
+template<typename T>
+inline unsigned int count_digits(T n)
+{
+    using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
+#ifdef SPDLOG_USE_STD_FORMAT
+    return count_digits_fallback(static_cast<count_type>(n));
+#else
+    return static_cast<unsigned int>(fmt::
+// fmt 7.0.0 renamed the internal namespace to detail.
+// See: https://github.com/fmtlib/fmt/issues/1538
+#    if FMT_VERSION < 70000
+            internal
+#    else
+            detail
+#    endif
+        ::count_digits(static_cast<count_type>(n)));
+#endif
+}
+
+inline void pad2(int n, memory_buf_t &dest)
+{
+    if (n >= 0 && n < 100) // 0-99
+    {
+        dest.push_back(static_cast<char>('0' + n / 10));
+        dest.push_back(static_cast<char>('0' + n % 10));
+    }
+    else // unlikely, but just in case, let fmt deal with it
+    {
+        fmt_lib::format_to(std::back_inserter(dest), SPDLOG_FMT_STRING("{:02}"), n);
+    }
+}
+
+template<typename T>
+inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
+{
+    static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
+    for (auto digits = count_digits(n); digits < width; digits++)
+    {
+        dest.push_back('0');
+    }
+    append_int(n, dest);
+}
+
+template<typename T>
+inline void pad3(T n, memory_buf_t &dest)
+{
+    static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T");
+    if (n < 1000)
+    {
+        dest.push_back(static_cast<char>(n / 100 + '0'));
+        n = n % 100;
+        dest.push_back(static_cast<char>((n / 10) + '0'));
+        dest.push_back(static_cast<char>((n % 10) + '0'));
+    }
+    else
+    {
+        append_int(n, dest);
+    }
+}
+
+template<typename T>
+inline void pad6(T n, memory_buf_t &dest)
+{
+    pad_uint(n, 6, dest);
+}
+
+template<typename T>
+inline void pad9(T n, memory_buf_t &dest)
+{
+    pad_uint(n, 9, dest);
+}
+
+// return fraction of a second of the given time_point.
+// e.g.
+// fraction<std::milliseconds>(tp) -> will return the millis part of the second
+template<typename ToDuration>
+inline ToDuration time_fraction(log_clock::time_point tp)
+{
+    using std::chrono::duration_cast;
+    using std::chrono::seconds;
+    auto duration = tp.time_since_epoch();
+    auto secs = duration_cast<seconds>(duration);
+    return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
+}
+
+} // namespace fmt_helper
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/log_msg-inl.h b/include/spdlog/details/log_msg-inl.h
new file mode 100644
index 000000000..c6e8a7e04
--- /dev/null
+++ b/include/spdlog/details/log_msg-inl.h
@@ -0,0 +1,37 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/details/log_msg.h>
+#endif
+
+#include <spdlog/details/os.h>
+
+namespace spdlog {
+namespace details {
+
+SPDLOG_INLINE log_msg::log_msg(spdlog::log_clock::time_point log_time, spdlog::source_loc loc, string_view_t a_logger_name,
+    spdlog::level::level_enum lvl, spdlog::string_view_t msg)
+    : logger_name(a_logger_name)
+    , level(lvl)
+    , time(log_time)
+#ifndef SPDLOG_NO_THREAD_ID
+    , thread_id(os::thread_id())
+#endif
+    , source(loc)
+    , payload(msg)
+{}
+
+SPDLOG_INLINE log_msg::log_msg(
+    spdlog::source_loc loc, string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
+    : log_msg(os::now(), loc, a_logger_name, lvl, msg)
+{}
+
+SPDLOG_INLINE log_msg::log_msg(string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
+    : log_msg(os::now(), source_loc{}, a_logger_name, lvl, msg)
+{}
+
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/log_msg.h b/include/spdlog/details/log_msg.h
new file mode 100644
index 000000000..fed51abdf
--- /dev/null
+++ b/include/spdlog/details/log_msg.h
@@ -0,0 +1,37 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/common.h>
+#include <string>
+
+namespace spdlog {
+namespace details {
+struct SPDLOG_API log_msg
+{
+    log_msg() = default;
+    log_msg(log_clock::time_point log_time, source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
+    log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
+    log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
+    log_msg(const log_msg &other) = default;
+    log_msg &operator=(const log_msg &other) = default;
+
+    string_view_t logger_name;
+    level::level_enum level{level::off};
+    log_clock::time_point time;
+    size_t thread_id{0};
+
+    // 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;
+    string_view_t payload;
+};
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "log_msg-inl.h"
+#endif
diff --git a/include/spdlog/details/log_msg_buffer-inl.h b/include/spdlog/details/log_msg_buffer-inl.h
new file mode 100644
index 000000000..84d83dc2f
--- /dev/null
+++ b/include/spdlog/details/log_msg_buffer-inl.h
@@ -0,0 +1,58 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/details/log_msg_buffer.h>
+#endif
+
+namespace spdlog {
+namespace details {
+
+SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg)
+    : log_msg{orig_msg}
+{
+    buffer.append(logger_name.begin(), logger_name.end());
+    buffer.append(payload.begin(), payload.end());
+    update_string_views();
+}
+
+SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
+    : log_msg{other}
+{
+    buffer.append(logger_name.begin(), logger_name.end());
+    buffer.append(payload.begin(), payload.end());
+    update_string_views();
+}
+
+SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT : log_msg{other}, buffer{std::move(other.buffer)}
+{
+    update_string_views();
+}
+
+SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other)
+{
+    log_msg::operator=(other);
+    buffer.clear();
+    buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size());
+    update_string_views();
+    return *this;
+}
+
+SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT
+{
+    log_msg::operator=(other);
+    buffer = std::move(other.buffer);
+    update_string_views();
+    return *this;
+}
+
+SPDLOG_INLINE void log_msg_buffer::update_string_views()
+{
+    logger_name = string_view_t{buffer.data(), logger_name.size()};
+    payload = string_view_t{buffer.data() + logger_name.size(), payload.size()};
+}
+
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/log_msg_buffer.h b/include/spdlog/details/log_msg_buffer.h
new file mode 100644
index 000000000..810550656
--- /dev/null
+++ b/include/spdlog/details/log_msg_buffer.h
@@ -0,0 +1,33 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/details/log_msg.h>
+
+namespace spdlog {
+namespace details {
+
+// Extend log_msg with internal buffer to store its payload.
+// This is needed since log_msg holds string_views that points to stack data.
+
+class SPDLOG_API log_msg_buffer : public log_msg
+{
+    memory_buf_t buffer;
+    void update_string_views();
+
+public:
+    log_msg_buffer() = default;
+    explicit log_msg_buffer(const log_msg &orig_msg);
+    log_msg_buffer(const log_msg_buffer &other);
+    log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
+    log_msg_buffer &operator=(const log_msg_buffer &other);
+    log_msg_buffer &operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
+};
+
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "log_msg_buffer-inl.h"
+#endif
diff --git a/include/spdlog/details/mpmc_blocking_q.h b/include/spdlog/details/mpmc_blocking_q.h
new file mode 100644
index 000000000..785180c18
--- /dev/null
+++ b/include/spdlog/details/mpmc_blocking_q.h
@@ -0,0 +1,132 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+// multi producer-multi consumer blocking queue.
+// enqueue(..) - will block until room found to put the new message.
+// enqueue_nowait(..) - will return immediately with false if no room left in
+// the queue.
+// dequeue_for(..) - will block until the queue is not empty or timeout have
+// passed.
+
+#include <spdlog/details/circular_q.h>
+
+#include <condition_variable>
+#include <mutex>
+
+namespace spdlog {
+namespace details {
+
+template<typename T>
+class mpmc_blocking_queue
+{
+public:
+    using item_type = T;
+    explicit mpmc_blocking_queue(size_t max_items)
+        : q_(max_items)
+    {}
+
+#ifndef __MINGW32__
+    // try to enqueue and block if no room left
+    void enqueue(T &&item)
+    {
+        {
+            std::unique_lock<std::mutex> lock(queue_mutex_);
+            pop_cv_.wait(lock, [this] { return !this->q_.full(); });
+            q_.push_back(std::move(item));
+        }
+        push_cv_.notify_one();
+    }
+
+    // enqueue immediately. overrun oldest message in the queue if no room left.
+    void enqueue_nowait(T &&item)
+    {
+        {
+            std::unique_lock<std::mutex> lock(queue_mutex_);
+            q_.push_back(std::move(item));
+        }
+        push_cv_.notify_one();
+    }
+
+    // try to dequeue item. if no item found. wait up to timeout and try again
+    // Return true, if succeeded dequeue item, false otherwise
+    bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
+    {
+        {
+            std::unique_lock<std::mutex> lock(queue_mutex_);
+            if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
+            {
+                return false;
+            }
+            popped_item = std::move(q_.front());
+            q_.pop_front();
+        }
+        pop_cv_.notify_one();
+        return true;
+    }
+
+#else
+    // apparently mingw deadlocks if the mutex is released before cv.notify_one(),
+    // so release the mutex at the very end each function.
+
+    // try to enqueue and block if no room left
+    void enqueue(T &&item)
+    {
+        std::unique_lock<std::mutex> lock(queue_mutex_);
+        pop_cv_.wait(lock, [this] { return !this->q_.full(); });
+        q_.push_back(std::move(item));
+        push_cv_.notify_one();
+    }
+
+    // enqueue immediately. overrun oldest message in the queue if no room left.
+    void enqueue_nowait(T &&item)
+    {
+        std::unique_lock<std::mutex> lock(queue_mutex_);
+        q_.push_back(std::move(item));
+        push_cv_.notify_one();
+    }
+
+    // try to dequeue item. if no item found. wait up to timeout and try again
+    // Return true, if succeeded dequeue item, false otherwise
+    bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
+    {
+        std::unique_lock<std::mutex> lock(queue_mutex_);
+        if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
+        {
+            return false;
+        }
+        popped_item = std::move(q_.front());
+        q_.pop_front();
+        pop_cv_.notify_one();
+        return true;
+    }
+
+#endif
+
+    size_t overrun_counter()
+    {
+        std::unique_lock<std::mutex> lock(queue_mutex_);
+        return q_.overrun_counter();
+    }
+
+    size_t size()
+    {
+        std::unique_lock<std::mutex> lock(queue_mutex_);
+        return q_.size();
+    }
+
+    void reset_overrun_counter()
+    {
+        std::unique_lock<std::mutex> lock(queue_mutex_);
+        q_.reset_overrun_counter();
+    }
+
+private:
+    std::mutex queue_mutex_;
+    std::condition_variable push_cv_;
+    std::condition_variable pop_cv_;
+    spdlog::details::circular_q<T> q_;
+};
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/null_mutex.h b/include/spdlog/details/null_mutex.h
new file mode 100644
index 000000000..6550a7bf6
--- /dev/null
+++ b/include/spdlog/details/null_mutex.h
@@ -0,0 +1,45 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <atomic>
+#include <utility>
+// null, no cost dummy "mutex" and dummy "atomic" int
+
+namespace spdlog {
+namespace details {
+struct null_mutex
+{
+    void lock() const {}
+    void unlock() const {}
+};
+
+struct null_atomic_int
+{
+    int value;
+    null_atomic_int() = default;
+
+    explicit null_atomic_int(int new_value)
+        : value(new_value)
+    {}
+
+    int load(std::memory_order = std::memory_order_relaxed) const
+    {
+        return value;
+    }
+
+    void store(int new_value, std::memory_order = std::memory_order_relaxed)
+    {
+        value = new_value;
+    }
+
+    int exchange(int new_value, std::memory_order = std::memory_order_relaxed)
+    {
+        std::swap(new_value, value);
+        return new_value; // return value before the call
+    }
+};
+
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h
new file mode 100644
index 000000000..b9bab53ce
--- /dev/null
+++ b/include/spdlog/details/os-inl.h
@@ -0,0 +1,606 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/details/os.h>
+#endif
+
+#include <spdlog/common.h>
+
+#include <algorithm>
+#include <chrono>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <string>
+#include <thread>
+#include <array>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef _WIN32
+
+#    include <io.h>      // _get_osfhandle and _isatty support
+#    include <process.h> //  _get_pid support
+#    include <spdlog/details/windows_include.h>
+
+#    ifdef __MINGW32__
+#        include <share.h>
+#    endif
+
+#    if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
+#        include <limits>
+#    endif
+
+#    include <direct.h> // for _mkdir/_wmkdir
+
+#else // unix
+
+#    include <fcntl.h>
+#    include <unistd.h>
+
+#    ifdef __linux__
+#        include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
+
+#    elif defined(_AIX)
+#        include <pthread.h> // for pthread_getthrds_np
+
+#    elif defined(__DragonFly__) || defined(__FreeBSD__)
+#        include <pthread_np.h> // for pthread_getthreadid_np
+
+#    elif defined(__NetBSD__)
+#        include <lwp.h> // for _lwp_self
+
+#    elif defined(__sun)
+#        include <thread.h> // for thr_self
+#    endif
+
+#endif // unix
+
+#ifndef __has_feature          // Clang - feature checking macros.
+#    define __has_feature(x) 0 // Compatibility with non-clang compilers.
+#endif
+
+namespace spdlog {
+namespace details {
+namespace os {
+
+SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
+{
+
+#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
+    timespec ts;
+    ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
+    return std::chrono::time_point<log_clock, typename log_clock::duration>(
+        std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
+
+#else
+    return log_clock::now();
+#endif
+}
+SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
+{
+
+#ifdef _WIN32
+    std::tm tm;
+    ::localtime_s(&tm, &time_tt);
+#else
+    std::tm tm;
+    ::localtime_r(&time_tt, &tm);
+#endif
+    return tm;
+}
+
+SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT
+{
+    std::time_t now_t = ::time(nullptr);
+    return localtime(now_t);
+}
+
+SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
+{
+
+#ifdef _WIN32
+    std::tm tm;
+    ::gmtime_s(&tm, &time_tt);
+#else
+    std::tm tm;
+    ::gmtime_r(&time_tt, &tm);
+#endif
+    return tm;
+}
+
+SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
+{
+    std::time_t now_t = ::time(nullptr);
+    return gmtime(now_t);
+}
+
+// fopen_s on non windows for writing
+SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
+{
+#ifdef _WIN32
+#    ifdef SPDLOG_WCHAR_FILENAMES
+    *fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
+#    else
+    *fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
+#    endif
+#    if defined(SPDLOG_PREVENT_CHILD_FD)
+    if (*fp != nullptr)
+    {
+        auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
+        if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
+        {
+            ::fclose(*fp);
+            *fp = nullptr;
+        }
+    }
+#    endif
+#else // unix
+#    if defined(SPDLOG_PREVENT_CHILD_FD)
+    const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
+    const int fd = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
+    if (fd == -1)
+    {
+        return true;
+    }
+    *fp = ::fdopen(fd, mode.c_str());
+    if (*fp == nullptr)
+    {
+        ::close(fd);
+    }
+#    else
+    *fp = ::fopen((filename.c_str()), mode.c_str());
+#    endif
+#endif
+
+    return *fp == nullptr;
+}
+
+SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT
+{
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+    return ::_wremove(filename.c_str());
+#else
+    return std::remove(filename.c_str());
+#endif
+}
+
+SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT
+{
+    return path_exists(filename) ? remove(filename) : 0;
+}
+
+SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
+{
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+    return ::_wrename(filename1.c_str(), filename2.c_str());
+#else
+    return std::rename(filename1.c_str(), filename2.c_str());
+#endif
+}
+
+// Return true if path exists (file or directory)
+SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT
+{
+#ifdef _WIN32
+#    ifdef SPDLOG_WCHAR_FILENAMES
+    auto attribs = ::GetFileAttributesW(filename.c_str());
+#    else
+    auto attribs = ::GetFileAttributesA(filename.c_str());
+#    endif
+    return attribs != INVALID_FILE_ATTRIBUTES;
+#else // common linux/unix all have the stat system call
+    struct stat buffer;
+    return (::stat(filename.c_str(), &buffer) == 0);
+#endif
+}
+
+#ifdef _MSC_VER
+// avoid warning about unreachable statement at the end of filesize()
+#    pragma warning(push)
+#    pragma warning(disable : 4702)
+#endif
+
+// Return file size according to open FILE* object
+SPDLOG_INLINE size_t filesize(FILE *f)
+{
+    if (f == nullptr)
+    {
+        throw_spdlog_ex("Failed getting file size. fd is null");
+    }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+    int fd = ::_fileno(f);
+#    if defined(_WIN64) // 64 bits
+    __int64 ret = ::_filelengthi64(fd);
+    if (ret >= 0)
+    {
+        return static_cast<size_t>(ret);
+    }
+
+#    else // windows 32 bits
+    long ret = ::_filelength(fd);
+    if (ret >= 0)
+    {
+        return static_cast<size_t>(ret);
+    }
+#    endif
+
+#else // unix
+// OpenBSD and AIX doesn't compile with :: before the fileno(..)
+#    if defined(__OpenBSD__) || defined(_AIX)
+    int fd = fileno(f);
+#    else
+    int fd = ::fileno(f);
+#    endif
+// 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
+#    if (defined(__linux__) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64))
+    struct stat64 st;
+    if (::fstat64(fd, &st) == 0)
+    {
+        return static_cast<size_t>(st.st_size);
+    }
+#    else // other unix or linux 32 bits or cygwin
+    struct stat st;
+    if (::fstat(fd, &st) == 0)
+    {
+        return static_cast<size_t>(st.st_size);
+    }
+#    endif
+#endif
+    throw_spdlog_ex("Failed getting file size from fd", errno);
+    return 0; // will not be reached.
+}
+
+#ifdef _MSC_VER
+#    pragma warning(pop)
+#endif
+
+// Return utc offset in minutes or throw spdlog_ex on failure
+SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
+{
+
+#ifdef _WIN32
+#    if _WIN32_WINNT < _WIN32_WINNT_WS08
+    TIME_ZONE_INFORMATION tzinfo;
+    auto rv = ::GetTimeZoneInformation(&tzinfo);
+#    else
+    DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
+    auto rv = ::GetDynamicTimeZoneInformation(&tzinfo);
+#    endif
+    if (rv == TIME_ZONE_ID_INVALID)
+        throw_spdlog_ex("Failed getting timezone info. ", errno);
+
+    int offset = -tzinfo.Bias;
+    if (tm.tm_isdst)
+    {
+        offset -= tzinfo.DaylightBias;
+    }
+    else
+    {
+        offset -= tzinfo.StandardBias;
+    }
+    return offset;
+#else
+
+#    if defined(sun) || defined(__sun) || defined(_AIX) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE))
+    // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
+    struct helper
+    {
+        static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime())
+        {
+            int local_year = localtm.tm_year + (1900 - 1);
+            int gmt_year = gmtm.tm_year + (1900 - 1);
+
+            long int days = (
+                // difference in day of year
+                localtm.tm_yday -
+                gmtm.tm_yday
+
+                // + intervening leap days
+                + ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
+                ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
+
+                // + difference in years * 365 */
+                + static_cast<long int>(local_year - gmt_year) * 365);
+
+            long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
+            long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
+            long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
+
+            return secs;
+        }
+    };
+
+    auto offset_seconds = helper::calculate_gmt_offset(tm);
+#    else
+    auto offset_seconds = tm.tm_gmtoff;
+#    endif
+
+    return static_cast<int>(offset_seconds / 60);
+#endif
+}
+
+// Return current thread id as size_t
+// It exists because the std::this_thread::get_id() is much slower(especially
+// under VS 2013)
+SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT
+{
+#ifdef _WIN32
+    return static_cast<size_t>(::GetCurrentThreadId());
+#elif defined(__linux__)
+#    if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
+#        define SYS_gettid __NR_gettid
+#    endif
+    return static_cast<size_t>(::syscall(SYS_gettid));
+#elif defined(_AIX)
+    struct __pthrdsinfo buf;
+    int reg_size = 0;
+    pthread_t pt = pthread_self();
+    int retval = pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_TID, &buf, sizeof(buf), NULL, &reg_size);
+    int tid = (!retval) ? buf.__pi_tid : 0;
+    return static_cast<size_t>(tid);
+#elif defined(__DragonFly__) || defined(__FreeBSD__)
+    return static_cast<size_t>(::pthread_getthreadid_np());
+#elif defined(__NetBSD__)
+    return static_cast<size_t>(::_lwp_self());
+#elif defined(__OpenBSD__)
+    return static_cast<size_t>(::getthrid());
+#elif defined(__sun)
+    return static_cast<size_t>(::thr_self());
+#elif __APPLE__
+    uint64_t tid;
+    pthread_threadid_np(nullptr, &tid);
+    return static_cast<size_t>(tid);
+#else // Default to standard C++11 (other Unix)
+    return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
+#endif
+}
+
+// Return current thread id as size_t (from thread local storage)
+SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT
+{
+#if defined(SPDLOG_NO_TLS)
+    return _thread_id();
+#else // cache thread id in tls
+    static thread_local const size_t tid = _thread_id();
+    return tid;
+#endif
+}
+
+// This is avoid msvc issue in sleep_for that happens if the clock changes.
+// See https://github.com/gabime/spdlog/issues/609
+SPDLOG_INLINE void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT
+{
+#if defined(_WIN32)
+    ::Sleep(milliseconds);
+#else
+    std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
+#endif
+}
+
+// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
+{
+    memory_buf_t buf;
+    wstr_to_utf8buf(filename, buf);
+    return SPDLOG_BUF_TO_STRING(buf);
+}
+#else
+SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
+{
+    return filename;
+}
+#endif
+
+SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT
+{
+
+#ifdef _WIN32
+    return conditional_static_cast<int>(::GetCurrentProcessId());
+#else
+    return conditional_static_cast<int>(::getpid());
+#endif
+}
+
+// Determine if the terminal supports colors
+// Based on: https://github.com/agauniyal/rang/
+SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
+{
+#ifdef _WIN32
+    return true;
+#else
+
+    static const bool result = []() {
+        const char *env_colorterm_p = std::getenv("COLORTERM");
+        if (env_colorterm_p != nullptr)
+        {
+            return true;
+        }
+
+        static constexpr std::array<const char *, 16> terms = {{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux",
+            "msys", "putty", "rxvt", "screen", "vt100", "xterm", "alacritty", "vt102"}};
+
+        const char *env_term_p = std::getenv("TERM");
+        if (env_term_p == nullptr)
+        {
+            return false;
+        }
+
+        return std::any_of(terms.begin(), terms.end(), [&](const char *term) { return std::strstr(env_term_p, term) != nullptr; });
+    }();
+
+    return result;
+#endif
+}
+
+// Determine if the terminal attached
+// Source: https://github.com/agauniyal/rang/
+SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
+{
+
+#ifdef _WIN32
+    return ::_isatty(_fileno(file)) != 0;
+#else
+    return ::isatty(fileno(file)) != 0;
+#endif
+}
+
+#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
+SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
+{
+    if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 2 - 1)
+    {
+        throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
+    }
+
+    int wstr_size = static_cast<int>(wstr.size());
+    if (wstr_size == 0)
+    {
+        target.resize(0);
+        return;
+    }
+
+    int result_size = static_cast<int>(target.capacity());
+    if ((wstr_size + 1) * 2 > result_size)
+    {
+        result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
+    }
+
+    if (result_size > 0)
+    {
+        target.resize(result_size);
+        result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(), result_size, NULL, NULL);
+
+        if (result_size > 0)
+        {
+            target.resize(result_size);
+            return;
+        }
+    }
+
+    throw_spdlog_ex(fmt_lib::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
+}
+
+SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target)
+{
+    if (str.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) - 1)
+    {
+        throw_spdlog_ex("UTF-8 string is too big to be converted to UTF-16");
+    }
+
+    int str_size = static_cast<int>(str.size());
+    if (str_size == 0)
+    {
+        target.resize(0);
+        return;
+    }
+
+    int result_size = static_cast<int>(target.capacity());
+    if (str_size + 1 > result_size)
+    {
+        result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0);
+    }
+
+    if (result_size > 0)
+    {
+        target.resize(result_size);
+        result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, target.data(), result_size);
+
+        if (result_size > 0)
+        {
+            target.resize(result_size);
+            return;
+        }
+    }
+
+    throw_spdlog_ex(fmt_lib::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError()));
+}
+#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
+
+// return true on success
+static SPDLOG_INLINE bool mkdir_(const filename_t &path)
+{
+#ifdef _WIN32
+#    ifdef SPDLOG_WCHAR_FILENAMES
+    return ::_wmkdir(path.c_str()) == 0;
+#    else
+    return ::_mkdir(path.c_str()) == 0;
+#    endif
+#else
+    return ::mkdir(path.c_str(), mode_t(0755)) == 0;
+#endif
+}
+
+// create the given directory - and all directories leading to it
+// return true on success or if the directory already exists
+SPDLOG_INLINE bool create_dir(const filename_t &path)
+{
+    if (path_exists(path))
+    {
+        return true;
+    }
+
+    if (path.empty())
+    {
+        return false;
+    }
+
+    size_t search_offset = 0;
+    do
+    {
+        auto token_pos = path.find_first_of(folder_seps_filename, search_offset);
+        // treat the entire path as a folder if no folder separator not found
+        if (token_pos == filename_t::npos)
+        {
+            token_pos = path.size();
+        }
+
+        auto subdir = path.substr(0, token_pos);
+
+        if (!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir))
+        {
+            return false; // return error if failed creating dir
+        }
+        search_offset = token_pos + 1;
+    } while (search_offset < path.size());
+
+    return true;
+}
+
+// Return directory name from given path or empty string
+// "abc/file" => "abc"
+// "abc/" => "abc"
+// "abc" => ""
+// "abc///" => "abc//"
+SPDLOG_INLINE filename_t dir_name(const filename_t &path)
+{
+    auto pos = path.find_last_of(folder_seps_filename);
+    return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
+}
+
+std::string SPDLOG_INLINE getenv(const char *field)
+{
+
+#if defined(_MSC_VER)
+#    if defined(__cplusplus_winrt)
+    return std::string{}; // not supported under uwp
+#    else
+    size_t len = 0;
+    char buf[128];
+    bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
+    return ok ? buf : std::string{};
+#    endif
+#else // revert to getenv
+    char *buf = ::getenv(field);
+    return buf ? buf : std::string{};
+#endif
+}
+
+} // namespace os
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/os.h b/include/spdlog/details/os.h
new file mode 100644
index 000000000..b154bc473
--- /dev/null
+++ b/include/spdlog/details/os.h
@@ -0,0 +1,118 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/common.h>
+#include <ctime> // std::time_t
+
+namespace spdlog {
+namespace details {
+namespace os {
+
+SPDLOG_API spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
+
+SPDLOG_API std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
+
+SPDLOG_API std::tm localtime() SPDLOG_NOEXCEPT;
+
+SPDLOG_API std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
+
+SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
+
+// eol definition
+#if !defined(SPDLOG_EOL)
+#    ifdef _WIN32
+#        define SPDLOG_EOL "\r\n"
+#    else
+#        define SPDLOG_EOL "\n"
+#    endif
+#endif
+
+SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
+
+// folder separator
+#if !defined(SPDLOG_FOLDER_SEPS)
+#    ifdef _WIN32
+#        define SPDLOG_FOLDER_SEPS "\\/"
+#    else
+#        define SPDLOG_FOLDER_SEPS "/"
+#    endif
+#endif
+
+SPDLOG_CONSTEXPR static const char folder_seps[] = SPDLOG_FOLDER_SEPS;
+SPDLOG_CONSTEXPR static const filename_t::value_type folder_seps_filename[] = SPDLOG_FILENAME_T(SPDLOG_FOLDER_SEPS);
+
+// fopen_s on non windows for writing
+SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
+
+// Remove filename. return 0 on success
+SPDLOG_API int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
+
+// Remove file if exists. return 0 on success
+// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
+SPDLOG_API int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
+
+SPDLOG_API int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
+
+// Return if file exists.
+SPDLOG_API bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
+
+// Return file size according to open FILE* object
+SPDLOG_API size_t filesize(FILE *f);
+
+// Return utc offset in minutes or throw spdlog_ex on failure
+SPDLOG_API int utc_minutes_offset(const std::tm &tm = details::os::localtime());
+
+// Return current thread id as size_t
+// It exists because the std::this_thread::get_id() is much slower(especially
+// under VS 2013)
+SPDLOG_API size_t _thread_id() SPDLOG_NOEXCEPT;
+
+// Return current thread id as size_t (from thread local storage)
+SPDLOG_API size_t thread_id() SPDLOG_NOEXCEPT;
+
+// This is avoid msvc issue in sleep_for that happens if the clock changes.
+// See https://github.com/gabime/spdlog/issues/609
+SPDLOG_API void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT;
+
+SPDLOG_API std::string filename_to_str(const filename_t &filename);
+
+SPDLOG_API int pid() SPDLOG_NOEXCEPT;
+
+// Determine if the terminal supports colors
+// Source: https://github.com/agauniyal/rang/
+SPDLOG_API bool is_color_terminal() SPDLOG_NOEXCEPT;
+
+// Determine if the terminal attached
+// Source: https://github.com/agauniyal/rang/
+SPDLOG_API bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
+
+#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
+SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
+
+SPDLOG_API void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target);
+#endif
+
+// Return directory name from given path or empty string
+// "abc/file" => "abc"
+// "abc/" => "abc"
+// "abc" => ""
+// "abc///" => "abc//"
+SPDLOG_API filename_t dir_name(const filename_t &path);
+
+// Create a dir from the given path.
+// Return true if succeeded or if this dir already exists.
+SPDLOG_API bool create_dir(const filename_t &path);
+
+// non thread safe, cross platform getenv/getenv_s
+// return empty string if field not found
+SPDLOG_API std::string getenv(const char *field);
+
+} // namespace os
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "os-inl.h"
+#endif
diff --git a/include/spdlog/details/periodic_worker-inl.h b/include/spdlog/details/periodic_worker-inl.h
new file mode 100644
index 000000000..520a2b339
--- /dev/null
+++ b/include/spdlog/details/periodic_worker-inl.h
@@ -0,0 +1,28 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/details/periodic_worker.h>
+#endif
+
+namespace spdlog {
+namespace details {
+
+// stop the worker thread and join it
+SPDLOG_INLINE periodic_worker::~periodic_worker()
+{
+    if (worker_thread_.joinable())
+    {
+        {
+            std::lock_guard<std::mutex> lock(mutex_);
+            active_ = false;
+        }
+        cv_.notify_one();
+        worker_thread_.join();
+    }
+}
+
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/periodic_worker.h b/include/spdlog/details/periodic_worker.h
new file mode 100644
index 000000000..d7d69b28c
--- /dev/null
+++ b/include/spdlog/details/periodic_worker.h
@@ -0,0 +1,60 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+// periodic worker thread - periodically executes the given callback function.
+//
+// RAII over the owned thread:
+//    creates the thread on construction.
+//    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>
+#include <functional>
+#include <mutex>
+#include <thread>
+namespace spdlog {
+namespace details {
+
+class SPDLOG_API periodic_worker
+{
+public:
+    template<typename Rep, typename Period>
+    periodic_worker(const std::function<void()> &callback_fun, std::chrono::duration<Rep, Period> interval)
+    {
+        active_ = (interval > std::chrono::duration<Rep, Period>::zero());
+        if (!active_)
+        {
+            return;
+        }
+
+        worker_thread_ = std::thread([this, callback_fun, interval]() {
+            for (;;)
+            {
+                std::unique_lock<std::mutex> lock(this->mutex_);
+                if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; }))
+                {
+                    return; // active_ == false, so exit this thread
+                }
+                callback_fun();
+            }
+        });
+    }
+    periodic_worker(const periodic_worker &) = delete;
+    periodic_worker &operator=(const periodic_worker &) = delete;
+    // stop the worker thread and join it
+    ~periodic_worker();
+
+private:
+    bool active_;
+    std::thread worker_thread_;
+    std::mutex mutex_;
+    std::condition_variable cv_;
+};
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "periodic_worker-inl.h"
+#endif
diff --git a/include/spdlog/details/registry-inl.h b/include/spdlog/details/registry-inl.h
new file mode 100644
index 000000000..e6ecc9b08
--- /dev/null
+++ b/include/spdlog/details/registry-inl.h
@@ -0,0 +1,306 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/details/registry.h>
+#endif
+
+#include <spdlog/common.h>
+#include <spdlog/details/periodic_worker.h>
+#include <spdlog/logger.h>
+#include <spdlog/pattern_formatter.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>
+#include <string>
+#include <unordered_map>
+
+namespace spdlog {
+namespace details {
+
+SPDLOG_INLINE registry::registry()
+    : 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
+}
+
+SPDLOG_INLINE registry::~registry() = default;
+
+SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger)
+{
+    std::lock_guard<std::mutex> lock(logger_map_mutex_);
+    register_logger_(std::move(new_logger));
+}
+
+SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger)
+{
+    std::lock_guard<std::mutex> lock(logger_map_mutex_);
+    new_logger->set_formatter(formatter_->clone());
+
+    if (err_handler_)
+    {
+        new_logger->set_error_handler(err_handler_);
+    }
+
+    // set new level according to previously configured level or default level
+    auto it = log_levels_.find(new_logger->name());
+    auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
+    new_logger->set_level(new_level);
+
+    new_logger->flush_on(flush_level_);
+
+    if (backtrace_n_messages_ > 0)
+    {
+        new_logger->enable_backtrace(backtrace_n_messages_);
+    }
+
+    if (automatic_registration_)
+    {
+        register_logger_(std::move(new_logger));
+    }
+}
+
+SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_name)
+{
+    std::lock_guard<std::mutex> lock(logger_map_mutex_);
+    auto found = loggers_.find(logger_name);
+    return found == loggers_.end() ? nullptr : found->second;
+}
+
+SPDLOG_INLINE std::shared_ptr<logger> registry::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.
+SPDLOG_INLINE logger *registry::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.
+SPDLOG_INLINE void registry::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);
+}
+
+SPDLOG_INLINE void registry::set_tp(std::shared_ptr<thread_pool> tp)
+{
+    std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
+    tp_ = std::move(tp);
+}
+
+SPDLOG_INLINE std::shared_ptr<thread_pool> registry::get_tp()
+{
+    std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
+    return tp_;
+}
+
+// Set global formatter. Each sink in each logger will get a clone of this object
+SPDLOG_INLINE void registry::set_formatter(std::unique_ptr<formatter> formatter)
+{
+    std::lock_guard<std::mutex> lock(logger_map_mutex_);
+    formatter_ = std::move(formatter);
+    for (auto &l : loggers_)
+    {
+        l.second->set_formatter(formatter_->clone());
+    }
+}
+
+SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages)
+{
+    std::lock_guard<std::mutex> lock(logger_map_mutex_);
+    backtrace_n_messages_ = n_messages;
+
+    for (auto &l : loggers_)
+    {
+        l.second->enable_backtrace(n_messages);
+    }
+}
+
+SPDLOG_INLINE void registry::disable_backtrace()
+{
+    std::lock_guard<std::mutex> lock(logger_map_mutex_);
+    backtrace_n_messages_ = 0;
+    for (auto &l : loggers_)
+    {
+        l.second->disable_backtrace();
+    }
+}
+
+SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
+{
+    std::lock_guard<std::mutex> lock(logger_map_mutex_);
+    for (auto &l : loggers_)
+    {
+        l.second->set_level(log_level);
+    }
+    global_log_level_ = log_level;
+}
+
+SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
+{
+    std::lock_guard<std::mutex> lock(logger_map_mutex_);
+    for (auto &l : loggers_)
+    {
+        l.second->flush_on(log_level);
+    }
+    flush_level_ = log_level;
+}
+
+SPDLOG_INLINE void registry::set_error_handler(err_handler handler)
+{
+    std::lock_guard<std::mutex> lock(logger_map_mutex_);
+    for (auto &l : loggers_)
+    {
+        l.second->set_error_handler(handler);
+    }
+    err_handler_ = std::move(handler);
+}
+
+SPDLOG_INLINE void registry::apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun)
+{
+    std::lock_guard<std::mutex> lock(logger_map_mutex_);
+    for (auto &l : loggers_)
+    {
+        fun(l.second);
+    }
+}
+
+SPDLOG_INLINE void registry::flush_all()
+{
+    std::lock_guard<std::mutex> lock(logger_map_mutex_);
+    for (auto &l : loggers_)
+    {
+        l.second->flush();
+    }
+}
+
+SPDLOG_INLINE void registry::drop(const std::string &logger_name)
+{
+    std::lock_guard<std::mutex> lock(logger_map_mutex_);
+    loggers_.erase(logger_name);
+    if (default_logger_ && default_logger_->name() == logger_name)
+    {
+        default_logger_.reset();
+    }
+}
+
+SPDLOG_INLINE void registry::drop_all()
+{
+    std::lock_guard<std::mutex> lock(logger_map_mutex_);
+    loggers_.clear();
+    default_logger_.reset();
+}
+
+// clean all resources and threads started by the registry
+SPDLOG_INLINE void registry::shutdown()
+{
+    {
+        std::lock_guard<std::mutex> lock(flusher_mutex_);
+        periodic_flusher_.reset();
+    }
+
+    drop_all();
+
+    {
+        std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
+        tp_.reset();
+    }
+}
+
+SPDLOG_INLINE std::recursive_mutex &registry::tp_mutex()
+{
+    return tp_mutex_;
+}
+
+SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registration)
+{
+    std::lock_guard<std::mutex> lock(logger_map_mutex_);
+    automatic_registration_ = automatic_registration;
+}
+
+SPDLOG_INLINE void registry::set_levels(log_levels levels, level::level_enum *global_level)
+{
+    std::lock_guard<std::mutex> lock(logger_map_mutex_);
+    log_levels_ = std::move(levels);
+    auto global_level_requested = global_level != nullptr;
+    global_log_level_ = global_level_requested ? *global_level : global_log_level_;
+
+    for (auto &logger : loggers_)
+    {
+        auto logger_entry = log_levels_.find(logger.first);
+        if (logger_entry != log_levels_.end())
+        {
+            logger.second->set_level(logger_entry->second);
+        }
+        else if (global_level_requested)
+        {
+            logger.second->set_level(*global_level);
+        }
+    }
+}
+
+SPDLOG_INLINE registry &registry::instance()
+{
+    static registry s_instance;
+    return s_instance;
+}
+
+SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name)
+{
+    if (loggers_.find(logger_name) != loggers_.end())
+    {
+        throw_spdlog_ex("logger with name '" + logger_name + "' already exists");
+    }
+}
+
+SPDLOG_INLINE void registry::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);
+}
+
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/registry.h b/include/spdlog/details/registry.h
new file mode 100644
index 000000000..6a4a5abce
--- /dev/null
+++ b/include/spdlog/details/registry.h
@@ -0,0 +1,121 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+// Loggers registry of unique name->logger pointer
+// An attempt to create a logger with an already existing name will result with spdlog_ex exception.
+// If user requests a non existing logger, nullptr will be returned
+// This class is thread safe
+
+#include <spdlog/common.h>
+#include <spdlog/details/periodic_worker.h>
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <mutex>
+
+namespace spdlog {
+class logger;
+
+namespace details {
+class thread_pool;
+
+class SPDLOG_API registry
+{
+public:
+    using log_levels = std::unordered_map<std::string, level::level_enum>;
+    registry(const registry &) = delete;
+    registry &operator=(const registry &) = delete;
+
+    void register_logger(std::shared_ptr<logger> new_logger);
+    void initialize_logger(std::shared_ptr<logger> new_logger);
+    std::shared_ptr<logger> get(const std::string &logger_name);
+    std::shared_ptr<logger> 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();
+
+    // 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);
+
+    void set_tp(std::shared_ptr<thread_pool> tp);
+
+    std::shared_ptr<thread_pool> get_tp();
+
+    // Set global formatter. Each sink in each logger will get a clone of this object
+    void set_formatter(std::unique_ptr<formatter> formatter);
+
+    void enable_backtrace(size_t n_messages);
+
+    void disable_backtrace();
+
+    void set_level(level::level_enum log_level);
+
+    void flush_on(level::level_enum log_level);
+
+    template<typename Rep, typename Period>
+    void flush_every(std::chrono::duration<Rep, Period> interval)
+    {
+        std::lock_guard<std::mutex> lock(flusher_mutex_);
+        auto clbk = [this]() { this->flush_all(); };
+        periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
+    }
+
+    void set_error_handler(err_handler handler);
+
+    void apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun);
+
+    void flush_all();
+
+    void drop(const std::string &logger_name);
+
+    void drop_all();
+
+    // clean all resources and threads started by the registry
+    void shutdown();
+
+    std::recursive_mutex &tp_mutex();
+
+    void set_automatic_registration(bool automatic_registration);
+
+    // set levels for all existing/future loggers. global_level can be null if should not set.
+    void set_levels(log_levels levels, level::level_enum *global_level);
+
+    static registry &instance();
+
+private:
+    registry();
+    ~registry();
+
+    void throw_if_exists_(const std::string &logger_name);
+    void register_logger_(std::shared_ptr<logger> new_logger);
+    bool set_level_from_cfg_(logger *logger);
+    std::mutex logger_map_mutex_, flusher_mutex_;
+    std::recursive_mutex tp_mutex_;
+    std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
+    log_levels log_levels_;
+    std::unique_ptr<formatter> formatter_;
+    spdlog::level::level_enum global_log_level_ = level::info;
+    level::level_enum flush_level_ = level::off;
+    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;
+    size_t backtrace_n_messages_ = 0;
+};
+
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "registry-inl.h"
+#endif
diff --git a/include/spdlog/details/synchronous_factory.h b/include/spdlog/details/synchronous_factory.h
new file mode 100644
index 000000000..1f729ab93
--- /dev/null
+++ b/include/spdlog/details/synchronous_factory.h
@@ -0,0 +1,24 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include "registry.h"
+
+namespace spdlog {
+
+// Default logger factory-  creates synchronous loggers
+class logger;
+
+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<spdlog::logger>(std::move(logger_name), std::move(sink));
+        details::registry::instance().initialize_logger(new_logger);
+        return new_logger;
+    }
+};
+} // namespace spdlog
diff --git a/include/spdlog/details/tcp_client-windows.h b/include/spdlog/details/tcp_client-windows.h
new file mode 100644
index 000000000..9d3647aaa
--- /dev/null
+++ b/include/spdlog/details/tcp_client-windows.h
@@ -0,0 +1,160 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN
+// tcp client helper
+#include <spdlog/common.h>
+#include <spdlog/details/os.h>
+
+#include <winsock2.h>
+#include <windows.h>
+#include <ws2tcpip.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string>
+
+#pragma comment(lib, "Ws2_32.lib")
+#pragma comment(lib, "Mswsock.lib")
+#pragma comment(lib, "AdvApi32.lib")
+
+namespace spdlog {
+namespace details {
+class tcp_client
+{
+    SOCKET socket_ = INVALID_SOCKET;
+
+    static void init_winsock_()
+    {
+        WSADATA wsaData;
+        auto rv = WSAStartup(MAKEWORD(2, 2), &wsaData);
+        if (rv != 0)
+        {
+            throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
+        }
+    }
+
+    static void throw_winsock_error_(const std::string &msg, int last_error)
+    {
+        char buf[512];
+        ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error,
+            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL);
+
+        throw_spdlog_ex(fmt_lib::format("tcp_sink - {}: {}", msg, buf));
+    }
+
+public:
+    tcp_client()
+    {
+        init_winsock_();
+    }
+
+    ~tcp_client()
+    {
+        close();
+        ::WSACleanup();
+    }
+
+    bool is_connected() const
+    {
+        return socket_ != INVALID_SOCKET;
+    }
+
+    void close()
+    {
+        ::closesocket(socket_);
+        socket_ = INVALID_SOCKET;
+    }
+
+    SOCKET fd() const
+    {
+        return socket_;
+    }
+
+    // try to connect or throw on failure
+    void connect(const std::string &host, int port)
+    {
+        if (is_connected())
+        {
+            close();
+        }
+        struct addrinfo hints
+        {};
+        ZeroMemory(&hints, sizeof(hints));
+
+        hints.ai_family = AF_INET;       // IPv4
+        hints.ai_socktype = SOCK_STREAM; // TCP
+        hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
+        hints.ai_protocol = 0;
+
+        auto port_str = std::to_string(port);
+        struct addrinfo *addrinfo_result;
+        auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
+        int last_error = 0;
+        if (rv != 0)
+        {
+            last_error = ::WSAGetLastError();
+            WSACleanup();
+            throw_winsock_error_("getaddrinfo failed", last_error);
+        }
+
+        // Try each address until we successfully connect(2).
+
+        for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
+        {
+            socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+            if (socket_ == INVALID_SOCKET)
+            {
+                last_error = ::WSAGetLastError();
+                WSACleanup();
+                continue;
+            }
+            if (::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen) == 0)
+            {
+                break;
+            }
+            else
+            {
+                last_error = ::WSAGetLastError();
+                close();
+            }
+        }
+        ::freeaddrinfo(addrinfo_result);
+        if (socket_ == INVALID_SOCKET)
+        {
+            WSACleanup();
+            throw_winsock_error_("connect failed", last_error);
+        }
+
+        // set TCP_NODELAY
+        int enable_flag = 1;
+        ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
+    }
+
+    // Send exactly n_bytes of the given data.
+    // On error close the connection and throw.
+    void send(const char *data, size_t n_bytes)
+    {
+        size_t bytes_sent = 0;
+        while (bytes_sent < n_bytes)
+        {
+            const int send_flags = 0;
+            auto write_result = ::send(socket_, data + bytes_sent, (int)(n_bytes - bytes_sent), send_flags);
+            if (write_result == SOCKET_ERROR)
+            {
+                int last_error = ::WSAGetLastError();
+                close();
+                throw_winsock_error_("send failed", last_error);
+            }
+
+            if (write_result == 0) // (probably should not happen but in any case..)
+            {
+                break;
+            }
+            bytes_sent += static_cast<size_t>(write_result);
+        }
+    }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/tcp_client.h b/include/spdlog/details/tcp_client.h
new file mode 100644
index 000000000..0daff0eb4
--- /dev/null
+++ b/include/spdlog/details/tcp_client.h
@@ -0,0 +1,145 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifdef _WIN32
+#    error include tcp_client-windows.h instead
+#endif
+
+// tcp client helper
+#include <spdlog/common.h>
+#include <spdlog/details/os.h>
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/tcp.h>
+
+#include <string>
+
+namespace spdlog {
+namespace details {
+class tcp_client
+{
+    int socket_ = -1;
+
+public:
+    bool is_connected() const
+    {
+        return socket_ != -1;
+    }
+
+    void close()
+    {
+        if (is_connected())
+        {
+            ::close(socket_);
+            socket_ = -1;
+        }
+    }
+
+    int fd() const
+    {
+        return socket_;
+    }
+
+    ~tcp_client()
+    {
+        close();
+    }
+
+    // try to connect or throw on failure
+    void connect(const std::string &host, int port)
+    {
+        close();
+        struct addrinfo hints
+        {};
+        memset(&hints, 0, sizeof(struct addrinfo));
+        hints.ai_family = AF_INET;       // IPv4
+        hints.ai_socktype = SOCK_STREAM; // TCP
+        hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
+        hints.ai_protocol = 0;
+
+        auto port_str = std::to_string(port);
+        struct addrinfo *addrinfo_result;
+        auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
+        if (rv != 0)
+        {
+            throw_spdlog_ex(fmt_lib::format("::getaddrinfo failed: {}", gai_strerror(rv)));
+        }
+
+        // Try each address until we successfully connect(2).
+        int last_errno = 0;
+        for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
+        {
+#if defined(SOCK_CLOEXEC)
+            const int flags = SOCK_CLOEXEC;
+#else
+            const int flags = 0;
+#endif
+            socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
+            if (socket_ == -1)
+            {
+                last_errno = errno;
+                continue;
+            }
+            rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
+            if (rv == 0)
+            {
+                break;
+            }
+            last_errno = errno;
+            ::close(socket_);
+            socket_ = -1;
+        }
+        ::freeaddrinfo(addrinfo_result);
+        if (socket_ == -1)
+        {
+            throw_spdlog_ex("::connect failed", last_errno);
+        }
+
+        // set TCP_NODELAY
+        int enable_flag = 1;
+        ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
+
+        // prevent sigpipe on systems where MSG_NOSIGNAL is not available
+#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
+        ::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
+#endif
+
+#if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
+#    error "tcp_sink would raise SIGPIPE since niether SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
+#endif
+    }
+
+    // Send exactly n_bytes of the given data.
+    // On error close the connection and throw.
+    void send(const char *data, size_t n_bytes)
+    {
+        size_t bytes_sent = 0;
+        while (bytes_sent < n_bytes)
+        {
+#if defined(MSG_NOSIGNAL)
+            const int send_flags = MSG_NOSIGNAL;
+#else
+            const int send_flags = 0;
+#endif
+            auto write_result = ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
+            if (write_result < 0)
+            {
+                close();
+                throw_spdlog_ex("write(2) failed", errno);
+            }
+
+            if (write_result == 0) // (probably should not happen but in any case..)
+            {
+                break;
+            }
+            bytes_sent += static_cast<size_t>(write_result);
+        }
+    }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/thread_pool-inl.h b/include/spdlog/details/thread_pool-inl.h
new file mode 100644
index 000000000..369f30fe0
--- /dev/null
+++ b/include/spdlog/details/thread_pool-inl.h
@@ -0,0 +1,141 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/details/thread_pool.h>
+#endif
+
+#include <spdlog/common.h>
+#include <cassert>
+
+namespace spdlog {
+namespace details {
+
+SPDLOG_INLINE thread_pool::thread_pool(
+    size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start, std::function<void()> on_thread_stop)
+    : q_(q_max_items)
+{
+    if (threads_n == 0 || threads_n > 1000)
+    {
+        throw_spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid "
+                        "range is 1-1000)");
+    }
+    for (size_t i = 0; i < threads_n; i++)
+    {
+        threads_.emplace_back([this, on_thread_start, on_thread_stop] {
+            on_thread_start();
+            this->thread_pool::worker_loop_();
+            on_thread_stop();
+        });
+    }
+}
+
+SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start)
+    : thread_pool(q_max_items, threads_n, on_thread_start, [] {})
+{}
+
+SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n)
+    : thread_pool(
+          q_max_items, threads_n, [] {}, [] {})
+{}
+
+// message all threads to terminate gracefully join them
+SPDLOG_INLINE thread_pool::~thread_pool()
+{
+    SPDLOG_TRY
+    {
+        for (size_t i = 0; i < threads_.size(); i++)
+        {
+            post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
+        }
+
+        for (auto &t : threads_)
+        {
+            t.join();
+        }
+    }
+    SPDLOG_CATCH_STD
+}
+
+void SPDLOG_INLINE thread_pool::post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy)
+{
+    async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
+    post_async_msg_(std::move(async_m), overflow_policy);
+}
+
+void SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy)
+{
+    post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
+}
+
+size_t SPDLOG_INLINE thread_pool::overrun_counter()
+{
+    return q_.overrun_counter();
+}
+
+void SPDLOG_INLINE thread_pool::reset_overrun_counter()
+{
+    q_.reset_overrun_counter();
+}
+
+size_t SPDLOG_INLINE thread_pool::queue_size()
+{
+    return q_.size();
+}
+
+void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
+{
+    if (overflow_policy == async_overflow_policy::block)
+    {
+        q_.enqueue(std::move(new_msg));
+    }
+    else
+    {
+        q_.enqueue_nowait(std::move(new_msg));
+    }
+}
+
+void SPDLOG_INLINE thread_pool::worker_loop_()
+{
+    while (process_next_msg_()) {}
+}
+
+// process next message in the queue
+// return true if this thread should still be active (while no terminate msg
+// was received)
+bool SPDLOG_INLINE thread_pool::process_next_msg_()
+{
+    async_msg incoming_async_msg;
+    bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10));
+    if (!dequeued)
+    {
+        return true;
+    }
+
+    switch (incoming_async_msg.msg_type)
+    {
+    case async_msg_type::log: {
+        incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg);
+        return true;
+    }
+    case async_msg_type::flush: {
+        incoming_async_msg.worker_ptr->backend_flush_();
+        return true;
+    }
+
+    case async_msg_type::terminate: {
+        return false;
+    }
+
+    default: {
+        assert(false);
+    }
+    }
+
+    return true;
+}
+
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/thread_pool.h b/include/spdlog/details/thread_pool.h
new file mode 100644
index 000000000..52c569b80
--- /dev/null
+++ b/include/spdlog/details/thread_pool.h
@@ -0,0 +1,122 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/details/log_msg_buffer.h>
+#include <spdlog/details/mpmc_blocking_q.h>
+#include <spdlog/details/os.h>
+
+#include <chrono>
+#include <memory>
+#include <thread>
+#include <vector>
+#include <functional>
+
+namespace spdlog {
+class async_logger;
+
+namespace details {
+
+using async_logger_ptr = std::shared_ptr<spdlog::async_logger>;
+
+enum class async_msg_type
+{
+    log,
+    flush,
+    terminate
+};
+
+// Async msg to move to/from the queue
+// Movable only. should never be copied
+struct async_msg : log_msg_buffer
+{
+    async_msg_type msg_type{async_msg_type::log};
+    async_logger_ptr worker_ptr;
+
+    async_msg() = default;
+    ~async_msg() = default;
+
+    // should only be moved in or out of the queue..
+    async_msg(const async_msg &) = delete;
+
+// support for vs2013 move
+#if defined(_MSC_VER) && _MSC_VER <= 1800
+    async_msg(async_msg &&other)
+        : log_msg_buffer(std::move(other))
+        , msg_type(other.msg_type)
+        , worker_ptr(std::move(other.worker_ptr))
+    {}
+
+    async_msg &operator=(async_msg &&other)
+    {
+        *static_cast<log_msg_buffer *>(this) = std::move(other);
+        msg_type = other.msg_type;
+        worker_ptr = std::move(other.worker_ptr);
+        return *this;
+    }
+#else // (_MSC_VER) && _MSC_VER <= 1800
+    async_msg(async_msg &&) = default;
+    async_msg &operator=(async_msg &&) = default;
+#endif
+
+    // construct from log_msg with given type
+    async_msg(async_logger_ptr &&worker, async_msg_type the_type, const details::log_msg &m)
+        : log_msg_buffer{m}
+        , msg_type{the_type}
+        , worker_ptr{std::move(worker)}
+    {}
+
+    async_msg(async_logger_ptr &&worker, async_msg_type the_type)
+        : log_msg_buffer{}
+        , msg_type{the_type}
+        , worker_ptr{std::move(worker)}
+    {}
+
+    explicit async_msg(async_msg_type the_type)
+        : async_msg{nullptr, the_type}
+    {}
+};
+
+class SPDLOG_API thread_pool
+{
+public:
+    using item_type = async_msg;
+    using q_type = details::mpmc_blocking_queue<item_type>;
+
+    thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start, std::function<void()> on_thread_stop);
+    thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start);
+    thread_pool(size_t q_max_items, size_t threads_n);
+
+    // message all threads to terminate gracefully and join them
+    ~thread_pool();
+
+    thread_pool(const thread_pool &) = delete;
+    thread_pool &operator=(thread_pool &&) = delete;
+
+    void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
+    void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
+    size_t overrun_counter();
+    void reset_overrun_counter();
+    size_t queue_size();
+
+private:
+    q_type q_;
+
+    std::vector<std::thread> threads_;
+
+    void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy);
+    void worker_loop_();
+
+    // process next message in the queue
+    // return true if this thread should still be active (while no terminate msg
+    // was received)
+    bool process_next_msg_();
+};
+
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "thread_pool-inl.h"
+#endif
diff --git a/include/spdlog/details/udp_client-windows.h b/include/spdlog/details/udp_client-windows.h
new file mode 100644
index 000000000..8e763356d
--- /dev/null
+++ b/include/spdlog/details/udp_client-windows.h
@@ -0,0 +1,111 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+// Helper RAII over winsock udp client socket.
+// Will throw on construction if socket creation failed.
+
+#include <spdlog/common.h>
+#include <spdlog/details/os.h>
+#include <spdlog/details/windows_include.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string>
+
+#pragma comment(lib, "Ws2_32.lib")
+#pragma comment(lib, "Mswsock.lib")
+#pragma comment(lib, "AdvApi32.lib")
+
+namespace spdlog {
+namespace details {
+class udp_client
+{
+    static constexpr int TX_BUFFER_SIZE = 1024 * 10;
+    SOCKET socket_ = INVALID_SOCKET;
+    sockaddr_in addr_ = {0};
+
+    static void init_winsock_()
+    {
+        WSADATA wsaData;
+        auto rv = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
+        if (rv != 0)
+        {
+            throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
+        }
+    }
+
+    static void throw_winsock_error_(const std::string &msg, int last_error)
+    {
+        char buf[512];
+        ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error,
+            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL);
+
+        throw_spdlog_ex(fmt_lib::format("udp_sink - {}: {}", msg, buf));
+    }
+
+    void cleanup_()
+    {
+        if (socket_ != INVALID_SOCKET)
+        {
+            ::closesocket(socket_);
+        }
+        socket_ = INVALID_SOCKET;
+        ::WSACleanup();
+    }
+
+public:
+    udp_client(const std::string &host, uint16_t port)
+    {
+        init_winsock_();
+
+        addr_.sin_family = PF_INET;
+        addr_.sin_port = htons(port);
+        addr_.sin_addr.s_addr = INADDR_ANY;
+        if (InetPtonA(PF_INET, host.c_str(), &addr_.sin_addr.s_addr) != 1)
+        {
+            int last_error = ::WSAGetLastError();
+            ::WSACleanup();
+            throw_winsock_error_("error: Invalid address!", last_error);
+        }
+
+        socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
+        if (socket_ == INVALID_SOCKET)
+        {
+            int last_error = ::WSAGetLastError();
+            ::WSACleanup();
+            throw_winsock_error_("error: Create Socket failed", last_error);
+        }
+
+        int option_value = TX_BUFFER_SIZE;
+        if (::setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<const char *>(&option_value), sizeof(option_value)) < 0)
+        {
+            int last_error = ::WSAGetLastError();
+            cleanup_();
+            throw_winsock_error_("error: setsockopt(SO_SNDBUF) Failed!", last_error);
+        }
+    }
+
+    ~udp_client()
+    {
+        cleanup_();
+    }
+
+    SOCKET fd() const
+    {
+        return socket_;
+    }
+
+    void send(const char *data, size_t n_bytes)
+    {
+        socklen_t tolen = sizeof(struct sockaddr);
+        if (::sendto(socket_, data, static_cast<int>(n_bytes), 0, (struct sockaddr *)&addr_, tolen) == -1)
+        {
+            throw_spdlog_ex("sendto(2) failed", errno);
+        }
+    }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/udp_client.h b/include/spdlog/details/udp_client.h
new file mode 100644
index 000000000..2db5b89e7
--- /dev/null
+++ b/include/spdlog/details/udp_client.h
@@ -0,0 +1,94 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+// Helper RAII over unix udp client socket.
+// Will throw on construction if the socket creation failed.
+
+#ifdef _WIN32
+#    error "include udp_client-windows.h instead"
+#endif
+
+#include <spdlog/common.h>
+#include <spdlog/details/os.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/udp.h>
+
+#include <string>
+
+namespace spdlog {
+namespace details {
+
+class udp_client
+{
+    static constexpr int TX_BUFFER_SIZE = 1024 * 10;
+    int socket_ = -1;
+    struct sockaddr_in sockAddr_;
+
+    void cleanup_()
+    {
+        if (socket_ != -1)
+        {
+            ::close(socket_);
+            socket_ = -1;
+        }
+    }
+
+public:
+    udp_client(const std::string &host, uint16_t port)
+    {
+        socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
+        if (socket_ < 0)
+        {
+            throw_spdlog_ex("error: Create Socket Failed!");
+        }
+
+        int option_value = TX_BUFFER_SIZE;
+        if (::setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<const char *>(&option_value), sizeof(option_value)) < 0)
+        {
+            cleanup_();
+            throw_spdlog_ex("error: setsockopt(SO_SNDBUF) Failed!");
+        }
+
+        sockAddr_.sin_family = AF_INET;
+        sockAddr_.sin_port = htons(port);
+
+        if (::inet_aton(host.c_str(), &sockAddr_.sin_addr) == 0)
+        {
+            cleanup_();
+            throw_spdlog_ex("error: Invalid address!");
+        }
+
+        ::memset(sockAddr_.sin_zero, 0x00, sizeof(sockAddr_.sin_zero));
+    }
+
+    ~udp_client()
+    {
+        cleanup_();
+    }
+
+    int fd() const
+    {
+        return socket_;
+    }
+
+    // Send exactly n_bytes of the given data.
+    // On error close the connection and throw.
+    void send(const char *data, size_t n_bytes)
+    {
+        ssize_t toslen = 0;
+        socklen_t tolen = sizeof(struct sockaddr);
+        if ((toslen = ::sendto(socket_, data, n_bytes, 0, (struct sockaddr *)&sockAddr_, tolen)) == -1)
+        {
+            throw_spdlog_ex("sendto(2) failed", errno);
+        }
+    }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/include/spdlog/details/windows_include.h b/include/spdlog/details/windows_include.h
new file mode 100644
index 000000000..a92390b9a
--- /dev/null
+++ b/include/spdlog/details/windows_include.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#ifndef NOMINMAX
+#    define NOMINMAX // prevent windows redefining min/max
+#endif
+
+#ifndef WIN32_LEAN_AND_MEAN
+#    define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <windows.h>
diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h
new file mode 100644
index 000000000..47fec05b9
--- /dev/null
+++ b/include/spdlog/fmt/bin_to_hex.h
@@ -0,0 +1,248 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+#include <cctype>
+#include <spdlog/common.h>
+
+#if defined(__has_include)
+#    if __has_include(<version>)
+#        include <version>
+#    endif
+#endif
+
+#if __cpp_lib_span >= 202002L
+#    include <span>
+#endif
+
+//
+// Support for logging binary data as hex
+// format flags, any combination of the following:
+// {:X} - print in uppercase.
+// {:s} - don't separate each byte with space.
+// {:p} - don't print the position on each line start.
+// {:n} - don't split the output to lines.
+// {:a} - show ASCII if :n is not set
+
+//
+// Examples:
+//
+// std::vector<char> v(200, 0x0b);
+// logger->info("Some buffer {}", spdlog::to_hex(v));
+// char buf[128];
+// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
+// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf), 16));
+
+namespace spdlog {
+namespace details {
+
+template<typename It>
+class dump_info
+{
+public:
+    dump_info(It range_begin, It range_end, size_t size_per_line)
+        : begin_(range_begin)
+        , end_(range_end)
+        , size_per_line_(size_per_line)
+    {}
+
+    // do not use begin() and end() to avoid collision with fmt/ranges
+    It get_begin() const
+    {
+        return begin_;
+    }
+    It get_end() const
+    {
+        return end_;
+    }
+    size_t size_per_line() const
+    {
+        return size_per_line_;
+    }
+
+private:
+    It begin_, end_;
+    size_t size_per_line_;
+};
+} // namespace details
+
+// create a dump_info that wraps the given container
+template<typename Container>
+inline details::dump_info<typename Container::const_iterator> to_hex(const Container &container, size_t size_per_line = 32)
+{
+    static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
+    using Iter = typename Container::const_iterator;
+    return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
+}
+
+#if __cpp_lib_span >= 202002L
+
+template<typename Value, size_t Extent>
+inline details::dump_info<typename std::span<Value, Extent>::iterator> to_hex(
+    const std::span<Value, Extent> &container, size_t size_per_line = 32)
+{
+    using Container = std::span<Value, Extent>;
+    static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
+    using Iter = typename Container::iterator;
+    return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
+}
+
+#endif
+
+// create dump_info from ranges
+template<typename It>
+inline details::dump_info<It> to_hex(const It range_begin, const It range_end, size_t size_per_line = 32)
+{
+    return details::dump_info<It>(range_begin, range_end, size_per_line);
+}
+
+} // namespace spdlog
+
+namespace
+#ifdef SPDLOG_USE_STD_FORMAT
+    std
+#else
+    fmt
+#endif
+{
+
+template<typename T>
+struct formatter<spdlog::details::dump_info<T>, char>
+{
+    const char delimiter = ' ';
+    bool put_newlines = true;
+    bool put_delimiters = true;
+    bool use_uppercase = false;
+    bool put_positions = true; // position on start of each line
+    bool show_ascii = false;
+
+    // parse the format string flags
+    template<typename ParseContext>
+    SPDLOG_CONSTEXPR_FUNC auto parse(ParseContext &ctx) -> decltype(ctx.begin())
+    {
+        auto it = ctx.begin();
+        while (it != ctx.end() && *it != '}')
+        {
+            switch (*it)
+            {
+            case 'X':
+                use_uppercase = true;
+                break;
+            case 's':
+                put_delimiters = false;
+                break;
+            case 'p':
+                put_positions = false;
+                break;
+            case 'n':
+                put_newlines = false;
+                show_ascii = false;
+                break;
+            case 'a':
+                if (put_newlines)
+                {
+                    show_ascii = true;
+                }
+                break;
+            }
+
+            ++it;
+        }
+        return it;
+    }
+
+    // format the given bytes range as hex
+    template<typename FormatContext, typename Container>
+    auto format(const spdlog::details::dump_info<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
+    {
+        SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
+        SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
+        const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
+
+#if !defined(SPDLOG_USE_STD_FORMAT) && FMT_VERSION < 60000
+        auto inserter = ctx.begin();
+#else
+        auto inserter = ctx.out();
+#endif
+
+        int size_per_line = static_cast<int>(the_range.size_per_line());
+        auto start_of_line = the_range.get_begin();
+        for (auto i = the_range.get_begin(); i != the_range.get_end(); i++)
+        {
+            auto ch = static_cast<unsigned char>(*i);
+
+            if (put_newlines && (i == the_range.get_begin() || i - start_of_line >= size_per_line))
+            {
+                if (show_ascii && i != the_range.get_begin())
+                {
+                    *inserter++ = delimiter;
+                    *inserter++ = delimiter;
+                    for (auto j = start_of_line; j < i; j++)
+                    {
+                        auto pc = static_cast<unsigned char>(*j);
+                        *inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
+                    }
+                }
+
+                put_newline(inserter, static_cast<size_t>(i - the_range.get_begin()));
+
+                // put first byte without delimiter in front of it
+                *inserter++ = hex_chars[(ch >> 4) & 0x0f];
+                *inserter++ = hex_chars[ch & 0x0f];
+                start_of_line = i;
+                continue;
+            }
+
+            if (put_delimiters)
+            {
+                *inserter++ = delimiter;
+            }
+
+            *inserter++ = hex_chars[(ch >> 4) & 0x0f];
+            *inserter++ = hex_chars[ch & 0x0f];
+        }
+        if (show_ascii) // add ascii to last line
+        {
+            if (the_range.get_end() - the_range.get_begin() > size_per_line)
+            {
+                auto blank_num = size_per_line - (the_range.get_end() - start_of_line);
+                while (blank_num-- > 0)
+                {
+                    *inserter++ = delimiter;
+                    *inserter++ = delimiter;
+                    if (put_delimiters)
+                    {
+                        *inserter++ = delimiter;
+                    }
+                }
+            }
+            *inserter++ = delimiter;
+            *inserter++ = delimiter;
+            for (auto j = start_of_line; j != the_range.get_end(); j++)
+            {
+                auto pc = static_cast<unsigned char>(*j);
+                *inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
+            }
+        }
+        return inserter;
+    }
+
+    // put newline(and position header)
+    template<typename It>
+    void put_newline(It inserter, std::size_t pos)
+    {
+#ifdef _WIN32
+        *inserter++ = '\r';
+#endif
+        *inserter++ = '\n';
+
+        if (put_positions)
+        {
+            spdlog::fmt_lib::format_to(inserter, SPDLOG_FMT_STRING("{:04X}: "), pos);
+        }
+    }
+};
+} // namespace std
diff --git a/include/spdlog/fmt/bundled/args.h b/include/spdlog/fmt/bundled/args.h
new file mode 100644
index 000000000..a3966d140
--- /dev/null
+++ b/include/spdlog/fmt/bundled/args.h
@@ -0,0 +1,234 @@
+// Formatting library for C++ - dynamic format arguments
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_ARGS_H_
+#define FMT_ARGS_H_
+
+#include <functional>  // std::reference_wrapper
+#include <memory>      // std::unique_ptr
+#include <vector>
+
+#include "core.h"
+
+FMT_BEGIN_NAMESPACE
+
+namespace detail {
+
+template <typename T> struct is_reference_wrapper : std::false_type {};
+template <typename T>
+struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
+
+template <typename T> const T& unwrap(const T& v) { return v; }
+template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
+  return static_cast<const T&>(v);
+}
+
+class dynamic_arg_list {
+  // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
+  // templates it doesn't complain about inability to deduce single translation
+  // unit for placing vtable. So storage_node_base is made a fake template.
+  template <typename = void> struct node {
+    virtual ~node() = default;
+    std::unique_ptr<node<>> next;
+  };
+
+  template <typename T> struct typed_node : node<> {
+    T value;
+
+    template <typename Arg>
+    FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
+
+    template <typename Char>
+    FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
+        : value(arg.data(), arg.size()) {}
+  };
+
+  std::unique_ptr<node<>> head_;
+
+ public:
+  template <typename T, typename Arg> const T& push(const Arg& arg) {
+    auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
+    auto& value = new_node->value;
+    new_node->next = std::move(head_);
+    head_ = std::move(new_node);
+    return value;
+  }
+};
+}  // namespace detail
+
+/**
+  \rst
+  A dynamic version of `fmt::format_arg_store`.
+  It's equipped with a storage to potentially temporary objects which lifetimes
+  could be shorter than the format arguments object.
+
+  It can be implicitly converted into `~fmt::basic_format_args` for passing
+  into type-erased formatting functions such as `~fmt::vformat`.
+  \endrst
+ */
+template <typename Context>
+class dynamic_format_arg_store
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+    // Workaround a GCC template argument substitution bug.
+    : public basic_format_args<Context>
+#endif
+{
+ private:
+  using char_type = typename Context::char_type;
+
+  template <typename T> struct need_copy {
+    static constexpr detail::type mapped_type =
+        detail::mapped_type_constant<T, Context>::value;
+
+    enum {
+      value = !(detail::is_reference_wrapper<T>::value ||
+                std::is_same<T, basic_string_view<char_type>>::value ||
+                std::is_same<T, detail::std_string_view<char_type>>::value ||
+                (mapped_type != detail::type::cstring_type &&
+                 mapped_type != detail::type::string_type &&
+                 mapped_type != detail::type::custom_type))
+    };
+  };
+
+  template <typename T>
+  using stored_type = conditional_t<
+      std::is_convertible<T, std::basic_string<char_type>>::value &&
+          !detail::is_reference_wrapper<T>::value,
+      std::basic_string<char_type>, T>;
+
+  // Storage of basic_format_arg must be contiguous.
+  std::vector<basic_format_arg<Context>> data_;
+  std::vector<detail::named_arg_info<char_type>> named_info_;
+
+  // Storage of arguments not fitting into basic_format_arg must grow
+  // without relocation because items in data_ refer to it.
+  detail::dynamic_arg_list dynamic_args_;
+
+  friend class basic_format_args<Context>;
+
+  unsigned long long get_types() const {
+    return detail::is_unpacked_bit | data_.size() |
+           (named_info_.empty()
+                ? 0ULL
+                : static_cast<unsigned long long>(detail::has_named_args_bit));
+  }
+
+  const basic_format_arg<Context>* data() const {
+    return named_info_.empty() ? data_.data() : data_.data() + 1;
+  }
+
+  template <typename T> void emplace_arg(const T& arg) {
+    data_.emplace_back(detail::make_arg<Context>(arg));
+  }
+
+  template <typename T>
+  void emplace_arg(const detail::named_arg<char_type, T>& arg) {
+    if (named_info_.empty()) {
+      constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
+      data_.insert(data_.begin(), {zero_ptr, 0});
+    }
+    data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
+    auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
+      data->pop_back();
+    };
+    std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
+        guard{&data_, pop_one};
+    named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
+    data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
+    guard.release();
+  }
+
+ public:
+  constexpr dynamic_format_arg_store() = default;
+
+  /**
+    \rst
+    Adds an argument into the dynamic store for later passing to a formatting
+    function.
+
+    Note that custom types and string types (but not string views) are copied
+    into the store dynamically allocating memory if necessary.
+
+    **Example**::
+
+      fmt::dynamic_format_arg_store<fmt::format_context> store;
+      store.push_back(42);
+      store.push_back("abc");
+      store.push_back(1.5f);
+      std::string result = fmt::vformat("{} and {} and {}", store);
+    \endrst
+  */
+  template <typename T> void push_back(const T& arg) {
+    if (detail::const_check(need_copy<T>::value))
+      emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
+    else
+      emplace_arg(detail::unwrap(arg));
+  }
+
+  /**
+    \rst
+    Adds a reference to the argument into the dynamic store for later passing to
+    a formatting function.
+
+    **Example**::
+
+      fmt::dynamic_format_arg_store<fmt::format_context> store;
+      char band[] = "Rolling Stones";
+      store.push_back(std::cref(band));
+      band[9] = 'c'; // Changing str affects the output.
+      std::string result = fmt::vformat("{}", store);
+      // result == "Rolling Scones"
+    \endrst
+  */
+  template <typename T> void push_back(std::reference_wrapper<T> arg) {
+    static_assert(
+        need_copy<T>::value,
+        "objects of built-in types and string views are always copied");
+    emplace_arg(arg.get());
+  }
+
+  /**
+    Adds named argument into the dynamic store for later passing to a formatting
+    function. ``std::reference_wrapper`` is supported to avoid copying of the
+    argument. The name is always copied into the store.
+  */
+  template <typename T>
+  void push_back(const detail::named_arg<char_type, T>& arg) {
+    const char_type* arg_name =
+        dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
+    if (detail::const_check(need_copy<T>::value)) {
+      emplace_arg(
+          fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
+    } else {
+      emplace_arg(fmt::arg(arg_name, arg.value));
+    }
+  }
+
+  /** Erase all elements from the store */
+  void clear() {
+    data_.clear();
+    named_info_.clear();
+    dynamic_args_ = detail::dynamic_arg_list();
+  }
+
+  /**
+    \rst
+    Reserves space to store at least *new_cap* arguments including
+    *new_cap_named* named arguments.
+    \endrst
+  */
+  void reserve(size_t new_cap, size_t new_cap_named) {
+    FMT_ASSERT(new_cap >= new_cap_named,
+               "Set of arguments includes set of named arguments");
+    data_.reserve(new_cap);
+    named_info_.reserve(new_cap_named);
+  }
+};
+
+FMT_END_NAMESPACE
+
+#endif  // FMT_ARGS_H_
diff --git a/include/spdlog/fmt/bundled/chrono.h b/include/spdlog/fmt/bundled/chrono.h
new file mode 100644
index 000000000..b112f76e9
--- /dev/null
+++ b/include/spdlog/fmt/bundled/chrono.h
@@ -0,0 +1,2069 @@
+// 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 <algorithm>
+#include <chrono>
+#include <cmath>    // std::isfinite
+#include <cstring>  // std::memcpy
+#include <ctime>
+#include <iterator>
+#include <locale>
+#include <ostream>
+#include <type_traits>
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+
+// Enable tzset.
+#ifndef FMT_USE_TZSET
+// UWP doesn't provide _tzset.
+#  if FMT_HAS_INCLUDE("winapifamily.h")
+#    include <winapifamily.h>
+#  endif
+#  if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \
+                          (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
+#    define FMT_USE_TZSET 1
+#  else
+#    define FMT_USE_TZSET 0
+#  endif
+#endif
+
+// Enable safe chrono durations, unless explicitly disabled.
+#ifndef FMT_SAFE_DURATION_CAST
+#  define FMT_SAFE_DURATION_CAST 1
+#endif
+#if FMT_SAFE_DURATION_CAST
+
+// For conversion between std::chrono::durations without undefined
+// behaviour or erroneous results.
+// This is a stripped down version of duration_cast, for inclusion in fmt.
+// See https://github.com/pauldreik/safe_duration_cast
+//
+// Copyright Paul Dreik 2019
+namespace safe_duration_cast {
+
+template <typename To, typename From,
+          FMT_ENABLE_IF(!std::is_same<From, To>::value &&
+                        std::numeric_limits<From>::is_signed ==
+                            std::numeric_limits<To>::is_signed)>
+FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
+  ec = 0;
+  using F = std::numeric_limits<From>;
+  using T = std::numeric_limits<To>;
+  static_assert(F::is_integer, "From must be integral");
+  static_assert(T::is_integer, "To must be integral");
+
+  // A and B are both signed, or both unsigned.
+  if (detail::const_check(F::digits <= T::digits)) {
+    // From fits in To without any problem.
+  } else {
+    // From does not always fit in To, resort to a dynamic check.
+    if (from < (T::min)() || from > (T::max)()) {
+      // outside range.
+      ec = 1;
+      return {};
+    }
+  }
+  return static_cast<To>(from);
+}
+
+/**
+ * converts From to To, without loss. If the dynamic value of from
+ * can't be converted to To without loss, ec is set.
+ */
+template <typename To, typename From,
+          FMT_ENABLE_IF(!std::is_same<From, To>::value &&
+                        std::numeric_limits<From>::is_signed !=
+                            std::numeric_limits<To>::is_signed)>
+FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
+  ec = 0;
+  using F = std::numeric_limits<From>;
+  using T = std::numeric_limits<To>;
+  static_assert(F::is_integer, "From must be integral");
+  static_assert(T::is_integer, "To must be integral");
+
+  if (detail::const_check(F::is_signed && !T::is_signed)) {
+    // From may be negative, not allowed!
+    if (fmt::detail::is_negative(from)) {
+      ec = 1;
+      return {};
+    }
+    // From is positive. Can it always fit in To?
+    if (detail::const_check(F::digits > T::digits) &&
+        from > static_cast<From>(detail::max_value<To>())) {
+      ec = 1;
+      return {};
+    }
+  }
+
+  if (detail::const_check(!F::is_signed && T::is_signed &&
+                          F::digits >= T::digits) &&
+      from > static_cast<From>(detail::max_value<To>())) {
+    ec = 1;
+    return {};
+  }
+  return static_cast<To>(from);  // Lossless conversion.
+}
+
+template <typename To, typename From,
+          FMT_ENABLE_IF(std::is_same<From, To>::value)>
+FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
+  ec = 0;
+  return from;
+}  // function
+
+// clang-format off
+/**
+ * converts From to To if possible, otherwise ec is set.
+ *
+ * input                            |    output
+ * ---------------------------------|---------------
+ * NaN                              | NaN
+ * Inf                              | Inf
+ * normal, fits in output           | converted (possibly lossy)
+ * normal, does not fit in output   | ec is set
+ * subnormal                        | best effort
+ * -Inf                             | -Inf
+ */
+// clang-format on
+template <typename To, typename From,
+          FMT_ENABLE_IF(!std::is_same<From, To>::value)>
+FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
+  ec = 0;
+  using T = std::numeric_limits<To>;
+  static_assert(std::is_floating_point<From>::value, "From must be floating");
+  static_assert(std::is_floating_point<To>::value, "To must be floating");
+
+  // catch the only happy case
+  if (std::isfinite(from)) {
+    if (from >= T::lowest() && from <= (T::max)()) {
+      return static_cast<To>(from);
+    }
+    // not within range.
+    ec = 1;
+    return {};
+  }
+
+  // nan and inf will be preserved
+  return static_cast<To>(from);
+}  // function
+
+template <typename To, typename From,
+          FMT_ENABLE_IF(std::is_same<From, To>::value)>
+FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
+  ec = 0;
+  static_assert(std::is_floating_point<From>::value, "From must be floating");
+  return from;
+}
+
+/**
+ * safe duration cast between integral durations
+ */
+template <typename To, typename FromRep, typename FromPeriod,
+          FMT_ENABLE_IF(std::is_integral<FromRep>::value),
+          FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
+To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
+                      int& ec) {
+  using From = std::chrono::duration<FromRep, FromPeriod>;
+  ec = 0;
+  // the basic idea is that we need to convert from count() in the from type
+  // to count() in the To type, by multiplying it with this:
+  struct Factor
+      : std::ratio_divide<typename From::period, typename To::period> {};
+
+  static_assert(Factor::num > 0, "num must be positive");
+  static_assert(Factor::den > 0, "den must be positive");
+
+  // the conversion is like this: multiply from.count() with Factor::num
+  // /Factor::den and convert it to To::rep, all this without
+  // overflow/underflow. let's start by finding a suitable type that can hold
+  // both To, From and Factor::num
+  using IntermediateRep =
+      typename std::common_type<typename From::rep, typename To::rep,
+                                decltype(Factor::num)>::type;
+
+  // safe conversion to IntermediateRep
+  IntermediateRep count =
+      lossless_integral_conversion<IntermediateRep>(from.count(), ec);
+  if (ec) return {};
+  // multiply with Factor::num without overflow or underflow
+  if (detail::const_check(Factor::num != 1)) {
+    const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
+    if (count > max1) {
+      ec = 1;
+      return {};
+    }
+    const auto min1 =
+        (std::numeric_limits<IntermediateRep>::min)() / Factor::num;
+    if (!std::is_unsigned<IntermediateRep>::value && count < min1) {
+      ec = 1;
+      return {};
+    }
+    count *= Factor::num;
+  }
+
+  if (detail::const_check(Factor::den != 1)) count /= Factor::den;
+  auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
+  return ec ? To() : To(tocount);
+}
+
+/**
+ * safe duration_cast between floating point durations
+ */
+template <typename To, typename FromRep, typename FromPeriod,
+          FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
+          FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
+To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
+                      int& ec) {
+  using From = std::chrono::duration<FromRep, FromPeriod>;
+  ec = 0;
+  if (std::isnan(from.count())) {
+    // nan in, gives nan out. easy.
+    return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
+  }
+  // maybe we should also check if from is denormal, and decide what to do about
+  // it.
+
+  // +-inf should be preserved.
+  if (std::isinf(from.count())) {
+    return To{from.count()};
+  }
+
+  // the basic idea is that we need to convert from count() in the from type
+  // to count() in the To type, by multiplying it with this:
+  struct Factor
+      : std::ratio_divide<typename From::period, typename To::period> {};
+
+  static_assert(Factor::num > 0, "num must be positive");
+  static_assert(Factor::den > 0, "den must be positive");
+
+  // the conversion is like this: multiply from.count() with Factor::num
+  // /Factor::den and convert it to To::rep, all this without
+  // overflow/underflow. let's start by finding a suitable type that can hold
+  // both To, From and Factor::num
+  using IntermediateRep =
+      typename std::common_type<typename From::rep, typename To::rep,
+                                decltype(Factor::num)>::type;
+
+  // force conversion of From::rep -> IntermediateRep to be safe,
+  // even if it will never happen be narrowing in this context.
+  IntermediateRep count =
+      safe_float_conversion<IntermediateRep>(from.count(), ec);
+  if (ec) {
+    return {};
+  }
+
+  // multiply with Factor::num without overflow or underflow
+  if (detail::const_check(Factor::num != 1)) {
+    constexpr auto max1 = detail::max_value<IntermediateRep>() /
+                          static_cast<IntermediateRep>(Factor::num);
+    if (count > max1) {
+      ec = 1;
+      return {};
+    }
+    constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
+                          static_cast<IntermediateRep>(Factor::num);
+    if (count < min1) {
+      ec = 1;
+      return {};
+    }
+    count *= static_cast<IntermediateRep>(Factor::num);
+  }
+
+  // this can't go wrong, right? den>0 is checked earlier.
+  if (detail::const_check(Factor::den != 1)) {
+    using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
+    count /= static_cast<common_t>(Factor::den);
+  }
+
+  // convert to the to type, safely
+  using ToRep = typename To::rep;
+
+  const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
+  if (ec) {
+    return {};
+  }
+  return To{tocount};
+}
+}  // namespace safe_duration_cast
+#endif
+
+// Prevents expansion of a preceding token as a function-style macro.
+// Usage: f FMT_NOMACRO()
+#define FMT_NOMACRO
+
+namespace detail {
+template <typename T = void> struct null {};
+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<>(); }
+
+inline const std::locale& get_classic_locale() {
+  static const auto& locale = std::locale::classic();
+  return locale;
+}
+
+template <typename CodeUnit> struct codecvt_result {
+  static constexpr const size_t max_size = 32;
+  CodeUnit buf[max_size];
+  CodeUnit* end;
+};
+template <typename CodeUnit>
+constexpr const size_t codecvt_result<CodeUnit>::max_size;
+
+template <typename CodeUnit>
+void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
+                   const std::locale& loc) {
+#if FMT_CLANG_VERSION
+#  pragma clang diagnostic push
+#  pragma clang diagnostic ignored "-Wdeprecated"
+  auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
+#  pragma clang diagnostic pop
+#else
+  auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
+#endif
+  auto mb = std::mbstate_t();
+  const char* from_next = nullptr;
+  auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next,
+                     std::begin(out.buf), std::end(out.buf), out.end);
+  if (result != std::codecvt_base::ok)
+    FMT_THROW(format_error("failed to format time"));
+}
+
+template <typename OutputIt>
+auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
+    -> OutputIt {
+  if (detail::is_utf8() && loc != get_classic_locale()) {
+    // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
+    // gcc-4.
+#if FMT_MSC_VERSION != 0 || \
+    (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
+    // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
+    // and newer.
+    using code_unit = wchar_t;
+#else
+    using code_unit = char32_t;
+#endif
+
+    using unit_t = codecvt_result<code_unit>;
+    unit_t unit;
+    write_codecvt(unit, in, loc);
+    // In UTF-8 is used one to four one-byte code units.
+    auto&& buf = basic_memory_buffer<char, unit_t::max_size * 4>();
+    for (code_unit* p = unit.buf; p != unit.end; ++p) {
+      uint32_t c = static_cast<uint32_t>(*p);
+      if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) {
+        // surrogate pair
+        ++p;
+        if (p == unit.end || (c & 0xfc00) != 0xd800 ||
+            (*p & 0xfc00) != 0xdc00) {
+          FMT_THROW(format_error("failed to format time"));
+        }
+        c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
+      }
+      if (c < 0x80) {
+        buf.push_back(static_cast<char>(c));
+      } else if (c < 0x800) {
+        buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
+        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
+      } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
+        buf.push_back(static_cast<char>(0xe0 | (c >> 12)));
+        buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
+        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
+      } else if (c >= 0x10000 && c <= 0x10ffff) {
+        buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
+        buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
+        buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
+        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
+      } else {
+        FMT_THROW(format_error("failed to format time"));
+      }
+    }
+    return copy_str<char>(buf.data(), buf.data() + buf.size(), out);
+  }
+  return copy_str<char>(in.data(), in.data() + in.size(), out);
+}
+
+template <typename Char, typename OutputIt,
+          FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
+    -> OutputIt {
+  codecvt_result<Char> unit;
+  write_codecvt(unit, sv, loc);
+  return copy_str<Char>(unit.buf, unit.end, out);
+}
+
+template <typename Char, typename OutputIt,
+          FMT_ENABLE_IF(std::is_same<Char, char>::value)>
+auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
+    -> OutputIt {
+  return write_encoded_tm_str(out, sv, loc);
+}
+
+template <typename Char>
+inline void do_write(buffer<Char>& buf, const std::tm& time,
+                     const std::locale& loc, char format, char modifier) {
+  auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
+  auto&& os = std::basic_ostream<Char>(&format_buf);
+  os.imbue(loc);
+  using iterator = std::ostreambuf_iterator<Char>;
+  const auto& facet = std::use_facet<std::time_put<Char, iterator>>(loc);
+  auto end = facet.put(os, os, Char(' '), &time, format, modifier);
+  if (end.failed()) FMT_THROW(format_error("failed to format time"));
+}
+
+template <typename Char, typename OutputIt,
+          FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+auto write(OutputIt out, const std::tm& time, const std::locale& loc,
+           char format, char modifier = 0) -> OutputIt {
+  auto&& buf = get_buffer<Char>(out);
+  do_write<Char>(buf, time, loc, format, modifier);
+  return buf.out();
+}
+
+template <typename Char, typename OutputIt,
+          FMT_ENABLE_IF(std::is_same<Char, char>::value)>
+auto write(OutputIt out, const std::tm& time, const std::locale& loc,
+           char format, char modifier = 0) -> OutputIt {
+  auto&& buf = basic_memory_buffer<Char>();
+  do_write<char>(buf, time, loc, format, modifier);
+  return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
+}
+
+}  // namespace detail
+
+FMT_MODULE_EXPORT_BEGIN
+
+/**
+  Converts given time since epoch as ``std::time_t`` value into calendar time,
+  expressed in local time. Unlike ``std::localtime``, this function is
+  thread-safe on most platforms.
+ */
+inline std::tm localtime(std::time_t time) {
+  struct dispatcher {
+    std::time_t time_;
+    std::tm tm_;
+
+    dispatcher(std::time_t t) : time_(t) {}
+
+    bool run() {
+      using namespace fmt::detail;
+      return handle(localtime_r(&time_, &tm_));
+    }
+
+    bool handle(std::tm* tm) { return tm != nullptr; }
+
+    bool handle(detail::null<>) {
+      using namespace fmt::detail;
+      return fallback(localtime_s(&tm_, &time_));
+    }
+
+    bool fallback(int res) { return res == 0; }
+
+#if !FMT_MSC_VERSION
+    bool fallback(detail::null<>) {
+      using namespace fmt::detail;
+      std::tm* tm = std::localtime(&time_);
+      if (tm) tm_ = *tm;
+      return tm != nullptr;
+    }
+#endif
+  };
+  dispatcher lt(time);
+  // Too big time values may be unsupported.
+  if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
+  return lt.tm_;
+}
+
+inline std::tm localtime(
+    std::chrono::time_point<std::chrono::system_clock> time_point) {
+  return localtime(std::chrono::system_clock::to_time_t(time_point));
+}
+
+/**
+  Converts given time since epoch as ``std::time_t`` value into calendar time,
+  expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
+  function is thread-safe on most platforms.
+ */
+inline std::tm gmtime(std::time_t time) {
+  struct dispatcher {
+    std::time_t time_;
+    std::tm tm_;
+
+    dispatcher(std::time_t t) : time_(t) {}
+
+    bool run() {
+      using namespace fmt::detail;
+      return handle(gmtime_r(&time_, &tm_));
+    }
+
+    bool handle(std::tm* tm) { return tm != nullptr; }
+
+    bool handle(detail::null<>) {
+      using namespace fmt::detail;
+      return fallback(gmtime_s(&tm_, &time_));
+    }
+
+    bool fallback(int res) { return res == 0; }
+
+#if !FMT_MSC_VERSION
+    bool fallback(detail::null<>) {
+      std::tm* tm = std::gmtime(&time_);
+      if (tm) tm_ = *tm;
+      return tm != nullptr;
+    }
+#endif
+  };
+  dispatcher gt(time);
+  // Too big time values may be unsupported.
+  if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
+  return gt.tm_;
+}
+
+inline std::tm gmtime(
+    std::chrono::time_point<std::chrono::system_clock> time_point) {
+  return gmtime(std::chrono::system_clock::to_time_t(time_point));
+}
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+// Writes two-digit numbers a, b and c separated by sep to buf.
+// The method by Pavel Novikov based on
+// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
+inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
+                                   unsigned c, char sep) {
+  unsigned long long digits =
+      a | (b << 24) | (static_cast<unsigned long long>(c) << 48);
+  // Convert each value to BCD.
+  // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
+  // The difference is
+  //   y - x = a * 6
+  // a can be found from x:
+  //   a = floor(x / 10)
+  // then
+  //   y = x + a * 6 = x + floor(x / 10) * 6
+  // floor(x / 10) is (x * 205) >> 11 (needs 16 bits).
+  digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6;
+  // Put low nibbles to high bytes and high nibbles to low bytes.
+  digits = ((digits & 0x00f00000f00000f0) >> 4) |
+           ((digits & 0x000f00000f00000f) << 8);
+  auto usep = static_cast<unsigned long long>(sep);
+  // Add ASCII '0' to each digit byte and insert separators.
+  digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
+
+  constexpr const size_t len = 8;
+  if (const_check(is_big_endian())) {
+    char tmp[len];
+    std::memcpy(tmp, &digits, len);
+    std::reverse_copy(tmp, tmp + len, buf);
+  } else {
+    std::memcpy(buf, &digits, len);
+  }
+}
+
+template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
+  if (std::is_same<Period, std::atto>::value) return "as";
+  if (std::is_same<Period, std::femto>::value) return "fs";
+  if (std::is_same<Period, std::pico>::value) return "ps";
+  if (std::is_same<Period, std::nano>::value) return "ns";
+  if (std::is_same<Period, std::micro>::value) return "µs";
+  if (std::is_same<Period, std::milli>::value) return "ms";
+  if (std::is_same<Period, std::centi>::value) return "cs";
+  if (std::is_same<Period, std::deci>::value) return "ds";
+  if (std::is_same<Period, std::ratio<1>>::value) return "s";
+  if (std::is_same<Period, std::deca>::value) return "das";
+  if (std::is_same<Period, std::hecto>::value) return "hs";
+  if (std::is_same<Period, std::kilo>::value) return "ks";
+  if (std::is_same<Period, std::mega>::value) return "Ms";
+  if (std::is_same<Period, std::giga>::value) return "Gs";
+  if (std::is_same<Period, std::tera>::value) return "Ts";
+  if (std::is_same<Period, std::peta>::value) return "Ps";
+  if (std::is_same<Period, std::exa>::value) return "Es";
+  if (std::is_same<Period, std::ratio<60>>::value) return "m";
+  if (std::is_same<Period, std::ratio<3600>>::value) return "h";
+  return nullptr;
+}
+
+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) FMT_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;
+    }
+    // Year:
+    case 'Y':
+      handler.on_year(numeric_system::standard);
+      break;
+    case 'y':
+      handler.on_short_year(numeric_system::standard);
+      break;
+    case 'C':
+      handler.on_century(numeric_system::standard);
+      break;
+    case 'G':
+      handler.on_iso_week_based_year();
+      break;
+    case 'g':
+      handler.on_iso_week_based_short_year();
+      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':
+    case 'h':
+      handler.on_abbr_month();
+      break;
+    case 'B':
+      handler.on_full_month();
+      break;
+    case 'm':
+      handler.on_dec_month(numeric_system::standard);
+      break;
+    // Day of the year/month:
+    case 'U':
+      handler.on_dec0_week_of_year(numeric_system::standard);
+      break;
+    case 'W':
+      handler.on_dec1_week_of_year(numeric_system::standard);
+      break;
+    case 'V':
+      handler.on_iso_week_of_year(numeric_system::standard);
+      break;
+    case 'j':
+      handler.on_day_of_year();
+      break;
+    case 'd':
+      handler.on_day_of_month(numeric_system::standard);
+      break;
+    case 'e':
+      handler.on_day_of_month_space(numeric_system::standard);
+      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 'Q':
+      handler.on_duration_value();
+      break;
+    case 'q':
+      handler.on_duration_unit();
+      break;
+    case 'z':
+      handler.on_utc_offset();
+      break;
+    case 'Z':
+      handler.on_tz_name();
+      break;
+    // Alternative representation:
+    case 'E': {
+      if (ptr == end) FMT_THROW(format_error("invalid format"));
+      c = *ptr++;
+      switch (c) {
+      case 'Y':
+        handler.on_year(numeric_system::alternative);
+        break;
+      case 'y':
+        handler.on_offset_year();
+        break;
+      case 'C':
+        handler.on_century(numeric_system::alternative);
+        break;
+      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:
+        FMT_THROW(format_error("invalid format"));
+      }
+      break;
+    }
+    case 'O':
+      if (ptr == end) FMT_THROW(format_error("invalid format"));
+      c = *ptr++;
+      switch (c) {
+      case 'y':
+        handler.on_short_year(numeric_system::alternative);
+        break;
+      case 'm':
+        handler.on_dec_month(numeric_system::alternative);
+        break;
+      case 'U':
+        handler.on_dec0_week_of_year(numeric_system::alternative);
+        break;
+      case 'W':
+        handler.on_dec1_week_of_year(numeric_system::alternative);
+        break;
+      case 'V':
+        handler.on_iso_week_of_year(numeric_system::alternative);
+        break;
+      case 'd':
+        handler.on_day_of_month(numeric_system::alternative);
+        break;
+      case 'e':
+        handler.on_day_of_month_space(numeric_system::alternative);
+        break;
+      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:
+        FMT_THROW(format_error("invalid format"));
+      }
+      break;
+    default:
+      FMT_THROW(format_error("invalid format"));
+    }
+    begin = ptr;
+  }
+  if (begin != ptr) handler.on_text(begin, ptr);
+  return ptr;
+}
+
+template <typename Derived> struct null_chrono_spec_handler {
+  FMT_CONSTEXPR void unsupported() {
+    static_cast<Derived*>(this)->unsupported();
+  }
+  FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_offset_year() { unsupported(); }
+  FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); }
+  FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); }
+  FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
+  FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
+  FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
+  FMT_CONSTEXPR void on_full_month() { unsupported(); }
+  FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_day_of_year() { unsupported(); }
+  FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); }
+  FMT_CONSTEXPR void on_us_date() { unsupported(); }
+  FMT_CONSTEXPR void on_iso_date() { unsupported(); }
+  FMT_CONSTEXPR void on_12_hour_time() { unsupported(); }
+  FMT_CONSTEXPR void on_24_hour_time() { unsupported(); }
+  FMT_CONSTEXPR void on_iso_time() { unsupported(); }
+  FMT_CONSTEXPR void on_am_pm() { unsupported(); }
+  FMT_CONSTEXPR void on_duration_value() { unsupported(); }
+  FMT_CONSTEXPR void on_duration_unit() { unsupported(); }
+  FMT_CONSTEXPR void on_utc_offset() { unsupported(); }
+  FMT_CONSTEXPR void on_tz_name() { unsupported(); }
+};
+
+struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
+  FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
+
+  template <typename Char>
+  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
+  FMT_CONSTEXPR void on_year(numeric_system) {}
+  FMT_CONSTEXPR void on_short_year(numeric_system) {}
+  FMT_CONSTEXPR void on_offset_year() {}
+  FMT_CONSTEXPR void on_century(numeric_system) {}
+  FMT_CONSTEXPR void on_iso_week_based_year() {}
+  FMT_CONSTEXPR void on_iso_week_based_short_year() {}
+  FMT_CONSTEXPR void on_abbr_weekday() {}
+  FMT_CONSTEXPR void on_full_weekday() {}
+  FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {}
+  FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {}
+  FMT_CONSTEXPR void on_abbr_month() {}
+  FMT_CONSTEXPR void on_full_month() {}
+  FMT_CONSTEXPR void on_dec_month(numeric_system) {}
+  FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {}
+  FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {}
+  FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {}
+  FMT_CONSTEXPR void on_day_of_year() {}
+  FMT_CONSTEXPR void on_day_of_month(numeric_system) {}
+  FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {}
+  FMT_CONSTEXPR void on_24_hour(numeric_system) {}
+  FMT_CONSTEXPR void on_12_hour(numeric_system) {}
+  FMT_CONSTEXPR void on_minute(numeric_system) {}
+  FMT_CONSTEXPR void on_second(numeric_system) {}
+  FMT_CONSTEXPR void on_datetime(numeric_system) {}
+  FMT_CONSTEXPR void on_loc_date(numeric_system) {}
+  FMT_CONSTEXPR void on_loc_time(numeric_system) {}
+  FMT_CONSTEXPR void on_us_date() {}
+  FMT_CONSTEXPR void on_iso_date() {}
+  FMT_CONSTEXPR void on_12_hour_time() {}
+  FMT_CONSTEXPR void on_24_hour_time() {}
+  FMT_CONSTEXPR void on_iso_time() {}
+  FMT_CONSTEXPR void on_am_pm() {}
+  FMT_CONSTEXPR void on_utc_offset() {}
+  FMT_CONSTEXPR void on_tz_name() {}
+};
+
+inline const char* tm_wday_full_name(int wday) {
+  static constexpr const char* full_name_list[] = {
+      "Sunday",   "Monday", "Tuesday", "Wednesday",
+      "Thursday", "Friday", "Saturday"};
+  return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?";
+}
+inline const char* tm_wday_short_name(int wday) {
+  static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed",
+                                                    "Thu", "Fri", "Sat"};
+  return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???";
+}
+
+inline const char* tm_mon_full_name(int mon) {
+  static constexpr const char* full_name_list[] = {
+      "January", "February", "March",     "April",   "May",      "June",
+      "July",    "August",   "September", "October", "November", "December"};
+  return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?";
+}
+inline const char* tm_mon_short_name(int mon) {
+  static constexpr const char* short_name_list[] = {
+      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+  };
+  return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???";
+}
+
+template <typename T, typename = void>
+struct has_member_data_tm_gmtoff : std::false_type {};
+template <typename T>
+struct has_member_data_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>>
+    : std::true_type {};
+
+template <typename T, typename = void>
+struct has_member_data_tm_zone : std::false_type {};
+template <typename T>
+struct has_member_data_tm_zone<T, void_t<decltype(T::tm_zone)>>
+    : std::true_type {};
+
+#if FMT_USE_TZSET
+inline void tzset_once() {
+  static bool init = []() -> bool {
+    _tzset();
+    return true;
+  }();
+  ignore_unused(init);
+}
+#endif
+
+template <typename OutputIt, typename Char> class tm_writer {
+ private:
+  static constexpr int days_per_week = 7;
+
+  const std::locale& loc_;
+  const bool is_classic_;
+  OutputIt out_;
+  const std::tm& tm_;
+
+  auto tm_sec() const noexcept -> int {
+    FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, "");
+    return tm_.tm_sec;
+  }
+  auto tm_min() const noexcept -> int {
+    FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, "");
+    return tm_.tm_min;
+  }
+  auto tm_hour() const noexcept -> int {
+    FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, "");
+    return tm_.tm_hour;
+  }
+  auto tm_mday() const noexcept -> int {
+    FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, "");
+    return tm_.tm_mday;
+  }
+  auto tm_mon() const noexcept -> int {
+    FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, "");
+    return tm_.tm_mon;
+  }
+  auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; }
+  auto tm_wday() const noexcept -> int {
+    FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, "");
+    return tm_.tm_wday;
+  }
+  auto tm_yday() const noexcept -> int {
+    FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, "");
+    return tm_.tm_yday;
+  }
+
+  auto tm_hour12() const noexcept -> int {
+    const auto h = tm_hour();
+    const auto z = h < 12 ? h : h - 12;
+    return z == 0 ? 12 : z;
+  }
+
+  // POSIX and the C Standard are unclear or inconsistent about what %C and %y
+  // do if the year is negative or exceeds 9999. Use the convention that %C
+  // concatenated with %y yields the same output as %Y, and that %Y contains at
+  // least 4 characters, with more only if necessary.
+  auto split_year_lower(long long year) const noexcept -> int {
+    auto l = year % 100;
+    if (l < 0) l = -l;  // l in [0, 99]
+    return static_cast<int>(l);
+  }
+
+  // Algorithm:
+  // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
+  auto iso_year_weeks(long long curr_year) const noexcept -> int {
+    const auto prev_year = curr_year - 1;
+    const auto curr_p =
+        (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
+        days_per_week;
+    const auto prev_p =
+        (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
+        days_per_week;
+    return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
+  }
+  auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int {
+    return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) /
+           days_per_week;
+  }
+  auto tm_iso_week_year() const noexcept -> long long {
+    const auto year = tm_year();
+    const auto w = iso_week_num(tm_yday(), tm_wday());
+    if (w < 1) return year - 1;
+    if (w > iso_year_weeks(year)) return year + 1;
+    return year;
+  }
+  auto tm_iso_week_of_year() const noexcept -> int {
+    const auto year = tm_year();
+    const auto w = iso_week_num(tm_yday(), tm_wday());
+    if (w < 1) return iso_year_weeks(year - 1);
+    if (w > iso_year_weeks(year)) return 1;
+    return w;
+  }
+
+  void write1(int value) {
+    *out_++ = static_cast<char>('0' + to_unsigned(value) % 10);
+  }
+  void write2(int value) {
+    const char* d = digits2(to_unsigned(value) % 100);
+    *out_++ = *d++;
+    *out_++ = *d;
+  }
+
+  void write_year_extended(long long year) {
+    // At least 4 characters.
+    int width = 4;
+    if (year < 0) {
+      *out_++ = '-';
+      year = 0 - year;
+      --width;
+    }
+    uint32_or_64_or_128_t<long long> n = to_unsigned(year);
+    const int num_digits = count_digits(n);
+    if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0');
+    out_ = format_decimal<Char>(out_, n, num_digits).end;
+  }
+  void write_year(long long year) {
+    if (year >= 0 && year < 10000) {
+      write2(static_cast<int>(year / 100));
+      write2(static_cast<int>(year % 100));
+    } else {
+      write_year_extended(year);
+    }
+  }
+
+  void write_utc_offset(long offset) {
+    if (offset < 0) {
+      *out_++ = '-';
+      offset = -offset;
+    } else {
+      *out_++ = '+';
+    }
+    offset /= 60;
+    write2(static_cast<int>(offset / 60));
+    write2(static_cast<int>(offset % 60));
+  }
+  template <typename T, FMT_ENABLE_IF(has_member_data_tm_gmtoff<T>::value)>
+  void format_utc_offset_impl(const T& tm) {
+    write_utc_offset(tm.tm_gmtoff);
+  }
+  template <typename T, FMT_ENABLE_IF(!has_member_data_tm_gmtoff<T>::value)>
+  void format_utc_offset_impl(const T& tm) {
+#if defined(_WIN32) && defined(_UCRT)
+#  if FMT_USE_TZSET
+    tzset_once();
+#  endif
+    long offset = 0;
+    _get_timezone(&offset);
+    if (tm.tm_isdst) {
+      long dstbias = 0;
+      _get_dstbias(&dstbias);
+      offset += dstbias;
+    }
+    write_utc_offset(-offset);
+#else
+    ignore_unused(tm);
+    format_localized('z');
+#endif
+  }
+
+  template <typename T, FMT_ENABLE_IF(has_member_data_tm_zone<T>::value)>
+  void format_tz_name_impl(const T& tm) {
+    if (is_classic_)
+      out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
+    else
+      format_localized('Z');
+  }
+  template <typename T, FMT_ENABLE_IF(!has_member_data_tm_zone<T>::value)>
+  void format_tz_name_impl(const T&) {
+    format_localized('Z');
+  }
+
+  void format_localized(char format, char modifier = 0) {
+    out_ = write<Char>(out_, tm_, loc_, format, modifier);
+  }
+
+ public:
+  tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm)
+      : loc_(loc),
+        is_classic_(loc_ == get_classic_locale()),
+        out_(out),
+        tm_(tm) {}
+
+  OutputIt out() const { return out_; }
+
+  FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
+    out_ = copy_str<Char>(begin, end, out_);
+  }
+
+  void on_abbr_weekday() {
+    if (is_classic_)
+      out_ = write(out_, tm_wday_short_name(tm_wday()));
+    else
+      format_localized('a');
+  }
+  void on_full_weekday() {
+    if (is_classic_)
+      out_ = write(out_, tm_wday_full_name(tm_wday()));
+    else
+      format_localized('A');
+  }
+  void on_dec0_weekday(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday());
+    format_localized('w', 'O');
+  }
+  void on_dec1_weekday(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard) {
+      auto wday = tm_wday();
+      write1(wday == 0 ? days_per_week : wday);
+    } else {
+      format_localized('u', 'O');
+    }
+  }
+
+  void on_abbr_month() {
+    if (is_classic_)
+      out_ = write(out_, tm_mon_short_name(tm_mon()));
+    else
+      format_localized('b');
+  }
+  void on_full_month() {
+    if (is_classic_)
+      out_ = write(out_, tm_mon_full_name(tm_mon()));
+    else
+      format_localized('B');
+  }
+
+  void on_datetime(numeric_system ns) {
+    if (is_classic_) {
+      on_abbr_weekday();
+      *out_++ = ' ';
+      on_abbr_month();
+      *out_++ = ' ';
+      on_day_of_month_space(numeric_system::standard);
+      *out_++ = ' ';
+      on_iso_time();
+      *out_++ = ' ';
+      on_year(numeric_system::standard);
+    } else {
+      format_localized('c', ns == numeric_system::standard ? '\0' : 'E');
+    }
+  }
+  void on_loc_date(numeric_system ns) {
+    if (is_classic_)
+      on_us_date();
+    else
+      format_localized('x', ns == numeric_system::standard ? '\0' : 'E');
+  }
+  void on_loc_time(numeric_system ns) {
+    if (is_classic_)
+      on_iso_time();
+    else
+      format_localized('X', ns == numeric_system::standard ? '\0' : 'E');
+  }
+  void on_us_date() {
+    char buf[8];
+    write_digit2_separated(buf, to_unsigned(tm_mon() + 1),
+                           to_unsigned(tm_mday()),
+                           to_unsigned(split_year_lower(tm_year())), '/');
+    out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
+  }
+  void on_iso_date() {
+    auto year = tm_year();
+    char buf[10];
+    size_t offset = 0;
+    if (year >= 0 && year < 10000) {
+      copy2(buf, digits2(static_cast<size_t>(year / 100)));
+    } else {
+      offset = 4;
+      write_year_extended(year);
+      year = 0;
+    }
+    write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100),
+                           to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
+                           '-');
+    out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_);
+  }
+
+  void on_utc_offset() { format_utc_offset_impl(tm_); }
+  void on_tz_name() { format_tz_name_impl(tm_); }
+
+  void on_year(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write_year(tm_year());
+    format_localized('Y', 'E');
+  }
+  void on_short_year(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write2(split_year_lower(tm_year()));
+    format_localized('y', 'O');
+  }
+  void on_offset_year() {
+    if (is_classic_) return write2(split_year_lower(tm_year()));
+    format_localized('y', 'E');
+  }
+
+  void on_century(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard) {
+      auto year = tm_year();
+      auto upper = year / 100;
+      if (year >= -99 && year < 0) {
+        // Zero upper on negative year.
+        *out_++ = '-';
+        *out_++ = '0';
+      } else if (upper >= 0 && upper < 100) {
+        write2(static_cast<int>(upper));
+      } else {
+        out_ = write<Char>(out_, upper);
+      }
+    } else {
+      format_localized('C', 'E');
+    }
+  }
+
+  void on_dec_month(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write2(tm_mon() + 1);
+    format_localized('m', 'O');
+  }
+
+  void on_dec0_week_of_year(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week);
+    format_localized('U', 'O');
+  }
+  void on_dec1_week_of_year(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard) {
+      auto wday = tm_wday();
+      write2((tm_yday() + days_per_week -
+              (wday == 0 ? (days_per_week - 1) : (wday - 1))) /
+             days_per_week);
+    } else {
+      format_localized('W', 'O');
+    }
+  }
+  void on_iso_week_of_year(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write2(tm_iso_week_of_year());
+    format_localized('V', 'O');
+  }
+
+  void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
+  void on_iso_week_based_short_year() {
+    write2(split_year_lower(tm_iso_week_year()));
+  }
+
+  void on_day_of_year() {
+    auto yday = tm_yday() + 1;
+    write1(yday / 100);
+    write2(yday % 100);
+  }
+  void on_day_of_month(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday());
+    format_localized('d', 'O');
+  }
+  void on_day_of_month_space(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard) {
+      auto mday = to_unsigned(tm_mday()) % 100;
+      const char* d2 = digits2(mday);
+      *out_++ = mday < 10 ? ' ' : d2[0];
+      *out_++ = d2[1];
+    } else {
+      format_localized('e', 'O');
+    }
+  }
+
+  void on_24_hour(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour());
+    format_localized('H', 'O');
+  }
+  void on_12_hour(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard)
+      return write2(tm_hour12());
+    format_localized('I', 'O');
+  }
+  void on_minute(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard) return write2(tm_min());
+    format_localized('M', 'O');
+  }
+  void on_second(numeric_system ns) {
+    if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec());
+    format_localized('S', 'O');
+  }
+
+  void on_12_hour_time() {
+    if (is_classic_) {
+      char buf[8];
+      write_digit2_separated(buf, to_unsigned(tm_hour12()),
+                             to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
+      out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
+      *out_++ = ' ';
+      on_am_pm();
+    } else {
+      format_localized('r');
+    }
+  }
+  void on_24_hour_time() {
+    write2(tm_hour());
+    *out_++ = ':';
+    write2(tm_min());
+  }
+  void on_iso_time() {
+    char buf[8];
+    write_digit2_separated(buf, to_unsigned(tm_hour()), to_unsigned(tm_min()),
+                           to_unsigned(tm_sec()), ':');
+    out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
+  }
+
+  void on_am_pm() {
+    if (is_classic_) {
+      *out_++ = tm_hour() < 12 ? 'A' : 'P';
+      *out_++ = 'M';
+    } else {
+      format_localized('p');
+    }
+  }
+
+  // These apply to chrono durations but not tm.
+  void on_duration_value() {}
+  void on_duration_unit() {}
+};
+
+struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
+  FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
+
+  template <typename Char>
+  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
+  FMT_CONSTEXPR void on_24_hour(numeric_system) {}
+  FMT_CONSTEXPR void on_12_hour(numeric_system) {}
+  FMT_CONSTEXPR void on_minute(numeric_system) {}
+  FMT_CONSTEXPR void on_second(numeric_system) {}
+  FMT_CONSTEXPR void on_12_hour_time() {}
+  FMT_CONSTEXPR void on_24_hour_time() {}
+  FMT_CONSTEXPR void on_iso_time() {}
+  FMT_CONSTEXPR void on_am_pm() {}
+  FMT_CONSTEXPR void on_duration_value() {}
+  FMT_CONSTEXPR void on_duration_unit() {}
+};
+
+template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+inline bool isfinite(T) {
+  return true;
+}
+
+// Converts value to Int and checks that it's in the range [0, upper).
+template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
+inline Int to_nonnegative_int(T value, Int upper) {
+  FMT_ASSERT(std::is_unsigned<Int>::value ||
+             (value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
+             "invalid value");
+  (void)upper;
+  return static_cast<Int>(value);
+}
+template <typename T, typename Int, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+inline Int to_nonnegative_int(T value, Int upper) {
+  if (value < 0 || value > static_cast<T>(upper))
+    FMT_THROW(format_error("invalid value"));
+  return static_cast<Int>(value);
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+inline T mod(T x, int y) {
+  return x % static_cast<T>(y);
+}
+template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+inline T mod(T x, int y) {
+  return std::fmod(x, static_cast<T>(y));
+}
+
+// If T is an integral type, maps T to its unsigned counterpart, otherwise
+// leaves it unchanged (unlike std::make_unsigned).
+template <typename T, bool INTEGRAL = std::is_integral<T>::value>
+struct make_unsigned_or_unchanged {
+  using type = T;
+};
+
+template <typename T> struct make_unsigned_or_unchanged<T, true> {
+  using type = typename std::make_unsigned<T>::type;
+};
+
+#if FMT_SAFE_DURATION_CAST
+// throwing version of safe_duration_cast
+template <typename To, typename FromRep, typename FromPeriod>
+To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
+  int ec;
+  To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
+  if (ec) FMT_THROW(format_error("cannot format duration"));
+  return to;
+}
+#endif
+
+template <typename Rep, typename Period,
+          FMT_ENABLE_IF(std::is_integral<Rep>::value)>
+inline std::chrono::duration<Rep, std::milli> get_milliseconds(
+    std::chrono::duration<Rep, Period> d) {
+  // this may overflow and/or the result may not fit in the
+  // target type.
+#if FMT_SAFE_DURATION_CAST
+  using CommonSecondsType =
+      typename std::common_type<decltype(d), std::chrono::seconds>::type;
+  const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
+  const auto d_as_whole_seconds =
+      fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
+  // this conversion should be nonproblematic
+  const auto diff = d_as_common - d_as_whole_seconds;
+  const auto ms =
+      fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
+  return ms;
+#else
+  auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
+  return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
+#endif
+}
+
+// Counts the number of fractional digits in the range [0, 18] according to the
+// C++20 spec. If more than 18 fractional digits are required then returns 6 for
+// microseconds precision.
+template <long long Num, long long Den, int N = 0,
+          bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
+struct count_fractional_digits {
+  static constexpr int value =
+      Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
+};
+
+// Base case that doesn't instantiate any more templates
+// in order to avoid overflow.
+template <long long Num, long long Den, int N>
+struct count_fractional_digits<Num, Den, N, false> {
+  static constexpr int value = (Num % Den == 0) ? N : 6;
+};
+
+constexpr long long pow10(std::uint32_t n) {
+  return n == 0 ? 1 : 10 * pow10(n - 1);
+}
+
+template <class Rep, class Period,
+          FMT_ENABLE_IF(std::numeric_limits<Rep>::is_signed)>
+constexpr std::chrono::duration<Rep, Period> abs(
+    std::chrono::duration<Rep, Period> d) {
+  // We need to compare the duration using the count() method directly
+  // due to a compiler bug in clang-11 regarding the spaceship operator,
+  // when -Wzero-as-null-pointer-constant is enabled.
+  // In clang-12 the bug has been fixed. See
+  // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example:
+  // https://www.godbolt.org/z/Knbb5joYx.
+  return d.count() >= d.zero().count() ? d : -d;
+}
+
+template <class Rep, class Period,
+          FMT_ENABLE_IF(!std::numeric_limits<Rep>::is_signed)>
+constexpr std::chrono::duration<Rep, Period> abs(
+    std::chrono::duration<Rep, Period> d) {
+  return d;
+}
+
+template <typename Char, typename Rep, typename OutputIt,
+          FMT_ENABLE_IF(std::is_integral<Rep>::value)>
+OutputIt format_duration_value(OutputIt out, Rep val, int) {
+  return write<Char>(out, val);
+}
+
+template <typename Char, typename Rep, typename OutputIt,
+          FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
+OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
+  auto specs = basic_format_specs<Char>();
+  specs.precision = precision;
+  specs.type = precision >= 0 ? presentation_type::fixed_lower
+                              : presentation_type::general_lower;
+  return write<Char>(out, val, specs);
+}
+
+template <typename Char, typename OutputIt>
+OutputIt copy_unit(string_view unit, OutputIt out, Char) {
+  return std::copy(unit.begin(), unit.end(), out);
+}
+
+template <typename OutputIt>
+OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
+  // This works when wchar_t is UTF-32 because units only contain characters
+  // that have the same representation in UTF-16 and UTF-32.
+  utf8_to_utf16 u(unit);
+  return std::copy(u.c_str(), u.c_str() + u.size(), out);
+}
+
+template <typename Char, typename Period, typename OutputIt>
+OutputIt format_duration_unit(OutputIt out) {
+  if (const char* unit = get_units<Period>())
+    return copy_unit(string_view(unit), out, Char());
+  *out++ = '[';
+  out = write<Char>(out, Period::num);
+  if (const_check(Period::den != 1)) {
+    *out++ = '/';
+    out = write<Char>(out, Period::den);
+  }
+  *out++ = ']';
+  *out++ = 's';
+  return out;
+}
+
+class get_locale {
+ private:
+  union {
+    std::locale locale_;
+  };
+  bool has_locale_ = false;
+
+ public:
+  get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
+    if (localized)
+      ::new (&locale_) std::locale(loc.template get<std::locale>());
+  }
+  ~get_locale() {
+    if (has_locale_) locale_.~locale();
+  }
+  operator const std::locale&() const {
+    return has_locale_ ? locale_ : get_classic_locale();
+  }
+};
+
+template <typename FormatContext, typename OutputIt, typename Rep,
+          typename Period>
+struct chrono_formatter {
+  FormatContext& context;
+  OutputIt out;
+  int precision;
+  bool localized = false;
+  // rep is unsigned to avoid overflow.
+  using rep =
+      conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
+                    unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
+  rep val;
+  using seconds = std::chrono::duration<rep>;
+  seconds s;
+  using milliseconds = std::chrono::duration<rep, std::milli>;
+  bool negative;
+
+  using char_type = typename FormatContext::char_type;
+  using tm_writer_type = tm_writer<OutputIt, char_type>;
+
+  chrono_formatter(FormatContext& ctx, OutputIt o,
+                   std::chrono::duration<Rep, Period> d)
+      : context(ctx),
+        out(o),
+        val(static_cast<rep>(d.count())),
+        negative(false) {
+    if (d.count() < 0) {
+      val = 0 - val;
+      negative = true;
+    }
+
+    // this may overflow and/or the result may not fit in the
+    // target type.
+#if FMT_SAFE_DURATION_CAST
+    // might need checked conversion (rep!=Rep)
+    auto tmpval = std::chrono::duration<rep, Period>(val);
+    s = fmt_safe_duration_cast<seconds>(tmpval);
+#else
+    s = std::chrono::duration_cast<seconds>(
+        std::chrono::duration<rep, Period>(val));
+#endif
+  }
+
+  // returns true if nan or inf, writes to out.
+  bool handle_nan_inf() {
+    if (isfinite(val)) {
+      return false;
+    }
+    if (isnan(val)) {
+      write_nan();
+      return true;
+    }
+    // must be +-inf
+    if (val > 0) {
+      write_pinf();
+    } else {
+      write_ninf();
+    }
+    return true;
+  }
+
+  Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
+
+  Rep hour12() const {
+    Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
+    return hour <= 0 ? 12 : hour;
+  }
+
+  Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); }
+  Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); }
+
+  std::tm time() const {
+    auto time = std::tm();
+    time.tm_hour = to_nonnegative_int(hour(), 24);
+    time.tm_min = to_nonnegative_int(minute(), 60);
+    time.tm_sec = to_nonnegative_int(second(), 60);
+    return time;
+  }
+
+  void write_sign() {
+    if (negative) {
+      *out++ = '-';
+      negative = false;
+    }
+  }
+
+  void write(Rep value, int width) {
+    write_sign();
+    if (isnan(value)) return write_nan();
+    uint32_or_64_or_128_t<int> n =
+        to_unsigned(to_nonnegative_int(value, max_value<int>()));
+    int num_digits = detail::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).end;
+  }
+
+  template <typename Duration> void write_fractional_seconds(Duration d) {
+    FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
+    constexpr auto num_fractional_digits =
+        count_fractional_digits<Duration::period::num,
+                                Duration::period::den>::value;
+
+    using subsecond_precision = std::chrono::duration<
+        typename std::common_type<typename Duration::rep,
+                                  std::chrono::seconds::rep>::type,
+        std::ratio<1, detail::pow10(num_fractional_digits)>>;
+    if (std::ratio_less<typename subsecond_precision::period,
+                        std::chrono::seconds::period>::value) {
+      *out++ = '.';
+      auto fractional =
+          detail::abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d);
+      auto subseconds =
+          std::chrono::treat_as_floating_point<
+              typename subsecond_precision::rep>::value
+              ? fractional.count()
+              : std::chrono::duration_cast<subsecond_precision>(fractional)
+                    .count();
+      uint32_or_64_or_128_t<long long> n =
+          to_unsigned(to_nonnegative_int(subseconds, max_value<long long>()));
+      int num_digits = detail::count_digits(n);
+      if (num_fractional_digits > num_digits)
+        out = std::fill_n(out, num_fractional_digits - num_digits, '0');
+      out = format_decimal<char_type>(out, n, num_digits).end;
+    }
+  }
+
+  void write_nan() { std::copy_n("nan", 3, out); }
+  void write_pinf() { std::copy_n("inf", 3, out); }
+  void write_ninf() { std::copy_n("-inf", 4, out); }
+
+  template <typename Callback, typename... Args>
+  void format_tm(const tm& time, Callback cb, Args... args) {
+    if (isnan(val)) return write_nan();
+    get_locale loc(localized, context.locale());
+    auto w = tm_writer_type(loc, out, time);
+    (w.*cb)(args...);
+    out = w.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_year(numeric_system) {}
+  void on_short_year(numeric_system) {}
+  void on_offset_year() {}
+  void on_century(numeric_system) {}
+  void on_iso_week_based_year() {}
+  void on_iso_week_based_short_year() {}
+  void on_dec_month(numeric_system) {}
+  void on_dec0_week_of_year(numeric_system) {}
+  void on_dec1_week_of_year(numeric_system) {}
+  void on_iso_week_of_year(numeric_system) {}
+  void on_day_of_year() {}
+  void on_day_of_month(numeric_system) {}
+  void on_day_of_month_space(numeric_system) {}
+
+  void on_24_hour(numeric_system ns) {
+    if (handle_nan_inf()) return;
+
+    if (ns == numeric_system::standard) return write(hour(), 2);
+    auto time = tm();
+    time.tm_hour = to_nonnegative_int(hour(), 24);
+    format_tm(time, &tm_writer_type::on_24_hour, ns);
+  }
+
+  void on_12_hour(numeric_system ns) {
+    if (handle_nan_inf()) return;
+
+    if (ns == numeric_system::standard) return write(hour12(), 2);
+    auto time = tm();
+    time.tm_hour = to_nonnegative_int(hour12(), 12);
+    format_tm(time, &tm_writer_type::on_12_hour, ns);
+  }
+
+  void on_minute(numeric_system ns) {
+    if (handle_nan_inf()) return;
+
+    if (ns == numeric_system::standard) return write(minute(), 2);
+    auto time = tm();
+    time.tm_min = to_nonnegative_int(minute(), 60);
+    format_tm(time, &tm_writer_type::on_minute, ns);
+  }
+
+  void on_second(numeric_system ns) {
+    if (handle_nan_inf()) return;
+
+    if (ns == numeric_system::standard) {
+      if (std::is_floating_point<rep>::value) {
+        constexpr auto num_fractional_digits =
+            count_fractional_digits<Period::num, Period::den>::value;
+        auto buf = memory_buffer();
+        format_to(std::back_inserter(buf), runtime("{:.{}f}"),
+                  std::fmod(val * static_cast<rep>(Period::num) /
+                                static_cast<rep>(Period::den),
+                            static_cast<rep>(60)),
+                  num_fractional_digits);
+        if (negative) *out++ = '-';
+        if (buf.size() < 2 || buf[1] == '.') *out++ = '0';
+        out = std::copy(buf.begin(), buf.end(), out);
+      } else {
+        write(second(), 2);
+        write_fractional_seconds(std::chrono::duration<rep, Period>(val));
+      }
+      return;
+    }
+    auto time = tm();
+    time.tm_sec = to_nonnegative_int(second(), 60);
+    format_tm(time, &tm_writer_type::on_second, ns);
+  }
+
+  void on_12_hour_time() {
+    if (handle_nan_inf()) return;
+    format_tm(time(), &tm_writer_type::on_12_hour_time);
+  }
+
+  void on_24_hour_time() {
+    if (handle_nan_inf()) {
+      *out++ = ':';
+      handle_nan_inf();
+      return;
+    }
+
+    write(hour(), 2);
+    *out++ = ':';
+    write(minute(), 2);
+  }
+
+  void on_iso_time() {
+    on_24_hour_time();
+    *out++ = ':';
+    if (handle_nan_inf()) return;
+    on_second(numeric_system::standard);
+  }
+
+  void on_am_pm() {
+    if (handle_nan_inf()) return;
+    format_tm(time(), &tm_writer_type::on_am_pm);
+  }
+
+  void on_duration_value() {
+    if (handle_nan_inf()) return;
+    write_sign();
+    out = format_duration_value<char_type>(out, val, precision);
+  }
+
+  void on_duration_unit() {
+    out = format_duration_unit<char_type, Period>(out);
+  }
+};
+
+FMT_END_DETAIL_NAMESPACE
+
+#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
+using weekday = std::chrono::weekday;
+#else
+// A fallback version of weekday.
+class weekday {
+ private:
+  unsigned char value;
+
+ public:
+  weekday() = default;
+  explicit constexpr weekday(unsigned wd) noexcept
+      : value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
+  constexpr unsigned c_encoding() const noexcept { return value; }
+};
+
+class year_month_day {};
+#endif
+
+// A rudimentary weekday formatter.
+template <typename Char> struct formatter<weekday, Char> {
+ private:
+  bool localized = false;
+
+ public:
+  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
+      -> decltype(ctx.begin()) {
+    auto begin = ctx.begin(), end = ctx.end();
+    if (begin != end && *begin == 'L') {
+      ++begin;
+      localized = true;
+    }
+    return begin;
+  }
+
+  template <typename FormatContext>
+  auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) {
+    auto time = std::tm();
+    time.tm_wday = static_cast<int>(wd.c_encoding());
+    detail::get_locale loc(localized, ctx.locale());
+    auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
+    w.on_abbr_weekday();
+    return w.out();
+  }
+};
+
+template <typename Rep, typename Period, typename Char>
+struct formatter<std::chrono::duration<Rep, Period>, Char> {
+ private:
+  basic_format_specs<Char> specs;
+  int precision = -1;
+  using arg_ref_type = detail::arg_ref<Char>;
+  arg_ref_type width_ref;
+  arg_ref_type precision_ref;
+  bool localized = false;
+  basic_string_view<Char> format_str;
+  using duration = std::chrono::duration<Rep, Period>;
+
+  struct spec_handler {
+    formatter& f;
+    basic_format_parse_context<Char>& context;
+    basic_string_view<Char> format_str;
+
+    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(basic_string_view<Char> arg_id) {
+      context.check_arg_id(arg_id);
+      return arg_ref_type(arg_id);
+    }
+
+    FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) {
+      return arg_ref_type(context.next_arg_id());
+    }
+
+    void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
+    FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
+      f.specs.fill = fill;
+    }
+    FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; }
+    FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; }
+    FMT_CONSTEXPR void on_precision(int _precision) {
+      f.precision = _precision;
+    }
+    FMT_CONSTEXPR void end_precision() {}
+
+    template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
+      f.width_ref = make_arg_ref(arg_id);
+    }
+
+    template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
+      f.precision_ref = make_arg_ref(arg_id);
+    }
+  };
+
+  using iterator = typename basic_format_parse_context<Char>::iterator;
+  struct parse_range {
+    iterator begin;
+    iterator end;
+  };
+
+  FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
+    auto begin = ctx.begin(), end = ctx.end();
+    if (begin == end || *begin == '}') return {begin, begin};
+    spec_handler handler{*this, ctx, format_str};
+    begin = detail::parse_align(begin, end, handler);
+    if (begin == end) return {begin, begin};
+    begin = detail::parse_width(begin, end, handler);
+    if (begin == end) return {begin, begin};
+    if (*begin == '.') {
+      if (std::is_floating_point<Rep>::value)
+        begin = detail::parse_precision(begin, end, handler);
+      else
+        handler.on_error("precision not allowed for this argument type");
+    }
+    if (begin != end && *begin == 'L') {
+      ++begin;
+      localized = true;
+    }
+    end = detail::parse_chrono_format(begin, end,
+                                      detail::chrono_format_checker());
+    return {begin, end};
+  }
+
+ public:
+  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
+      -> decltype(ctx.begin()) {
+    auto range = do_parse(ctx);
+    format_str = basic_string_view<Char>(
+        &*range.begin, detail::to_unsigned(range.end - range.begin));
+    return range.end;
+  }
+
+  template <typename FormatContext>
+  auto format(const duration& d, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    auto specs_copy = specs;
+    auto precision_copy = precision;
+    auto begin = format_str.begin(), end = format_str.end();
+    // As a possible future optimization, we could avoid extra copying if width
+    // is not specified.
+    basic_memory_buffer<Char> buf;
+    auto out = std::back_inserter(buf);
+    detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
+                                                       width_ref, ctx);
+    detail::handle_dynamic_spec<detail::precision_checker>(precision_copy,
+                                                           precision_ref, ctx);
+    if (begin == end || *begin == '}') {
+      out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
+      detail::format_duration_unit<Char, Period>(out);
+    } else {
+      detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
+          ctx, out, d);
+      f.precision = precision_copy;
+      f.localized = localized;
+      detail::parse_chrono_format(begin, end, f);
+    }
+    return detail::write(
+        ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
+  }
+};
+
+template <typename Char, typename Duration>
+struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
+                 Char> : formatter<std::tm, Char> {
+  FMT_CONSTEXPR formatter() {
+    basic_string_view<Char> default_specs =
+        detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
+    this->do_parse(default_specs.begin(), default_specs.end());
+  }
+
+  template <typename FormatContext>
+  auto format(std::chrono::time_point<std::chrono::system_clock> val,
+              FormatContext& ctx) const -> decltype(ctx.out()) {
+    return formatter<std::tm, Char>::format(localtime(val), ctx);
+  }
+};
+
+template <typename Char> struct formatter<std::tm, Char> {
+ private:
+  enum class spec {
+    unknown,
+    year_month_day,
+    hh_mm_ss,
+  };
+  spec spec_ = spec::unknown;
+  basic_string_view<Char> specs;
+
+ protected:
+  template <typename It> FMT_CONSTEXPR auto do_parse(It begin, It end) -> It {
+    if (begin != end && *begin == ':') ++begin;
+    end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
+    // Replace default spec only if the new spec is not empty.
+    if (end != begin) specs = {begin, detail::to_unsigned(end - begin)};
+    return end;
+  }
+
+ public:
+  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
+      -> decltype(ctx.begin()) {
+    auto end = this->do_parse(ctx.begin(), ctx.end());
+    // basic_string_view<>::compare isn't constexpr before C++17.
+    if (specs.size() == 2 && specs[0] == Char('%')) {
+      if (specs[1] == Char('F'))
+        spec_ = spec::year_month_day;
+      else if (specs[1] == Char('T'))
+        spec_ = spec::hh_mm_ss;
+    }
+    return end;
+  }
+
+  template <typename FormatContext>
+  auto format(const std::tm& tm, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    const auto loc_ref = ctx.locale();
+    detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
+    auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), tm);
+    if (spec_ == spec::year_month_day)
+      w.on_iso_date();
+    else if (spec_ == spec::hh_mm_ss)
+      w.on_iso_time();
+    else
+      detail::parse_chrono_format(specs.begin(), specs.end(), w);
+    return w.out();
+  }
+};
+
+FMT_MODULE_EXPORT_END
+FMT_END_NAMESPACE
+
+#endif  // FMT_CHRONO_H_
diff --git a/include/spdlog/fmt/bundled/color.h b/include/spdlog/fmt/bundled/color.h
new file mode 100644
index 000000000..4c163277e
--- /dev/null
+++ b/include/spdlog/fmt/bundled/color.h
@@ -0,0 +1,651 @@
+// 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
+FMT_MODULE_EXPORT_BEGIN
+
+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 emphasis : uint8_t {
+  bold = 1,
+  faint = 1 << 1,
+  italic = 1 << 2,
+  underline = 1 << 3,
+  blink = 1 << 4,
+  reverse = 1 << 5,
+  conceal = 1 << 6,
+  strikethrough = 1 << 7,
+};
+
+// rgb is a struct for red, green and blue colors.
+// Using the name "rgb" makes some editors show the color in a tooltip.
+struct rgb {
+  FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
+  FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
+  FMT_CONSTEXPR rgb(uint32_t hex)
+      : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
+  FMT_CONSTEXPR 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;
+};
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+// color is a struct of either a rgb color or a terminal color.
+struct color_type {
+  FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
+  FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} {
+    value.rgb_color = static_cast<uint32_t>(rgb_color);
+  }
+  FMT_CONSTEXPR color_type(rgb rgb_color) 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) 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;
+};
+
+FMT_END_DETAIL_NAMESPACE
+
+/** A text style consisting of foreground and background colors and emphasis. */
+class text_style {
+ public:
+  FMT_CONSTEXPR text_style(emphasis em = emphasis()) 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)
+        FMT_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)
+        FMT_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 bool has_foreground() const noexcept {
+    return set_foreground_color;
+  }
+  FMT_CONSTEXPR bool has_background() const noexcept {
+    return set_background_color;
+  }
+  FMT_CONSTEXPR bool has_emphasis() const noexcept {
+    return static_cast<uint8_t>(ems) != 0;
+  }
+  FMT_CONSTEXPR detail::color_type get_foreground() const noexcept {
+    FMT_ASSERT(has_foreground(), "no foreground specified for this style");
+    return foreground_color;
+  }
+  FMT_CONSTEXPR detail::color_type get_background() const noexcept {
+    FMT_ASSERT(has_background(), "no background specified for this style");
+    return background_color;
+  }
+  FMT_CONSTEXPR emphasis get_emphasis() const noexcept {
+    FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
+    return ems;
+  }
+
+ private:
+  FMT_CONSTEXPR text_style(bool is_foreground,
+                           detail::color_type text_color) 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 text_style fg(detail::color_type foreground) noexcept;
+
+  friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept;
+
+  detail::color_type foreground_color;
+  detail::color_type background_color;
+  bool set_foreground_color;
+  bool set_background_color;
+  emphasis ems;
+};
+
+/** Creates a text style from the foreground (text) color. */
+FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept {
+  return text_style(true, foreground);
+}
+
+/** Creates a text style from the background color. */
+FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept {
+  return text_style(false, background);
+}
+
+FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
+  return text_style(lhs) | rhs;
+}
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+template <typename Char> struct ansi_color_escape {
+  FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
+                                  const char* esc) noexcept {
+    // If we have a terminal color, we need to output another escape code
+    // sequence.
+    if (!text_color.is_rgb) {
+      bool is_background = esc == string_view("\x1b[48;2;");
+      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;
+
+      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) noexcept {
+    uint8_t em_codes[num_emphases] = {};
+    if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
+    if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
+    if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
+    if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
+    if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
+    if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
+    if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
+    if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
+
+    size_t index = 0;
+    for (size_t i = 0; i < num_emphases; ++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 noexcept { return buffer; }
+
+  FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; }
+  FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept {
+    return buffer + std::char_traits<Char>::length(buffer);
+  }
+
+ private:
+  static constexpr size_t num_emphases = 8;
+  Char buffer[7u + 3u * num_emphases + 1u];
+
+  static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
+                                   char delimiter) 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);
+  }
+  static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept {
+    return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
+  }
+};
+
+template <typename Char>
+FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
+    detail::color_type foreground) noexcept {
+  return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
+}
+
+template <typename Char>
+FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
+    detail::color_type background) noexcept {
+  return ansi_color_escape<Char>(background, "\x1b[48;2;");
+}
+
+template <typename Char>
+FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
+  return ansi_color_escape<Char>(em);
+}
+
+template <typename Char> inline void fputs(const Char* chars, FILE* stream) {
+  int result = std::fputs(chars, stream);
+  if (result < 0)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
+}
+
+template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) {
+  int result = std::fputws(chars, stream);
+  if (result < 0)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
+}
+
+template <typename Char> inline void reset_color(FILE* stream) {
+  fputs("\x1b[0m", stream);
+}
+
+template <> inline void reset_color<wchar_t>(FILE* stream) {
+  fputs(L"\x1b[0m", stream);
+}
+
+template <typename Char> inline void reset_color(buffer<Char>& buffer) {
+  auto reset_color = string_view("\x1b[0m");
+  buffer.append(reset_color.begin(), reset_color.end());
+}
+
+template <typename T> struct styled_arg {
+  const T& value;
+  text_style style;
+};
+
+template <typename Char>
+void vformat_to(buffer<Char>& buf, const text_style& ts,
+                basic_string_view<Char> format_str,
+                basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+  bool has_style = false;
+  if (ts.has_emphasis()) {
+    has_style = true;
+    auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
+    buf.append(emphasis.begin(), emphasis.end());
+  }
+  if (ts.has_foreground()) {
+    has_style = true;
+    auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
+    buf.append(foreground.begin(), foreground.end());
+  }
+  if (ts.has_background()) {
+    has_style = true;
+    auto background = detail::make_background_color<Char>(ts.get_background());
+    buf.append(background.begin(), background.end());
+  }
+  detail::vformat_to(buf, format_str, args, {});
+  if (has_style) detail::reset_color<Char>(buf);
+}
+
+FMT_END_DETAIL_NAMESPACE
+
+template <typename S, typename Char = char_t<S>>
+void vprint(std::FILE* f, const text_style& ts, const S& format,
+            basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+  basic_memory_buffer<Char> buf;
+  detail::vformat_to(buf, ts, detail::to_string_view(format), args);
+  if (detail::is_utf8()) {
+    detail::print(f, basic_string_view<Char>(buf.begin(), buf.size()));
+  } else {
+    buf.push_back(Char(0));
+    detail::fputs(buf.data(), f);
+  }
+}
+
+/**
+  \rst
+  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);
+  \endrst
+ */
+template <typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_string<S>::value)>
+void print(std::FILE* f, const text_style& ts, const S& format_str,
+           const Args&... args) {
+  vprint(f, ts, format_str,
+         fmt::make_format_args<buffer_context<char_t<S>>>(args...));
+}
+
+/**
+  \rst
+  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);
+  \endrst
+ */
+template <typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_string<S>::value)>
+void print(const text_style& ts, const S& format_str, const Args&... args) {
+  return print(stdout, ts, format_str, args...);
+}
+
+template <typename S, typename Char = char_t<S>>
+inline std::basic_string<Char> vformat(
+    const text_style& ts, const S& format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+  basic_memory_buffer<Char> buf;
+  detail::vformat_to(buf, ts, detail::to_string_view(format_str), args);
+  return fmt::to_string(buf);
+}
+
+/**
+  \rst
+  Formats arguments and returns the result as a string using ANSI
+  escape sequences to specify text formatting.
+
+  **Example**::
+
+    #include <fmt/color.h>
+    std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
+                                      "The answer is {}", 42);
+  \endrst
+*/
+template <typename S, typename... Args, typename Char = char_t<S>>
+inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
+                                      const Args&... args) {
+  return fmt::vformat(ts, detail::to_string_view(format_str),
+                      fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+/**
+  Formats a string with the given text_style and writes the output to ``out``.
+ */
+template <typename OutputIt, typename Char,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
+OutputIt vformat_to(
+    OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+  auto&& buf = detail::get_buffer<Char>(out);
+  detail::vformat_to(buf, ts, format_str, args);
+  return detail::get_iterator(buf);
+}
+
+/**
+  \rst
+  Formats arguments with the given text_style, writes the result to the output
+  iterator ``out`` and returns the iterator past the end of the output range.
+
+  **Example**::
+
+    std::vector<char> out;
+    fmt::format_to(std::back_inserter(out),
+                   fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
+  \endrst
+*/
+template <typename OutputIt, typename S, typename... Args,
+          bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
+              detail::is_string<S>::value>
+inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
+                      Args&&... args) ->
+    typename std::enable_if<enable, OutputIt>::type {
+  return vformat_to(out, ts, detail::to_string_view(format_str),
+                    fmt::make_format_args<buffer_context<char_t<S>>>(args...));
+}
+
+template <typename T, typename Char>
+struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
+  template <typename FormatContext>
+  auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    const auto& ts = arg.style;
+    const auto& value = arg.value;
+    auto out = ctx.out();
+
+    bool has_style = false;
+    if (ts.has_emphasis()) {
+      has_style = true;
+      auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
+      out = std::copy(emphasis.begin(), emphasis.end(), out);
+    }
+    if (ts.has_foreground()) {
+      has_style = true;
+      auto foreground =
+          detail::make_foreground_color<Char>(ts.get_foreground());
+      out = std::copy(foreground.begin(), foreground.end(), out);
+    }
+    if (ts.has_background()) {
+      has_style = true;
+      auto background =
+          detail::make_background_color<Char>(ts.get_background());
+      out = std::copy(background.begin(), background.end(), out);
+    }
+    out = formatter<T, Char>::format(value, ctx);
+    if (has_style) {
+      auto reset_color = string_view("\x1b[0m");
+      out = std::copy(reset_color.begin(), reset_color.end(), out);
+    }
+    return out;
+  }
+};
+
+/**
+  \rst
+  Returns an argument that will be formatted using ANSI escape sequences,
+  to be used in a formatting function.
+
+  **Example**::
+
+    fmt::print("Elapsed time: {0:.2f} seconds",
+               fmt::styled(1.23, fmt::fg(fmt::color::green) |
+                                 fmt::bg(fmt::color::blue)));
+  \endrst
+ */
+template <typename T>
+FMT_CONSTEXPR auto styled(const T& value, text_style ts)
+    -> detail::styled_arg<remove_cvref_t<T>> {
+  return detail::styled_arg<remove_cvref_t<T>>{value, ts};
+}
+
+FMT_MODULE_EXPORT_END
+FMT_END_NAMESPACE
+
+#endif  // FMT_COLOR_H_
diff --git a/include/spdlog/fmt/bundled/compile.h b/include/spdlog/fmt/bundled/compile.h
new file mode 100644
index 000000000..933668c41
--- /dev/null
+++ b/include/spdlog/fmt/bundled/compile.h
@@ -0,0 +1,611 @@
+// Formatting library for C++ - experimental format string compilation
+//
+// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_COMPILE_H_
+#define FMT_COMPILE_H_
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+namespace detail {
+
+template <typename Char, typename InputIt>
+FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end,
+                                                counting_iterator it) {
+  return it + (end - begin);
+}
+
+template <typename OutputIt> class truncating_iterator_base {
+ protected:
+  OutputIt out_;
+  size_t limit_;
+  size_t count_ = 0;
+
+  truncating_iterator_base() : out_(), limit_(0) {}
+
+  truncating_iterator_base(OutputIt out, size_t limit)
+      : out_(out), limit_(limit) {}
+
+ public:
+  using iterator_category = std::output_iterator_tag;
+  using value_type = typename std::iterator_traits<OutputIt>::value_type;
+  using difference_type = std::ptrdiff_t;
+  using pointer = void;
+  using reference = void;
+  FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
+
+  OutputIt base() const { return out_; }
+  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<OutputIt, std::false_type>
+    : public truncating_iterator_base<OutputIt> {
+  mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
+
+ public:
+  using value_type = typename truncating_iterator_base<OutputIt>::value_type;
+
+  truncating_iterator() = default;
+
+  truncating_iterator(OutputIt out, size_t limit)
+      : truncating_iterator_base<OutputIt>(out, limit) {}
+
+  truncating_iterator& operator++() {
+    if (this->count_++ < this->limit_) ++this->out_;
+    return *this;
+  }
+
+  truncating_iterator operator++(int) {
+    auto it = *this;
+    ++*this;
+    return it;
+  }
+
+  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:
+  truncating_iterator() = default;
+
+  truncating_iterator(OutputIt out, size_t limit)
+      : truncating_iterator_base<OutputIt>(out, limit) {}
+
+  template <typename T> truncating_iterator& operator=(T 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; }
+};
+
+// A compile-time string which is compiled into fast formatting code.
+class compiled_string {};
+
+template <typename S>
+struct is_compiled_string : std::is_base_of<compiled_string, S> {};
+
+/**
+  \rst
+  Converts a string literal *s* into a format string that will be parsed at
+  compile time and converted into efficient formatting code. Requires C++17
+  ``constexpr if`` compiler support.
+
+  **Example**::
+
+    // Converts 42 into std::string using the most efficient method and no
+    // runtime format string processing.
+    std::string s = fmt::format(FMT_COMPILE("{}"), 42);
+  \endrst
+ */
+#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
+#  define FMT_COMPILE(s) \
+    FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
+#else
+#  define FMT_COMPILE(s) FMT_STRING(s)
+#endif
+
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+template <typename Char, size_t N,
+          fmt::detail_exported::fixed_string<Char, N> Str>
+struct udl_compiled_string : compiled_string {
+  using char_type = Char;
+  explicit constexpr operator basic_string_view<char_type>() const {
+    return {Str.data, N - 1};
+  }
+};
+#endif
+
+template <typename T, typename... Tail>
+const T& first(const T& value, const Tail&...) {
+  return value;
+}
+
+#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
+template <typename... Args> struct type_list {};
+
+// Returns a reference to the argument at index N from [first, rest...].
+template <int N, typename T, typename... Args>
+constexpr const auto& get([[maybe_unused]] const T& first,
+                          [[maybe_unused]] const Args&... rest) {
+  static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
+  if constexpr (N == 0)
+    return first;
+  else
+    return detail::get<N - 1>(rest...);
+}
+
+template <typename Char, typename... Args>
+constexpr int get_arg_index_by_name(basic_string_view<Char> name,
+                                    type_list<Args...>) {
+  return get_arg_index_by_name<Args...>(name);
+}
+
+template <int N, typename> struct get_type_impl;
+
+template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
+  using type =
+      remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
+};
+
+template <int N, typename T>
+using get_type = typename get_type_impl<N, T>::type;
+
+template <typename T> struct is_compiled_format : std::false_type {};
+
+template <typename Char> struct text {
+  basic_string_view<Char> data;
+  using char_type = Char;
+
+  template <typename OutputIt, typename... Args>
+  constexpr OutputIt format(OutputIt out, const Args&...) const {
+    return write<Char>(out, data);
+  }
+};
+
+template <typename Char>
+struct is_compiled_format<text<Char>> : std::true_type {};
+
+template <typename Char>
+constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
+                               size_t size) {
+  return {{&s[pos], size}};
+}
+
+template <typename Char> struct code_unit {
+  Char value;
+  using char_type = Char;
+
+  template <typename OutputIt, typename... Args>
+  constexpr OutputIt format(OutputIt out, const Args&...) const {
+    return write<Char>(out, value);
+  }
+};
+
+// This ensures that the argument type is convertible to `const T&`.
+template <typename T, int N, typename... Args>
+constexpr const T& get_arg_checked(const Args&... args) {
+  const auto& arg = detail::get<N>(args...);
+  if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
+    return arg.value;
+  } else {
+    return arg;
+  }
+}
+
+template <typename Char>
+struct is_compiled_format<code_unit<Char>> : std::true_type {};
+
+// A replacement field that refers to argument N.
+template <typename Char, typename T, int N> struct field {
+  using char_type = Char;
+
+  template <typename OutputIt, typename... Args>
+  constexpr OutputIt format(OutputIt out, const Args&... args) const {
+    return write<Char>(out, get_arg_checked<T, N>(args...));
+  }
+};
+
+template <typename Char, typename T, int N>
+struct is_compiled_format<field<Char, T, N>> : std::true_type {};
+
+// A replacement field that refers to argument with name.
+template <typename Char> struct runtime_named_field {
+  using char_type = Char;
+  basic_string_view<Char> name;
+
+  template <typename OutputIt, typename T>
+  constexpr static bool try_format_argument(
+      OutputIt& out,
+      // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
+      [[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
+    if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
+      if (arg_name == arg.name) {
+        out = write<Char>(out, arg.value);
+        return true;
+      }
+    }
+    return false;
+  }
+
+  template <typename OutputIt, typename... Args>
+  constexpr OutputIt format(OutputIt out, const Args&... args) const {
+    bool found = (try_format_argument(out, name, args) || ...);
+    if (!found) {
+      FMT_THROW(format_error("argument with specified name is not found"));
+    }
+    return out;
+  }
+};
+
+template <typename Char>
+struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
+
+// A replacement field that refers to argument N and has format specifiers.
+template <typename Char, typename T, int N> struct spec_field {
+  using char_type = Char;
+  formatter<T, Char> fmt;
+
+  template <typename OutputIt, typename... Args>
+  constexpr FMT_INLINE OutputIt format(OutputIt out,
+                                       const Args&... args) const {
+    const auto& vargs =
+        fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
+    basic_format_context<OutputIt, Char> ctx(out, vargs);
+    return fmt.format(get_arg_checked<T, N>(args...), ctx);
+  }
+};
+
+template <typename Char, typename T, int N>
+struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
+
+template <typename L, typename R> struct concat {
+  L lhs;
+  R rhs;
+  using char_type = typename L::char_type;
+
+  template <typename OutputIt, typename... Args>
+  constexpr OutputIt format(OutputIt out, const Args&... args) const {
+    out = lhs.format(out, args...);
+    return rhs.format(out, args...);
+  }
+};
+
+template <typename L, typename R>
+struct is_compiled_format<concat<L, R>> : std::true_type {};
+
+template <typename L, typename R>
+constexpr concat<L, R> make_concat(L lhs, R rhs) {
+  return {lhs, rhs};
+}
+
+struct unknown_format {};
+
+template <typename Char>
+constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
+  for (size_t size = str.size(); pos != size; ++pos) {
+    if (str[pos] == '{' || str[pos] == '}') break;
+  }
+  return pos;
+}
+
+template <typename Args, size_t POS, int ID, typename S>
+constexpr auto compile_format_string(S format_str);
+
+template <typename Args, size_t POS, int ID, typename T, typename S>
+constexpr auto parse_tail(T head, S format_str) {
+  if constexpr (POS !=
+                basic_string_view<typename S::char_type>(format_str).size()) {
+    constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
+    if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
+                               unknown_format>())
+      return tail;
+    else
+      return make_concat(head, tail);
+  } else {
+    return head;
+  }
+}
+
+template <typename T, typename Char> struct parse_specs_result {
+  formatter<T, Char> fmt;
+  size_t end;
+  int next_arg_id;
+};
+
+constexpr int manual_indexing_id = -1;
+
+template <typename T, typename Char>
+constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
+                                                  size_t pos, int next_arg_id) {
+  str.remove_prefix(pos);
+  auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, {},
+                                         next_arg_id);
+  auto f = formatter<T, Char>();
+  auto end = f.parse(ctx);
+  return {f, pos + fmt::detail::to_unsigned(end - str.data()),
+          next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
+}
+
+template <typename Char> struct arg_id_handler {
+  arg_ref<Char> arg_id;
+
+  constexpr int operator()() {
+    FMT_ASSERT(false, "handler cannot be used with automatic indexing");
+    return 0;
+  }
+  constexpr int operator()(int id) {
+    arg_id = arg_ref<Char>(id);
+    return 0;
+  }
+  constexpr int operator()(basic_string_view<Char> id) {
+    arg_id = arg_ref<Char>(id);
+    return 0;
+  }
+
+  constexpr void on_error(const char* message) {
+    FMT_THROW(format_error(message));
+  }
+};
+
+template <typename Char> struct parse_arg_id_result {
+  arg_ref<Char> arg_id;
+  const Char* arg_id_end;
+};
+
+template <int ID, typename Char>
+constexpr auto parse_arg_id(const Char* begin, const Char* end) {
+  auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
+  auto arg_id_end = parse_arg_id(begin, end, handler);
+  return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
+}
+
+template <typename T, typename Enable = void> struct field_type {
+  using type = remove_cvref_t<T>;
+};
+
+template <typename T>
+struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
+  using type = remove_cvref_t<decltype(T::value)>;
+};
+
+template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
+          typename S>
+constexpr auto parse_replacement_field_then_tail(S format_str) {
+  using char_type = typename S::char_type;
+  constexpr auto str = basic_string_view<char_type>(format_str);
+  constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
+  if constexpr (c == '}') {
+    return parse_tail<Args, END_POS + 1, NEXT_ID>(
+        field<char_type, typename field_type<T>::type, ARG_INDEX>(),
+        format_str);
+  } else if constexpr (c != ':') {
+    FMT_THROW(format_error("expected ':'"));
+  } else {
+    constexpr auto result = parse_specs<typename field_type<T>::type>(
+        str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
+    if constexpr (result.end >= str.size() || str[result.end] != '}') {
+      FMT_THROW(format_error("expected '}'"));
+      return 0;
+    } else {
+      return parse_tail<Args, result.end + 1, result.next_arg_id>(
+          spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
+              result.fmt},
+          format_str);
+    }
+  }
+}
+
+// Compiles a non-empty format string and returns the compiled representation
+// or unknown_format() on unrecognized input.
+template <typename Args, size_t POS, int ID, typename S>
+constexpr auto compile_format_string(S format_str) {
+  using char_type = typename S::char_type;
+  constexpr auto str = basic_string_view<char_type>(format_str);
+  if constexpr (str[POS] == '{') {
+    if constexpr (POS + 1 == str.size())
+      FMT_THROW(format_error("unmatched '{' in format string"));
+    if constexpr (str[POS + 1] == '{') {
+      return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
+    } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
+      static_assert(ID != manual_indexing_id,
+                    "cannot switch from manual to automatic argument indexing");
+      constexpr auto next_id =
+          ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
+      return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
+                                               POS + 1, ID, next_id>(
+          format_str);
+    } else {
+      constexpr auto arg_id_result =
+          parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
+      constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
+      constexpr char_type c =
+          arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
+      static_assert(c == '}' || c == ':', "missing '}' in format string");
+      if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
+        static_assert(
+            ID == manual_indexing_id || ID == 0,
+            "cannot switch from automatic to manual argument indexing");
+        constexpr auto arg_index = arg_id_result.arg_id.val.index;
+        return parse_replacement_field_then_tail<get_type<arg_index, Args>,
+                                                 Args, arg_id_end_pos,
+                                                 arg_index, manual_indexing_id>(
+            format_str);
+      } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
+        constexpr auto arg_index =
+            get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
+        if constexpr (arg_index != invalid_arg_index) {
+          constexpr auto next_id =
+              ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
+          return parse_replacement_field_then_tail<
+              decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
+              arg_index, next_id>(format_str);
+        } else {
+          if constexpr (c == '}') {
+            return parse_tail<Args, arg_id_end_pos + 1, ID>(
+                runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
+                format_str);
+          } else if constexpr (c == ':') {
+            return unknown_format();  // no type info for specs parsing
+          }
+        }
+      }
+    }
+  } else if constexpr (str[POS] == '}') {
+    if constexpr (POS + 1 == str.size())
+      FMT_THROW(format_error("unmatched '}' in format string"));
+    return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
+  } else {
+    constexpr auto end = parse_text(str, POS + 1);
+    if constexpr (end - POS > 1) {
+      return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
+                                       format_str);
+    } else {
+      return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
+                                       format_str);
+    }
+  }
+}
+
+template <typename... Args, typename S,
+          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+constexpr auto compile(S format_str) {
+  constexpr auto str = basic_string_view<typename S::char_type>(format_str);
+  if constexpr (str.size() == 0) {
+    return detail::make_text(str, 0, 0);
+  } else {
+    constexpr auto result =
+        detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
+            format_str);
+    return result;
+  }
+}
+#endif  // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
+}  // namespace detail
+
+FMT_MODULE_EXPORT_BEGIN
+
+#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
+
+template <typename CompiledFormat, typename... Args,
+          typename Char = typename CompiledFormat::char_type,
+          FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
+FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
+                                          const Args&... args) {
+  auto s = std::basic_string<Char>();
+  cf.format(std::back_inserter(s), args...);
+  return s;
+}
+
+template <typename OutputIt, typename CompiledFormat, typename... Args,
+          FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
+constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
+                                        const Args&... args) {
+  return cf.format(out, args...);
+}
+
+template <typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
+                                                           Args&&... args) {
+  if constexpr (std::is_same<typename S::char_type, char>::value) {
+    constexpr auto str = basic_string_view<typename S::char_type>(S());
+    if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
+      const auto& first = detail::first(args...);
+      if constexpr (detail::is_named_arg<
+                        remove_cvref_t<decltype(first)>>::value) {
+        return fmt::to_string(first.value);
+      } else {
+        return fmt::to_string(first);
+      }
+    }
+  }
+  constexpr auto compiled = detail::compile<Args...>(S());
+  if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
+                             detail::unknown_format>()) {
+    return fmt::format(
+        static_cast<basic_string_view<typename S::char_type>>(S()),
+        std::forward<Args>(args)...);
+  } else {
+    return fmt::format(compiled, std::forward<Args>(args)...);
+  }
+}
+
+template <typename OutputIt, typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
+  constexpr auto compiled = detail::compile<Args...>(S());
+  if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
+                             detail::unknown_format>()) {
+    return fmt::format_to(
+        out, static_cast<basic_string_view<typename S::char_type>>(S()),
+        std::forward<Args>(args)...);
+  } else {
+    return fmt::format_to(out, compiled, std::forward<Args>(args)...);
+  }
+}
+#endif
+
+template <typename OutputIt, typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
+                                         const S& format_str, Args&&... args) {
+  auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
+                           format_str, std::forward<Args>(args)...);
+  return {it.base(), it.count()};
+}
+
+template <typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+FMT_CONSTEXPR20 size_t formatted_size(const S& format_str,
+                                      const Args&... args) {
+  return fmt::format_to(detail::counting_iterator(), format_str, args...)
+      .count();
+}
+
+template <typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+void print(std::FILE* f, const S& format_str, const Args&... args) {
+  memory_buffer buffer;
+  fmt::format_to(std::back_inserter(buffer), format_str, args...);
+  detail::print(f, {buffer.data(), buffer.size()});
+}
+
+template <typename S, typename... Args,
+          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+void print(const S& format_str, const Args&... args) {
+  print(stdout, format_str, args...);
+}
+
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+inline namespace literals {
+template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
+  using char_t = remove_cvref_t<decltype(Str.data[0])>;
+  return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
+                                     Str>();
+}
+}  // namespace literals
+#endif
+
+FMT_MODULE_EXPORT_END
+FMT_END_NAMESPACE
+
+#endif  // FMT_COMPILE_H_
diff --git a/include/spdlog/fmt/bundled/core.h b/include/spdlog/fmt/bundled/core.h
new file mode 100644
index 000000000..f6a37af9e
--- /dev/null
+++ b/include/spdlog/fmt/bundled/core.h
@@ -0,0 +1,3323 @@
+// Formatting library for C++ - the core API for char/UTF-8
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_CORE_H_
+#define FMT_CORE_H_
+
+#include <cstddef>  // std::byte
+#include <cstdio>   // std::FILE
+#include <cstring>  // std::strlen
+#include <iterator>
+#include <limits>
+#include <string>
+#include <type_traits>
+
+// The fmt library version in the form major * 10000 + minor * 100 + patch.
+#define FMT_VERSION 90100
+
+#if defined(__clang__) && !defined(__ibmxl__)
+#  define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
+#else
+#  define FMT_CLANG_VERSION 0
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \
+    !defined(__NVCOMPILER)
+#  define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#else
+#  define FMT_GCC_VERSION 0
+#endif
+
+#ifndef FMT_GCC_PRAGMA
+// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884.
+#  if FMT_GCC_VERSION >= 504
+#    define FMT_GCC_PRAGMA(arg) _Pragma(arg)
+#  else
+#    define FMT_GCC_PRAGMA(arg)
+#  endif
+#endif
+
+#ifdef __ICL
+#  define FMT_ICC_VERSION __ICL
+#elif defined(__INTEL_COMPILER)
+#  define FMT_ICC_VERSION __INTEL_COMPILER
+#else
+#  define FMT_ICC_VERSION 0
+#endif
+
+#ifdef _MSC_VER
+#  define FMT_MSC_VERSION _MSC_VER
+#  define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__))
+#else
+#  define FMT_MSC_VERSION 0
+#  define FMT_MSC_WARNING(...)
+#endif
+
+#ifdef _MSVC_LANG
+#  define FMT_CPLUSPLUS _MSVC_LANG
+#else
+#  define FMT_CPLUSPLUS __cplusplus
+#endif
+
+#ifdef __has_feature
+#  define FMT_HAS_FEATURE(x) __has_feature(x)
+#else
+#  define FMT_HAS_FEATURE(x) 0
+#endif
+
+#if (defined(__has_include) || FMT_ICC_VERSION >= 1600 || \
+     FMT_MSC_VERSION > 1900) &&                           \
+    !defined(__INTELLISENSE__)
+#  define FMT_HAS_INCLUDE(x) __has_include(x)
+#else
+#  define FMT_HAS_INCLUDE(x) 0
+#endif
+
+#ifdef __has_cpp_attribute
+#  define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
+#else
+#  define FMT_HAS_CPP_ATTRIBUTE(x) 0
+#endif
+
+#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
+  (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+
+#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
+  (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+
+// Check if relaxed C++14 constexpr is supported.
+// GCC doesn't allow throw in constexpr until version 6 (bug 67371).
+#ifndef FMT_USE_CONSTEXPR
+#  if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \
+       (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) &&             \
+      !FMT_ICC_VERSION && !defined(__NVCC__)
+#    define FMT_USE_CONSTEXPR 1
+#  else
+#    define FMT_USE_CONSTEXPR 0
+#  endif
+#endif
+#if FMT_USE_CONSTEXPR
+#  define FMT_CONSTEXPR constexpr
+#else
+#  define FMT_CONSTEXPR
+#endif
+
+#if ((FMT_CPLUSPLUS >= 202002L) &&                            \
+     (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \
+    (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
+#  define FMT_CONSTEXPR20 constexpr
+#else
+#  define FMT_CONSTEXPR20
+#endif
+
+// Check if constexpr std::char_traits<>::{compare,length} are supported.
+#if defined(__GLIBCXX__)
+#  if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \
+      _GLIBCXX_RELEASE >= 7  // GCC 7+ libstdc++ has _GLIBCXX_RELEASE.
+#    define FMT_CONSTEXPR_CHAR_TRAITS constexpr
+#  endif
+#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \
+    _LIBCPP_VERSION >= 4000
+#  define FMT_CONSTEXPR_CHAR_TRAITS constexpr
+#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L
+#  define FMT_CONSTEXPR_CHAR_TRAITS constexpr
+#endif
+#ifndef FMT_CONSTEXPR_CHAR_TRAITS
+#  define FMT_CONSTEXPR_CHAR_TRAITS
+#endif
+
+// Check if exceptions are disabled.
+#ifndef FMT_EXCEPTIONS
+#  if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
+      (FMT_MSC_VERSION && !_HAS_EXCEPTIONS)
+#    define FMT_EXCEPTIONS 0
+#  else
+#    define FMT_EXCEPTIONS 1
+#  endif
+#endif
+
+#ifndef FMT_DEPRECATED
+#  if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900
+#    define FMT_DEPRECATED [[deprecated]]
+#  else
+#    if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
+#      define FMT_DEPRECATED __attribute__((deprecated))
+#    elif FMT_MSC_VERSION
+#      define FMT_DEPRECATED __declspec(deprecated)
+#    else
+#      define FMT_DEPRECATED /* deprecated */
+#    endif
+#  endif
+#endif
+
+// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code
+// warnings.
+#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \
+    !defined(__NVCC__)
+#  define FMT_NORETURN [[noreturn]]
+#else
+#  define FMT_NORETURN
+#endif
+
+#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
+#  define FMT_FALLTHROUGH [[fallthrough]]
+#elif defined(__clang__)
+#  define FMT_FALLTHROUGH [[clang::fallthrough]]
+#elif FMT_GCC_VERSION >= 700 && \
+    (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
+#  define FMT_FALLTHROUGH [[gnu::fallthrough]]
+#else
+#  define FMT_FALLTHROUGH
+#endif
+
+#ifndef FMT_NODISCARD
+#  if FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
+#    define FMT_NODISCARD [[nodiscard]]
+#  else
+#    define FMT_NODISCARD
+#  endif
+#endif
+
+#ifndef FMT_USE_FLOAT
+#  define FMT_USE_FLOAT 1
+#endif
+#ifndef FMT_USE_DOUBLE
+#  define FMT_USE_DOUBLE 1
+#endif
+#ifndef FMT_USE_LONG_DOUBLE
+#  define FMT_USE_LONG_DOUBLE 1
+#endif
+
+#ifndef FMT_INLINE
+#  if FMT_GCC_VERSION || FMT_CLANG_VERSION
+#    define FMT_INLINE inline __attribute__((always_inline))
+#  else
+#    define FMT_INLINE inline
+#  endif
+#endif
+
+// An inline std::forward replacement.
+#define FMT_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
+
+#ifdef _MSC_VER
+#  define FMT_UNCHECKED_ITERATOR(It) \
+    using _Unchecked_type = It  // Mark iterator as checked.
+#else
+#  define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It
+#endif
+
+#ifndef FMT_BEGIN_NAMESPACE
+#  define FMT_BEGIN_NAMESPACE \
+    namespace fmt {           \
+    inline namespace v9 {
+#  define FMT_END_NAMESPACE \
+    }                       \
+    }
+#endif
+
+#ifndef FMT_MODULE_EXPORT
+#  define FMT_MODULE_EXPORT
+#  define FMT_MODULE_EXPORT_BEGIN
+#  define FMT_MODULE_EXPORT_END
+#  define FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
+#  define FMT_END_DETAIL_NAMESPACE }
+#endif
+
+#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
+#  define FMT_CLASS_API FMT_MSC_WARNING(suppress : 4275)
+#  ifdef FMT_EXPORT
+#    define FMT_API __declspec(dllexport)
+#  elif defined(FMT_SHARED)
+#    define FMT_API __declspec(dllimport)
+#  endif
+#else
+#  define FMT_CLASS_API
+#  if defined(FMT_EXPORT) || defined(FMT_SHARED)
+#    if defined(__GNUC__) || defined(__clang__)
+#      define FMT_API __attribute__((visibility("default")))
+#    endif
+#  endif
+#endif
+#ifndef FMT_API
+#  define FMT_API
+#endif
+
+// libc++ supports string_view in pre-c++17.
+#if FMT_HAS_INCLUDE(<string_view>) && \
+    (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION))
+#  include <string_view>
+#  define FMT_USE_STRING_VIEW
+#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L
+#  include <experimental/string_view>
+#  define FMT_USE_EXPERIMENTAL_STRING_VIEW
+#endif
+
+#ifndef FMT_UNICODE
+#  define FMT_UNICODE !FMT_MSC_VERSION
+#endif
+
+#ifndef FMT_CONSTEVAL
+#  if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) &&         \
+       FMT_CPLUSPLUS >= 202002L && !defined(__apple_build_version__)) || \
+      (defined(__cpp_consteval) &&                                       \
+       (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704))
+// consteval is broken in MSVC before VS2022 and Apple clang 13.
+#    define FMT_CONSTEVAL consteval
+#    define FMT_HAS_CONSTEVAL
+#  else
+#    define FMT_CONSTEVAL
+#  endif
+#endif
+
+#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS
+#  if defined(__cpp_nontype_template_args) &&                  \
+      ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \
+       __cpp_nontype_template_args >= 201911L) &&              \
+      !defined(__NVCOMPILER)
+#    define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
+#  else
+#    define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
+#  endif
+#endif
+
+// Enable minimal optimizations for more compact code in debug mode.
+FMT_GCC_PRAGMA("GCC push_options")
+#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER)
+FMT_GCC_PRAGMA("GCC optimize(\"Og\")")
+#endif
+
+FMT_BEGIN_NAMESPACE
+FMT_MODULE_EXPORT_BEGIN
+
+// Implementations of enable_if_t and other metafunctions for older systems.
+template <bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+template <bool B, typename T, typename F>
+using conditional_t = typename std::conditional<B, T, F>::type;
+template <bool B> using bool_constant = std::integral_constant<bool, B>;
+template <typename T>
+using remove_reference_t = typename std::remove_reference<T>::type;
+template <typename T>
+using remove_const_t = typename std::remove_const<T>::type;
+template <typename T>
+using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
+template <typename T> struct type_identity { using type = T; };
+template <typename T> using type_identity_t = typename type_identity<T>::type;
+template <typename T>
+using underlying_t = typename std::underlying_type<T>::type;
+
+template <typename...> struct disjunction : std::false_type {};
+template <typename P> struct disjunction<P> : P {};
+template <typename P1, typename... Pn>
+struct disjunction<P1, Pn...>
+    : conditional_t<bool(P1::value), P1, disjunction<Pn...>> {};
+
+template <typename...> struct conjunction : std::true_type {};
+template <typename P> struct conjunction<P> : P {};
+template <typename P1, typename... Pn>
+struct conjunction<P1, Pn...>
+    : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
+
+struct monostate {
+  constexpr monostate() {}
+};
+
+// An enable_if helper to be used in template parameters which results in much
+// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed
+// to workaround a bug in MSVC 2019 (see #1140 and #1186).
+#ifdef FMT_DOC
+#  define FMT_ENABLE_IF(...)
+#else
+#  define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0
+#endif
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+// Suppresses "unused variable" warnings with the method described in
+// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
+// (void)var does not work on many Intel compilers.
+template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
+
+constexpr FMT_INLINE auto is_constant_evaluated(
+    bool default_value = false) noexcept -> bool {
+#ifdef __cpp_lib_is_constant_evaluated
+  ignore_unused(default_value);
+  return std::is_constant_evaluated();
+#else
+  return default_value;
+#endif
+}
+
+// Suppresses "conditional expression is constant" warnings.
+template <typename T> constexpr FMT_INLINE auto const_check(T value) -> T {
+  return value;
+}
+
+FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
+                                      const char* message);
+
+#ifndef FMT_ASSERT
+#  ifdef NDEBUG
+// FMT_ASSERT is not empty to avoid -Wempty-body.
+#    define FMT_ASSERT(condition, message) \
+      ::fmt::detail::ignore_unused((condition), (message))
+#  else
+#    define FMT_ASSERT(condition, message)                                    \
+      ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
+           ? (void)0                                                          \
+           : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
+#  endif
+#endif
+
+#if defined(FMT_USE_STRING_VIEW)
+template <typename Char> using std_string_view = std::basic_string_view<Char>;
+#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
+template <typename Char>
+using std_string_view = std::experimental::basic_string_view<Char>;
+#else
+template <typename T> struct std_string_view {};
+#endif
+
+#ifdef FMT_USE_INT128
+// Do nothing.
+#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \
+    !(FMT_CLANG_VERSION && FMT_MSC_VERSION)
+#  define FMT_USE_INT128 1
+using int128_opt = __int128_t;  // An optional native 128-bit integer.
+using uint128_opt = __uint128_t;
+template <typename T> inline auto convert_for_visit(T value) -> T {
+  return value;
+}
+#else
+#  define FMT_USE_INT128 0
+#endif
+#if !FMT_USE_INT128
+enum class int128_opt {};
+enum class uint128_opt {};
+// Reduce template instantiations.
+template <typename T> auto convert_for_visit(T) -> monostate { return {}; }
+#endif
+
+// Casts a nonnegative integer to unsigned.
+template <typename Int>
+FMT_CONSTEXPR auto to_unsigned(Int value) ->
+    typename std::make_unsigned<Int>::type {
+  FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value");
+  return static_cast<typename std::make_unsigned<Int>::type>(value);
+}
+
+FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5";
+
+constexpr auto is_utf8() -> bool {
+  // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297).
+  using uchar = unsigned char;
+  return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 &&
+                         uchar(micro[1]) == 0xB5);
+}
+FMT_END_DETAIL_NAMESPACE
+
+/**
+  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
+  if ``std::string_view`` is available to prevent issues when a library is
+  compiled with a different ``-std`` option than the client code (which is not
+  recommended).
+ */
+template <typename Char> class basic_string_view {
+ private:
+  const Char* data_;
+  size_t size_;
+
+ public:
+  using value_type = Char;
+  using iterator = const Char*;
+
+  constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
+
+  /** Constructs a string reference object from a C string and a size. */
+  constexpr basic_string_view(const Char* s, size_t count) noexcept
+      : data_(s), size_(count) {}
+
+  /**
+    \rst
+    Constructs a string reference object from a C string computing
+    the size with ``std::char_traits<Char>::length``.
+    \endrst
+   */
+  FMT_CONSTEXPR_CHAR_TRAITS
+  FMT_INLINE
+  basic_string_view(const Char* s)
+      : data_(s),
+        size_(detail::const_check(std::is_same<Char, char>::value &&
+                                  !detail::is_constant_evaluated(true))
+                  ? std::strlen(reinterpret_cast<const char*>(s))
+                  : std::char_traits<Char>::length(s)) {}
+
+  /** Constructs a string reference from a ``std::basic_string`` object. */
+  template <typename Traits, typename Alloc>
+  FMT_CONSTEXPR basic_string_view(
+      const std::basic_string<Char, Traits, Alloc>& s) noexcept
+      : data_(s.data()), size_(s.size()) {}
+
+  template <typename S, FMT_ENABLE_IF(std::is_same<
+                                      S, detail::std_string_view<Char>>::value)>
+  FMT_CONSTEXPR basic_string_view(S s) noexcept
+      : data_(s.data()), size_(s.size()) {}
+
+  /** Returns a pointer to the string data. */
+  constexpr auto data() const noexcept -> const Char* { return data_; }
+
+  /** Returns the string size. */
+  constexpr auto size() const noexcept -> size_t { return size_; }
+
+  constexpr auto begin() const noexcept -> iterator { return data_; }
+  constexpr auto end() const noexcept -> iterator { return data_ + size_; }
+
+  constexpr auto operator[](size_t pos) const noexcept -> const Char& {
+    return data_[pos];
+  }
+
+  FMT_CONSTEXPR void remove_prefix(size_t n) noexcept {
+    data_ += n;
+    size_ -= n;
+  }
+
+  // Lexicographically compare this string reference to other.
+  FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int {
+    size_t str_size = size_ < other.size_ ? size_ : other.size_;
+    int result = std::char_traits<Char>::compare(data_, other.data_, str_size);
+    if (result == 0)
+      result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
+    return result;
+  }
+
+  FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs,
+                                                   basic_string_view rhs)
+      -> bool {
+    return lhs.compare(rhs) == 0;
+  }
+  friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool {
+    return lhs.compare(rhs) != 0;
+  }
+  friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool {
+    return lhs.compare(rhs) < 0;
+  }
+  friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool {
+    return lhs.compare(rhs) <= 0;
+  }
+  friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool {
+    return lhs.compare(rhs) > 0;
+  }
+  friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool {
+    return lhs.compare(rhs) >= 0;
+  }
+};
+
+using string_view = basic_string_view<char>;
+
+/** Specifies if ``T`` is a character type. Can be specialized by users. */
+template <typename T> struct is_char : std::false_type {};
+template <> struct is_char<char> : std::true_type {};
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+// A base class for compile-time strings.
+struct compile_string {};
+
+template <typename S>
+struct is_compile_string : std::is_base_of<compile_string, S> {};
+
+// Returns a string view of `s`.
+template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
+FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view<Char> {
+  return s;
+}
+template <typename Char, typename Traits, typename Alloc>
+inline auto to_string_view(const std::basic_string<Char, Traits, Alloc>& s)
+    -> basic_string_view<Char> {
+  return s;
+}
+template <typename Char>
+constexpr auto to_string_view(basic_string_view<Char> s)
+    -> basic_string_view<Char> {
+  return s;
+}
+template <typename Char,
+          FMT_ENABLE_IF(!std::is_empty<std_string_view<Char>>::value)>
+inline auto to_string_view(std_string_view<Char> s) -> basic_string_view<Char> {
+  return s;
+}
+template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
+constexpr auto to_string_view(const S& s)
+    -> basic_string_view<typename S::char_type> {
+  return basic_string_view<typename S::char_type>(s);
+}
+void to_string_view(...);
+
+// Specifies whether S is a string type convertible to fmt::basic_string_view.
+// It should be a constexpr function but MSVC 2017 fails to compile it in
+// enable_if and MSVC 2015 fails to compile it as an alias template.
+// ADL invocation of to_string_view is DEPRECATED!
+template <typename S>
+struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))> {
+};
+
+template <typename S, typename = void> struct char_t_impl {};
+template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
+  using result = decltype(to_string_view(std::declval<S>()));
+  using type = typename result::value_type;
+};
+
+enum class type {
+  none_type,
+  // Integer types should go first,
+  int_type,
+  uint_type,
+  long_long_type,
+  ulong_long_type,
+  int128_type,
+  uint128_type,
+  bool_type,
+  char_type,
+  last_integer_type = char_type,
+  // followed by floating-point types.
+  float_type,
+  double_type,
+  long_double_type,
+  last_numeric_type = long_double_type,
+  cstring_type,
+  string_type,
+  pointer_type,
+  custom_type
+};
+
+// Maps core type T to the corresponding type enum constant.
+template <typename T, typename Char>
+struct type_constant : std::integral_constant<type, type::custom_type> {};
+
+#define FMT_TYPE_CONSTANT(Type, constant) \
+  template <typename Char>                \
+  struct type_constant<Type, Char>        \
+      : std::integral_constant<type, type::constant> {}
+
+FMT_TYPE_CONSTANT(int, int_type);
+FMT_TYPE_CONSTANT(unsigned, uint_type);
+FMT_TYPE_CONSTANT(long long, long_long_type);
+FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
+FMT_TYPE_CONSTANT(int128_opt, int128_type);
+FMT_TYPE_CONSTANT(uint128_opt, uint128_type);
+FMT_TYPE_CONSTANT(bool, bool_type);
+FMT_TYPE_CONSTANT(Char, char_type);
+FMT_TYPE_CONSTANT(float, float_type);
+FMT_TYPE_CONSTANT(double, double_type);
+FMT_TYPE_CONSTANT(long double, long_double_type);
+FMT_TYPE_CONSTANT(const Char*, cstring_type);
+FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
+FMT_TYPE_CONSTANT(const void*, pointer_type);
+
+constexpr bool is_integral_type(type t) {
+  return t > type::none_type && t <= type::last_integer_type;
+}
+
+constexpr bool is_arithmetic_type(type t) {
+  return t > type::none_type && t <= type::last_numeric_type;
+}
+
+FMT_NORETURN FMT_API void throw_format_error(const char* message);
+
+struct error_handler {
+  constexpr error_handler() = default;
+  constexpr error_handler(const error_handler&) = default;
+
+  // This function is intentionally not constexpr to give a compile-time error.
+  FMT_NORETURN void on_error(const char* message) {
+    throw_format_error(message);
+  }
+};
+FMT_END_DETAIL_NAMESPACE
+
+/** String's character type. */
+template <typename S> using char_t = typename detail::char_t_impl<S>::type;
+
+/**
+  \rst
+  Parsing context consisting of a format string range being parsed and an
+  argument counter for automatic indexing.
+  You can use the ``format_parse_context`` type alias for ``char`` instead.
+  \endrst
+ */
+template <typename Char, typename ErrorHandler = detail::error_handler>
+class basic_format_parse_context : private ErrorHandler {
+ private:
+  basic_string_view<Char> format_str_;
+  int next_arg_id_;
+
+  FMT_CONSTEXPR void do_check_arg_id(int id);
+
+ public:
+  using char_type = Char;
+  using iterator = typename basic_string_view<Char>::iterator;
+
+  explicit constexpr basic_format_parse_context(
+      basic_string_view<Char> format_str, ErrorHandler eh = {},
+      int next_arg_id = 0)
+      : ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {}
+
+  /**
+    Returns an iterator to the beginning of the format string range being
+    parsed.
+   */
+  constexpr auto begin() const noexcept -> iterator {
+    return format_str_.begin();
+  }
+
+  /**
+    Returns an iterator past the end of the format string range being parsed.
+   */
+  constexpr auto end() const noexcept -> iterator { return format_str_.end(); }
+
+  /** Advances the begin iterator to ``it``. */
+  FMT_CONSTEXPR void advance_to(iterator it) {
+    format_str_.remove_prefix(detail::to_unsigned(it - begin()));
+  }
+
+  /**
+    Reports an error if using the manual argument indexing; otherwise returns
+    the next argument index and switches to the automatic indexing.
+   */
+  FMT_CONSTEXPR auto next_arg_id() -> int {
+    if (next_arg_id_ < 0) {
+      on_error("cannot switch from manual to automatic argument indexing");
+      return 0;
+    }
+    int id = next_arg_id_++;
+    do_check_arg_id(id);
+    return id;
+  }
+
+  /**
+    Reports an error if using the automatic argument indexing; otherwise
+    switches to the manual indexing.
+   */
+  FMT_CONSTEXPR void check_arg_id(int id) {
+    if (next_arg_id_ > 0) {
+      on_error("cannot switch from automatic to manual argument indexing");
+      return;
+    }
+    next_arg_id_ = -1;
+    do_check_arg_id(id);
+  }
+  FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
+  FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
+
+  FMT_CONSTEXPR void on_error(const char* message) {
+    ErrorHandler::on_error(message);
+  }
+
+  constexpr auto error_handler() const -> ErrorHandler { return *this; }
+};
+
+using format_parse_context = basic_format_parse_context<char>;
+
+FMT_BEGIN_DETAIL_NAMESPACE
+// A parse context with extra data used only in compile-time checks.
+template <typename Char, typename ErrorHandler = detail::error_handler>
+class compile_parse_context
+    : public basic_format_parse_context<Char, ErrorHandler> {
+ private:
+  int num_args_;
+  const type* types_;
+  using base = basic_format_parse_context<Char, ErrorHandler>;
+
+ public:
+  explicit FMT_CONSTEXPR compile_parse_context(
+      basic_string_view<Char> format_str, int num_args, const type* types,
+      ErrorHandler eh = {}, int next_arg_id = 0)
+      : base(format_str, eh, next_arg_id), num_args_(num_args), types_(types) {}
+
+  constexpr auto num_args() const -> int { return num_args_; }
+  constexpr auto arg_type(int id) const -> type { return types_[id]; }
+
+  FMT_CONSTEXPR auto next_arg_id() -> int {
+    int id = base::next_arg_id();
+    if (id >= num_args_) this->on_error("argument not found");
+    return id;
+  }
+
+  FMT_CONSTEXPR void check_arg_id(int id) {
+    base::check_arg_id(id);
+    if (id >= num_args_) this->on_error("argument not found");
+  }
+  using base::check_arg_id;
+
+  FMT_CONSTEXPR void check_dynamic_spec(int arg_id) {
+    if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id]))
+      this->on_error("width/precision is not integer");
+  }
+};
+FMT_END_DETAIL_NAMESPACE
+
+template <typename Char, typename ErrorHandler>
+FMT_CONSTEXPR void
+basic_format_parse_context<Char, ErrorHandler>::do_check_arg_id(int id) {
+  // Argument id is only checked at compile-time during parsing because
+  // formatting has its own validation.
+  if (detail::is_constant_evaluated() && FMT_GCC_VERSION >= 1200) {
+    using context = detail::compile_parse_context<Char, ErrorHandler>;
+    if (id >= static_cast<context*>(this)->num_args())
+      on_error("argument not found");
+  }
+}
+
+template <typename Char, typename ErrorHandler>
+FMT_CONSTEXPR void
+basic_format_parse_context<Char, ErrorHandler>::check_dynamic_spec(int arg_id) {
+  if (detail::is_constant_evaluated()) {
+    using context = detail::compile_parse_context<Char, ErrorHandler>;
+    static_cast<context*>(this)->check_dynamic_spec(arg_id);
+  }
+}
+
+template <typename Context> class basic_format_arg;
+template <typename Context> class basic_format_args;
+template <typename Context> class dynamic_format_arg_store;
+
+// A formatter for objects of type T.
+template <typename T, typename Char = char, typename Enable = void>
+struct formatter {
+  // A deleted default constructor indicates a disabled formatter.
+  formatter() = delete;
+};
+
+// Specifies if T has an enabled formatter specialization. A type can be
+// formattable even if it doesn't have a formatter e.g. via a conversion.
+template <typename T, typename Context>
+using has_formatter =
+    std::is_constructible<typename Context::template formatter_type<T>>;
+
+// Checks whether T is a container with contiguous storage.
+template <typename T> struct is_contiguous : std::false_type {};
+template <typename Char>
+struct is_contiguous<std::basic_string<Char>> : std::true_type {};
+
+class appender;
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+template <typename Context, typename T>
+constexpr auto has_const_formatter_impl(T*)
+    -> decltype(typename Context::template formatter_type<T>().format(
+                    std::declval<const T&>(), std::declval<Context&>()),
+                true) {
+  return true;
+}
+template <typename Context>
+constexpr auto has_const_formatter_impl(...) -> bool {
+  return false;
+}
+template <typename T, typename Context>
+constexpr auto has_const_formatter() -> bool {
+  return has_const_formatter_impl<Context>(static_cast<T*>(nullptr));
+}
+
+// Extracts a reference to the container from back_insert_iterator.
+template <typename Container>
+inline auto get_container(std::back_insert_iterator<Container> it)
+    -> Container& {
+  using base = std::back_insert_iterator<Container>;
+  struct accessor : base {
+    accessor(base b) : base(b) {}
+    using base::container;
+  };
+  return *accessor(it).container;
+}
+
+template <typename Char, typename InputIt, typename OutputIt>
+FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out)
+    -> OutputIt {
+  while (begin != end) *out++ = static_cast<Char>(*begin++);
+  return out;
+}
+
+template <typename Char, typename T, typename U,
+          FMT_ENABLE_IF(
+              std::is_same<remove_const_t<T>, U>::value&& is_char<U>::value)>
+FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* {
+  if (is_constant_evaluated()) return copy_str<Char, T*, U*>(begin, end, out);
+  auto size = to_unsigned(end - begin);
+  memcpy(out, begin, size * sizeof(U));
+  return out + size;
+}
+
+/**
+  \rst
+  A contiguous memory buffer with an optional growing ability. It is an internal
+  class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`.
+  \endrst
+ */
+template <typename T> class buffer {
+ private:
+  T* ptr_;
+  size_t size_;
+  size_t capacity_;
+
+ protected:
+  // Don't initialize ptr_ since it is not accessed to save a few cycles.
+  FMT_MSC_WARNING(suppress : 26495)
+  buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {}
+
+  FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept
+      : ptr_(p), size_(sz), capacity_(cap) {}
+
+  FMT_CONSTEXPR20 ~buffer() = default;
+  buffer(buffer&&) = default;
+
+  /** Sets the buffer data and capacity. */
+  FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept {
+    ptr_ = buf_data;
+    capacity_ = buf_capacity;
+  }
+
+  /** Increases the buffer capacity to hold at least *capacity* elements. */
+  virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0;
+
+ public:
+  using value_type = T;
+  using const_reference = const T&;
+
+  buffer(const buffer&) = delete;
+  void operator=(const buffer&) = delete;
+
+  auto begin() noexcept -> T* { return ptr_; }
+  auto end() noexcept -> T* { return ptr_ + size_; }
+
+  auto begin() const noexcept -> const T* { return ptr_; }
+  auto end() const noexcept -> const T* { return ptr_ + size_; }
+
+  /** Returns the size of this buffer. */
+  constexpr auto size() const noexcept -> size_t { return size_; }
+
+  /** Returns the capacity of this buffer. */
+  constexpr auto capacity() const noexcept -> size_t { return capacity_; }
+
+  /** Returns a pointer to the buffer data. */
+  FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
+
+  /** Returns a pointer to the buffer data. */
+  FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; }
+
+  /** Clears this buffer. */
+  void clear() { size_ = 0; }
+
+  // Tries resizing the buffer to contain *count* elements. If T is a POD type
+  // the new elements may not be initialized.
+  FMT_CONSTEXPR20 void try_resize(size_t count) {
+    try_reserve(count);
+    size_ = count <= capacity_ ? count : capacity_;
+  }
+
+  // Tries increasing the buffer capacity to *new_capacity*. It can increase the
+  // capacity by a smaller amount than requested but guarantees there is space
+  // for at least one additional element either by increasing the capacity or by
+  // flushing the buffer if it is full.
+  FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) {
+    if (new_capacity > capacity_) grow(new_capacity);
+  }
+
+  FMT_CONSTEXPR20 void push_back(const T& value) {
+    try_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);
+
+  template <typename Idx> FMT_CONSTEXPR auto operator[](Idx index) -> T& {
+    return ptr_[index];
+  }
+  template <typename Idx>
+  FMT_CONSTEXPR auto operator[](Idx index) const -> const T& {
+    return ptr_[index];
+  }
+};
+
+struct buffer_traits {
+  explicit buffer_traits(size_t) {}
+  auto count() const -> size_t { return 0; }
+  auto limit(size_t size) -> size_t { return size; }
+};
+
+class fixed_buffer_traits {
+ private:
+  size_t count_ = 0;
+  size_t limit_;
+
+ public:
+  explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
+  auto count() const -> size_t { return count_; }
+  auto limit(size_t size) -> size_t {
+    size_t n = limit_ > count_ ? limit_ - count_ : 0;
+    count_ += size;
+    return size < n ? size : n;
+  }
+};
+
+// A buffer that writes to an output iterator when flushed.
+template <typename OutputIt, typename T, typename Traits = buffer_traits>
+class iterator_buffer final : public Traits, public buffer<T> {
+ private:
+  OutputIt out_;
+  enum { buffer_size = 256 };
+  T data_[buffer_size];
+
+ protected:
+  FMT_CONSTEXPR20 void grow(size_t) override {
+    if (this->size() == buffer_size) flush();
+  }
+
+  void flush() {
+    auto size = this->size();
+    this->clear();
+    out_ = copy_str<T>(data_, data_ + this->limit(size), out_);
+  }
+
+ public:
+  explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
+      : Traits(n), buffer<T>(data_, 0, buffer_size), out_(out) {}
+  iterator_buffer(iterator_buffer&& other)
+      : Traits(other), buffer<T>(data_, 0, buffer_size), out_(other.out_) {}
+  ~iterator_buffer() { flush(); }
+
+  auto out() -> OutputIt {
+    flush();
+    return out_;
+  }
+  auto count() const -> size_t { return Traits::count() + this->size(); }
+};
+
+template <typename T>
+class iterator_buffer<T*, T, fixed_buffer_traits> final
+    : public fixed_buffer_traits,
+      public buffer<T> {
+ private:
+  T* out_;
+  enum { buffer_size = 256 };
+  T data_[buffer_size];
+
+ protected:
+  FMT_CONSTEXPR20 void grow(size_t) override {
+    if (this->size() == this->capacity()) flush();
+  }
+
+  void flush() {
+    size_t n = this->limit(this->size());
+    if (this->data() == out_) {
+      out_ += n;
+      this->set(data_, buffer_size);
+    }
+    this->clear();
+  }
+
+ public:
+  explicit iterator_buffer(T* out, size_t n = buffer_size)
+      : fixed_buffer_traits(n), buffer<T>(out, 0, n), out_(out) {}
+  iterator_buffer(iterator_buffer&& other)
+      : fixed_buffer_traits(other),
+        buffer<T>(std::move(other)),
+        out_(other.out_) {
+    if (this->data() != out_) {
+      this->set(data_, buffer_size);
+      this->clear();
+    }
+  }
+  ~iterator_buffer() { flush(); }
+
+  auto out() -> T* {
+    flush();
+    return out_;
+  }
+  auto count() const -> size_t {
+    return fixed_buffer_traits::count() + this->size();
+  }
+};
+
+template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
+ protected:
+  FMT_CONSTEXPR20 void grow(size_t) override {}
+
+ public:
+  explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
+
+  auto out() -> T* { return &*this->end(); }
+};
+
+// A buffer that writes to a container with the contiguous storage.
+template <typename Container>
+class iterator_buffer<std::back_insert_iterator<Container>,
+                      enable_if_t<is_contiguous<Container>::value,
+                                  typename Container::value_type>>
+    final : public buffer<typename Container::value_type> {
+ private:
+  Container& container_;
+
+ protected:
+  FMT_CONSTEXPR20 void grow(size_t capacity) override {
+    container_.resize(capacity);
+    this->set(&container_[0], capacity);
+  }
+
+ public:
+  explicit iterator_buffer(Container& c)
+      : buffer<typename Container::value_type>(c.size()), container_(c) {}
+  explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
+      : iterator_buffer(get_container(out)) {}
+
+  auto out() -> std::back_insert_iterator<Container> {
+    return std::back_inserter(container_);
+  }
+};
+
+// A buffer that counts the number of code units written discarding the output.
+template <typename T = char> class counting_buffer final : public buffer<T> {
+ private:
+  enum { buffer_size = 256 };
+  T data_[buffer_size];
+  size_t count_ = 0;
+
+ protected:
+  FMT_CONSTEXPR20 void grow(size_t) override {
+    if (this->size() != buffer_size) return;
+    count_ += this->size();
+    this->clear();
+  }
+
+ public:
+  counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
+
+  auto count() -> size_t { return count_ + this->size(); }
+};
+
+template <typename T>
+using buffer_appender = conditional_t<std::is_same<T, char>::value, appender,
+                                      std::back_insert_iterator<buffer<T>>>;
+
+// Maps an output iterator to a buffer.
+template <typename T, typename OutputIt>
+auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> {
+  return iterator_buffer<OutputIt, T>(out);
+}
+
+template <typename Buffer>
+auto get_iterator(Buffer& buf) -> decltype(buf.out()) {
+  return buf.out();
+}
+template <typename T> auto get_iterator(buffer<T>& buf) -> buffer_appender<T> {
+  return buffer_appender<T>(buf);
+}
+
+template <typename T, typename Char = char, typename Enable = void>
+struct fallback_formatter {
+  fallback_formatter() = delete;
+};
+
+// Specifies if T has an enabled fallback_formatter specialization.
+template <typename T, typename Char>
+using has_fallback_formatter =
+#ifdef FMT_DEPRECATED_OSTREAM
+    std::is_constructible<fallback_formatter<T, Char>>;
+#else
+    std::false_type;
+#endif
+
+struct view {};
+
+template <typename Char, typename T> struct named_arg : view {
+  const Char* name;
+  const T& value;
+  named_arg(const Char* n, const T& v) : name(n), value(v) {}
+};
+
+template <typename Char> struct named_arg_info {
+  const Char* name;
+  int id;
+};
+
+template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
+struct arg_data {
+  // args_[0].named_args points to named_args_ to avoid bloating format_args.
+  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
+  T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
+  named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
+
+  template <typename... U>
+  arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {}
+  arg_data(const arg_data& other) = delete;
+  auto args() const -> const T* { return args_ + 1; }
+  auto named_args() -> named_arg_info<Char>* { return named_args_; }
+};
+
+template <typename T, typename Char, size_t NUM_ARGS>
+struct arg_data<T, Char, NUM_ARGS, 0> {
+  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
+  T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
+
+  template <typename... U>
+  FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {}
+  FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; }
+  FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t {
+    return nullptr;
+  }
+};
+
+template <typename Char>
+inline void init_named_args(named_arg_info<Char>*, int, int) {}
+
+template <typename T> struct is_named_arg : std::false_type {};
+template <typename T> struct is_statically_named_arg : std::false_type {};
+
+template <typename T, typename Char>
+struct is_named_arg<named_arg<Char, T>> : std::true_type {};
+
+template <typename Char, typename T, typename... Tail,
+          FMT_ENABLE_IF(!is_named_arg<T>::value)>
+void init_named_args(named_arg_info<Char>* named_args, int arg_count,
+                     int named_arg_count, const T&, const Tail&... args) {
+  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
+}
+
+template <typename Char, typename T, typename... Tail,
+          FMT_ENABLE_IF(is_named_arg<T>::value)>
+void init_named_args(named_arg_info<Char>* named_args, int arg_count,
+                     int named_arg_count, const T& arg, const Tail&... args) {
+  named_args[named_arg_count++] = {arg.name, arg_count};
+  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
+}
+
+template <typename... Args>
+FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int,
+                                              const Args&...) {}
+
+template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; }
+template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t {
+  return (B1 ? 1 : 0) + count<B2, Tail...>();
+}
+
+template <typename... Args> constexpr auto count_named_args() -> size_t {
+  return count<is_named_arg<Args>::value...>();
+}
+
+template <typename... Args>
+constexpr auto count_statically_named_args() -> size_t {
+  return count<is_statically_named_arg<Args>::value...>();
+}
+
+struct unformattable {};
+struct unformattable_char : unformattable {};
+struct unformattable_const : unformattable {};
+struct unformattable_pointer : unformattable {};
+
+template <typename Char> struct string_value {
+  const Char* data;
+  size_t size;
+};
+
+template <typename Char> struct named_arg_value {
+  const named_arg_info<Char>* data;
+  size_t size;
+};
+
+template <typename Context> struct custom_value {
+  using parse_context = typename Context::parse_context_type;
+  void* value;
+  void (*format)(void* arg, parse_context& parse_ctx, Context& ctx);
+};
+
+// A formatting argument value.
+template <typename Context> class value {
+ public:
+  using char_type = typename Context::char_type;
+
+  union {
+    monostate no_value;
+    int int_value;
+    unsigned uint_value;
+    long long long_long_value;
+    unsigned long long ulong_long_value;
+    int128_opt int128_value;
+    uint128_opt uint128_value;
+    bool bool_value;
+    char_type char_value;
+    float float_value;
+    double double_value;
+    long double long_double_value;
+    const void* pointer;
+    string_value<char_type> string;
+    custom_value<Context> custom;
+    named_arg_value<char_type> named_args;
+  };
+
+  constexpr FMT_INLINE value() : no_value() {}
+  constexpr FMT_INLINE value(int val) : int_value(val) {}
+  constexpr FMT_INLINE value(unsigned val) : uint_value(val) {}
+  constexpr FMT_INLINE value(long long val) : long_long_value(val) {}
+  constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
+  FMT_INLINE value(int128_opt val) : int128_value(val) {}
+  FMT_INLINE value(uint128_opt val) : uint128_value(val) {}
+  constexpr FMT_INLINE value(float val) : float_value(val) {}
+  constexpr FMT_INLINE value(double val) : double_value(val) {}
+  FMT_INLINE value(long double val) : long_double_value(val) {}
+  constexpr FMT_INLINE value(bool val) : bool_value(val) {}
+  constexpr FMT_INLINE value(char_type val) : char_value(val) {}
+  FMT_CONSTEXPR FMT_INLINE value(const char_type* val) {
+    string.data = val;
+    if (is_constant_evaluated()) string.size = {};
+  }
+  FMT_CONSTEXPR FMT_INLINE value(basic_string_view<char_type> val) {
+    string.data = val.data();
+    string.size = val.size();
+  }
+  FMT_INLINE value(const void* val) : pointer(val) {}
+  FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
+      : named_args{args, size} {}
+
+  template <typename T> FMT_CONSTEXPR FMT_INLINE value(T& val) {
+    using value_type = remove_cvref_t<T>;
+    custom.value = const_cast<value_type*>(&val);
+    // Get the formatter type through the context to allow different contexts
+    // have different extension points, e.g. `formatter<T>` for `format` and
+    // `printf_formatter<T>` for `printf`.
+    custom.format = format_custom_arg<
+        value_type,
+        conditional_t<has_formatter<value_type, Context>::value,
+                      typename Context::template formatter_type<value_type>,
+                      fallback_formatter<value_type, char_type>>>;
+  }
+  value(unformattable);
+  value(unformattable_char);
+  value(unformattable_const);
+  value(unformattable_pointer);
+
+ private:
+  // Formats an argument of a custom type, such as a user-defined class.
+  template <typename T, typename Formatter>
+  static void format_custom_arg(void* arg,
+                                typename Context::parse_context_type& parse_ctx,
+                                Context& ctx) {
+    auto f = Formatter();
+    parse_ctx.advance_to(f.parse(parse_ctx));
+    using qualified_type =
+        conditional_t<has_const_formatter<T, Context>(), const T, T>;
+    ctx.advance_to(f.format(*static_cast<qualified_type*>(arg), ctx));
+  }
+};
+
+template <typename Context, typename T>
+FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context>;
+
+// To minimize the number of types we need to deal with, long is translated
+// either to int or to long long depending on its size.
+enum { long_short = sizeof(long) == sizeof(int) };
+using long_type = conditional_t<long_short, int, long long>;
+using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
+
+#ifdef __cpp_lib_byte
+inline auto format_as(std::byte b) -> unsigned char {
+  return static_cast<unsigned char>(b);
+}
+#endif
+
+template <typename T> struct has_format_as {
+  template <typename U, typename V = decltype(format_as(U())),
+            FMT_ENABLE_IF(std::is_enum<U>::value&& std::is_integral<V>::value)>
+  static auto check(U*) -> std::true_type;
+  static auto check(...) -> std::false_type;
+
+  enum { value = decltype(check(static_cast<T*>(nullptr)))::value };
+};
+
+// Maps formatting arguments to core types.
+// arg_mapper reports errors by returning unformattable instead of using
+// static_assert because it's used in the is_formattable trait.
+template <typename Context> struct arg_mapper {
+  using char_type = typename Context::char_type;
+
+  FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val)
+      -> unsigned long long {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; }
+
+  template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value ||
+                                      std::is_same<T, char_type>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type {
+    return val;
+  }
+  template <typename T, enable_if_t<(std::is_same<T, wchar_t>::value ||
+#ifdef __cpp_char8_t
+                                     std::is_same<T, char8_t>::value ||
+#endif
+                                     std::is_same<T, char16_t>::value ||
+                                     std::is_same<T, char32_t>::value) &&
+                                        !std::is_same<T, char_type>::value,
+                                    int> = 0>
+  FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char {
+    return {};
+  }
+
+  FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double {
+    return val;
+  }
+
+  FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* {
+    return val;
+  }
+  template <typename T,
+            FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
+                          std::is_same<char_type, char_t<T>>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
+      -> basic_string_view<char_type> {
+    return to_string_view(val);
+  }
+  template <typename T,
+            FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
+                          !std::is_same<char_type, char_t<T>>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char {
+    return {};
+  }
+  template <typename T,
+            FMT_ENABLE_IF(
+                std::is_convertible<T, basic_string_view<char_type>>::value &&
+                !is_string<T>::value && !has_formatter<T, Context>::value &&
+                !has_fallback_formatter<T, char_type>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
+      -> basic_string_view<char_type> {
+    return basic_string_view<char_type>(val);
+  }
+  template <typename T,
+            FMT_ENABLE_IF(
+                std::is_convertible<T, std_string_view<char_type>>::value &&
+                !std::is_convertible<T, basic_string_view<char_type>>::value &&
+                !is_string<T>::value && !has_formatter<T, Context>::value &&
+                !has_fallback_formatter<T, char_type>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
+      -> basic_string_view<char_type> {
+    return std_string_view<char_type>(val);
+  }
+
+  FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; }
+  FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* {
+    return val;
+  }
+  FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* {
+    return val;
+  }
+
+  // We use SFINAE instead of a const T* parameter to avoid conflicting with
+  // the C array overload.
+  template <
+      typename T,
+      FMT_ENABLE_IF(
+          std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
+          std::is_function<typename std::remove_pointer<T>::type>::value ||
+          (std::is_convertible<const T&, const void*>::value &&
+           !std::is_convertible<const T&, const char_type*>::value &&
+           !has_formatter<T, Context>::value))>
+  FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer {
+    return {};
+  }
+
+  template <typename T, std::size_t N,
+            FMT_ENABLE_IF(!std::is_same<T, wchar_t>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] {
+    return values;
+  }
+
+  template <typename T,
+            FMT_ENABLE_IF(
+                std::is_enum<T>::value&& std::is_convertible<T, int>::value &&
+                !has_format_as<T>::value && !has_formatter<T, Context>::value &&
+                !has_fallback_formatter<T, char_type>::value)>
+  FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
+      -> decltype(std::declval<arg_mapper>().map(
+          static_cast<underlying_t<T>>(val))) {
+    return map(static_cast<underlying_t<T>>(val));
+  }
+
+  template <typename T, FMT_ENABLE_IF(has_format_as<T>::value &&
+                                      !has_formatter<T, Context>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
+      -> decltype(std::declval<arg_mapper>().map(format_as(T()))) {
+    return map(format_as(val));
+  }
+
+  template <typename T, typename U = remove_cvref_t<T>>
+  struct formattable
+      : bool_constant<has_const_formatter<U, Context>() ||
+                      !std::is_const<remove_reference_t<T>>::value ||
+                      has_fallback_formatter<U, char_type>::value> {};
+
+#if (FMT_MSC_VERSION != 0 && FMT_MSC_VERSION < 1910) || \
+    FMT_ICC_VERSION != 0 || defined(__NVCC__)
+  // Workaround a bug in MSVC and Intel (Issue 2746).
+  template <typename T> FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
+    return val;
+  }
+#else
+  template <typename T, FMT_ENABLE_IF(formattable<T>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
+    return val;
+  }
+  template <typename T, FMT_ENABLE_IF(!formattable<T>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable_const {
+    return {};
+  }
+#endif
+
+  template <typename T, typename U = remove_cvref_t<T>,
+            FMT_ENABLE_IF(!is_string<U>::value && !is_char<U>::value &&
+                          !std::is_array<U>::value &&
+                          !std::is_pointer<U>::value &&
+                          !has_format_as<U>::value &&
+                          (has_formatter<U, Context>::value ||
+                           has_fallback_formatter<U, char_type>::value))>
+  FMT_CONSTEXPR FMT_INLINE auto map(T&& val)
+      -> decltype(this->do_map(std::forward<T>(val))) {
+    return do_map(std::forward<T>(val));
+  }
+
+  template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
+  FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg)
+      -> decltype(std::declval<arg_mapper>().map(named_arg.value)) {
+    return map(named_arg.value);
+  }
+
+  auto map(...) -> unformattable { return {}; }
+};
+
+// A type constant after applying arg_mapper<Context>.
+template <typename T, typename Context>
+using mapped_type_constant =
+    type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
+                  typename Context::char_type>;
+
+enum { packed_arg_bits = 4 };
+// Maximum number of arguments with packed types.
+enum { max_packed_args = 62 / packed_arg_bits };
+enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
+enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
+
+FMT_END_DETAIL_NAMESPACE
+
+// An output iterator that appends to a buffer.
+// It is used to reduce symbol sizes for the common case.
+class appender : public std::back_insert_iterator<detail::buffer<char>> {
+  using base = std::back_insert_iterator<detail::buffer<char>>;
+
+  template <typename T>
+  friend auto get_buffer(appender out) -> detail::buffer<char>& {
+    return detail::get_container(out);
+  }
+
+ public:
+  using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
+  appender(base it) noexcept : base(it) {}
+  FMT_UNCHECKED_ITERATOR(appender);
+
+  auto operator++() noexcept -> appender& { return *this; }
+  auto operator++(int) noexcept -> appender { return *this; }
+};
+
+// A formatting argument. It is a trivially copyable/constructible type to
+// allow storage in basic_memory_buffer.
+template <typename Context> class basic_format_arg {
+ private:
+  detail::value<Context> value_;
+  detail::type type_;
+
+  template <typename ContextType, typename T>
+  friend FMT_CONSTEXPR auto detail::make_arg(T&& value)
+      -> basic_format_arg<ContextType>;
+
+  template <typename Visitor, typename Ctx>
+  friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
+                                             const basic_format_arg<Ctx>& arg)
+      -> decltype(vis(0));
+
+  friend class basic_format_args<Context>;
+  friend class dynamic_format_arg_store<Context>;
+
+  using char_type = typename Context::char_type;
+
+  template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
+  friend struct detail::arg_data;
+
+  basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
+      : value_(args, size) {}
+
+ public:
+  class handle {
+   public:
+    explicit handle(detail::custom_value<Context> custom) : custom_(custom) {}
+
+    void format(typename Context::parse_context_type& parse_ctx,
+                Context& ctx) const {
+      custom_.format(custom_.value, parse_ctx, ctx);
+    }
+
+   private:
+    detail::custom_value<Context> custom_;
+  };
+
+  constexpr basic_format_arg() : type_(detail::type::none_type) {}
+
+  constexpr explicit operator bool() const noexcept {
+    return type_ != detail::type::none_type;
+  }
+
+  auto type() const -> detail::type { return type_; }
+
+  auto is_integral() const -> bool { return detail::is_integral_type(type_); }
+  auto is_arithmetic() const -> bool {
+    return detail::is_arithmetic_type(type_);
+  }
+};
+
+/**
+  \rst
+  Visits an argument dispatching to the appropriate visit method based on
+  the argument type. For example, if the argument type is ``double`` then
+  ``vis(value)`` will be called with the value of type ``double``.
+  \endrst
+ */
+template <typename Visitor, typename Context>
+FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
+    Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) {
+  switch (arg.type_) {
+  case detail::type::none_type:
+    break;
+  case detail::type::int_type:
+    return vis(arg.value_.int_value);
+  case detail::type::uint_type:
+    return vis(arg.value_.uint_value);
+  case detail::type::long_long_type:
+    return vis(arg.value_.long_long_value);
+  case detail::type::ulong_long_type:
+    return vis(arg.value_.ulong_long_value);
+  case detail::type::int128_type:
+    return vis(detail::convert_for_visit(arg.value_.int128_value));
+  case detail::type::uint128_type:
+    return vis(detail::convert_for_visit(arg.value_.uint128_value));
+  case detail::type::bool_type:
+    return vis(arg.value_.bool_value);
+  case detail::type::char_type:
+    return vis(arg.value_.char_value);
+  case detail::type::float_type:
+    return vis(arg.value_.float_value);
+  case detail::type::double_type:
+    return vis(arg.value_.double_value);
+  case detail::type::long_double_type:
+    return vis(arg.value_.long_double_value);
+  case detail::type::cstring_type:
+    return vis(arg.value_.string.data);
+  case detail::type::string_type:
+    using sv = basic_string_view<typename Context::char_type>;
+    return vis(sv(arg.value_.string.data, arg.value_.string.size));
+  case detail::type::pointer_type:
+    return vis(arg.value_.pointer);
+  case detail::type::custom_type:
+    return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
+  }
+  return vis(monostate());
+}
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+template <typename Char, typename InputIt>
+auto copy_str(InputIt begin, InputIt end, appender out) -> appender {
+  get_container(out).append(begin, end);
+  return out;
+}
+
+template <typename Char, typename R, typename OutputIt>
+FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt {
+  return detail::copy_str<Char>(rng.begin(), rng.end(), out);
+}
+
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
+// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
+template <typename... Ts> struct void_t_impl { using type = void; };
+template <typename... Ts>
+using void_t = typename detail::void_t_impl<Ts...>::type;
+#else
+template <typename...> using void_t = void;
+#endif
+
+template <typename It, typename T, typename Enable = void>
+struct is_output_iterator : std::false_type {};
+
+template <typename It, typename T>
+struct is_output_iterator<
+    It, T,
+    void_t<typename std::iterator_traits<It>::iterator_category,
+           decltype(*std::declval<It>() = std::declval<T>())>>
+    : std::true_type {};
+
+template <typename OutputIt>
+struct is_back_insert_iterator : std::false_type {};
+template <typename Container>
+struct is_back_insert_iterator<std::back_insert_iterator<Container>>
+    : std::true_type {};
+
+template <typename OutputIt>
+struct is_contiguous_back_insert_iterator : std::false_type {};
+template <typename Container>
+struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
+    : is_contiguous<Container> {};
+template <>
+struct is_contiguous_back_insert_iterator<appender> : std::true_type {};
+
+// A type-erased reference to an std::locale to avoid a heavy <locale> include.
+class locale_ref {
+ private:
+  const void* locale_;  // A type-erased pointer to std::locale.
+
+ public:
+  constexpr locale_ref() : locale_(nullptr) {}
+  template <typename Locale> explicit locale_ref(const Locale& loc);
+
+  explicit operator bool() const noexcept { return locale_ != nullptr; }
+
+  template <typename Locale> auto get() const -> Locale;
+};
+
+template <typename> constexpr auto encode_types() -> unsigned long long {
+  return 0;
+}
+
+template <typename Context, typename Arg, typename... Args>
+constexpr auto encode_types() -> unsigned long long {
+  return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
+         (encode_types<Context, Args...>() << packed_arg_bits);
+}
+
+template <typename Context, typename T>
+FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value<Context> {
+  const auto& arg = arg_mapper<Context>().map(FMT_FORWARD(val));
+
+  constexpr bool formattable_char =
+      !std::is_same<decltype(arg), const unformattable_char&>::value;
+  static_assert(formattable_char, "Mixing character types is disallowed.");
+
+  constexpr bool formattable_const =
+      !std::is_same<decltype(arg), const unformattable_const&>::value;
+  static_assert(formattable_const, "Cannot format a const argument.");
+
+  // Formatting of arbitrary pointers is disallowed. If you want to output
+  // a pointer cast it to "void *" or "const void *". In particular, this
+  // forbids formatting of "[const] volatile char *" which is printed as bool
+  // by iostreams.
+  constexpr bool formattable_pointer =
+      !std::is_same<decltype(arg), const unformattable_pointer&>::value;
+  static_assert(formattable_pointer,
+                "Formatting of non-void pointers is disallowed.");
+
+  constexpr bool formattable =
+      !std::is_same<decltype(arg), const unformattable&>::value;
+  static_assert(
+      formattable,
+      "Cannot format an argument. To make type T formattable provide a "
+      "formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
+  return {arg};
+}
+
+template <typename Context, typename T>
+FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context> {
+  basic_format_arg<Context> arg;
+  arg.type_ = mapped_type_constant<T, Context>::value;
+  arg.value_ = make_value<Context>(value);
+  return arg;
+}
+
+// The type template parameter is there to avoid an ODR violation when using
+// a fallback formatter in one translation unit and an implicit conversion in
+// another (not recommended).
+template <bool IS_PACKED, typename Context, type, typename T,
+          FMT_ENABLE_IF(IS_PACKED)>
+FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
+  return make_value<Context>(val);
+}
+
+template <bool IS_PACKED, typename Context, type, typename T,
+          FMT_ENABLE_IF(!IS_PACKED)>
+FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg<Context> {
+  return make_arg<Context>(value);
+}
+FMT_END_DETAIL_NAMESPACE
+
+// Formatting context.
+template <typename OutputIt, typename Char> class basic_format_context {
+ public:
+  /** The character type for the output. */
+  using char_type = Char;
+
+ private:
+  OutputIt out_;
+  basic_format_args<basic_format_context> args_;
+  detail::locale_ref loc_;
+
+ public:
+  using iterator = OutputIt;
+  using format_arg = basic_format_arg<basic_format_context>;
+  using parse_context_type = basic_format_parse_context<Char>;
+  template <typename T> using formatter_type = formatter<T, char_type>;
+
+  basic_format_context(basic_format_context&&) = default;
+  basic_format_context(const basic_format_context&) = delete;
+  void operator=(const basic_format_context&) = delete;
+  /**
+   Constructs a ``basic_format_context`` object. References to the arguments are
+   stored in the object so make sure they have appropriate lifetimes.
+   */
+  constexpr basic_format_context(
+      OutputIt out, basic_format_args<basic_format_context> ctx_args,
+      detail::locale_ref loc = detail::locale_ref())
+      : out_(out), args_(ctx_args), loc_(loc) {}
+
+  constexpr auto arg(int id) const -> format_arg { return args_.get(id); }
+  FMT_CONSTEXPR auto arg(basic_string_view<char_type> name) -> format_arg {
+    return args_.get(name);
+  }
+  FMT_CONSTEXPR auto arg_id(basic_string_view<char_type> name) -> int {
+    return args_.get_id(name);
+  }
+  auto args() const -> const basic_format_args<basic_format_context>& {
+    return args_;
+  }
+
+  FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; }
+  void on_error(const char* message) { error_handler().on_error(message); }
+
+  // Returns an iterator to the beginning of the output range.
+  FMT_CONSTEXPR auto out() -> iterator { return out_; }
+
+  // Advances the begin iterator to ``it``.
+  void advance_to(iterator it) {
+    if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
+  }
+
+  FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; }
+};
+
+template <typename Char>
+using buffer_context =
+    basic_format_context<detail::buffer_appender<Char>, Char>;
+using format_context = buffer_context<char>;
+
+// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164.
+#define FMT_BUFFER_CONTEXT(Char) \
+  basic_format_context<detail::buffer_appender<Char>, Char>
+
+template <typename T, typename Char = char>
+using is_formattable = bool_constant<
+    !std::is_base_of<detail::unformattable,
+                     decltype(detail::arg_mapper<buffer_context<Char>>().map(
+                         std::declval<T>()))>::value &&
+    !detail::has_fallback_formatter<T, Char>::value>;
+
+/**
+  \rst
+  An array of references to arguments. It can be implicitly converted into
+  `~fmt::basic_format_args` for passing into type-erased formatting functions
+  such as `~fmt::vformat`.
+  \endrst
+ */
+template <typename Context, typename... Args>
+class format_arg_store
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+    // Workaround a GCC template argument substitution bug.
+    : public basic_format_args<Context>
+#endif
+{
+ private:
+  static const size_t num_args = sizeof...(Args);
+  static const size_t num_named_args = detail::count_named_args<Args...>();
+  static const bool is_packed = num_args <= detail::max_packed_args;
+
+  using value_type = conditional_t<is_packed, detail::value<Context>,
+                                   basic_format_arg<Context>>;
+
+  detail::arg_data<value_type, typename Context::char_type, num_args,
+                   num_named_args>
+      data_;
+
+  friend class basic_format_args<Context>;
+
+  static constexpr unsigned long long desc =
+      (is_packed ? detail::encode_types<Context, Args...>()
+                 : detail::is_unpacked_bit | num_args) |
+      (num_named_args != 0
+           ? static_cast<unsigned long long>(detail::has_named_args_bit)
+           : 0);
+
+ public:
+  template <typename... T>
+  FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args)
+      :
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+        basic_format_args<Context>(*this),
+#endif
+        data_{detail::make_arg<
+            is_packed, Context,
+            detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>(
+            FMT_FORWARD(args))...} {
+    detail::init_named_args(data_.named_args(), 0, 0, args...);
+  }
+};
+
+/**
+  \rst
+  Constructs a `~fmt::format_arg_store` object that contains references to
+  arguments and can be implicitly converted to `~fmt::format_args`. `Context`
+  can be omitted in which case it defaults to `~fmt::context`.
+  See `~fmt::arg` for lifetime considerations.
+  \endrst
+ */
+template <typename Context = format_context, typename... Args>
+constexpr auto make_format_args(Args&&... args)
+    -> format_arg_store<Context, remove_cvref_t<Args>...> {
+  return {FMT_FORWARD(args)...};
+}
+
+/**
+  \rst
+  Returns a named argument to be used in a formatting function.
+  It should only be used in a call to a formatting function or
+  `dynamic_format_arg_store::push_back`.
+
+  **Example**::
+
+    fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23));
+  \endrst
+ */
+template <typename Char, typename T>
+inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
+  static_assert(!detail::is_named_arg<T>(), "nested named arguments");
+  return {name, arg};
+}
+
+/**
+  \rst
+  A view of a collection of formatting arguments. To avoid lifetime issues it
+  should only be used as a parameter type in type-erased functions such as
+  ``vformat``::
+
+    void vlog(string_view format_str, format_args args);  // OK
+    format_args args = make_format_args(42);  // Error: dangling reference
+  \endrst
+ */
+template <typename Context> class basic_format_args {
+ public:
+  using size_type = int;
+  using format_arg = basic_format_arg<Context>;
+
+ private:
+  // A descriptor that contains information about formatting arguments.
+  // If the number of arguments is less or equal to max_packed_args then
+  // argument types are passed in the descriptor. This reduces binary code size
+  // per formatting function call.
+  unsigned long long desc_;
+  union {
+    // If is_packed() returns true then argument values are stored in values_;
+    // otherwise they are stored in args_. This is done to improve cache
+    // locality and reduce compiled code size since storing larger objects
+    // may require more code (at least on x86-64) even if the same amount of
+    // data is actually copied to stack. It saves ~10% on the bloat test.
+    const detail::value<Context>* values_;
+    const format_arg* args_;
+  };
+
+  constexpr auto is_packed() const -> bool {
+    return (desc_ & detail::is_unpacked_bit) == 0;
+  }
+  auto has_named_args() const -> bool {
+    return (desc_ & detail::has_named_args_bit) != 0;
+  }
+
+  FMT_CONSTEXPR auto type(int index) const -> detail::type {
+    int shift = index * detail::packed_arg_bits;
+    unsigned int mask = (1 << detail::packed_arg_bits) - 1;
+    return static_cast<detail::type>((desc_ >> shift) & mask);
+  }
+
+  constexpr FMT_INLINE basic_format_args(unsigned long long desc,
+                                         const detail::value<Context>* values)
+      : desc_(desc), values_(values) {}
+  constexpr basic_format_args(unsigned long long desc, const format_arg* args)
+      : desc_(desc), args_(args) {}
+
+ public:
+  constexpr basic_format_args() : desc_(0), args_(nullptr) {}
+
+  /**
+   \rst
+   Constructs a `basic_format_args` object from `~fmt::format_arg_store`.
+   \endrst
+   */
+  template <typename... Args>
+  constexpr FMT_INLINE basic_format_args(
+      const format_arg_store<Context, Args...>& store)
+      : basic_format_args(format_arg_store<Context, Args...>::desc,
+                          store.data_.args()) {}
+
+  /**
+   \rst
+   Constructs a `basic_format_args` object from
+   `~fmt::dynamic_format_arg_store`.
+   \endrst
+   */
+  constexpr FMT_INLINE basic_format_args(
+      const dynamic_format_arg_store<Context>& store)
+      : basic_format_args(store.get_types(), store.data()) {}
+
+  /**
+   \rst
+   Constructs a `basic_format_args` object from a dynamic set of arguments.
+   \endrst
+   */
+  constexpr basic_format_args(const format_arg* args, int count)
+      : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count),
+                          args) {}
+
+  /** Returns the argument with the specified id. */
+  FMT_CONSTEXPR auto get(int id) const -> format_arg {
+    format_arg arg;
+    if (!is_packed()) {
+      if (id < max_size()) arg = args_[id];
+      return arg;
+    }
+    if (id >= detail::max_packed_args) return arg;
+    arg.type_ = type(id);
+    if (arg.type_ == detail::type::none_type) return arg;
+    arg.value_ = values_[id];
+    return arg;
+  }
+
+  template <typename Char>
+  auto get(basic_string_view<Char> name) const -> format_arg {
+    int id = get_id(name);
+    return id >= 0 ? get(id) : format_arg();
+  }
+
+  template <typename Char>
+  auto get_id(basic_string_view<Char> name) const -> int {
+    if (!has_named_args()) return -1;
+    const auto& named_args =
+        (is_packed() ? values_[-1] : args_[-1].value_).named_args;
+    for (size_t i = 0; i < named_args.size; ++i) {
+      if (named_args.data[i].name == name) return named_args.data[i].id;
+    }
+    return -1;
+  }
+
+  auto max_size() const -> int {
+    unsigned long long max_packed = detail::max_packed_args;
+    return static_cast<int>(is_packed() ? max_packed
+                                        : desc_ & ~detail::is_unpacked_bit);
+  }
+};
+
+/** An alias to ``basic_format_args<format_context>``. */
+// A separate type would result in shorter symbols but break ABI compatibility
+// between clang and gcc on ARM (#1919).
+using format_args = basic_format_args<format_context>;
+
+// We cannot use enum classes as bit fields because of a gcc bug, so we put them
+// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414).
+// Additionally, if an underlying type is specified, older gcc incorrectly warns
+// that the type is too small. Both bugs are fixed in gcc 9.3.
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903
+#  define FMT_ENUM_UNDERLYING_TYPE(type)
+#else
+#  define FMT_ENUM_UNDERLYING_TYPE(type) : type
+#endif
+namespace align {
+enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center,
+                                                  numeric};
+}
+using align_t = align::type;
+namespace sign {
+enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space};
+}
+using sign_t = sign::type;
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+// Workaround an array initialization issue in gcc 4.8.
+template <typename Char> struct fill_t {
+ private:
+  enum { max_size = 4 };
+  Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)};
+  unsigned char size_ = 1;
+
+ public:
+  FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
+    auto size = s.size();
+    if (size > max_size) return throw_format_error("invalid fill");
+    for (size_t i = 0; i < size; ++i) data_[i] = s[i];
+    size_ = static_cast<unsigned char>(size);
+  }
+
+  constexpr auto size() const -> size_t { return size_; }
+  constexpr auto data() const -> const Char* { return data_; }
+
+  FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; }
+  FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& {
+    return data_[index];
+  }
+};
+FMT_END_DETAIL_NAMESPACE
+
+enum class presentation_type : unsigned char {
+  none,
+  // Integer types should go first,
+  dec,             // 'd'
+  oct,             // 'o'
+  hex_lower,       // 'x'
+  hex_upper,       // 'X'
+  bin_lower,       // 'b'
+  bin_upper,       // 'B'
+  hexfloat_lower,  // 'a'
+  hexfloat_upper,  // 'A'
+  exp_lower,       // 'e'
+  exp_upper,       // 'E'
+  fixed_lower,     // 'f'
+  fixed_upper,     // 'F'
+  general_lower,   // 'g'
+  general_upper,   // 'G'
+  chr,             // 'c'
+  string,          // 's'
+  pointer,         // 'p'
+  debug            // '?'
+};
+
+// Format specifiers for built-in and string types.
+template <typename Char> struct basic_format_specs {
+  int width;
+  int precision;
+  presentation_type type;
+  align_t align : 4;
+  sign_t sign : 3;
+  bool alt : 1;  // Alternate form ('#').
+  bool localized : 1;
+  detail::fill_t<Char> fill;
+
+  constexpr basic_format_specs()
+      : width(0),
+        precision(-1),
+        type(presentation_type::none),
+        align(align::none),
+        sign(sign::none),
+        alt(false),
+        localized(false) {}
+};
+
+using format_specs = basic_format_specs<char>;
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+enum class arg_id_kind { none, index, name };
+
+// An argument reference.
+template <typename Char> struct arg_ref {
+  FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
+
+  FMT_CONSTEXPR explicit arg_ref(int index)
+      : kind(arg_id_kind::index), val(index) {}
+  FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
+      : kind(arg_id_kind::name), val(name) {}
+
+  FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& {
+    kind = arg_id_kind::index;
+    val.index = idx;
+    return *this;
+  }
+
+  arg_id_kind kind;
+  union value {
+    FMT_CONSTEXPR value(int id = 0) : index{id} {}
+    FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
+
+    int index;
+    basic_string_view<Char> name;
+  } val;
+};
+
+// Format specifiers with width and precision resolved at formatting rather
+// than parsing time to allow re-using the same parsed specifiers with
+// different sets of arguments (precompilation of format strings).
+template <typename Char>
+struct dynamic_format_specs : basic_format_specs<Char> {
+  arg_ref<Char> width_ref;
+  arg_ref<Char> precision_ref;
+};
+
+struct auto_id {};
+
+// A format specifier handler that sets fields in basic_format_specs.
+template <typename Char> class specs_setter {
+ protected:
+  basic_format_specs<Char>& specs_;
+
+ public:
+  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 void on_align(align_t align) { specs_.align = align; }
+  FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
+    specs_.fill = fill;
+  }
+  FMT_CONSTEXPR void on_sign(sign_t s) { specs_.sign = s; }
+  FMT_CONSTEXPR void on_hash() { specs_.alt = true; }
+  FMT_CONSTEXPR void on_localized() { specs_.localized = true; }
+
+  FMT_CONSTEXPR void on_zero() {
+    if (specs_.align == align::none) specs_.align = align::numeric;
+    specs_.fill[0] = Char('0');
+  }
+
+  FMT_CONSTEXPR void on_width(int width) { specs_.width = width; }
+  FMT_CONSTEXPR void on_precision(int precision) {
+    specs_.precision = precision;
+  }
+  FMT_CONSTEXPR void end_precision() {}
+
+  FMT_CONSTEXPR void on_type(presentation_type type) { specs_.type = type; }
+};
+
+// Format spec handler that saves references to arguments representing dynamic
+// width and precision to be resolved at formatting time.
+template <typename ParseContext>
+class dynamic_specs_handler
+    : public specs_setter<typename ParseContext::char_type> {
+ public:
+  using char_type = typename ParseContext::char_type;
+
+  FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs<char_type>& specs,
+                                      ParseContext& ctx)
+      : specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
+
+  FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other)
+      : specs_setter<char_type>(other),
+        specs_(other.specs_),
+        context_(other.context_) {}
+
+  template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
+    specs_.width_ref = make_arg_ref(arg_id);
+  }
+
+  template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
+    specs_.precision_ref = make_arg_ref(arg_id);
+  }
+
+  FMT_CONSTEXPR void on_error(const char* message) {
+    context_.on_error(message);
+  }
+
+ private:
+  dynamic_format_specs<char_type>& specs_;
+  ParseContext& context_;
+
+  using arg_ref_type = arg_ref<char_type>;
+
+  FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type {
+    context_.check_arg_id(arg_id);
+    context_.check_dynamic_spec(arg_id);
+    return arg_ref_type(arg_id);
+  }
+
+  FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type {
+    int arg_id = context_.next_arg_id();
+    context_.check_dynamic_spec(arg_id);
+    return arg_ref_type(arg_id);
+  }
+
+  FMT_CONSTEXPR auto make_arg_ref(basic_string_view<char_type> arg_id)
+      -> arg_ref_type {
+    context_.check_arg_id(arg_id);
+    basic_string_view<char_type> format_str(
+        context_.begin(), to_unsigned(context_.end() - context_.begin()));
+    return arg_ref_type(arg_id);
+  }
+};
+
+template <typename Char> constexpr bool is_ascii_letter(Char c) {
+  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+}
+
+// Converts a character to ASCII. Returns a number > 127 on conversion failure.
+template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
+constexpr auto to_ascii(Char c) -> Char {
+  return c;
+}
+template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
+constexpr auto to_ascii(Char c) -> underlying_t<Char> {
+  return c;
+}
+
+FMT_CONSTEXPR inline auto code_point_length_impl(char c) -> int {
+  return "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"
+      [static_cast<unsigned char>(c) >> 3];
+}
+
+template <typename Char>
+FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
+  if (const_check(sizeof(Char) != 1)) return 1;
+  int len = code_point_length_impl(static_cast<char>(*begin));
+
+  // Compute the pointer to the next character early so that the next
+  // iteration can start working on the next character. Neither Clang
+  // nor GCC figure out this reordering on their own.
+  return len + !len;
+}
+
+// Return the result via the out param to workaround gcc bug 77539.
+template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
+FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
+  for (out = first; out != last; ++out) {
+    if (*out == value) return true;
+  }
+  return false;
+}
+
+template <>
+inline auto find<false, char>(const char* first, const char* last, char value,
+                              const char*& out) -> bool {
+  out = static_cast<const char*>(
+      std::memchr(first, value, to_unsigned(last - first)));
+  return out != nullptr;
+}
+
+// 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>
+FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end,
+                                         int error_value) noexcept -> int {
+  FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
+  unsigned value = 0, prev = 0;
+  auto p = begin;
+  do {
+    prev = value;
+    value = value * 10 + unsigned(*p - '0');
+    ++p;
+  } while (p != end && '0' <= *p && *p <= '9');
+  auto num_digits = p - begin;
+  begin = p;
+  if (num_digits <= std::numeric_limits<int>::digits10)
+    return static_cast<int>(value);
+  // Check for overflow.
+  const unsigned max = to_unsigned((std::numeric_limits<int>::max)());
+  return num_digits == std::numeric_limits<int>::digits10 + 1 &&
+                 prev * 10ull + unsigned(p[-1] - '0') <= max
+             ? static_cast<int>(value)
+             : error_value;
+}
+
+// Parses fill and alignment.
+template <typename Char, typename Handler>
+FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
+                               Handler&& handler) -> const Char* {
+  FMT_ASSERT(begin != end, "");
+  auto align = align::none;
+  auto p = begin + code_point_length(begin);
+  if (end - p <= 0) p = begin;
+  for (;;) {
+    switch (to_ascii(*p)) {
+    case '<':
+      align = align::left;
+      break;
+    case '>':
+      align = align::right;
+      break;
+    case '^':
+      align = align::center;
+      break;
+    default:
+      break;
+    }
+    if (align != align::none) {
+      if (p != begin) {
+        auto c = *begin;
+        if (c == '{')
+          return handler.on_error("invalid fill character '{'"), begin;
+        handler.on_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
+        begin = p + 1;
+      } else
+        ++begin;
+      handler.on_align(align);
+      break;
+    } else if (p == begin) {
+      break;
+    }
+    p = begin;
+  }
+  return begin;
+}
+
+template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
+  return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
+}
+
+template <typename Char, typename IDHandler>
+FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end,
+                                   IDHandler&& handler) -> const Char* {
+  FMT_ASSERT(begin != end, "");
+  Char c = *begin;
+  if (c >= '0' && c <= '9') {
+    int index = 0;
+    if (c != '0')
+      index =
+          parse_nonnegative_int(begin, end, (std::numeric_limits<int>::max)());
+    else
+      ++begin;
+    if (begin == end || (*begin != '}' && *begin != ':'))
+      handler.on_error("invalid format string");
+    else
+      handler(index);
+    return begin;
+  }
+  if (!is_name_start(c)) {
+    handler.on_error("invalid format string");
+    return begin;
+  }
+  auto it = begin;
+  do {
+    ++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;
+}
+
+template <typename Char, typename IDHandler>
+FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end,
+                                           IDHandler&& handler) -> const Char* {
+  Char c = *begin;
+  if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler);
+  handler();
+  return begin;
+}
+
+template <typename Char, typename Handler>
+FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end,
+                               Handler&& handler) -> const Char* {
+  using detail::auto_id;
+  struct width_adapter {
+    Handler& handler;
+
+    FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
+    FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); }
+    FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
+      handler.on_dynamic_width(id);
+    }
+    FMT_CONSTEXPR void on_error(const char* message) {
+      if (message) handler.on_error(message);
+    }
+  };
+
+  FMT_ASSERT(begin != end, "");
+  if ('0' <= *begin && *begin <= '9') {
+    int width = parse_nonnegative_int(begin, end, -1);
+    if (width != -1)
+      handler.on_width(width);
+    else
+      handler.on_error("number is too big");
+  } else if (*begin == '{') {
+    ++begin;
+    if (begin != end) begin = parse_arg_id(begin, end, width_adapter{handler});
+    if (begin == end || *begin != '}')
+      return handler.on_error("invalid format string"), begin;
+    ++begin;
+  }
+  return begin;
+}
+
+template <typename Char, typename Handler>
+FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
+                                   Handler&& handler) -> const Char* {
+  using detail::auto_id;
+  struct precision_adapter {
+    Handler& handler;
+
+    FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
+    FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); }
+    FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
+      handler.on_dynamic_precision(id);
+    }
+    FMT_CONSTEXPR void on_error(const char* message) {
+      if (message) handler.on_error(message);
+    }
+  };
+
+  ++begin;
+  auto c = begin != end ? *begin : Char();
+  if ('0' <= c && c <= '9') {
+    auto precision = parse_nonnegative_int(begin, end, -1);
+    if (precision != -1)
+      handler.on_precision(precision);
+    else
+      handler.on_error("number is too big");
+  } else if (c == '{') {
+    ++begin;
+    if (begin != end)
+      begin = parse_arg_id(begin, end, precision_adapter{handler});
+    if (begin == end || *begin++ != '}')
+      return handler.on_error("invalid format string"), begin;
+  } else {
+    return handler.on_error("missing precision specifier"), begin;
+  }
+  handler.end_precision();
+  return begin;
+}
+
+template <typename Char>
+FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type {
+  switch (to_ascii(type)) {
+  case 'd':
+    return presentation_type::dec;
+  case 'o':
+    return presentation_type::oct;
+  case 'x':
+    return presentation_type::hex_lower;
+  case 'X':
+    return presentation_type::hex_upper;
+  case 'b':
+    return presentation_type::bin_lower;
+  case 'B':
+    return presentation_type::bin_upper;
+  case 'a':
+    return presentation_type::hexfloat_lower;
+  case 'A':
+    return presentation_type::hexfloat_upper;
+  case 'e':
+    return presentation_type::exp_lower;
+  case 'E':
+    return presentation_type::exp_upper;
+  case 'f':
+    return presentation_type::fixed_lower;
+  case 'F':
+    return presentation_type::fixed_upper;
+  case 'g':
+    return presentation_type::general_lower;
+  case 'G':
+    return presentation_type::general_upper;
+  case 'c':
+    return presentation_type::chr;
+  case 's':
+    return presentation_type::string;
+  case 'p':
+    return presentation_type::pointer;
+  case '?':
+    return presentation_type::debug;
+  default:
+    return presentation_type::none;
+  }
+}
+
+// Parses standard format specifiers and sends notifications about parsed
+// components to handler.
+template <typename Char, typename SpecHandler>
+FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin,
+                                                 const Char* end,
+                                                 SpecHandler&& handler)
+    -> const Char* {
+  if (1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) &&
+      *begin != 'L') {
+    presentation_type type = parse_presentation_type(*begin++);
+    if (type == presentation_type::none)
+      handler.on_error("invalid type specifier");
+    handler.on_type(type);
+    return begin;
+  }
+
+  if (begin == end) return begin;
+
+  begin = parse_align(begin, end, handler);
+  if (begin == end) return begin;
+
+  // Parse sign.
+  switch (to_ascii(*begin)) {
+  case '+':
+    handler.on_sign(sign::plus);
+    ++begin;
+    break;
+  case '-':
+    handler.on_sign(sign::minus);
+    ++begin;
+    break;
+  case ' ':
+    handler.on_sign(sign::space);
+    ++begin;
+    break;
+  default:
+    break;
+  }
+  if (begin == end) return begin;
+
+  if (*begin == '#') {
+    handler.on_hash();
+    if (++begin == end) return begin;
+  }
+
+  // Parse zero flag.
+  if (*begin == '0') {
+    handler.on_zero();
+    if (++begin == end) return begin;
+  }
+
+  begin = parse_width(begin, end, handler);
+  if (begin == end) return begin;
+
+  // Parse precision.
+  if (*begin == '.') {
+    begin = parse_precision(begin, end, handler);
+    if (begin == end) return begin;
+  }
+
+  if (*begin == 'L') {
+    handler.on_localized();
+    ++begin;
+  }
+
+  // Parse type.
+  if (begin != end && *begin != '}') {
+    presentation_type type = parse_presentation_type(*begin++);
+    if (type == presentation_type::none)
+      handler.on_error("invalid type specifier");
+    handler.on_type(type);
+  }
+  return begin;
+}
+
+template <typename Char, typename Handler>
+FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end,
+                                           Handler&& handler) -> const Char* {
+  struct id_adapter {
+    Handler& handler;
+    int arg_id;
+
+    FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); }
+    FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); }
+    FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
+      arg_id = handler.on_arg_id(id);
+    }
+    FMT_CONSTEXPR void on_error(const char* message) {
+      if (message) handler.on_error(message);
+    }
+  };
+
+  ++begin;
+  if (begin == end) return handler.on_error("invalid format string"), end;
+  if (*begin == '}') {
+    handler.on_replacement_field(handler.on_arg_id(), begin);
+  } else if (*begin == '{') {
+    handler.on_text(begin, begin + 1);
+  } else {
+    auto adapter = id_adapter{handler, 0};
+    begin = parse_arg_id(begin, end, adapter);
+    Char c = begin != end ? *begin : Char();
+    if (c == '}') {
+      handler.on_replacement_field(adapter.arg_id, begin);
+    } else if (c == ':') {
+      begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
+      if (begin == end || *begin != '}')
+        return handler.on_error("unknown format specifier"), end;
+    } else {
+      return handler.on_error("missing '}' in format string"), end;
+    }
+  }
+  return begin + 1;
+}
+
+template <bool IS_CONSTEXPR, typename Char, typename Handler>
+FMT_CONSTEXPR FMT_INLINE void parse_format_string(
+    basic_string_view<Char> format_str, Handler&& handler) {
+  // Workaround a name-lookup bug in MSVC's modules implementation.
+  using detail::find;
+
+  auto begin = format_str.data();
+  auto end = begin + format_str.size();
+  if (end - begin < 32) {
+    // Use a simple loop instead of memchr for small strings.
+    const Char* p = begin;
+    while (p != end) {
+      auto c = *p++;
+      if (c == '{') {
+        handler.on_text(begin, p - 1);
+        begin = p = parse_replacement_field(p - 1, end, handler);
+      } else if (c == '}') {
+        if (p == end || *p != '}')
+          return handler.on_error("unmatched '}' in format string");
+        handler.on_text(begin, p);
+        begin = ++p;
+      }
+    }
+    handler.on_text(begin, end);
+    return;
+  }
+  struct writer {
+    FMT_CONSTEXPR void operator()(const Char* from, const Char* to) {
+      if (from == to) return;
+      for (;;) {
+        const Char* p = nullptr;
+        if (!find<IS_CONSTEXPR>(from, to, Char('}'), p))
+          return handler_.on_text(from, to);
+        ++p;
+        if (p == to || *p != '}')
+          return handler_.on_error("unmatched '}' in format string");
+        handler_.on_text(from, p);
+        from = p + 1;
+      }
+    }
+    Handler& handler_;
+  } write = {handler};
+  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.
+    const Char* p = begin;
+    if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, Char('{'), p))
+      return write(begin, end);
+    write(begin, p);
+    begin = parse_replacement_field(p, end, handler);
+  }
+}
+
+template <typename T, bool = is_named_arg<T>::value> struct strip_named_arg {
+  using type = T;
+};
+template <typename T> struct strip_named_arg<T, true> {
+  using type = remove_cvref_t<decltype(T::value)>;
+};
+
+template <typename T, typename ParseContext>
+FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
+    -> decltype(ctx.begin()) {
+  using char_type = typename ParseContext::char_type;
+  using context = buffer_context<char_type>;
+  using stripped_type = typename strip_named_arg<T>::type;
+  using mapped_type = conditional_t<
+      mapped_type_constant<T, context>::value != type::custom_type,
+      decltype(arg_mapper<context>().map(std::declval<const T&>())),
+      stripped_type>;
+  auto f = conditional_t<has_formatter<mapped_type, context>::value,
+                         formatter<mapped_type, char_type>,
+                         fallback_formatter<stripped_type, char_type>>();
+  return f.parse(ctx);
+}
+
+template <typename ErrorHandler>
+FMT_CONSTEXPR void check_int_type_spec(presentation_type type,
+                                       ErrorHandler&& eh) {
+  if (type > presentation_type::bin_upper && type != presentation_type::chr)
+    eh.on_error("invalid type specifier");
+}
+
+// Checks char specs and returns true if the type spec is char (and not int).
+template <typename Char, typename ErrorHandler = error_handler>
+FMT_CONSTEXPR auto check_char_specs(const basic_format_specs<Char>& specs,
+                                    ErrorHandler&& eh = {}) -> bool {
+  if (specs.type != presentation_type::none &&
+      specs.type != presentation_type::chr &&
+      specs.type != presentation_type::debug) {
+    check_int_type_spec(specs.type, eh);
+    return false;
+  }
+  if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
+    eh.on_error("invalid format specifier for char");
+  return true;
+}
+
+// A floating-point presentation format.
+enum class float_format : unsigned char {
+  general,  // General: exponent notation or fixed point based on magnitude.
+  exp,      // Exponent notation with the default precision of 6, e.g. 1.2e-3.
+  fixed,    // Fixed point with the default precision of 6, e.g. 0.0012.
+  hex
+};
+
+struct float_specs {
+  int precision;
+  float_format format : 8;
+  sign_t sign : 8;
+  bool upper : 1;
+  bool locale : 1;
+  bool binary32 : 1;
+  bool showpoint : 1;
+};
+
+template <typename ErrorHandler = error_handler, typename Char>
+FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs<Char>& specs,
+                                         ErrorHandler&& eh = {})
+    -> float_specs {
+  auto result = float_specs();
+  result.showpoint = specs.alt;
+  result.locale = specs.localized;
+  switch (specs.type) {
+  case presentation_type::none:
+    result.format = float_format::general;
+    break;
+  case presentation_type::general_upper:
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case presentation_type::general_lower:
+    result.format = float_format::general;
+    break;
+  case presentation_type::exp_upper:
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case presentation_type::exp_lower:
+    result.format = float_format::exp;
+    result.showpoint |= specs.precision != 0;
+    break;
+  case presentation_type::fixed_upper:
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case presentation_type::fixed_lower:
+    result.format = float_format::fixed;
+    result.showpoint |= specs.precision != 0;
+    break;
+  case presentation_type::hexfloat_upper:
+    result.upper = true;
+    FMT_FALLTHROUGH;
+  case presentation_type::hexfloat_lower:
+    result.format = float_format::hex;
+    break;
+  default:
+    eh.on_error("invalid type specifier");
+    break;
+  }
+  return result;
+}
+
+template <typename ErrorHandler = error_handler>
+FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type,
+                                           ErrorHandler&& eh = {}) -> bool {
+  if (type == presentation_type::none || type == presentation_type::string ||
+      type == presentation_type::debug)
+    return true;
+  if (type != presentation_type::pointer) eh.on_error("invalid type specifier");
+  return false;
+}
+
+template <typename ErrorHandler = error_handler>
+FMT_CONSTEXPR void check_string_type_spec(presentation_type type,
+                                          ErrorHandler&& eh = {}) {
+  if (type != presentation_type::none && type != presentation_type::string &&
+      type != presentation_type::debug)
+    eh.on_error("invalid type specifier");
+}
+
+template <typename ErrorHandler>
+FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type,
+                                           ErrorHandler&& eh) {
+  if (type != presentation_type::none && type != presentation_type::pointer)
+    eh.on_error("invalid type specifier");
+}
+
+// A parse_format_specs handler that checks if specifiers are consistent with
+// the argument type.
+template <typename Handler> class specs_checker : public Handler {
+ private:
+  detail::type arg_type_;
+
+  FMT_CONSTEXPR void require_numeric_argument() {
+    if (!is_arithmetic_type(arg_type_))
+      this->on_error("format specifier requires numeric argument");
+  }
+
+ public:
+  FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type)
+      : Handler(handler), arg_type_(arg_type) {}
+
+  FMT_CONSTEXPR void on_align(align_t align) {
+    if (align == align::numeric) require_numeric_argument();
+    Handler::on_align(align);
+  }
+
+  FMT_CONSTEXPR void on_sign(sign_t s) {
+    require_numeric_argument();
+    if (is_integral_type(arg_type_) && arg_type_ != type::int_type &&
+        arg_type_ != type::long_long_type && arg_type_ != type::int128_type &&
+        arg_type_ != type::char_type) {
+      this->on_error("format specifier requires signed argument");
+    }
+    Handler::on_sign(s);
+  }
+
+  FMT_CONSTEXPR void on_hash() {
+    require_numeric_argument();
+    Handler::on_hash();
+  }
+
+  FMT_CONSTEXPR void on_localized() {
+    require_numeric_argument();
+    Handler::on_localized();
+  }
+
+  FMT_CONSTEXPR void on_zero() {
+    require_numeric_argument();
+    Handler::on_zero();
+  }
+
+  FMT_CONSTEXPR void end_precision() {
+    if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type)
+      this->on_error("precision not allowed for this argument type");
+  }
+};
+
+constexpr int invalid_arg_index = -1;
+
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+template <int N, typename T, typename... Args, typename Char>
+constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
+  if constexpr (detail::is_statically_named_arg<T>()) {
+    if (name == T::name) return N;
+  }
+  if constexpr (sizeof...(Args) > 0)
+    return get_arg_index_by_name<N + 1, Args...>(name);
+  (void)name;  // Workaround an MSVC bug about "unused" parameter.
+  return invalid_arg_index;
+}
+#endif
+
+template <typename... Args, typename Char>
+FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+  if constexpr (sizeof...(Args) > 0)
+    return get_arg_index_by_name<0, Args...>(name);
+#endif
+  (void)name;
+  return invalid_arg_index;
+}
+
+template <typename Char, typename ErrorHandler, typename... Args>
+class format_string_checker {
+ private:
+  // In the future basic_format_parse_context will replace compile_parse_context
+  // here and will use is_constant_evaluated and downcasting to access the data
+  // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1.
+  using parse_context_type = compile_parse_context<Char, ErrorHandler>;
+  static constexpr int num_args = sizeof...(Args);
+
+  // Format specifier parsing function.
+  using parse_func = const Char* (*)(parse_context_type&);
+
+  parse_context_type context_;
+  parse_func parse_funcs_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
+  type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
+
+ public:
+  explicit FMT_CONSTEXPR format_string_checker(
+      basic_string_view<Char> format_str, ErrorHandler eh)
+      : context_(format_str, num_args, types_, eh),
+        parse_funcs_{&parse_format_specs<Args, parse_context_type>...},
+        types_{
+            mapped_type_constant<Args,
+                                 basic_format_context<Char*, Char>>::value...} {
+  }
+
+  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
+
+  FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); }
+  FMT_CONSTEXPR auto on_arg_id(int id) -> int {
+    return context_.check_arg_id(id), id;
+  }
+  FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+    auto index = get_arg_index_by_name<Args...>(id);
+    if (index == invalid_arg_index) on_error("named argument is not found");
+    return context_.check_arg_id(index), index;
+#else
+    (void)id;
+    on_error("compile-time checks for named arguments require C++20 support");
+    return 0;
+#endif
+  }
+
+  FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
+
+  FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*)
+      -> const Char* {
+    context_.advance_to(context_.begin() + (begin - &*context_.begin()));
+    // id >= 0 check is a workaround for gcc 10 bug (#2065).
+    return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin;
+  }
+
+  FMT_CONSTEXPR void on_error(const char* message) {
+    context_.on_error(message);
+  }
+};
+
+// Reports a compile-time error if S is not a valid format string.
+template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
+FMT_INLINE void check_format_string(const S&) {
+#ifdef FMT_ENFORCE_COMPILE_STRING
+  static_assert(is_compile_string<S>::value,
+                "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
+                "FMT_STRING.");
+#endif
+}
+template <typename... Args, typename S,
+          FMT_ENABLE_IF(is_compile_string<S>::value)>
+void check_format_string(S format_str) {
+  FMT_CONSTEXPR auto s = basic_string_view<typename S::char_type>(format_str);
+  using checker = format_string_checker<typename S::char_type, error_handler,
+                                        remove_cvref_t<Args>...>;
+  FMT_CONSTEXPR bool invalid_format =
+      (parse_format_string<true>(s, checker(s, {})), true);
+  ignore_unused(invalid_format);
+}
+
+template <typename Char>
+void vformat_to(
+    buffer<Char>& buf, basic_string_view<Char> fmt,
+    basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
+    locale_ref loc = {});
+
+FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
+#ifndef _WIN32
+inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
+#endif
+FMT_END_DETAIL_NAMESPACE
+
+// A formatter specialization for the core types corresponding to detail::type
+// constants.
+template <typename T, typename Char>
+struct formatter<T, Char,
+                 enable_if_t<detail::type_constant<T, Char>::value !=
+                             detail::type::custom_type>> {
+ private:
+  detail::dynamic_format_specs<Char> specs_;
+
+ public:
+  // Parses format specifiers stopping either at the end of the range or at the
+  // terminating '}'.
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    auto begin = ctx.begin(), end = ctx.end();
+    if (begin == end) return begin;
+    using handler_type = detail::dynamic_specs_handler<ParseContext>;
+    auto type = detail::type_constant<T, Char>::value;
+    auto checker =
+        detail::specs_checker<handler_type>(handler_type(specs_, ctx), type);
+    auto it = detail::parse_format_specs(begin, end, checker);
+    auto eh = ctx.error_handler();
+    switch (type) {
+    case detail::type::none_type:
+      FMT_ASSERT(false, "invalid argument type");
+      break;
+    case detail::type::bool_type:
+      if (specs_.type == presentation_type::none ||
+          specs_.type == presentation_type::string) {
+        break;
+      }
+      FMT_FALLTHROUGH;
+    case detail::type::int_type:
+    case detail::type::uint_type:
+    case detail::type::long_long_type:
+    case detail::type::ulong_long_type:
+    case detail::type::int128_type:
+    case detail::type::uint128_type:
+      detail::check_int_type_spec(specs_.type, eh);
+      break;
+    case detail::type::char_type:
+      detail::check_char_specs(specs_, eh);
+      break;
+    case detail::type::float_type:
+      if (detail::const_check(FMT_USE_FLOAT))
+        detail::parse_float_type_spec(specs_, eh);
+      else
+        FMT_ASSERT(false, "float support disabled");
+      break;
+    case detail::type::double_type:
+      if (detail::const_check(FMT_USE_DOUBLE))
+        detail::parse_float_type_spec(specs_, eh);
+      else
+        FMT_ASSERT(false, "double support disabled");
+      break;
+    case detail::type::long_double_type:
+      if (detail::const_check(FMT_USE_LONG_DOUBLE))
+        detail::parse_float_type_spec(specs_, eh);
+      else
+        FMT_ASSERT(false, "long double support disabled");
+      break;
+    case detail::type::cstring_type:
+      detail::check_cstring_type_spec(specs_.type, eh);
+      break;
+    case detail::type::string_type:
+      detail::check_string_type_spec(specs_.type, eh);
+      break;
+    case detail::type::pointer_type:
+      detail::check_pointer_type_spec(specs_.type, eh);
+      break;
+    case detail::type::custom_type:
+      // Custom format specifiers are checked in parse functions of
+      // formatter specializations.
+      break;
+    }
+    return it;
+  }
+
+  template <detail::type U = detail::type_constant<T, Char>::value,
+            enable_if_t<(U == detail::type::string_type ||
+                         U == detail::type::cstring_type ||
+                         U == detail::type::char_type),
+                        int> = 0>
+  FMT_CONSTEXPR void set_debug_format() {
+    specs_.type = presentation_type::debug;
+  }
+
+  template <typename FormatContext>
+  FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
+      -> decltype(ctx.out());
+};
+
+#define FMT_FORMAT_AS(Type, Base)                                        \
+  template <typename Char>                                               \
+  struct formatter<Type, Char> : formatter<Base, Char> {                 \
+    template <typename FormatContext>                                    \
+    auto format(Type const& val, FormatContext& ctx) const               \
+        -> decltype(ctx.out()) {                                         \
+      return formatter<Base, Char>::format(static_cast<Base>(val), ctx); \
+    }                                                                    \
+  }
+
+FMT_FORMAT_AS(signed char, int);
+FMT_FORMAT_AS(unsigned char, unsigned);
+FMT_FORMAT_AS(short, int);
+FMT_FORMAT_AS(unsigned short, unsigned);
+FMT_FORMAT_AS(long, long long);
+FMT_FORMAT_AS(unsigned long, unsigned long long);
+FMT_FORMAT_AS(Char*, const Char*);
+FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
+FMT_FORMAT_AS(std::nullptr_t, const void*);
+FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
+
+template <typename Char> struct basic_runtime { basic_string_view<Char> str; };
+
+/** A compile-time format string. */
+template <typename Char, typename... Args> class basic_format_string {
+ private:
+  basic_string_view<Char> str_;
+
+ public:
+  template <typename S,
+            FMT_ENABLE_IF(
+                std::is_convertible<const S&, basic_string_view<Char>>::value)>
+  FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) {
+    static_assert(
+        detail::count<
+            (std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
+             std::is_reference<Args>::value)...>() == 0,
+        "passing views as lvalues is disallowed");
+#ifdef FMT_HAS_CONSTEVAL
+    if constexpr (detail::count_named_args<Args...>() ==
+                  detail::count_statically_named_args<Args...>()) {
+      using checker = detail::format_string_checker<Char, detail::error_handler,
+                                                    remove_cvref_t<Args>...>;
+      detail::parse_format_string<true>(str_, checker(s, {}));
+    }
+#else
+    detail::check_format_string<Args...>(s);
+#endif
+  }
+  basic_format_string(basic_runtime<Char> r) : str_(r.str) {}
+
+  FMT_INLINE operator basic_string_view<Char>() const { return str_; }
+};
+
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+// Workaround broken conversion on older gcc.
+template <typename...> using format_string = string_view;
+inline auto runtime(string_view s) -> string_view { return s; }
+#else
+template <typename... Args>
+using format_string = basic_format_string<char, type_identity_t<Args>...>;
+/**
+  \rst
+  Creates a runtime format string.
+
+  **Example**::
+
+    // Check format string at runtime instead of compile-time.
+    fmt::print(fmt::runtime("{:d}"), "I am not a number");
+  \endrst
+ */
+inline auto runtime(string_view s) -> basic_runtime<char> { return {{s}}; }
+#endif
+
+FMT_API auto vformat(string_view fmt, format_args args) -> std::string;
+
+/**
+  \rst
+  Formats ``args`` according to specifications in ``fmt`` and returns the result
+  as a string.
+
+  **Example**::
+
+    #include <fmt/core.h>
+    std::string message = fmt::format("The answer is {}.", 42);
+  \endrst
+*/
+template <typename... T>
+FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T&&... args)
+    -> std::string {
+  return vformat(fmt, fmt::make_format_args(args...));
+}
+
+/** Formats a string and writes the output to ``out``. */
+template <typename OutputIt,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt {
+  using detail::get_buffer;
+  auto&& buf = get_buffer<char>(out);
+  detail::vformat_to(buf, fmt, args, {});
+  return detail::get_iterator(buf);
+}
+
+/**
+ \rst
+ Formats ``args`` according to specifications in ``fmt``, writes the result to
+ the output iterator ``out`` and returns the iterator past the end of the output
+ range. `format_to` does not append a terminating null character.
+
+ **Example**::
+
+   auto out = std::vector<char>();
+   fmt::format_to(std::back_inserter(out), "{}", 42);
+ \endrst
+ */
+template <typename OutputIt, typename... T,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+FMT_INLINE auto format_to(OutputIt out, format_string<T...> fmt, T&&... args)
+    -> OutputIt {
+  return vformat_to(out, fmt, fmt::make_format_args(args...));
+}
+
+template <typename OutputIt> struct format_to_n_result {
+  /** Iterator past the end of the output range. */
+  OutputIt out;
+  /** Total (not truncated) output size. */
+  size_t size;
+};
+
+template <typename OutputIt, typename... T,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args)
+    -> format_to_n_result<OutputIt> {
+  using traits = detail::fixed_buffer_traits;
+  auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
+  detail::vformat_to(buf, fmt, args, {});
+  return {buf.out(), buf.count()};
+}
+
+/**
+  \rst
+  Formats ``args`` according to specifications in ``fmt``, writes up to ``n``
+  characters of the result to the output iterator ``out`` and returns the total
+  (not truncated) output size and the iterator past the end of the output range.
+  `format_to_n` does not append a terminating null character.
+  \endrst
+ */
+template <typename OutputIt, typename... T,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt,
+                            T&&... args) -> format_to_n_result<OutputIt> {
+  return vformat_to_n(out, n, fmt, fmt::make_format_args(args...));
+}
+
+/** Returns the number of chars in the output of ``format(fmt, args...)``. */
+template <typename... T>
+FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt,
+                                             T&&... args) -> size_t {
+  auto buf = detail::counting_buffer<>();
+  detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...), {});
+  return buf.count();
+}
+
+FMT_API void vprint(string_view fmt, format_args args);
+FMT_API void vprint(std::FILE* f, string_view fmt, format_args args);
+
+/**
+  \rst
+  Formats ``args`` according to specifications in ``fmt`` and writes the output
+  to ``stdout``.
+
+  **Example**::
+
+    fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
+  \endrst
+ */
+template <typename... T>
+FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
+  const auto& vargs = fmt::make_format_args(args...);
+  return detail::is_utf8() ? vprint(fmt, vargs)
+                           : detail::vprint_mojibake(stdout, fmt, vargs);
+}
+
+/**
+  \rst
+  Formats ``args`` according to specifications in ``fmt`` and writes the
+  output to the file ``f``.
+
+  **Example**::
+
+    fmt::print(stderr, "Don't {}!", "panic");
+  \endrst
+ */
+template <typename... T>
+FMT_INLINE void print(std::FILE* f, format_string<T...> fmt, T&&... args) {
+  const auto& vargs = fmt::make_format_args(args...);
+  return detail::is_utf8() ? vprint(f, fmt, vargs)
+                           : detail::vprint_mojibake(f, fmt, vargs);
+}
+
+FMT_MODULE_EXPORT_END
+FMT_GCC_PRAGMA("GCC pop_options")
+FMT_END_NAMESPACE
+
+#ifdef FMT_HEADER_ONLY
+#  include "format.h"
+#endif
+#endif  // FMT_CORE_H_
diff --git a/include/spdlog/fmt/bundled/fmt.license.rst b/include/spdlog/fmt/bundled/fmt.license.rst
new file mode 100644
index 000000000..f0ec3db4d
--- /dev/null
+++ b/include/spdlog/fmt/bundled/fmt.license.rst
@@ -0,0 +1,27 @@
+Copyright (c) 2012 - present, Victor Zverovich
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+--- Optional exception to the license ---
+
+As an exception, if, as a result of your compiling your source code, portions
+of this Software are embedded into a machine-executable object form of such
+source code, you may redistribute such embedded portions in such object form
+without including the above copyright and permission notices.
diff --git a/include/spdlog/fmt/bundled/format-inl.h b/include/spdlog/fmt/bundled/format-inl.h
new file mode 100644
index 000000000..22b1ec8df
--- /dev/null
+++ b/include/spdlog/fmt/bundled/format-inl.h
@@ -0,0 +1,1723 @@
+// Formatting library for C++ - implementation
+//
+// Copyright (c) 2012 - 2016, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_FORMAT_INL_H_
+#define FMT_FORMAT_INL_H_
+
+#include <algorithm>
+#include <cctype>
+#include <cerrno>  // errno
+#include <climits>
+#include <cmath>
+#include <cstdarg>
+#include <cstring>  // std::memmove
+#include <cwchar>
+#include <exception>
+
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+#  include <locale>
+#endif
+
+#ifdef _WIN32
+#  include <io.h>  // _isatty
+#endif
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+namespace detail {
+
+FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
+  // Use unchecked std::fprintf to avoid triggering another assertion when
+  // writing to stderr fails
+  std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
+  // Chosen instead of std::abort to satisfy Clang in CUDA mode during device
+  // code pass.
+  std::terminate();
+}
+
+FMT_FUNC void throw_format_error(const char* message) {
+  FMT_THROW(format_error(message));
+}
+
+FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
+                                string_view message) noexcept {
+  // Report error code making sure that the output fits into
+  // inline_buffer_size to avoid dynamic memory allocation and potential
+  // bad_alloc.
+  out.try_resize(0);
+  static const char SEP[] = ": ";
+  static const char ERROR_STR[] = "error ";
+  // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
+  size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
+  auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
+  if (detail::is_negative(error_code)) {
+    abs_value = 0 - abs_value;
+    ++error_code_size;
+  }
+  error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
+  auto it = buffer_appender<char>(out);
+  if (message.size() <= inline_buffer_size - error_code_size)
+    format_to(it, FMT_STRING("{}{}"), message, SEP);
+  format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
+  FMT_ASSERT(out.size() <= inline_buffer_size, "");
+}
+
+FMT_FUNC void report_error(format_func func, int error_code,
+                           const char* message) noexcept {
+  memory_buffer full_message;
+  func(full_message, error_code, message);
+  // Don't use fwrite_fully because the latter may throw.
+  if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0)
+    std::fputc('\n', stderr);
+}
+
+// A wrapper around fwrite that throws on error.
+inline void fwrite_fully(const void* ptr, size_t size, size_t count,
+                         FILE* stream) {
+  size_t written = std::fwrite(ptr, size, count, stream);
+  if (written < count)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
+}
+
+#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
+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 auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
+  auto& facet = std::use_facet<std::numpunct<Char>>(loc.get<std::locale>());
+  auto grouping = facet.grouping();
+  auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
+  return {std::move(grouping), thousands_sep};
+}
+template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
+  return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
+      .decimal_point();
+}
+#else
+template <typename Char>
+FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result<Char> {
+  return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR};
+}
+template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
+  return '.';
+}
+#endif
+}  // namespace detail
+
+#if !FMT_MSC_VERSION
+FMT_API FMT_FUNC format_error::~format_error() noexcept = default;
+#endif
+
+FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str,
+                                         format_args args) {
+  auto ec = std::error_code(error_code, std::generic_category());
+  return std::system_error(ec, vformat(format_str, args));
+}
+
+namespace detail {
+
+template <typename F> inline bool operator==(basic_fp<F> x, basic_fp<F> y) {
+  return x.f == y.f && x.e == y.e;
+}
+
+// Compilers should be able to optimize this into the ror instruction.
+FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept {
+  r &= 31;
+  return (n >> r) | (n << (32 - r));
+}
+FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept {
+  r &= 63;
+  return (n >> r) | (n << (64 - r));
+}
+
+// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
+inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept {
+#if FMT_USE_INT128
+  auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
+  return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
+#elif defined(_MSC_VER) && defined(_M_X64)
+  auto result = uint128_fallback();
+  result.lo_ = _umul128(x, y, &result.hi_);
+  return result;
+#else
+  const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>());
+
+  uint64_t a = x >> 32;
+  uint64_t b = x & mask;
+  uint64_t c = y >> 32;
+  uint64_t d = y & mask;
+
+  uint64_t ac = a * c;
+  uint64_t bc = b * c;
+  uint64_t ad = a * d;
+  uint64_t bd = b * d;
+
+  uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask);
+
+  return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32),
+          (intermediate << 32) + (bd & mask)};
+#endif
+}
+
+// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox.
+namespace dragonbox {
+// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
+inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept {
+#if FMT_USE_INT128
+  auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
+  return static_cast<uint64_t>(p >> 64);
+#elif defined(_MSC_VER) && defined(_M_X64)
+  return __umulh(x, y);
+#else
+  return umul128(x, y).high();
+#endif
+}
+
+// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
+// 128-bit unsigned integer.
+inline uint128_fallback umul192_upper128(uint64_t x,
+                                         uint128_fallback y) noexcept {
+  uint128_fallback r = umul128(x, y.high());
+  r += umul128_upper64(x, y.low());
+  return r;
+}
+
+// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a
+// 64-bit unsigned integer.
+inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept {
+  return umul128_upper64(static_cast<uint64_t>(x) << 32, y);
+}
+
+// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a
+// 128-bit unsigned integer.
+inline uint128_fallback umul192_lower128(uint64_t x,
+                                         uint128_fallback y) noexcept {
+  uint64_t high = x * y.high();
+  uint128_fallback high_low = umul128(x, y.low());
+  return {high + high_low.high(), high_low.low()};
+}
+
+// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a
+// 64-bit unsigned integer.
+inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept {
+  return x * y;
+}
+
+// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from
+// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1.
+inline int floor_log10_pow2(int e) noexcept {
+  FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent");
+  static_assert((-1 >> 1) == -1, "right shift is not arithmetic");
+  return (e * 315653) >> 20;
+}
+
+// Various fast log computations.
+inline int floor_log2_pow10(int e) noexcept {
+  FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
+  return (e * 1741647) >> 19;
+}
+inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept {
+  FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent");
+  return (e * 631305 - 261663) >> 21;
+}
+
+static constexpr struct {
+  uint32_t divisor;
+  int shift_amount;
+} div_small_pow10_infos[] = {{10, 16}, {100, 16}};
+
+// Replaces n by floor(n / pow(10, N)) returning true if and only if n is
+// divisible by pow(10, N).
+// Precondition: n <= pow(10, N + 1).
+template <int N>
+bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept {
+  // The numbers below are chosen such that:
+  //   1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100,
+  //   2. nm mod 2^k < m if and only if n is divisible by d,
+  // where m is magic_number, k is shift_amount
+  // and d is divisor.
+  //
+  // Item 1 is a common technique of replacing division by a constant with
+  // multiplication, see e.g. "Division by Invariant Integers Using
+  // Multiplication" by Granlund and Montgomery (1994). magic_number (m) is set
+  // to ceil(2^k/d) for large enough k.
+  // The idea for item 2 originates from Schubfach.
+  constexpr auto info = div_small_pow10_infos[N - 1];
+  FMT_ASSERT(n <= info.divisor * 10, "n is too large");
+  constexpr uint32_t magic_number =
+      (1u << info.shift_amount) / info.divisor + 1;
+  n *= magic_number;
+  const uint32_t comparison_mask = (1u << info.shift_amount) - 1;
+  bool result = (n & comparison_mask) < magic_number;
+  n >>= info.shift_amount;
+  return result;
+}
+
+// Computes floor(n / pow(10, N)) for small n and N.
+// Precondition: n <= pow(10, N + 1).
+template <int N> uint32_t small_division_by_pow10(uint32_t n) noexcept {
+  constexpr auto info = div_small_pow10_infos[N - 1];
+  FMT_ASSERT(n <= info.divisor * 10, "n is too large");
+  constexpr uint32_t magic_number =
+      (1u << info.shift_amount) / info.divisor + 1;
+  return (n * magic_number) >> info.shift_amount;
+}
+
+// Computes floor(n / 10^(kappa + 1)) (float)
+inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept {
+  // 1374389535 = ceil(2^37/100)
+  return static_cast<uint32_t>((static_cast<uint64_t>(n) * 1374389535) >> 37);
+}
+// Computes floor(n / 10^(kappa + 1)) (double)
+inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept {
+  // 2361183241434822607 = ceil(2^(64+7)/1000)
+  return umul128_upper64(n, 2361183241434822607ull) >> 7;
+}
+
+// Various subroutines using pow10 cache
+template <class T> struct cache_accessor;
+
+template <> struct cache_accessor<float> {
+  using carrier_uint = float_info<float>::carrier_uint;
+  using cache_entry_type = uint64_t;
+
+  static uint64_t get_cached_power(int k) noexcept {
+    FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
+               "k is out of range");
+    static constexpr const uint64_t pow10_significands[] = {
+        0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
+        0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
+        0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
+        0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb,
+        0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a,
+        0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810,
+        0xe12e13424bb40e14, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff,
+        0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd,
+        0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424,
+        0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b,
+        0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000,
+        0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000,
+        0xc350000000000000, 0xf424000000000000, 0x9896800000000000,
+        0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000,
+        0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000,
+        0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000,
+        0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000,
+        0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000,
+        0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0,
+        0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940985,
+        0xa18f07d736b90be6, 0xc9f2c9cd04674edf, 0xfc6f7c4045812297,
+        0x9dc5ada82b70b59e, 0xc5371912364ce306, 0xf684df56c3e01bc7,
+        0x9a130b963a6c115d, 0xc097ce7bc90715b4, 0xf0bdc21abb48db21,
+        0x96769950b50d88f5, 0xbc143fa4e250eb32, 0xeb194f8e1ae525fe,
+        0x92efd1b8d0cf37bf, 0xb7abc627050305ae, 0xe596b7b0c643c71a,
+        0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f};
+    return pow10_significands[k - float_info<float>::min_k];
+  }
+
+  struct compute_mul_result {
+    carrier_uint result;
+    bool is_integer;
+  };
+  struct compute_mul_parity_result {
+    bool parity;
+    bool is_integer;
+  };
+
+  static compute_mul_result compute_mul(
+      carrier_uint u, const cache_entry_type& cache) noexcept {
+    auto r = umul96_upper64(u, cache);
+    return {static_cast<carrier_uint>(r >> 32),
+            static_cast<carrier_uint>(r) == 0};
+  }
+
+  static uint32_t compute_delta(const cache_entry_type& cache,
+                                int beta) noexcept {
+    return static_cast<uint32_t>(cache >> (64 - 1 - beta));
+  }
+
+  static compute_mul_parity_result compute_mul_parity(
+      carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
+    FMT_ASSERT(beta >= 1, "");
+    FMT_ASSERT(beta < 64, "");
+
+    auto r = umul96_lower64(two_f, cache);
+    return {((r >> (64 - beta)) & 1) != 0,
+            static_cast<uint32_t>(r >> (32 - beta)) == 0};
+  }
+
+  static carrier_uint compute_left_endpoint_for_shorter_interval_case(
+      const cache_entry_type& cache, int beta) noexcept {
+    return static_cast<carrier_uint>(
+        (cache - (cache >> (num_significand_bits<float>() + 2))) >>
+        (64 - num_significand_bits<float>() - 1 - beta));
+  }
+
+  static carrier_uint compute_right_endpoint_for_shorter_interval_case(
+      const cache_entry_type& cache, int beta) noexcept {
+    return static_cast<carrier_uint>(
+        (cache + (cache >> (num_significand_bits<float>() + 1))) >>
+        (64 - num_significand_bits<float>() - 1 - beta));
+  }
+
+  static carrier_uint compute_round_up_for_shorter_interval_case(
+      const cache_entry_type& cache, int beta) noexcept {
+    return (static_cast<carrier_uint>(
+                cache >> (64 - num_significand_bits<float>() - 2 - beta)) +
+            1) /
+           2;
+  }
+};
+
+template <> struct cache_accessor<double> {
+  using carrier_uint = float_info<double>::carrier_uint;
+  using cache_entry_type = uint128_fallback;
+
+  static uint128_fallback get_cached_power(int k) noexcept {
+    FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
+               "k is out of range");
+
+    static constexpr const uint128_fallback pow10_significands[] = {
+#if FMT_USE_FULL_CACHE_DRAGONBOX
+      {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
+      {0x9faacf3df73609b1, 0x77b191618c54e9ad},
+      {0xc795830d75038c1d, 0xd59df5b9ef6a2418},
+      {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e},
+      {0x9becce62836ac577, 0x4ee367f9430aec33},
+      {0xc2e801fb244576d5, 0x229c41f793cda740},
+      {0xf3a20279ed56d48a, 0x6b43527578c11110},
+      {0x9845418c345644d6, 0x830a13896b78aaaa},
+      {0xbe5691ef416bd60c, 0x23cc986bc656d554},
+      {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9},
+      {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa},
+      {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54},
+      {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69},
+      {0x91376c36d99995be, 0x23100809b9c21fa2},
+      {0xb58547448ffffb2d, 0xabd40a0c2832a78b},
+      {0xe2e69915b3fff9f9, 0x16c90c8f323f516d},
+      {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4},
+      {0xb1442798f49ffb4a, 0x99cd11cfdf41779d},
+      {0xdd95317f31c7fa1d, 0x40405643d711d584},
+      {0x8a7d3eef7f1cfc52, 0x482835ea666b2573},
+      {0xad1c8eab5ee43b66, 0xda3243650005eed0},
+      {0xd863b256369d4a40, 0x90bed43e40076a83},
+      {0x873e4f75e2224e68, 0x5a7744a6e804a292},
+      {0xa90de3535aaae202, 0x711515d0a205cb37},
+      {0xd3515c2831559a83, 0x0d5a5b44ca873e04},
+      {0x8412d9991ed58091, 0xe858790afe9486c3},
+      {0xa5178fff668ae0b6, 0x626e974dbe39a873},
+      {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
+      {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a},
+      {0xa139029f6a239f72, 0x1c1fffc1ebc44e81},
+      {0xc987434744ac874e, 0xa327ffb266b56221},
+      {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9},
+      {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa},
+      {0xc4ce17b399107c22, 0xcb550fb4384d21d4},
+      {0xf6019da07f549b2b, 0x7e2a53a146606a49},
+      {0x99c102844f94e0fb, 0x2eda7444cbfc426e},
+      {0xc0314325637a1939, 0xfa911155fefb5309},
+      {0xf03d93eebc589f88, 0x793555ab7eba27cb},
+      {0x96267c7535b763b5, 0x4bc1558b2f3458df},
+      {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17},
+      {0xea9c227723ee8bcb, 0x465e15a979c1cadd},
+      {0x92a1958a7675175f, 0x0bfacd89ec191eca},
+      {0xb749faed14125d36, 0xcef980ec671f667c},
+      {0xe51c79a85916f484, 0x82b7e12780e7401b},
+      {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811},
+      {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16},
+      {0xdfbdcece67006ac9, 0x67a791e093e1d49b},
+      {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1},
+      {0xaecc49914078536d, 0x58fae9f773886e19},
+      {0xda7f5bf590966848, 0xaf39a475506a899f},
+      {0x888f99797a5e012d, 0x6d8406c952429604},
+      {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84},
+      {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65},
+      {0x855c3be0a17fcd26, 0x5cf2eea09a550680},
+      {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f},
+      {0xd0601d8efc57b08b, 0xf13b94daf124da27},
+      {0x823c12795db6ce57, 0x76c53d08d6b70859},
+      {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f},
+      {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a},
+      {0xfe5d54150b090b02, 0xd3f93b35435d7c4d},
+      {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0},
+      {0xc6b8e9b0709f109a, 0x359ab6419ca1091c},
+      {0xf867241c8cc6d4c0, 0xc30163d203c94b63},
+      {0x9b407691d7fc44f8, 0x79e0de63425dcf1e},
+      {0xc21094364dfb5636, 0x985915fc12f542e5},
+      {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e},
+      {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43},
+      {0xbd8430bd08277231, 0x50c6ff782a838354},
+      {0xece53cec4a314ebd, 0xa4f8bf5635246429},
+      {0x940f4613ae5ed136, 0x871b7795e136be9a},
+      {0xb913179899f68584, 0x28e2557b59846e40},
+      {0xe757dd7ec07426e5, 0x331aeada2fe589d0},
+      {0x9096ea6f3848984f, 0x3ff0d2c85def7622},
+      {0xb4bca50b065abe63, 0x0fed077a756b53aa},
+      {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895},
+      {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d},
+      {0xb080392cc4349dec, 0xbd8d794d96aacfb4},
+      {0xdca04777f541c567, 0xecf0d7a0fc5583a1},
+      {0x89e42caaf9491b60, 0xf41686c49db57245},
+      {0xac5d37d5b79b6239, 0x311c2875c522ced6},
+      {0xd77485cb25823ac7, 0x7d633293366b828c},
+      {0x86a8d39ef77164bc, 0xae5dff9c02033198},
+      {0xa8530886b54dbdeb, 0xd9f57f830283fdfd},
+      {0xd267caa862a12d66, 0xd072df63c324fd7c},
+      {0x8380dea93da4bc60, 0x4247cb9e59f71e6e},
+      {0xa46116538d0deb78, 0x52d9be85f074e609},
+      {0xcd795be870516656, 0x67902e276c921f8c},
+      {0x806bd9714632dff6, 0x00ba1cd8a3db53b7},
+      {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5},
+      {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce},
+      {0xfad2a4b13d1b5d6c, 0x796b805720085f82},
+      {0x9cc3a6eec6311a63, 0xcbe3303674053bb1},
+      {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d},
+      {0xf4f1b4d515acb93b, 0xee92fb5515482d45},
+      {0x991711052d8bf3c5, 0x751bdd152d4d1c4b},
+      {0xbf5cd54678eef0b6, 0xd262d45a78a0635e},
+      {0xef340a98172aace4, 0x86fb897116c87c35},
+      {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1},
+      {0xbae0a846d2195712, 0x8974836059cca10a},
+      {0xe998d258869facd7, 0x2bd1a438703fc94c},
+      {0x91ff83775423cc06, 0x7b6306a34627ddd0},
+      {0xb67f6455292cbf08, 0x1a3bc84c17b1d543},
+      {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94},
+      {0x8e938662882af53e, 0x547eb47b7282ee9d},
+      {0xb23867fb2a35b28d, 0xe99e619a4f23aa44},
+      {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5},
+      {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05},
+      {0xae0b158b4738705e, 0x9624ab50b148d446},
+      {0xd98ddaee19068c76, 0x3badd624dd9b0958},
+      {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7},
+      {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d},
+      {0xd47487cc8470652b, 0x7647c32000696720},
+      {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074},
+      {0xa5fb0a17c777cf09, 0xf468107100525891},
+      {0xcf79cc9db955c2cc, 0x7182148d4066eeb5},
+      {0x81ac1fe293d599bf, 0xc6f14cd848405531},
+      {0xa21727db38cb002f, 0xb8ada00e5a506a7d},
+      {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d},
+      {0xfd442e4688bd304a, 0x908f4a166d1da664},
+      {0x9e4a9cec15763e2e, 0x9a598e4e043287ff},
+      {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe},
+      {0xf7549530e188c128, 0xd12bee59e68ef47d},
+      {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf},
+      {0xc13a148e3032d6e7, 0xe36a52363c1faf02},
+      {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2},
+      {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba},
+      {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8},
+      {0xebdf661791d60f56, 0x111b495b3464ad22},
+      {0x936b9fcebb25c995, 0xcab10dd900beec35},
+      {0xb84687c269ef3bfb, 0x3d5d514f40eea743},
+      {0xe65829b3046b0afa, 0x0cb4a5a3112a5113},
+      {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac},
+      {0xb3f4e093db73a093, 0x59ed216765690f57},
+      {0xe0f218b8d25088b8, 0x306869c13ec3532d},
+      {0x8c974f7383725573, 0x1e414218c73a13fc},
+      {0xafbd2350644eeacf, 0xe5d1929ef90898fb},
+      {0xdbac6c247d62a583, 0xdf45f746b74abf3a},
+      {0x894bc396ce5da772, 0x6b8bba8c328eb784},
+      {0xab9eb47c81f5114f, 0x066ea92f3f326565},
+      {0xd686619ba27255a2, 0xc80a537b0efefebe},
+      {0x8613fd0145877585, 0xbd06742ce95f5f37},
+      {0xa798fc4196e952e7, 0x2c48113823b73705},
+      {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6},
+      {0x82ef85133de648c4, 0x9a984d73dbe722fc},
+      {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb},
+      {0xcc963fee10b7d1b3, 0x318df905079926a9},
+      {0xffbbcfe994e5c61f, 0xfdf17746497f7053},
+      {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634},
+      {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1},
+      {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1},
+      {0x9c1661a651213e2d, 0x06bea10ca65c084f},
+      {0xc31bfa0fe5698db8, 0x486e494fcff30a63},
+      {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb},
+      {0x986ddb5c6b3a76b7, 0xf89629465a75e01d},
+      {0xbe89523386091465, 0xf6bbb397f1135824},
+      {0xee2ba6c0678b597f, 0x746aa07ded582e2d},
+      {0x94db483840b717ef, 0xa8c2a44eb4571cdd},
+      {0xba121a4650e4ddeb, 0x92f34d62616ce414},
+      {0xe896a0d7e51e1566, 0x77b020baf9c81d18},
+      {0x915e2486ef32cd60, 0x0ace1474dc1d122f},
+      {0xb5b5ada8aaff80b8, 0x0d819992132456bb},
+      {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a},
+      {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2},
+      {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3},
+      {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf},
+      {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c},
+      {0xad4ab7112eb3929d, 0x86c16c98d2c953c7},
+      {0xd89d64d57a607744, 0xe871c7bf077ba8b8},
+      {0x87625f056c7c4a8b, 0x11471cd764ad4973},
+      {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0},
+      {0xd389b47879823479, 0x4aff1d108d4ec2c4},
+      {0x843610cb4bf160cb, 0xcedf722a585139bb},
+      {0xa54394fe1eedb8fe, 0xc2974eb4ee658829},
+      {0xce947a3da6a9273e, 0x733d226229feea33},
+      {0x811ccc668829b887, 0x0806357d5a3f5260},
+      {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8},
+      {0xc9bcff6034c13052, 0xfc89b393dd02f0b6},
+      {0xfc2c3f3841f17c67, 0xbbac2078d443ace3},
+      {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e},
+      {0xc5029163f384a931, 0x0a9e795e65d4df12},
+      {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6},
+      {0x99ea0196163fa42e, 0x504bced1bf8e4e46},
+      {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7},
+      {0xf07da27a82c37088, 0x5d767327bb4e5a4d},
+      {0x964e858c91ba2655, 0x3a6a07f8d510f870},
+      {0xbbe226efb628afea, 0x890489f70a55368c},
+      {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f},
+      {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e},
+      {0xb77ada0617e3bbcb, 0x09ce6ebb40173745},
+      {0xe55990879ddcaabd, 0xcc420a6a101d0516},
+      {0x8f57fa54c2a9eab6, 0x9fa946824a12232e},
+      {0xb32df8e9f3546564, 0x47939822dc96abfa},
+      {0xdff9772470297ebd, 0x59787e2b93bc56f8},
+      {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b},
+      {0xaefae51477a06b03, 0xede622920b6b23f2},
+      {0xdab99e59958885c4, 0xe95fab368e45ecee},
+      {0x88b402f7fd75539b, 0x11dbcb0218ebb415},
+      {0xaae103b5fcd2a881, 0xd652bdc29f26a11a},
+      {0xd59944a37c0752a2, 0x4be76d3346f04960},
+      {0x857fcae62d8493a5, 0x6f70a4400c562ddc},
+      {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953},
+      {0xd097ad07a71f26b2, 0x7e2000a41346a7a8},
+      {0x825ecc24c873782f, 0x8ed400668c0c28c9},
+      {0xa2f67f2dfa90563b, 0x728900802f0f32fb},
+      {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba},
+      {0xfea126b7d78186bc, 0xe2f610c84987bfa9},
+      {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca},
+      {0xc6ede63fa05d3143, 0x91503d1c79720dbc},
+      {0xf8a95fcf88747d94, 0x75a44c6397ce912b},
+      {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb},
+      {0xc24452da229b021b, 0xfbe85badce996169},
+      {0xf2d56790ab41c2a2, 0xfae27299423fb9c4},
+      {0x97c560ba6b0919a5, 0xdccd879fc967d41b},
+      {0xbdb6b8e905cb600f, 0x5400e987bbc1c921},
+      {0xed246723473e3813, 0x290123e9aab23b69},
+      {0x9436c0760c86e30b, 0xf9a0b6720aaf6522},
+      {0xb94470938fa89bce, 0xf808e40e8d5b3e6a},
+      {0xe7958cb87392c2c2, 0xb60b1d1230b20e05},
+      {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3},
+      {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4},
+      {0xe2280b6c20dd5232, 0x25c6da63c38de1b1},
+      {0x8d590723948a535f, 0x579c487e5a38ad0f},
+      {0xb0af48ec79ace837, 0x2d835a9df0c6d852},
+      {0xdcdb1b2798182244, 0xf8e431456cf88e66},
+      {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900},
+      {0xac8b2d36eed2dac5, 0xe272467e3d222f40},
+      {0xd7adf884aa879177, 0x5b0ed81dcc6abb10},
+      {0x86ccbb52ea94baea, 0x98e947129fc2b4ea},
+      {0xa87fea27a539e9a5, 0x3f2398d747b36225},
+      {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae},
+      {0x83a3eeeef9153e89, 0x1953cf68300424ad},
+      {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8},
+      {0xcdb02555653131b6, 0x3792f412cb06794e},
+      {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1},
+      {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5},
+      {0xc8de047564d20a8b, 0xf245825a5a445276},
+      {0xfb158592be068d2e, 0xeed6e2f0f0d56713},
+      {0x9ced737bb6c4183d, 0x55464dd69685606c},
+      {0xc428d05aa4751e4c, 0xaa97e14c3c26b887},
+      {0xf53304714d9265df, 0xd53dd99f4b3066a9},
+      {0x993fe2c6d07b7fab, 0xe546a8038efe402a},
+      {0xbf8fdb78849a5f96, 0xde98520472bdd034},
+      {0xef73d256a5c0f77c, 0x963e66858f6d4441},
+      {0x95a8637627989aad, 0xdde7001379a44aa9},
+      {0xbb127c53b17ec159, 0x5560c018580d5d53},
+      {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7},
+      {0x9226712162ab070d, 0xcab3961304ca70e9},
+      {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23},
+      {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b},
+      {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243},
+      {0xb267ed1940f1c61c, 0x55f038b237591ed4},
+      {0xdf01e85f912e37a3, 0x6b6c46dec52f6689},
+      {0x8b61313bbabce2c6, 0x2323ac4b3b3da016},
+      {0xae397d8aa96c1b77, 0xabec975e0a0d081b},
+      {0xd9c7dced53c72255, 0x96e7bd358c904a22},
+      {0x881cea14545c7575, 0x7e50d64177da2e55},
+      {0xaa242499697392d2, 0xdde50bd1d5d0b9ea},
+      {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865},
+      {0x84ec3c97da624ab4, 0xbd5af13bef0b113f},
+      {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f},
+      {0xcfb11ead453994ba, 0x67de18eda5814af3},
+      {0x81ceb32c4b43fcf4, 0x80eacf948770ced8},
+      {0xa2425ff75e14fc31, 0xa1258379a94d028e},
+      {0xcad2f7f5359a3b3e, 0x096ee45813a04331},
+      {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd},
+      {0x9e74d1b791e07e48, 0x775ea264cf55347e},
+      {0xc612062576589dda, 0x95364afe032a819e},
+      {0xf79687aed3eec551, 0x3a83ddbd83f52205},
+      {0x9abe14cd44753b52, 0xc4926a9672793543},
+      {0xc16d9a0095928a27, 0x75b7053c0f178294},
+      {0xf1c90080baf72cb1, 0x5324c68b12dd6339},
+      {0x971da05074da7bee, 0xd3f6fc16ebca5e04},
+      {0xbce5086492111aea, 0x88f4bb1ca6bcf585},
+      {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6},
+      {0x9392ee8e921d5d07, 0x3aff322e62439fd0},
+      {0xb877aa3236a4b449, 0x09befeb9fad487c3},
+      {0xe69594bec44de15b, 0x4c2ebe687989a9b4},
+      {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11},
+      {0xb424dc35095cd80f, 0x538484c19ef38c95},
+      {0xe12e13424bb40e13, 0x2865a5f206b06fba},
+      {0x8cbccc096f5088cb, 0xf93f87b7442e45d4},
+      {0xafebff0bcb24aafe, 0xf78f69a51539d749},
+      {0xdbe6fecebdedd5be, 0xb573440e5a884d1c},
+      {0x89705f4136b4a597, 0x31680a88f8953031},
+      {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e},
+      {0xd6bf94d5e57a42bc, 0x3d32907604691b4d},
+      {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110},
+      {0xa7c5ac471b478423, 0x0fcf80dc33721d54},
+      {0xd1b71758e219652b, 0xd3c36113404ea4a9},
+      {0x83126e978d4fdf3b, 0x645a1cac083126ea},
+      {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4},
+      {0xcccccccccccccccc, 0xcccccccccccccccd},
+      {0x8000000000000000, 0x0000000000000000},
+      {0xa000000000000000, 0x0000000000000000},
+      {0xc800000000000000, 0x0000000000000000},
+      {0xfa00000000000000, 0x0000000000000000},
+      {0x9c40000000000000, 0x0000000000000000},
+      {0xc350000000000000, 0x0000000000000000},
+      {0xf424000000000000, 0x0000000000000000},
+      {0x9896800000000000, 0x0000000000000000},
+      {0xbebc200000000000, 0x0000000000000000},
+      {0xee6b280000000000, 0x0000000000000000},
+      {0x9502f90000000000, 0x0000000000000000},
+      {0xba43b74000000000, 0x0000000000000000},
+      {0xe8d4a51000000000, 0x0000000000000000},
+      {0x9184e72a00000000, 0x0000000000000000},
+      {0xb5e620f480000000, 0x0000000000000000},
+      {0xe35fa931a0000000, 0x0000000000000000},
+      {0x8e1bc9bf04000000, 0x0000000000000000},
+      {0xb1a2bc2ec5000000, 0x0000000000000000},
+      {0xde0b6b3a76400000, 0x0000000000000000},
+      {0x8ac7230489e80000, 0x0000000000000000},
+      {0xad78ebc5ac620000, 0x0000000000000000},
+      {0xd8d726b7177a8000, 0x0000000000000000},
+      {0x878678326eac9000, 0x0000000000000000},
+      {0xa968163f0a57b400, 0x0000000000000000},
+      {0xd3c21bcecceda100, 0x0000000000000000},
+      {0x84595161401484a0, 0x0000000000000000},
+      {0xa56fa5b99019a5c8, 0x0000000000000000},
+      {0xcecb8f27f4200f3a, 0x0000000000000000},
+      {0x813f3978f8940984, 0x4000000000000000},
+      {0xa18f07d736b90be5, 0x5000000000000000},
+      {0xc9f2c9cd04674ede, 0xa400000000000000},
+      {0xfc6f7c4045812296, 0x4d00000000000000},
+      {0x9dc5ada82b70b59d, 0xf020000000000000},
+      {0xc5371912364ce305, 0x6c28000000000000},
+      {0xf684df56c3e01bc6, 0xc732000000000000},
+      {0x9a130b963a6c115c, 0x3c7f400000000000},
+      {0xc097ce7bc90715b3, 0x4b9f100000000000},
+      {0xf0bdc21abb48db20, 0x1e86d40000000000},
+      {0x96769950b50d88f4, 0x1314448000000000},
+      {0xbc143fa4e250eb31, 0x17d955a000000000},
+      {0xeb194f8e1ae525fd, 0x5dcfab0800000000},
+      {0x92efd1b8d0cf37be, 0x5aa1cae500000000},
+      {0xb7abc627050305ad, 0xf14a3d9e40000000},
+      {0xe596b7b0c643c719, 0x6d9ccd05d0000000},
+      {0x8f7e32ce7bea5c6f, 0xe4820023a2000000},
+      {0xb35dbf821ae4f38b, 0xdda2802c8a800000},
+      {0xe0352f62a19e306e, 0xd50b2037ad200000},
+      {0x8c213d9da502de45, 0x4526f422cc340000},
+      {0xaf298d050e4395d6, 0x9670b12b7f410000},
+      {0xdaf3f04651d47b4c, 0x3c0cdd765f114000},
+      {0x88d8762bf324cd0f, 0xa5880a69fb6ac800},
+      {0xab0e93b6efee0053, 0x8eea0d047a457a00},
+      {0xd5d238a4abe98068, 0x72a4904598d6d880},
+      {0x85a36366eb71f041, 0x47a6da2b7f864750},
+      {0xa70c3c40a64e6c51, 0x999090b65f67d924},
+      {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d},
+      {0x82818f1281ed449f, 0xbff8f10e7a8921a5},
+      {0xa321f2d7226895c7, 0xaff72d52192b6a0e},
+      {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764491},
+      {0xfee50b7025c36a08, 0x02f236d04753d5b5},
+      {0x9f4f2726179a2245, 0x01d762422c946591},
+      {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef6},
+      {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb3},
+      {0x9b934c3b330c8577, 0x63cc55f49f88eb30},
+      {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fc},
+      {0xf316271c7fc3908a, 0x8bef464e3945ef7b},
+      {0x97edd871cfda3a56, 0x97758bf0e3cbb5ad},
+      {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea318},
+      {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bde},
+      {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6b},
+      {0xb975d6b6ee39e436, 0xb3e2fd538e122b45},
+      {0xe7d34c64a9c85d44, 0x60dbbca87196b617},
+      {0x90e40fbeea1d3a4a, 0xbc8955e946fe31ce},
+      {0xb51d13aea4a488dd, 0x6babab6398bdbe42},
+      {0xe264589a4dcdab14, 0xc696963c7eed2dd2},
+      {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca3},
+      {0xb0de65388cc8ada8, 0x3b25a55f43294bcc},
+      {0xdd15fe86affad912, 0x49ef0eb713f39ebf},
+      {0x8a2dbf142dfcc7ab, 0x6e3569326c784338},
+      {0xacb92ed9397bf996, 0x49c2c37f07965405},
+      {0xd7e77a8f87daf7fb, 0xdc33745ec97be907},
+      {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a4},
+      {0xa8acd7c0222311bc, 0xc40832ea0d68ce0d},
+      {0xd2d80db02aabd62b, 0xf50a3fa490c30191},
+      {0x83c7088e1aab65db, 0x792667c6da79e0fb},
+      {0xa4b8cab1a1563f52, 0x577001b891185939},
+      {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87},
+      {0x80b05e5ac60b6178, 0x544f8158315b05b5},
+      {0xa0dc75f1778e39d6, 0x696361ae3db1c722},
+      {0xc913936dd571c84c, 0x03bc3a19cd1e38ea},
+      {0xfb5878494ace3a5f, 0x04ab48a04065c724},
+      {0x9d174b2dcec0e47b, 0x62eb0d64283f9c77},
+      {0xc45d1df942711d9a, 0x3ba5d0bd324f8395},
+      {0xf5746577930d6500, 0xca8f44ec7ee3647a},
+      {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecc},
+      {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67f},
+      {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101f},
+      {0x95d04aee3b80ece5, 0xbba1f1d158724a13},
+      {0xbb445da9ca61281f, 0x2a8a6e45ae8edc98},
+      {0xea1575143cf97226, 0xf52d09d71a3293be},
+      {0x924d692ca61be758, 0x593c2626705f9c57},
+      {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836d},
+      {0xe498f455c38b997a, 0x0b6dfb9c0f956448},
+      {0x8edf98b59a373fec, 0x4724bd4189bd5ead},
+      {0xb2977ee300c50fe7, 0x58edec91ec2cb658},
+      {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ee},
+      {0x8b865b215899f46c, 0xbd79e0d20082ee75},
+      {0xae67f1e9aec07187, 0xecd8590680a3aa12},
+      {0xda01ee641a708de9, 0xe80e6f4820cc9496},
+      {0x884134fe908658b2, 0x3109058d147fdcde},
+      {0xaa51823e34a7eede, 0xbd4b46f0599fd416},
+      {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91b},
+      {0x850fadc09923329e, 0x03e2cf6bc604ddb1},
+      {0xa6539930bf6bff45, 0x84db8346b786151d},
+      {0xcfe87f7cef46ff16, 0xe612641865679a64},
+      {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07f},
+      {0xa26da3999aef7749, 0xe3be5e330f38f09e},
+      {0xcb090c8001ab551c, 0x5cadf5bfd3072cc6},
+      {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f7},
+      {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afb},
+      {0xc646d63501a1511d, 0xb281e1fd541501b9},
+      {0xf7d88bc24209a565, 0x1f225a7ca91a4227},
+      {0x9ae757596946075f, 0x3375788de9b06959},
+      {0xc1a12d2fc3978937, 0x0052d6b1641c83af},
+      {0xf209787bb47d6b84, 0xc0678c5dbd23a49b},
+      {0x9745eb4d50ce6332, 0xf840b7ba963646e1},
+      {0xbd176620a501fbff, 0xb650e5a93bc3d899},
+      {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebf},
+      {0x93ba47c980e98cdf, 0xc66f336c36b10138},
+      {0xb8a8d9bbe123f017, 0xb80b0047445d4185},
+      {0xe6d3102ad96cec1d, 0xa60dc059157491e6},
+      {0x9043ea1ac7e41392, 0x87c89837ad68db30},
+      {0xb454e4a179dd1877, 0x29babe4598c311fc},
+      {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67b},
+      {0x8ce2529e2734bb1d, 0x1899e4a65f58660d},
+      {0xb01ae745b101e9e4, 0x5ec05dcff72e7f90},
+      {0xdc21a1171d42645d, 0x76707543f4fa1f74},
+      {0x899504ae72497eba, 0x6a06494a791c53a9},
+      {0xabfa45da0edbde69, 0x0487db9d17636893},
+      {0xd6f8d7509292d603, 0x45a9d2845d3c42b7},
+      {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3},
+      {0xa7f26836f282b732, 0x8e6cac7768d7141f},
+      {0xd1ef0244af2364ff, 0x3207d795430cd927},
+      {0x8335616aed761f1f, 0x7f44e6bd49e807b9},
+      {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a7},
+      {0xcd036837130890a1, 0x36dba887c37a8c10},
+      {0x802221226be55a64, 0xc2494954da2c978a},
+      {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6d},
+      {0xc83553c5c8965d3d, 0x6f92829494e5acc8},
+      {0xfa42a8b73abbf48c, 0xcb772339ba1f17fa},
+      {0x9c69a97284b578d7, 0xff2a760414536efc},
+      {0xc38413cf25e2d70d, 0xfef5138519684abb},
+      {0xf46518c2ef5b8cd1, 0x7eb258665fc25d6a},
+      {0x98bf2f79d5993802, 0xef2f773ffbd97a62},
+      {0xbeeefb584aff8603, 0xaafb550ffacfd8fb},
+      {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf39},
+      {0x952ab45cfa97a0b2, 0xdd945a747bf26184},
+      {0xba756174393d88df, 0x94f971119aeef9e5},
+      {0xe912b9d1478ceb17, 0x7a37cd5601aab85e},
+      {0x91abb422ccb812ee, 0xac62e055c10ab33b},
+      {0xb616a12b7fe617aa, 0x577b986b314d600a},
+      {0xe39c49765fdf9d94, 0xed5a7e85fda0b80c},
+      {0x8e41ade9fbebc27d, 0x14588f13be847308},
+      {0xb1d219647ae6b31c, 0x596eb2d8ae258fc9},
+      {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bc},
+      {0x8aec23d680043bee, 0x25de7bb9480d5855},
+      {0xada72ccc20054ae9, 0xaf561aa79a10ae6b},
+      {0xd910f7ff28069da4, 0x1b2ba1518094da05},
+      {0x87aa9aff79042286, 0x90fb44d2f05d0843},
+      {0xa99541bf57452b28, 0x353a1607ac744a54},
+      {0xd3fa922f2d1675f2, 0x42889b8997915ce9},
+      {0x847c9b5d7c2e09b7, 0x69956135febada12},
+      {0xa59bc234db398c25, 0x43fab9837e699096},
+      {0xcf02b2c21207ef2e, 0x94f967e45e03f4bc},
+      {0x8161afb94b44f57d, 0x1d1be0eebac278f6},
+      {0xa1ba1ba79e1632dc, 0x6462d92a69731733},
+      {0xca28a291859bbf93, 0x7d7b8f7503cfdcff},
+      {0xfcb2cb35e702af78, 0x5cda735244c3d43f},
+      {0x9defbf01b061adab, 0x3a0888136afa64a8},
+      {0xc56baec21c7a1916, 0x088aaa1845b8fdd1},
+      {0xf6c69a72a3989f5b, 0x8aad549e57273d46},
+      {0x9a3c2087a63f6399, 0x36ac54e2f678864c},
+      {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7de},
+      {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d6},
+      {0x969eb7c47859e743, 0x9f644ae5a4b1b326},
+      {0xbc4665b596706114, 0x873d5d9f0dde1fef},
+      {0xeb57ff22fc0c7959, 0xa90cb506d155a7eb},
+      {0x9316ff75dd87cbd8, 0x09a7f12442d588f3},
+      {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb30},
+      {0xe5d3ef282a242e81, 0x8f1668c8a86da5fb},
+      {0x8fa475791a569d10, 0xf96e017d694487bd},
+      {0xb38d92d760ec4455, 0x37c981dcc395a9ad},
+      {0xe070f78d3927556a, 0x85bbe253f47b1418},
+      {0x8c469ab843b89562, 0x93956d7478ccec8f},
+      {0xaf58416654a6babb, 0x387ac8d1970027b3},
+      {0xdb2e51bfe9d0696a, 0x06997b05fcc0319f},
+      {0x88fcf317f22241e2, 0x441fece3bdf81f04},
+      {0xab3c2fddeeaad25a, 0xd527e81cad7626c4},
+      {0xd60b3bd56a5586f1, 0x8a71e223d8d3b075},
+      {0x85c7056562757456, 0xf6872d5667844e4a},
+      {0xa738c6bebb12d16c, 0xb428f8ac016561dc},
+      {0xd106f86e69d785c7, 0xe13336d701beba53},
+      {0x82a45b450226b39c, 0xecc0024661173474},
+      {0xa34d721642b06084, 0x27f002d7f95d0191},
+      {0xcc20ce9bd35c78a5, 0x31ec038df7b441f5},
+      {0xff290242c83396ce, 0x7e67047175a15272},
+      {0x9f79a169bd203e41, 0x0f0062c6e984d387},
+      {0xc75809c42c684dd1, 0x52c07b78a3e60869},
+      {0xf92e0c3537826145, 0xa7709a56ccdf8a83},
+      {0x9bbcc7a142b17ccb, 0x88a66076400bb692},
+      {0xc2abf989935ddbfe, 0x6acff893d00ea436},
+      {0xf356f7ebf83552fe, 0x0583f6b8c4124d44},
+      {0x98165af37b2153de, 0xc3727a337a8b704b},
+      {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5d},
+      {0xeda2ee1c7064130c, 0x1162def06f79df74},
+      {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba9},
+      {0xb9a74a0637ce2ee1, 0x6d953e2bd7173693},
+      {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0438},
+      {0x910ab1d4db9914a0, 0x1d9c9892400a22a3},
+      {0xb54d5e4a127f59c8, 0x2503beb6d00cab4c},
+      {0xe2a0b5dc971f303a, 0x2e44ae64840fd61e},
+      {0x8da471a9de737e24, 0x5ceaecfed289e5d3},
+      {0xb10d8e1456105dad, 0x7425a83e872c5f48},
+      {0xdd50f1996b947518, 0xd12f124e28f7771a},
+      {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa70},
+      {0xace73cbfdc0bfb7b, 0x636cc64d1001550c},
+      {0xd8210befd30efa5a, 0x3c47f7e05401aa4f},
+      {0x8714a775e3e95c78, 0x65acfaec34810a72},
+      {0xa8d9d1535ce3b396, 0x7f1839a741a14d0e},
+      {0xd31045a8341ca07c, 0x1ede48111209a051},
+      {0x83ea2b892091e44d, 0x934aed0aab460433},
+      {0xa4e4b66b68b65d60, 0xf81da84d56178540},
+      {0xce1de40642e3f4b9, 0x36251260ab9d668f},
+      {0x80d2ae83e9ce78f3, 0xc1d72b7c6b42601a},
+      {0xa1075a24e4421730, 0xb24cf65b8612f820},
+      {0xc94930ae1d529cfc, 0xdee033f26797b628},
+      {0xfb9b7cd9a4a7443c, 0x169840ef017da3b2},
+      {0x9d412e0806e88aa5, 0x8e1f289560ee864f},
+      {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e3},
+      {0xf5b5d7ec8acb58a2, 0xae10af696774b1dc},
+      {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef2a},
+      {0xbff610b0cc6edd3f, 0x17fd090a58d32af4},
+      {0xeff394dcff8a948e, 0xddfc4b4cef07f5b1},
+      {0x95f83d0a1fb69cd9, 0x4abdaf101564f98f},
+      {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f2},
+      {0xea53df5fd18d5513, 0x84c86189216dc5ee},
+      {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb5},
+      {0xb7118682dbb66a77, 0x3fbc8c33221dc2a2},
+      {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
+      {0x8f05b1163ba6832d, 0x29cb4d87f2a7400f},
+      {0xb2c71d5bca9023f8, 0x743e20e9ef511013},
+      {0xdf78e4b2bd342cf6, 0x914da9246b255417},
+      {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548f},
+      {0xae9672aba3d0c320, 0xa184ac2473b529b2},
+      {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741f},
+      {0x8865899617fb1871, 0x7e2fa67c7a658893},
+      {0xaa7eebfb9df9de8d, 0xddbb901b98feeab8},
+      {0xd51ea6fa85785631, 0x552a74227f3ea566},
+      {0x8533285c936b35de, 0xd53a88958f872760},
+      {0xa67ff273b8460356, 0x8a892abaf368f138},
+      {0xd01fef10a657842c, 0x2d2b7569b0432d86},
+      {0x8213f56a67f6b29b, 0x9c3b29620e29fc74},
+      {0xa298f2c501f45f42, 0x8349f3ba91b47b90},
+      {0xcb3f2f7642717713, 0x241c70a936219a74},
+      {0xfe0efb53d30dd4d7, 0xed238cd383aa0111},
+      {0x9ec95d1463e8a506, 0xf4363804324a40ab},
+      {0xc67bb4597ce2ce48, 0xb143c6053edcd0d6},
+      {0xf81aa16fdc1b81da, 0xdd94b7868e94050b},
+      {0x9b10a4e5e9913128, 0xca7cf2b4191c8327},
+      {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f1},
+      {0xf24a01a73cf2dccf, 0xbc633b39673c8ced},
+      {0x976e41088617ca01, 0xd5be0503e085d814},
+      {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e19},
+      {0xec9c459d51852ba2, 0xddf8e7d60ed1219f},
+      {0x93e1ab8252f33b45, 0xcabb90e5c942b504},
+      {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
+      {0xe7109bfba19c0c9d, 0x0cc512670a783ad5},
+      {0x906a617d450187e2, 0x27fb2b80668b24c6},
+      {0xb484f9dc9641e9da, 0xb1f9f660802dedf7},
+      {0xe1a63853bbd26451, 0x5e7873f8a0396974},
+      {0x8d07e33455637eb2, 0xdb0b487b6423e1e9},
+      {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda63},
+      {0xdc5c5301c56b75f7, 0x7641a140cc7810fc},
+      {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9e},
+      {0xac2820d9623bf429, 0x546345fa9fbdcd45},
+      {0xd732290fbacaf133, 0xa97c177947ad4096},
+      {0x867f59a9d4bed6c0, 0x49ed8eabcccc485e},
+      {0xa81f301449ee8c70, 0x5c68f256bfff5a75},
+      {0xd226fc195c6a2f8c, 0x73832eec6fff3112},
+      {0x83585d8fd9c25db7, 0xc831fd53c5ff7eac},
+      {0xa42e74f3d032f525, 0xba3e7ca8b77f5e56},
+      {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35ec},
+      {0x80444b5e7aa7cf85, 0x7980d163cf5b81b4},
+      {0xa0555e361951c366, 0xd7e105bcc3326220},
+      {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa8},
+      {0xfa856334878fc150, 0xb14f98f6f0feb952},
+      {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d4},
+      {0xc3b8358109e84f07, 0x0a862f80ec4700c9},
+      {0xf4a642e14c6262c8, 0xcd27bb612758c0fb},
+      {0x98e7e9cccfbd7dbd, 0x8038d51cb897789d},
+      {0xbf21e44003acdd2c, 0xe0470a63e6bd56c4},
+      {0xeeea5d5004981478, 0x1858ccfce06cac75},
+      {0x95527a5202df0ccb, 0x0f37801e0c43ebc9},
+      {0xbaa718e68396cffd, 0xd30560258f54e6bb},
+      {0xe950df20247c83fd, 0x47c6b82ef32a206a},
+      {0x91d28b7416cdd27e, 0x4cdc331d57fa5442},
+      {0xb6472e511c81471d, 0xe0133fe4adf8e953},
+      {0xe3d8f9e563a198e5, 0x58180fddd97723a7},
+      {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7649},
+      {0xb201833b35d63f73, 0x2cd2cc6551e513db},
+      {0xde81e40a034bcf4f, 0xf8077f7ea65e58d2},
+      {0x8b112e86420f6191, 0xfb04afaf27faf783},
+      {0xadd57a27d29339f6, 0x79c5db9af1f9b564},
+      {0xd94ad8b1c7380874, 0x18375281ae7822bd},
+      {0x87cec76f1c830548, 0x8f2293910d0b15b6},
+      {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb23},
+      {0xd433179d9c8cb841, 0x5fa60692a46151ec},
+      {0x849feec281d7f328, 0xdbc7c41ba6bcd334},
+      {0xa5c7ea73224deff3, 0x12b9b522906c0801},
+      {0xcf39e50feae16bef, 0xd768226b34870a01},
+      {0x81842f29f2cce375, 0xe6a1158300d46641},
+      {0xa1e53af46f801c53, 0x60495ae3c1097fd1},
+      {0xca5e89b18b602368, 0x385bb19cb14bdfc5},
+      {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6},
+      {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2},
+      {0xc5a05277621be293, 0xc7098b7305241886},
+      { 0xf70867153aa2db38,
+        0xb8cbee4fc66d1ea8 }
+#else
+      {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
+      {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
+      {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f},
+      {0x86a8d39ef77164bc, 0xae5dff9c02033198},
+      {0xd98ddaee19068c76, 0x3badd624dd9b0958},
+      {0xafbd2350644eeacf, 0xe5d1929ef90898fb},
+      {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2},
+      {0xe55990879ddcaabd, 0xcc420a6a101d0516},
+      {0xb94470938fa89bce, 0xf808e40e8d5b3e6a},
+      {0x95a8637627989aad, 0xdde7001379a44aa9},
+      {0xf1c90080baf72cb1, 0x5324c68b12dd6339},
+      {0xc350000000000000, 0x0000000000000000},
+      {0x9dc5ada82b70b59d, 0xf020000000000000},
+      {0xfee50b7025c36a08, 0x02f236d04753d5b5},
+      {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87},
+      {0xa6539930bf6bff45, 0x84db8346b786151d},
+      {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3},
+      {0xd910f7ff28069da4, 0x1b2ba1518094da05},
+      {0xaf58416654a6babb, 0x387ac8d1970027b3},
+      {0x8da471a9de737e24, 0x5ceaecfed289e5d3},
+      {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
+      {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
+      { 0x95527a5202df0ccb,
+        0x0f37801e0c43ebc9 }
+#endif
+    };
+
+#if FMT_USE_FULL_CACHE_DRAGONBOX
+    return pow10_significands[k - float_info<double>::min_k];
+#else
+    static constexpr const uint64_t powers_of_5_64[] = {
+        0x0000000000000001, 0x0000000000000005, 0x0000000000000019,
+        0x000000000000007d, 0x0000000000000271, 0x0000000000000c35,
+        0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1,
+        0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd,
+        0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9,
+        0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5,
+        0x000003782dace9d9, 0x00001158e460913d, 0x000056bc75e2d631,
+        0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed,
+        0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9};
+
+    static const int compression_ratio = 27;
+
+    // Compute base index.
+    int cache_index = (k - float_info<double>::min_k) / compression_ratio;
+    int kb = cache_index * compression_ratio + float_info<double>::min_k;
+    int offset = k - kb;
+
+    // Get base cache.
+    uint128_fallback base_cache = pow10_significands[cache_index];
+    if (offset == 0) return base_cache;
+
+    // Compute the required amount of bit-shift.
+    int alpha = floor_log2_pow10(kb + offset) - floor_log2_pow10(kb) - offset;
+    FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected");
+
+    // Try to recover the real cache.
+    uint64_t pow5 = powers_of_5_64[offset];
+    uint128_fallback recovered_cache = umul128(base_cache.high(), pow5);
+    uint128_fallback middle_low = umul128(base_cache.low(), pow5);
+
+    recovered_cache += middle_low.high();
+
+    uint64_t high_to_middle = recovered_cache.high() << (64 - alpha);
+    uint64_t middle_to_low = recovered_cache.low() << (64 - alpha);
+
+    recovered_cache =
+        uint128_fallback{(recovered_cache.low() >> alpha) | high_to_middle,
+                         ((middle_low.low() >> alpha) | middle_to_low)};
+    FMT_ASSERT(recovered_cache.low() + 1 != 0, "");
+    return {recovered_cache.high(), recovered_cache.low() + 1};
+#endif
+  }
+
+  struct compute_mul_result {
+    carrier_uint result;
+    bool is_integer;
+  };
+  struct compute_mul_parity_result {
+    bool parity;
+    bool is_integer;
+  };
+
+  static compute_mul_result compute_mul(
+      carrier_uint u, const cache_entry_type& cache) noexcept {
+    auto r = umul192_upper128(u, cache);
+    return {r.high(), r.low() == 0};
+  }
+
+  static uint32_t compute_delta(cache_entry_type const& cache,
+                                int beta) noexcept {
+    return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
+  }
+
+  static compute_mul_parity_result compute_mul_parity(
+      carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
+    FMT_ASSERT(beta >= 1, "");
+    FMT_ASSERT(beta < 64, "");
+
+    auto r = umul192_lower128(two_f, cache);
+    return {((r.high() >> (64 - beta)) & 1) != 0,
+            ((r.high() << beta) | (r.low() >> (64 - beta))) == 0};
+  }
+
+  static carrier_uint compute_left_endpoint_for_shorter_interval_case(
+      const cache_entry_type& cache, int beta) noexcept {
+    return (cache.high() -
+            (cache.high() >> (num_significand_bits<double>() + 2))) >>
+           (64 - num_significand_bits<double>() - 1 - beta);
+  }
+
+  static carrier_uint compute_right_endpoint_for_shorter_interval_case(
+      const cache_entry_type& cache, int beta) noexcept {
+    return (cache.high() +
+            (cache.high() >> (num_significand_bits<double>() + 1))) >>
+           (64 - num_significand_bits<double>() - 1 - beta);
+  }
+
+  static carrier_uint compute_round_up_for_shorter_interval_case(
+      const cache_entry_type& cache, int beta) noexcept {
+    return ((cache.high() >> (64 - num_significand_bits<double>() - 2 - beta)) +
+            1) /
+           2;
+  }
+};
+
+// Various integer checks
+template <class T>
+bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
+  const int case_shorter_interval_left_endpoint_lower_threshold = 2;
+  const int case_shorter_interval_left_endpoint_upper_threshold = 3;
+  return exponent >= case_shorter_interval_left_endpoint_lower_threshold &&
+         exponent <= case_shorter_interval_left_endpoint_upper_threshold;
+}
+
+// Remove trailing zeros from n and return the number of zeros removed (float)
+FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept {
+  FMT_ASSERT(n != 0, "");
+  const uint32_t mod_inv_5 = 0xcccccccd;
+  const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
+
+  int s = 0;
+  while (true) {
+    auto q = rotr(n * mod_inv_25, 2);
+    if (q > max_value<uint32_t>() / 100) break;
+    n = q;
+    s += 2;
+  }
+  auto q = rotr(n * mod_inv_5, 1);
+  if (q <= max_value<uint32_t>() / 10) {
+    n = q;
+    s |= 1;
+  }
+
+  return s;
+}
+
+// Removes trailing zeros and returns the number of zeros removed (double)
+FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
+  FMT_ASSERT(n != 0, "");
+
+  // This magic number is ceil(2^90 / 10^8).
+  constexpr uint64_t magic_number = 12379400392853802749ull;
+  auto nm = umul128(n, magic_number);
+
+  // Is n is divisible by 10^8?
+  if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
+    // If yes, work with the quotient.
+    auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
+
+    const uint32_t mod_inv_5 = 0xcccccccd;
+    const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
+
+    int s = 8;
+    while (true) {
+      auto q = rotr(n32 * mod_inv_25, 2);
+      if (q > max_value<uint32_t>() / 100) break;
+      n32 = q;
+      s += 2;
+    }
+    auto q = rotr(n32 * mod_inv_5, 1);
+    if (q <= max_value<uint32_t>() / 10) {
+      n32 = q;
+      s |= 1;
+    }
+
+    n = n32;
+    return s;
+  }
+
+  // If n is not divisible by 10^8, work with n itself.
+  const uint64_t mod_inv_5 = 0xcccccccccccccccd;
+  const uint64_t mod_inv_25 = mod_inv_5 * mod_inv_5;
+
+  int s = 0;
+  while (true) {
+    auto q = rotr(n * mod_inv_25, 2);
+    if (q > max_value<uint64_t>() / 100) break;
+    n = q;
+    s += 2;
+  }
+  auto q = rotr(n * mod_inv_5, 1);
+  if (q <= max_value<uint64_t>() / 10) {
+    n = q;
+    s |= 1;
+  }
+
+  return s;
+}
+
+// The main algorithm for shorter interval case
+template <class T>
+FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
+  decimal_fp<T> ret_value;
+  // Compute k and beta
+  const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
+  const int beta = exponent + floor_log2_pow10(-minus_k);
+
+  // Compute xi and zi
+  using cache_entry_type = typename cache_accessor<T>::cache_entry_type;
+  const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k);
+
+  auto xi = cache_accessor<T>::compute_left_endpoint_for_shorter_interval_case(
+      cache, beta);
+  auto zi = cache_accessor<T>::compute_right_endpoint_for_shorter_interval_case(
+      cache, beta);
+
+  // If the left endpoint is not an integer, increase it
+  if (!is_left_endpoint_integer_shorter_interval<T>(exponent)) ++xi;
+
+  // Try bigger divisor
+  ret_value.significand = zi / 10;
+
+  // If succeed, remove trailing zeros if necessary and return
+  if (ret_value.significand * 10 >= xi) {
+    ret_value.exponent = minus_k + 1;
+    ret_value.exponent += remove_trailing_zeros(ret_value.significand);
+    return ret_value;
+  }
+
+  // Otherwise, compute the round-up of y
+  ret_value.significand =
+      cache_accessor<T>::compute_round_up_for_shorter_interval_case(cache,
+                                                                    beta);
+  ret_value.exponent = minus_k;
+
+  // When tie occurs, choose one of them according to the rule
+  if (exponent >= float_info<T>::shorter_interval_tie_lower_threshold &&
+      exponent <= float_info<T>::shorter_interval_tie_upper_threshold) {
+    ret_value.significand = ret_value.significand % 2 == 0
+                                ? ret_value.significand
+                                : ret_value.significand - 1;
+  } else if (ret_value.significand < xi) {
+    ++ret_value.significand;
+  }
+  return ret_value;
+}
+
+template <typename T> decimal_fp<T> to_decimal(T x) noexcept {
+  // Step 1: integer promotion & Schubfach multiplier calculation.
+
+  using carrier_uint = typename float_info<T>::carrier_uint;
+  using cache_entry_type = typename cache_accessor<T>::cache_entry_type;
+  auto br = bit_cast<carrier_uint>(x);
+
+  // Extract significand bits and exponent bits.
+  const carrier_uint significand_mask =
+      (static_cast<carrier_uint>(1) << num_significand_bits<T>()) - 1;
+  carrier_uint significand = (br & significand_mask);
+  int exponent =
+      static_cast<int>((br & exponent_mask<T>()) >> num_significand_bits<T>());
+
+  if (exponent != 0) {  // Check if normal.
+    exponent -= exponent_bias<T>() + num_significand_bits<T>();
+
+    // Shorter interval case; proceed like Schubfach.
+    // In fact, when exponent == 1 and significand == 0, the interval is
+    // regular. However, it can be shown that the end-results are anyway same.
+    if (significand == 0) return shorter_interval_case<T>(exponent);
+
+    significand |= (static_cast<carrier_uint>(1) << num_significand_bits<T>());
+  } else {
+    // Subnormal case; the interval is always regular.
+    if (significand == 0) return {0, 0};
+    exponent =
+        std::numeric_limits<T>::min_exponent - num_significand_bits<T>() - 1;
+  }
+
+  const bool include_left_endpoint = (significand % 2 == 0);
+  const bool include_right_endpoint = include_left_endpoint;
+
+  // Compute k and beta.
+  const int minus_k = floor_log10_pow2(exponent) - float_info<T>::kappa;
+  const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k);
+  const int beta = exponent + floor_log2_pow10(-minus_k);
+
+  // Compute zi and deltai.
+  // 10^kappa <= deltai < 10^(kappa + 1)
+  const uint32_t deltai = cache_accessor<T>::compute_delta(cache, beta);
+  const carrier_uint two_fc = significand << 1;
+
+  // For the case of binary32, the result of integer check is not correct for
+  // 29711844 * 2^-82
+  // = 6.1442653300000000008655037797566933477355632930994033813476... * 10^-18
+  // and 29711844 * 2^-81
+  // = 1.2288530660000000001731007559513386695471126586198806762695... * 10^-17,
+  // and they are the unique counterexamples. However, since 29711844 is even,
+  // this does not cause any problem for the endpoints calculations; it can only
+  // cause a problem when we need to perform integer check for the center.
+  // Fortunately, with these inputs, that branch is never executed, so we are
+  // fine.
+  const typename cache_accessor<T>::compute_mul_result z_mul =
+      cache_accessor<T>::compute_mul((two_fc | 1) << beta, cache);
+
+  // Step 2: Try larger divisor; remove trailing zeros if necessary.
+
+  // Using an upper bound on zi, we might be able to optimize the division
+  // better than the compiler; we are computing zi / big_divisor here.
+  decimal_fp<T> ret_value;
+  ret_value.significand = divide_by_10_to_kappa_plus_1(z_mul.result);
+  uint32_t r = static_cast<uint32_t>(z_mul.result - float_info<T>::big_divisor *
+                                                        ret_value.significand);
+
+  if (r < deltai) {
+    // Exclude the right endpoint if necessary.
+    if (r == 0 && (z_mul.is_integer & !include_right_endpoint)) {
+      --ret_value.significand;
+      r = float_info<T>::big_divisor;
+      goto small_divisor_case_label;
+    }
+  } else if (r > deltai) {
+    goto small_divisor_case_label;
+  } else {
+    // r == deltai; compare fractional parts.
+    const typename cache_accessor<T>::compute_mul_parity_result x_mul =
+        cache_accessor<T>::compute_mul_parity(two_fc - 1, cache, beta);
+
+    if (!(x_mul.parity | (x_mul.is_integer & include_left_endpoint)))
+      goto small_divisor_case_label;
+  }
+  ret_value.exponent = minus_k + float_info<T>::kappa + 1;
+
+  // We may need to remove trailing zeros.
+  ret_value.exponent += remove_trailing_zeros(ret_value.significand);
+  return ret_value;
+
+  // Step 3: Find the significand with the smaller divisor.
+
+small_divisor_case_label:
+  ret_value.significand *= 10;
+  ret_value.exponent = minus_k + float_info<T>::kappa;
+
+  uint32_t dist = r - (deltai / 2) + (float_info<T>::small_divisor / 2);
+  const bool approx_y_parity =
+      ((dist ^ (float_info<T>::small_divisor / 2)) & 1) != 0;
+
+  // Is dist divisible by 10^kappa?
+  const bool divisible_by_small_divisor =
+      check_divisibility_and_divide_by_pow10<float_info<T>::kappa>(dist);
+
+  // Add dist / 10^kappa to the significand.
+  ret_value.significand += dist;
+
+  if (!divisible_by_small_divisor) return ret_value;
+
+  // Check z^(f) >= epsilon^(f).
+  // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1,
+  // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f).
+  // Since there are only 2 possibilities, we only need to care about the
+  // parity. Also, zi and r should have the same parity since the divisor
+  // is an even number.
+  const auto y_mul = cache_accessor<T>::compute_mul_parity(two_fc, cache, beta);
+
+  // If z^(f) >= epsilon^(f), we might have a tie when z^(f) == epsilon^(f),
+  // or equivalently, when y is an integer.
+  if (y_mul.parity != approx_y_parity)
+    --ret_value.significand;
+  else if (y_mul.is_integer & (ret_value.significand % 2 != 0))
+    --ret_value.significand;
+  return ret_value;
+}
+}  // namespace dragonbox
+
+#ifdef _MSC_VER
+FMT_FUNC auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...)
+    -> int {
+  auto args = va_list();
+  va_start(args, fmt);
+  int result = vsnprintf_s(buf, size, _TRUNCATE, fmt, args);
+  va_end(args);
+  return result;
+}
+#endif
+}  // namespace detail
+
+template <> struct formatter<detail::bigint> {
+  FMT_CONSTEXPR auto parse(format_parse_context& ctx)
+      -> format_parse_context::iterator {
+    return ctx.begin();
+  }
+
+  template <typename FormatContext>
+  auto format(const detail::bigint& n, FormatContext& ctx) const ->
+      typename FormatContext::iterator {
+    auto out = ctx.out();
+    bool first = true;
+    for (auto i = n.bigits_.size(); i > 0; --i) {
+      auto value = n.bigits_[i - 1u];
+      if (first) {
+        out = format_to(out, FMT_STRING("{:x}"), value);
+        first = false;
+        continue;
+      }
+      out = format_to(out, FMT_STRING("{:08x}"), value);
+    }
+    if (n.exp_ > 0)
+      out = format_to(out, FMT_STRING("p{}"),
+                      n.exp_ * detail::bigint::bigit_bits);
+    return out;
+  }
+};
+
+FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
+  for_each_codepoint(s, [this](uint32_t cp, string_view) {
+    if (cp == invalid_code_point) FMT_THROW(std::runtime_error("invalid utf8"));
+    if (cp <= 0xFFFF) {
+      buffer_.push_back(static_cast<wchar_t>(cp));
+    } else {
+      cp -= 0x10000;
+      buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
+      buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
+    }
+    return true;
+  });
+  buffer_.push_back(0);
+}
+
+FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
+                                  const char* message) noexcept {
+  FMT_TRY {
+    auto ec = std::error_code(error_code, std::generic_category());
+    write(std::back_inserter(out), std::system_error(ec, message).what());
+    return;
+  }
+  FMT_CATCH(...) {}
+  format_error_code(out, error_code, message);
+}
+
+FMT_FUNC void report_system_error(int error_code,
+                                  const char* message) noexcept {
+  report_error(format_system_error, error_code, message);
+}
+
+FMT_FUNC std::string vformat(string_view fmt, format_args args) {
+  // Don't optimize the "{}" case to keep the binary size small and because it
+  // can be better optimized in fmt::format anyway.
+  auto buffer = memory_buffer();
+  detail::vformat_to(buffer, fmt, args);
+  return to_string(buffer);
+}
+
+namespace detail {
+#ifdef _WIN32
+using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
+extern "C" __declspec(dllimport) int __stdcall WriteConsoleW(  //
+    void*, const void*, dword, dword*, void*);
+
+FMT_FUNC bool write_console(std::FILE* f, string_view text) {
+  auto fd = _fileno(f);
+  if (_isatty(fd)) {
+    detail::utf8_to_utf16 u16(string_view(text.data(), text.size()));
+    auto written = detail::dword();
+    if (detail::WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)),
+                              u16.c_str(), static_cast<uint32_t>(u16.size()),
+                              &written, nullptr)) {
+      return true;
+    }
+  }
+  // We return false if the file descriptor was not TTY, or it was but
+  // SetConsoleW failed which can happen if the output has been redirected to
+  // NUL. In both cases when we return false, we should attempt to do regular
+  // write via fwrite or std::ostream::write.
+  return false;
+}
+#endif
+
+FMT_FUNC void print(std::FILE* f, string_view text) {
+#ifdef _WIN32
+  if (write_console(f, text)) return;
+#endif
+  detail::fwrite_fully(text.data(), 1, text.size(), f);
+}
+}  // namespace detail
+
+FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
+  memory_buffer buffer;
+  detail::vformat_to(buffer, format_str, args);
+  detail::print(f, {buffer.data(), buffer.size()});
+}
+
+#ifdef _WIN32
+// Print assuming legacy (non-Unicode) encoding.
+FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str,
+                                      format_args args) {
+  memory_buffer buffer;
+  detail::vformat_to(buffer, format_str,
+                     basic_format_args<buffer_context<char>>(args));
+  fwrite_fully(buffer.data(), 1, buffer.size(), f);
+}
+#endif
+
+FMT_FUNC void vprint(string_view format_str, format_args args) {
+  vprint(stdout, format_str, args);
+}
+
+namespace detail {
+
+struct singleton {
+  unsigned char upper;
+  unsigned char lower_count;
+};
+
+inline auto is_printable(uint16_t x, const singleton* singletons,
+                         size_t singletons_size,
+                         const unsigned char* singleton_lowers,
+                         const unsigned char* normal, size_t normal_size)
+    -> bool {
+  auto upper = x >> 8;
+  auto lower_start = 0;
+  for (size_t i = 0; i < singletons_size; ++i) {
+    auto s = singletons[i];
+    auto lower_end = lower_start + s.lower_count;
+    if (upper < s.upper) break;
+    if (upper == s.upper) {
+      for (auto j = lower_start; j < lower_end; ++j) {
+        if (singleton_lowers[j] == (x & 0xff)) return false;
+      }
+    }
+    lower_start = lower_end;
+  }
+
+  auto xsigned = static_cast<int>(x);
+  auto current = true;
+  for (size_t i = 0; i < normal_size; ++i) {
+    auto v = static_cast<int>(normal[i]);
+    auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v;
+    xsigned -= len;
+    if (xsigned < 0) break;
+    current = !current;
+  }
+  return current;
+}
+
+// This code is generated by support/printable.py.
+FMT_FUNC auto is_printable(uint32_t cp) -> bool {
+  static constexpr singleton singletons0[] = {
+      {0x00, 1},  {0x03, 5},  {0x05, 6},  {0x06, 3},  {0x07, 6},  {0x08, 8},
+      {0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13},
+      {0x0f, 4},  {0x10, 3},  {0x12, 18}, {0x13, 9},  {0x16, 1},  {0x17, 5},
+      {0x18, 2},  {0x19, 3},  {0x1a, 7},  {0x1c, 2},  {0x1d, 1},  {0x1f, 22},
+      {0x20, 3},  {0x2b, 3},  {0x2c, 2},  {0x2d, 11}, {0x2e, 1},  {0x30, 3},
+      {0x31, 2},  {0x32, 1},  {0xa7, 2},  {0xa9, 2},  {0xaa, 4},  {0xab, 8},
+      {0xfa, 2},  {0xfb, 5},  {0xfd, 4},  {0xfe, 3},  {0xff, 9},
+  };
+  static constexpr unsigned char singletons0_lower[] = {
+      0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90,
+      0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f,
+      0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
+      0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04,
+      0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d,
+      0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
+      0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
+      0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d,
+      0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
+      0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d,
+      0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5,
+      0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
+      0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49,
+      0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7,
+      0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
+      0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e,
+      0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16,
+      0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
+      0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f,
+      0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf,
+      0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
+      0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27,
+      0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91,
+      0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
+      0xfe, 0xff,
+  };
+  static constexpr singleton singletons1[] = {
+      {0x00, 6},  {0x01, 1}, {0x03, 1},  {0x04, 2}, {0x08, 8},  {0x09, 2},
+      {0x0a, 5},  {0x0b, 2}, {0x0e, 4},  {0x10, 1}, {0x11, 2},  {0x12, 5},
+      {0x13, 17}, {0x14, 1}, {0x15, 2},  {0x17, 2}, {0x19, 13}, {0x1c, 5},
+      {0x1d, 8},  {0x24, 1}, {0x6a, 3},  {0x6b, 2}, {0xbc, 2},  {0xd1, 2},
+      {0xd4, 12}, {0xd5, 9}, {0xd6, 2},  {0xd7, 2}, {0xda, 1},  {0xe0, 5},
+      {0xe1, 2},  {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2},  {0xf9, 2},
+      {0xfa, 2},  {0xfb, 1},
+  };
+  static constexpr unsigned char singletons1_lower[] = {
+      0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07,
+      0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36,
+      0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
+      0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
+      0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b,
+      0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
+      0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66,
+      0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27,
+      0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
+      0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7,
+      0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6,
+      0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
+      0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66,
+      0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0,
+      0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
+  };
+  static constexpr unsigned char normal0[] = {
+      0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04,
+      0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0,
+      0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
+      0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03,
+      0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03,
+      0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
+      0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15,
+      0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f,
+      0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
+      0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07,
+      0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06,
+      0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
+      0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac,
+      0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c,
+      0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
+      0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c,
+      0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b,
+      0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
+      0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03,
+      0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80,
+      0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
+      0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c,
+      0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17,
+      0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
+      0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80,
+      0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d,
+  };
+  static constexpr unsigned char normal1[] = {
+      0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f,
+      0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e,
+      0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
+      0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09,
+      0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16,
+      0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
+      0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36,
+      0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33,
+      0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
+      0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e,
+      0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41,
+      0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
+      0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22,
+      0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04,
+      0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
+      0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03,
+      0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81,
+      0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
+      0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1,
+      0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a,
+      0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
+      0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09,
+      0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89,
+      0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
+      0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09,
+      0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50,
+      0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
+      0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83,
+      0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05,
+      0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
+      0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80,
+      0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07,
+      0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
+      0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07,
+      0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06,
+  };
+  auto lower = static_cast<uint16_t>(cp);
+  if (cp < 0x10000) {
+    return is_printable(lower, singletons0,
+                        sizeof(singletons0) / sizeof(*singletons0),
+                        singletons0_lower, normal0, sizeof(normal0));
+  }
+  if (cp < 0x20000) {
+    return is_printable(lower, singletons1,
+                        sizeof(singletons1) / sizeof(*singletons1),
+                        singletons1_lower, normal1, sizeof(normal1));
+  }
+  if (0x2a6de <= cp && cp < 0x2a700) return false;
+  if (0x2b735 <= cp && cp < 0x2b740) return false;
+  if (0x2b81e <= cp && cp < 0x2b820) return false;
+  if (0x2cea2 <= cp && cp < 0x2ceb0) return false;
+  if (0x2ebe1 <= cp && cp < 0x2f800) return false;
+  if (0x2fa1e <= cp && cp < 0x30000) return false;
+  if (0x3134b <= cp && cp < 0xe0100) return false;
+  if (0xe01f0 <= cp && cp < 0x110000) return false;
+  return cp < 0x110000;
+}
+
+}  // namespace detail
+
+FMT_END_NAMESPACE
+
+#endif  // FMT_FORMAT_INL_H_
diff --git a/include/spdlog/fmt/bundled/format.h b/include/spdlog/fmt/bundled/format.h
new file mode 100644
index 000000000..7c607dbd3
--- /dev/null
+++ b/include/spdlog/fmt/bundled/format.h
@@ -0,0 +1,4217 @@
+/*
+  Formatting library for C++
+
+  Copyright (c) 2012 - present, Victor Zverovich
+
+  Permission is hereby granted, free of charge, to any person obtaining
+  a copy of this software and associated documentation files (the
+  "Software"), to deal in the Software without restriction, including
+  without limitation the rights to use, copy, modify, merge, publish,
+  distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so, subject to
+  the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+  --- Optional exception to the license ---
+
+  As an exception, if, as a result of your compiling your source code, portions
+  of this Software are embedded into a machine-executable object form of such
+  source code, you may redistribute such embedded portions in such object form
+  without including the above copyright and permission notices.
+ */
+
+#ifndef FMT_FORMAT_H_
+#define FMT_FORMAT_H_
+
+#include <cmath>         // std::signbit
+#include <cstdint>       // uint32_t
+#include <cstring>       // std::memcpy
+#include <limits>        // std::numeric_limits
+#include <memory>        // std::uninitialized_copy
+#include <stdexcept>     // std::runtime_error
+#include <system_error>  // std::system_error
+
+#ifdef __cpp_lib_bit_cast
+#  include <bit>  // std::bitcast
+#endif
+
+#include "core.h"
+
+#if FMT_GCC_VERSION
+#  define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden")))
+#else
+#  define FMT_GCC_VISIBILITY_HIDDEN
+#endif
+
+#ifdef __NVCC__
+#  define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__)
+#else
+#  define FMT_CUDA_VERSION 0
+#endif
+
+#ifdef __has_builtin
+#  define FMT_HAS_BUILTIN(x) __has_builtin(x)
+#else
+#  define FMT_HAS_BUILTIN(x) 0
+#endif
+
+#if FMT_GCC_VERSION || FMT_CLANG_VERSION
+#  define FMT_NOINLINE __attribute__((noinline))
+#else
+#  define FMT_NOINLINE
+#endif
+
+#if FMT_MSC_VERSION
+#  define FMT_MSC_DEFAULT = default
+#else
+#  define FMT_MSC_DEFAULT
+#endif
+
+#ifndef FMT_THROW
+#  if FMT_EXCEPTIONS
+#    if FMT_MSC_VERSION || defined(__NVCC__)
+FMT_BEGIN_NAMESPACE
+namespace detail {
+template <typename Exception> inline void do_throw(const Exception& x) {
+  // Silence unreachable code warnings in MSVC and NVCC because these
+  // are nearly impossible to fix in a generic code.
+  volatile bool b = true;
+  if (b) throw x;
+}
+}  // namespace detail
+FMT_END_NAMESPACE
+#      define FMT_THROW(x) detail::do_throw(x)
+#    else
+#      define FMT_THROW(x) throw x
+#    endif
+#  else
+#    define FMT_THROW(x)               \
+      do {                             \
+        FMT_ASSERT(false, (x).what()); \
+      } while (false)
+#  endif
+#endif
+
+#if FMT_EXCEPTIONS
+#  define FMT_TRY try
+#  define FMT_CATCH(x) catch (x)
+#else
+#  define FMT_TRY if (true)
+#  define FMT_CATCH(x) if (false)
+#endif
+
+#ifndef FMT_MAYBE_UNUSED
+#  if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
+#    define FMT_MAYBE_UNUSED [[maybe_unused]]
+#  else
+#    define FMT_MAYBE_UNUSED
+#  endif
+#endif
+
+#ifndef FMT_USE_USER_DEFINED_LITERALS
+// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs.
+#  if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \
+       FMT_MSC_VERSION >= 1900) &&                                     \
+      (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480)
+#    define FMT_USE_USER_DEFINED_LITERALS 1
+#  else
+#    define FMT_USE_USER_DEFINED_LITERALS 0
+#  endif
+#endif
+
+// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of
+// integer formatter template instantiations to just one by only using the
+// largest integer type. This results in a reduction in binary size but will
+// cause a decrease in integer formatting performance.
+#if !defined(FMT_REDUCE_INT_INSTANTIATIONS)
+#  define FMT_REDUCE_INT_INSTANTIATIONS 0
+#endif
+
+// __builtin_clz is broken in clang with Microsoft CodeGen:
+// https://github.com/fmtlib/fmt/issues/519.
+#if !FMT_MSC_VERSION
+#  if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION
+#    define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
+#  endif
+#  if FMT_HAS_BUILTIN(__builtin_clzll) || FMT_GCC_VERSION || FMT_ICC_VERSION
+#    define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
+#  endif
+#endif
+
+// __builtin_ctz is broken in Intel Compiler Classic on Windows:
+// https://github.com/fmtlib/fmt/issues/2510.
+#ifndef __ICL
+#  if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \
+      defined(__NVCOMPILER)
+#    define FMT_BUILTIN_CTZ(n) __builtin_ctz(n)
+#  endif
+#  if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \
+      FMT_ICC_VERSION || defined(__NVCOMPILER)
+#    define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n)
+#  endif
+#endif
+
+#if FMT_MSC_VERSION
+#  include <intrin.h>  // _BitScanReverse[64], _BitScanForward[64], _umul128
+#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.
+#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) && \
+    !defined(FMT_BUILTIN_CTZLL)
+FMT_BEGIN_NAMESPACE
+namespace detail {
+// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning.
+#  if !defined(__clang__)
+#    pragma intrinsic(_BitScanForward)
+#    pragma intrinsic(_BitScanReverse)
+#    if defined(_WIN64)
+#      pragma intrinsic(_BitScanForward64)
+#      pragma intrinsic(_BitScanReverse64)
+#    endif
+#  endif
+
+inline auto clz(uint32_t x) -> int {
+  unsigned long r = 0;
+  _BitScanReverse(&r, x);
+  FMT_ASSERT(x != 0, "");
+  // Static analysis complains about using uninitialized data
+  // "r", but the only way that can happen is if "x" is 0,
+  // which the callers guarantee to not happen.
+  FMT_MSC_WARNING(suppress : 6102)
+  return 31 ^ static_cast<int>(r);
+}
+#  define FMT_BUILTIN_CLZ(n) detail::clz(n)
+
+inline auto clzll(uint64_t x) -> int {
+  unsigned long r = 0;
+#  ifdef _WIN64
+  _BitScanReverse64(&r, x);
+#  else
+  // Scan the high 32 bits.
+  if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32))) return 63 ^ (r + 32);
+  // Scan the low 32 bits.
+  _BitScanReverse(&r, static_cast<uint32_t>(x));
+#  endif
+  FMT_ASSERT(x != 0, "");
+  FMT_MSC_WARNING(suppress : 6102)  // Suppress a bogus static analysis warning.
+  return 63 ^ static_cast<int>(r);
+}
+#  define FMT_BUILTIN_CLZLL(n) detail::clzll(n)
+
+inline auto ctz(uint32_t x) -> int {
+  unsigned long r = 0;
+  _BitScanForward(&r, x);
+  FMT_ASSERT(x != 0, "");
+  FMT_MSC_WARNING(suppress : 6102)  // Suppress a bogus static analysis warning.
+  return static_cast<int>(r);
+}
+#  define FMT_BUILTIN_CTZ(n) detail::ctz(n)
+
+inline auto ctzll(uint64_t x) -> int {
+  unsigned long r = 0;
+  FMT_ASSERT(x != 0, "");
+  FMT_MSC_WARNING(suppress : 6102)  // Suppress a bogus static analysis warning.
+#  ifdef _WIN64
+  _BitScanForward64(&r, x);
+#  else
+  // Scan the low 32 bits.
+  if (_BitScanForward(&r, static_cast<uint32_t>(x))) return static_cast<int>(r);
+  // Scan the high 32 bits.
+  _BitScanForward(&r, static_cast<uint32_t>(x >> 32));
+  r += 32;
+#  endif
+  return static_cast<int>(r);
+}
+#  define FMT_BUILTIN_CTZLL(n) detail::ctzll(n)
+}  // namespace detail
+FMT_END_NAMESPACE
+#endif
+
+FMT_BEGIN_NAMESPACE
+namespace detail {
+
+FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) {
+  ignore_unused(condition);
+#ifdef FMT_FUZZ
+  if (condition) throw std::runtime_error("fuzzing limit reached");
+#endif
+}
+
+template <typename CharT, CharT... C> struct string_literal {
+  static constexpr CharT value[sizeof...(C)] = {C...};
+  constexpr operator basic_string_view<CharT>() const {
+    return {value, sizeof...(C)};
+  }
+};
+
+#if FMT_CPLUSPLUS < 201703L
+template <typename CharT, CharT... C>
+constexpr CharT string_literal<CharT, C...>::value[sizeof...(C)];
+#endif
+
+template <typename Streambuf> class formatbuf : public Streambuf {
+ private:
+  using char_type = typename Streambuf::char_type;
+  using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
+  using int_type = typename Streambuf::int_type;
+  using traits_type = typename Streambuf::traits_type;
+
+  buffer<char_type>& buffer_;
+
+ public:
+  explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {}
+
+ protected:
+  // The put area is always empty. This makes the implementation simpler and has
+  // the advantage that the streambuf and the buffer are always in sync and
+  // sputc never writes into uninitialized memory. A disadvantage is that each
+  // call to sputc always results in a (virtual) call to overflow. There is no
+  // disadvantage here for sputn since this always results in a call to xsputn.
+
+  auto overflow(int_type ch) -> int_type override {
+    if (!traits_type::eq_int_type(ch, traits_type::eof()))
+      buffer_.push_back(static_cast<char_type>(ch));
+    return ch;
+  }
+
+  auto xsputn(const char_type* s, streamsize count) -> streamsize override {
+    buffer_.append(s, s + count);
+    return count;
+  }
+};
+
+// Implementation of std::bit_cast for pre-C++20.
+template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) == sizeof(From))>
+FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To {
+#ifdef __cpp_lib_bit_cast
+  if (is_constant_evaluated()) return std::bit_cast<To>(from);
+#endif
+  auto to = To();
+  // The cast suppresses a bogus -Wclass-memaccess on GCC.
+  std::memcpy(static_cast<void*>(&to), &from, sizeof(to));
+  return to;
+}
+
+inline auto is_big_endian() -> bool {
+#ifdef _WIN32
+  return false;
+#elif defined(__BIG_ENDIAN__)
+  return true;
+#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
+  return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__;
+#else
+  struct bytes {
+    char data[sizeof(int)];
+  };
+  return bit_cast<bytes>(1).data[0] == 0;
+#endif
+}
+
+class uint128_fallback {
+ private:
+  uint64_t lo_, hi_;
+
+  friend uint128_fallback umul128(uint64_t x, uint64_t y) noexcept;
+
+ public:
+  constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
+  constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {}
+
+  constexpr uint64_t high() const noexcept { return hi_; }
+  constexpr uint64_t low() const noexcept { return lo_; }
+
+  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+  constexpr explicit operator T() const {
+    return static_cast<T>(lo_);
+  }
+
+  friend constexpr auto operator==(const uint128_fallback& lhs,
+                                   const uint128_fallback& rhs) -> bool {
+    return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_;
+  }
+  friend constexpr auto operator!=(const uint128_fallback& lhs,
+                                   const uint128_fallback& rhs) -> bool {
+    return !(lhs == rhs);
+  }
+  friend constexpr auto operator>(const uint128_fallback& lhs,
+                                  const uint128_fallback& rhs) -> bool {
+    return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_;
+  }
+  friend constexpr auto operator|(const uint128_fallback& lhs,
+                                  const uint128_fallback& rhs)
+      -> uint128_fallback {
+    return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_};
+  }
+  friend constexpr auto operator&(const uint128_fallback& lhs,
+                                  const uint128_fallback& rhs)
+      -> uint128_fallback {
+    return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_};
+  }
+  friend auto operator+(const uint128_fallback& lhs,
+                        const uint128_fallback& rhs) -> uint128_fallback {
+    auto result = uint128_fallback(lhs);
+    result += rhs;
+    return result;
+  }
+  friend auto operator*(const uint128_fallback& lhs, uint32_t rhs)
+      -> uint128_fallback {
+    FMT_ASSERT(lhs.hi_ == 0, "");
+    uint64_t hi = (lhs.lo_ >> 32) * rhs;
+    uint64_t lo = (lhs.lo_ & ~uint32_t()) * rhs;
+    uint64_t new_lo = (hi << 32) + lo;
+    return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo};
+  }
+  friend auto operator-(const uint128_fallback& lhs, uint64_t rhs)
+      -> uint128_fallback {
+    return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs};
+  }
+  FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback {
+    if (shift == 64) return {0, hi_};
+    if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64);
+    return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)};
+  }
+  FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback {
+    if (shift == 64) return {lo_, 0};
+    if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64);
+    return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)};
+  }
+  FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& {
+    return *this = *this >> shift;
+  }
+  FMT_CONSTEXPR void operator+=(uint128_fallback n) {
+    uint64_t new_lo = lo_ + n.lo_;
+    uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0);
+    FMT_ASSERT(new_hi >= hi_, "");
+    lo_ = new_lo;
+    hi_ = new_hi;
+  }
+
+  FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept {
+    if (is_constant_evaluated()) {
+      lo_ += n;
+      hi_ += (lo_ < n ? 1 : 0);
+      return *this;
+    }
+#if FMT_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__)
+    unsigned long long carry;
+    lo_ = __builtin_addcll(lo_, n, 0, &carry);
+    hi_ += carry;
+#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__)
+    unsigned long long result;
+    auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result);
+    lo_ = result;
+    hi_ += carry;
+#elif defined(_MSC_VER) && defined(_M_X64)
+    auto carry = _addcarry_u64(0, lo_, n, &lo_);
+    _addcarry_u64(carry, hi_, 0, &hi_);
+#else
+    lo_ += n;
+    hi_ += (lo_ < n ? 1 : 0);
+#endif
+    return *this;
+  }
+};
+
+using uint128_t = conditional_t<FMT_USE_INT128, uint128_opt, uint128_fallback>;
+
+#ifdef UINTPTR_MAX
+using uintptr_t = ::uintptr_t;
+#else
+using uintptr_t = uint128_t;
+#endif
+
+// Returns the largest possible value for type T. Same as
+// std::numeric_limits<T>::max() but shorter and not affected by the max macro.
+template <typename T> constexpr auto max_value() -> T {
+  return (std::numeric_limits<T>::max)();
+}
+template <typename T> constexpr auto num_bits() -> int {
+  return std::numeric_limits<T>::digits;
+}
+// std::numeric_limits<T>::digits may return 0 for 128-bit ints.
+template <> constexpr auto num_bits<int128_opt>() -> int { return 128; }
+template <> constexpr auto num_bits<uint128_t>() -> int { return 128; }
+
+// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t
+// and 128-bit pointers to uint128_fallback.
+template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) > sizeof(From))>
+inline auto bit_cast(const From& from) -> To {
+  constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned));
+  struct data_t {
+    unsigned value[static_cast<unsigned>(size)];
+  } data = bit_cast<data_t>(from);
+  auto result = To();
+  if (const_check(is_big_endian())) {
+    for (int i = 0; i < size; ++i)
+      result = (result << num_bits<unsigned>()) | data.value[i];
+  } else {
+    for (int i = size - 1; i >= 0; --i)
+      result = (result << num_bits<unsigned>()) | data.value[i];
+  }
+  return result;
+}
+
+FMT_INLINE void assume(bool condition) {
+  (void)condition;
+#if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION
+  __builtin_assume(condition);
+#endif
+}
+
+// An approximation of iterator_t for pre-C++20 systems.
+template <typename T>
+using iterator_t = decltype(std::begin(std::declval<T&>()));
+template <typename T> using sentinel_t = decltype(std::end(std::declval<T&>()));
+
+// A workaround for std::string not having mutable data() until C++17.
+template <typename Char>
+inline auto get_data(std::basic_string<Char>& s) -> Char* {
+  return &s[0];
+}
+template <typename Container>
+inline auto get_data(Container& c) -> typename Container::value_type* {
+  return c.data();
+}
+
+#if defined(_SECURE_SCL) && _SECURE_SCL
+// Make a checked iterator to avoid MSVC warnings.
+template <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
+template <typename T>
+constexpr auto make_checked(T* p, size_t size) -> checked_ptr<T> {
+  return {p, size};
+}
+#else
+template <typename T> using checked_ptr = T*;
+template <typename T> constexpr auto make_checked(T* p, size_t) -> T* {
+  return p;
+}
+#endif
+
+// Attempts to reserve space for n extra characters in the output range.
+// Returns a pointer to the reserved range or a reference to it.
+template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
+#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION
+__attribute__((no_sanitize("undefined")))
+#endif
+inline auto
+reserve(std::back_insert_iterator<Container> it, size_t n)
+    -> checked_ptr<typename Container::value_type> {
+  Container& c = get_container(it);
+  size_t size = c.size();
+  c.resize(size + n);
+  return make_checked(get_data(c) + size, n);
+}
+
+template <typename T>
+inline auto reserve(buffer_appender<T> it, size_t n) -> buffer_appender<T> {
+  buffer<T>& buf = get_container(it);
+  buf.try_reserve(buf.size() + n);
+  return it;
+}
+
+template <typename Iterator>
+constexpr auto reserve(Iterator& it, size_t) -> Iterator& {
+  return it;
+}
+
+template <typename OutputIt>
+using reserve_iterator =
+    remove_reference_t<decltype(reserve(std::declval<OutputIt&>(), 0))>;
+
+template <typename T, typename OutputIt>
+constexpr auto to_pointer(OutputIt, size_t) -> T* {
+  return nullptr;
+}
+template <typename T> auto to_pointer(buffer_appender<T> it, size_t n) -> T* {
+  buffer<T>& buf = get_container(it);
+  auto size = buf.size();
+  if (buf.capacity() < size + n) return nullptr;
+  buf.try_resize(size + n);
+  return buf.data() + size;
+}
+
+template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
+inline auto base_iterator(std::back_insert_iterator<Container>& it,
+                          checked_ptr<typename Container::value_type>)
+    -> std::back_insert_iterator<Container> {
+  return it;
+}
+
+template <typename Iterator>
+constexpr auto base_iterator(Iterator, Iterator it) -> Iterator {
+  return it;
+}
+
+// <algorithm> is spectacularly slow to compile in C++20 so use a simple fill_n
+// instead (#1998).
+template <typename OutputIt, typename Size, typename T>
+FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value)
+    -> OutputIt {
+  for (Size i = 0; i < count; ++i) *out++ = value;
+  return out;
+}
+template <typename T, typename Size>
+FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* {
+  if (is_constant_evaluated()) {
+    return fill_n<T*, Size, T>(out, count, value);
+  }
+  std::memset(out, value, to_unsigned(count));
+  return out + count;
+}
+
+#ifdef __cpp_char8_t
+using char8_type = char8_t;
+#else
+enum char8_type : unsigned char {};
+#endif
+
+template <typename OutChar, typename InputIt, typename OutputIt>
+FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end,
+                                                  OutputIt out) -> OutputIt {
+  return copy_str<OutChar>(begin, end, out);
+}
+
+// A public domain branchless UTF-8 decoder by Christopher Wellons:
+// https://github.com/skeeto/branchless-utf8
+/* Decode the next character, c, from s, reporting errors in e.
+ *
+ * Since this is a branchless decoder, four bytes will be read from the
+ * buffer regardless of the actual length of the next character. This
+ * means the buffer _must_ have at least three bytes of zero padding
+ * following the end of the data stream.
+ *
+ * Errors are reported in e, which will be non-zero if the parsed
+ * character was somehow invalid: invalid byte sequence, non-canonical
+ * encoding, or a surrogate half.
+ *
+ * The function returns a pointer to the next character. When an error
+ * occurs, this pointer will be a guess that depends on the particular
+ * error, but it will always advance at least one byte.
+ */
+FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e)
+    -> const char* {
+  constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07};
+  constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536};
+  constexpr const int shiftc[] = {0, 18, 12, 6, 0};
+  constexpr const int shifte[] = {0, 6, 4, 2, 0};
+
+  int len = code_point_length_impl(*s);
+  // Compute the pointer to the next character early so that the next
+  // iteration can start working on the next character. Neither Clang
+  // nor GCC figure out this reordering on their own.
+  const char* next = s + len + !len;
+
+  using uchar = unsigned char;
+
+  // Assume a four-byte character and load four bytes. Unused bits are
+  // shifted out.
+  *c = uint32_t(uchar(s[0]) & masks[len]) << 18;
+  *c |= uint32_t(uchar(s[1]) & 0x3f) << 12;
+  *c |= uint32_t(uchar(s[2]) & 0x3f) << 6;
+  *c |= uint32_t(uchar(s[3]) & 0x3f) << 0;
+  *c >>= shiftc[len];
+
+  // Accumulate the various error conditions.
+  *e = (*c < mins[len]) << 6;       // non-canonical encoding
+  *e |= ((*c >> 11) == 0x1b) << 7;  // surrogate half?
+  *e |= (*c > 0x10FFFF) << 8;       // out of range?
+  *e |= (uchar(s[1]) & 0xc0) >> 2;
+  *e |= (uchar(s[2]) & 0xc0) >> 4;
+  *e |= uchar(s[3]) >> 6;
+  *e ^= 0x2a;  // top two bits of each tail byte correct?
+  *e >>= shifte[len];
+
+  return next;
+}
+
+constexpr uint32_t invalid_code_point = ~uint32_t();
+
+// Invokes f(cp, sv) for every code point cp in s with sv being the string view
+// corresponding to the code point. cp is invalid_code_point on error.
+template <typename F>
+FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) {
+  auto decode = [f](const char* buf_ptr, const char* ptr) {
+    auto cp = uint32_t();
+    auto error = 0;
+    auto end = utf8_decode(buf_ptr, &cp, &error);
+    bool result = f(error ? invalid_code_point : cp,
+                    string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr)));
+    return result ? (error ? buf_ptr + 1 : end) : nullptr;
+  };
+  auto p = s.data();
+  const size_t block_size = 4;  // utf8_decode always reads blocks of 4 chars.
+  if (s.size() >= block_size) {
+    for (auto end = p + s.size() - block_size + 1; p < end;) {
+      p = decode(p, p);
+      if (!p) return;
+    }
+  }
+  if (auto num_chars_left = s.data() + s.size() - p) {
+    char buf[2 * block_size - 1] = {};
+    copy_str<char>(p, p + num_chars_left, buf);
+    const char* buf_ptr = buf;
+    do {
+      auto end = decode(buf_ptr, p);
+      if (!end) return;
+      p += end - buf_ptr;
+      buf_ptr = end;
+    } while (buf_ptr - buf < num_chars_left);
+  }
+}
+
+template <typename Char>
+inline auto compute_width(basic_string_view<Char> s) -> size_t {
+  return s.size();
+}
+
+// Computes approximate display width of a UTF-8 string.
+FMT_CONSTEXPR inline size_t compute_width(string_view s) {
+  size_t num_code_points = 0;
+  // It is not a lambda for compatibility with C++14.
+  struct count_code_points {
+    size_t* count;
+    FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool {
+      *count += detail::to_unsigned(
+          1 +
+          (cp >= 0x1100 &&
+           (cp <= 0x115f ||  // Hangul Jamo init. consonants
+            cp == 0x2329 ||  // LEFT-POINTING ANGLE BRACKET
+            cp == 0x232a ||  // RIGHT-POINTING ANGLE BRACKET
+            // CJK ... Yi except IDEOGRAPHIC HALF FILL SPACE:
+            (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) ||
+            (cp >= 0xac00 && cp <= 0xd7a3) ||    // Hangul Syllables
+            (cp >= 0xf900 && cp <= 0xfaff) ||    // CJK Compatibility Ideographs
+            (cp >= 0xfe10 && cp <= 0xfe19) ||    // Vertical Forms
+            (cp >= 0xfe30 && cp <= 0xfe6f) ||    // CJK Compatibility Forms
+            (cp >= 0xff00 && cp <= 0xff60) ||    // Fullwidth Forms
+            (cp >= 0xffe0 && cp <= 0xffe6) ||    // Fullwidth Forms
+            (cp >= 0x20000 && cp <= 0x2fffd) ||  // CJK
+            (cp >= 0x30000 && cp <= 0x3fffd) ||
+            // Miscellaneous Symbols and Pictographs + Emoticons:
+            (cp >= 0x1f300 && cp <= 0x1f64f) ||
+            // Supplemental Symbols and Pictographs:
+            (cp >= 0x1f900 && cp <= 0x1f9ff))));
+      return true;
+    }
+  };
+  for_each_codepoint(s, count_code_points{&num_code_points});
+  return num_code_points;
+}
+
+inline auto compute_width(basic_string_view<char8_type> s) -> size_t {
+  return compute_width(
+      string_view(reinterpret_cast<const char*>(s.data()), s.size()));
+}
+
+template <typename Char>
+inline auto code_point_index(basic_string_view<Char> s, size_t n) -> size_t {
+  size_t size = s.size();
+  return n < size ? n : size;
+}
+
+// Calculates the index of the nth code point in a UTF-8 string.
+inline auto code_point_index(string_view s, size_t n) -> size_t {
+  const char* data = s.data();
+  size_t num_code_points = 0;
+  for (size_t i = 0, size = s.size(); i != size; ++i) {
+    if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i;
+  }
+  return s.size();
+}
+
+inline auto code_point_index(basic_string_view<char8_type> s, size_t n)
+    -> size_t {
+  return code_point_index(
+      string_view(reinterpret_cast<const char*>(s.data()), s.size()), n);
+}
+
+#ifndef FMT_USE_FLOAT128
+#  ifdef __SIZEOF_FLOAT128__
+#    define FMT_USE_FLOAT128 1
+#  else
+#    define FMT_USE_FLOAT128 0
+#  endif
+#endif
+#if FMT_USE_FLOAT128
+using float128 = __float128;
+#else
+using float128 = void;
+#endif
+template <typename T> using is_float128 = std::is_same<T, float128>;
+
+template <typename T>
+using is_floating_point =
+    bool_constant<std::is_floating_point<T>::value || is_float128<T>::value>;
+
+template <typename T, bool = std::is_floating_point<T>::value>
+struct is_fast_float : bool_constant<std::numeric_limits<T>::is_iec559 &&
+                                     sizeof(T) <= sizeof(double)> {};
+template <typename T> struct is_fast_float<T, false> : std::false_type {};
+
+template <typename T>
+using is_double_double = bool_constant<std::numeric_limits<T>::digits == 106>;
+
+#ifndef FMT_USE_FULL_CACHE_DRAGONBOX
+#  define FMT_USE_FULL_CACHE_DRAGONBOX 0
+#endif
+
+template <typename T>
+template <typename U>
+void buffer<T>::append(const U* begin, const U* end) {
+  while (begin != end) {
+    auto count = to_unsigned(end - begin);
+    try_reserve(size_ + count);
+    auto free_cap = capacity_ - size_;
+    if (free_cap < count) count = free_cap;
+    std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count));
+    size_ += count;
+    begin += count;
+  }
+}
+
+template <typename T, typename Enable = void>
+struct is_locale : std::false_type {};
+template <typename T>
+struct is_locale<T, void_t<decltype(T::classic())>> : std::true_type {};
+}  // namespace detail
+
+FMT_MODULE_EXPORT_BEGIN
+
+// The number of characters to store in the basic_memory_buffer object itself
+// to avoid dynamic memory allocation.
+enum { inline_buffer_size = 500 };
+
+/**
+  \rst
+  A dynamically growing memory buffer for trivially copyable/constructible types
+  with the first ``SIZE`` elements stored in the object itself.
+
+  You can use the ``memory_buffer`` type alias for ``char`` instead.
+
+  **Example**::
+
+     auto out = fmt::memory_buffer();
+     format_to(std::back_inserter(out), "The answer is {}.", 42);
+
+  This will append the following output to the ``out`` object:
+
+  .. code-block:: none
+
+     The answer is 42.
+
+  The output can be converted to an ``std::string`` with ``to_string(out)``.
+  \endrst
+ */
+template <typename T, size_t SIZE = inline_buffer_size,
+          typename Allocator = std::allocator<T>>
+class basic_memory_buffer final : public detail::buffer<T> {
+ private:
+  T store_[SIZE];
+
+  // Don't inherit from Allocator avoid generating type_info for it.
+  Allocator alloc_;
+
+  // Deallocate memory allocated by the buffer.
+  FMT_CONSTEXPR20 void deallocate() {
+    T* data = this->data();
+    if (data != store_) alloc_.deallocate(data, this->capacity());
+  }
+
+ protected:
+  FMT_CONSTEXPR20 void grow(size_t size) override;
+
+ public:
+  using value_type = T;
+  using const_reference = const T&;
+
+  FMT_CONSTEXPR20 explicit basic_memory_buffer(
+      const Allocator& alloc = Allocator())
+      : alloc_(alloc) {
+    this->set(store_, SIZE);
+    if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T());
+  }
+  FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); }
+
+ private:
+  // Move data from other to this buffer.
+  FMT_CONSTEXPR20 void move(basic_memory_buffer& other) {
+    alloc_ = std::move(other.alloc_);
+    T* data = other.data();
+    size_t size = other.size(), capacity = other.capacity();
+    if (data == other.store_) {
+      this->set(store_, capacity);
+      detail::copy_str<T>(other.store_, other.store_ + size,
+                          detail::make_checked(store_, capacity));
+    } else {
+      this->set(data, capacity);
+      // Set pointer to the inline array so that delete is not called
+      // when deallocating.
+      other.set(other.store_, 0);
+      other.clear();
+    }
+    this->resize(size);
+  }
+
+ public:
+  /**
+    \rst
+    Constructs a :class:`fmt::basic_memory_buffer` object moving the content
+    of the other object to it.
+    \endrst
+   */
+  FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept {
+    move(other);
+  }
+
+  /**
+    \rst
+    Moves the content of the other ``basic_memory_buffer`` object to this one.
+    \endrst
+   */
+  auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& {
+    FMT_ASSERT(this != &other, "");
+    deallocate();
+    move(other);
+    return *this;
+  }
+
+  // Returns a copy of the allocator associated with this buffer.
+  auto get_allocator() const -> Allocator { return alloc_; }
+
+  /**
+    Resizes the buffer to contain *count* elements. If T is a POD type new
+    elements may not be initialized.
+   */
+  FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); }
+
+  /** Increases the buffer capacity to *new_capacity*. */
+  void reserve(size_t new_capacity) { this->try_reserve(new_capacity); }
+
+  // Directly append data into the buffer
+  using detail::buffer<T>::append;
+  template <typename ContiguousRange>
+  void append(const ContiguousRange& range) {
+    append(range.data(), range.data() + range.size());
+  }
+};
+
+template <typename T, size_t SIZE, typename Allocator>
+FMT_CONSTEXPR20 void basic_memory_buffer<T, SIZE, Allocator>::grow(
+    size_t size) {
+  detail::abort_fuzzing_if(size > 5000);
+  const size_t max_size = std::allocator_traits<Allocator>::max_size(alloc_);
+  size_t old_capacity = this->capacity();
+  size_t new_capacity = old_capacity + old_capacity / 2;
+  if (size > new_capacity)
+    new_capacity = size;
+  else if (new_capacity > max_size)
+    new_capacity = size > max_size ? size : max_size;
+  T* old_data = this->data();
+  T* new_data =
+      std::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
+  // The following code doesn't throw, so the raw pointer above doesn't leak.
+  std::uninitialized_copy(old_data, old_data + this->size(),
+                          detail::make_checked(new_data, new_capacity));
+  this->set(new_data, new_capacity);
+  // deallocate must not throw according to the standard, but even if it does,
+  // the buffer already uses the new storage and will deallocate it in
+  // destructor.
+  if (old_data != store_) alloc_.deallocate(old_data, old_capacity);
+}
+
+using memory_buffer = basic_memory_buffer<char>;
+
+template <typename T, size_t SIZE, typename Allocator>
+struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
+};
+
+namespace detail {
+#ifdef _WIN32
+FMT_API bool write_console(std::FILE* f, string_view text);
+#endif
+FMT_API void print(std::FILE*, string_view);
+}  // namespace detail
+
+/** A formatting error such as invalid format string. */
+FMT_CLASS_API
+class FMT_API format_error : public std::runtime_error {
+ public:
+  explicit format_error(const char* message) : std::runtime_error(message) {}
+  explicit format_error(const std::string& message)
+      : std::runtime_error(message) {}
+  format_error(const format_error&) = default;
+  format_error& operator=(const format_error&) = default;
+  format_error(format_error&&) = default;
+  format_error& operator=(format_error&&) = default;
+  ~format_error() noexcept override FMT_MSC_DEFAULT;
+};
+
+namespace detail_exported {
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+template <typename Char, size_t N> struct fixed_string {
+  constexpr fixed_string(const Char (&str)[N]) {
+    detail::copy_str<Char, const Char*, Char*>(static_cast<const Char*>(str),
+                                               str + N, data);
+  }
+  Char data[N] = {};
+};
+#endif
+
+// Converts a compile-time string to basic_string_view.
+template <typename Char, size_t N>
+constexpr auto compile_string_to_view(const Char (&s)[N])
+    -> basic_string_view<Char> {
+  // Remove trailing NUL character if needed. Won't be present if this is used
+  // with a raw character array (i.e. not defined as a string).
+  return {s, N - (std::char_traits<Char>::to_int_type(s[N - 1]) == 0 ? 1 : 0)};
+}
+template <typename Char>
+constexpr auto compile_string_to_view(detail::std_string_view<Char> s)
+    -> basic_string_view<Char> {
+  return {s.data(), s.size()};
+}
+}  // namespace detail_exported
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+template <typename T> struct is_integral : std::is_integral<T> {};
+template <> struct is_integral<int128_opt> : std::true_type {};
+template <> struct is_integral<uint128_t> : std::true_type {};
+
+template <typename T>
+using is_signed =
+    std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
+                                     std::is_same<T, int128_opt>::value>;
+
+// Returns true if value is negative, false otherwise.
+// Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
+template <typename T, FMT_ENABLE_IF(is_signed<T>::value)>
+constexpr auto is_negative(T value) -> bool {
+  return value < 0;
+}
+template <typename T, FMT_ENABLE_IF(!is_signed<T>::value)>
+constexpr auto is_negative(T) -> bool {
+  return false;
+}
+
+template <typename T>
+FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool {
+  if (std::is_same<T, float>()) return FMT_USE_FLOAT;
+  if (std::is_same<T, double>()) return FMT_USE_DOUBLE;
+  if (std::is_same<T, long double>()) return FMT_USE_LONG_DOUBLE;
+  return true;
+}
+
+// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
+// represent all values of an integral type T.
+template <typename T>
+using uint32_or_64_or_128_t =
+    conditional_t<num_bits<T>() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS,
+                  uint32_t,
+                  conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;
+template <typename T>
+using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>;
+
+#define FMT_POWERS_OF_10(factor)                                             \
+  factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
+      (factor)*1000000, (factor)*10000000, (factor)*100000000,               \
+      (factor)*1000000000
+
+// Converts value in the range [0, 100) to a string.
+constexpr const char* digits2(size_t value) {
+  // GCC generates slightly better code when value is pointer-size.
+  return &"0001020304050607080910111213141516171819"
+         "2021222324252627282930313233343536373839"
+         "4041424344454647484950515253545556575859"
+         "6061626364656667686970717273747576777879"
+         "8081828384858687888990919293949596979899"[value * 2];
+}
+
+// Sign is a template parameter to workaround a bug in gcc 4.8.
+template <typename Char, typename Sign> constexpr Char sign(Sign s) {
+#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604
+  static_assert(std::is_same<Sign, sign_t>::value, "");
+#endif
+  return static_cast<Char>("\0-+ "[s]);
+}
+
+template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
+  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
+    // "Three Optimization Tips for C++". See speed-test for a comparison.
+    if (n < 10) return count;
+    if (n < 100) return count + 1;
+    if (n < 1000) return count + 2;
+    if (n < 10000) return count + 3;
+    n /= 10000u;
+    count += 4;
+  }
+}
+#if FMT_USE_INT128
+FMT_CONSTEXPR inline auto count_digits(uint128_opt n) -> int {
+  return count_digits_fallback(n);
+}
+#endif
+
+#ifdef FMT_BUILTIN_CLZLL
+// It is a separate function rather than a part of count_digits to workaround
+// the lack of static constexpr in constexpr functions.
+inline auto do_count_digits(uint64_t n) -> int {
+  // This has comparable performance to the version by Kendall Willets
+  // (https://github.com/fmtlib/format-benchmark/blob/master/digits10)
+  // but uses smaller tables.
+  // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)).
+  static constexpr uint8_t bsr2log10[] = {
+      1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4,  4,  5,  5,  5,
+      6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  9,  9,  9,  10, 10, 10,
+      10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
+      15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20};
+  auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63];
+  static constexpr const uint64_t zero_or_powers_of_10[] = {
+      0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL),
+      10000000000000000000ULL};
+  return t - (n < zero_or_powers_of_10[t]);
+}
+#endif
+
+// Returns the number of decimal digits in n. Leading zeros are not counted
+// except for n == 0 in which case count_digits returns 1.
+FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int {
+#ifdef FMT_BUILTIN_CLZLL
+  if (!is_constant_evaluated()) {
+    return do_count_digits(n);
+  }
+#endif
+  return count_digits_fallback(n);
+}
+
+// Counts the number of digits in n. BITS = log2(radix).
+template <int BITS, typename UInt>
+FMT_CONSTEXPR auto count_digits(UInt n) -> int {
+#ifdef FMT_BUILTIN_CLZ
+  if (!is_constant_evaluated() && num_bits<UInt>() == 32)
+    return (FMT_BUILTIN_CLZ(static_cast<uint32_t>(n) | 1) ^ 31) / BITS + 1;
+#endif
+  // Lambda avoids unreachable code warnings from NVHPC.
+  return [](UInt m) {
+    int num_digits = 0;
+    do {
+      ++num_digits;
+    } while ((m >>= BITS) != 0);
+    return num_digits;
+  }(n);
+}
+
+#ifdef FMT_BUILTIN_CLZ
+// It is a separate function rather than a part of count_digits to workaround
+// the lack of static constexpr in constexpr functions.
+FMT_INLINE auto do_count_digits(uint32_t n) -> int {
+// An optimization by Kendall Willets from https://bit.ly/3uOIQrB.
+// This increments the upper 32 bits (log10(T) - 1) when >= T is added.
+#  define FMT_INC(T) (((sizeof(#  T) - 1ull) << 32) - T)
+  static constexpr uint64_t table[] = {
+      FMT_INC(0),          FMT_INC(0),          FMT_INC(0),           // 8
+      FMT_INC(10),         FMT_INC(10),         FMT_INC(10),          // 64
+      FMT_INC(100),        FMT_INC(100),        FMT_INC(100),         // 512
+      FMT_INC(1000),       FMT_INC(1000),       FMT_INC(1000),        // 4096
+      FMT_INC(10000),      FMT_INC(10000),      FMT_INC(10000),       // 32k
+      FMT_INC(100000),     FMT_INC(100000),     FMT_INC(100000),      // 256k
+      FMT_INC(1000000),    FMT_INC(1000000),    FMT_INC(1000000),     // 2048k
+      FMT_INC(10000000),   FMT_INC(10000000),   FMT_INC(10000000),    // 16M
+      FMT_INC(100000000),  FMT_INC(100000000),  FMT_INC(100000000),   // 128M
+      FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000),  // 1024M
+      FMT_INC(1000000000), FMT_INC(1000000000)                        // 4B
+  };
+  auto inc = table[FMT_BUILTIN_CLZ(n | 1) ^ 31];
+  return static_cast<int>((n + inc) >> 32);
+}
+#endif
+
+// Optional version of count_digits for better performance on 32-bit platforms.
+FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int {
+#ifdef FMT_BUILTIN_CLZ
+  if (!is_constant_evaluated()) {
+    return do_count_digits(n);
+  }
+#endif
+  return count_digits_fallback(n);
+}
+
+template <typename Int> constexpr auto digits10() noexcept -> int {
+  return std::numeric_limits<Int>::digits10;
+}
+template <> constexpr auto digits10<int128_opt>() noexcept -> int { return 38; }
+template <> constexpr auto digits10<uint128_t>() noexcept -> int { return 38; }
+
+template <typename Char> struct thousands_sep_result {
+  std::string grouping;
+  Char thousands_sep;
+};
+
+template <typename Char>
+FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char>;
+template <typename Char>
+inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<Char> {
+  auto result = thousands_sep_impl<char>(loc);
+  return {result.grouping, Char(result.thousands_sep)};
+}
+template <>
+inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<wchar_t> {
+  return thousands_sep_impl<wchar_t>(loc);
+}
+
+template <typename Char>
+FMT_API auto decimal_point_impl(locale_ref loc) -> Char;
+template <typename Char> inline auto decimal_point(locale_ref loc) -> Char {
+  return Char(decimal_point_impl<char>(loc));
+}
+template <> inline auto decimal_point(locale_ref loc) -> wchar_t {
+  return decimal_point_impl<wchar_t>(loc);
+}
+
+// Compares two characters for equality.
+template <typename Char> auto equal2(const Char* lhs, const char* rhs) -> bool {
+  return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]);
+}
+inline auto equal2(const char* lhs, const char* rhs) -> bool {
+  return memcmp(lhs, rhs, 2) == 0;
+}
+
+// Copies two characters from src to dst.
+template <typename Char>
+FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) {
+  if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) {
+    memcpy(dst, src, 2);
+    return;
+  }
+  *dst++ = static_cast<Char>(*src++);
+  *dst = static_cast<Char>(*src);
+}
+
+template <typename Iterator> struct format_decimal_result {
+  Iterator begin;
+  Iterator end;
+};
+
+// Formats a decimal unsigned integer value writing into out pointing to a
+// buffer of specified size. The caller must ensure that the buffer is large
+// enough.
+template <typename Char, typename UInt>
+FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size)
+    -> format_decimal_result<Char*> {
+  FMT_ASSERT(size >= count_digits(value), "invalid digit count");
+  out += size;
+  Char* end = out;
+  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
+    // "Three Optimization Tips for C++". See speed-test for a comparison.
+    out -= 2;
+    copy2(out, digits2(static_cast<size_t>(value % 100)));
+    value /= 100;
+  }
+  if (value < 10) {
+    *--out = static_cast<Char>('0' + value);
+    return {out, end};
+  }
+  out -= 2;
+  copy2(out, digits2(static_cast<size_t>(value)));
+  return {out, end};
+}
+
+template <typename Char, typename UInt, typename Iterator,
+          FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)>
+FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size)
+    -> format_decimal_result<Iterator> {
+  // Buffer is large enough to hold all digits (digits10 + 1).
+  Char buffer[digits10<UInt>() + 1];
+  auto end = format_decimal(buffer, value, size).end;
+  return {out, detail::copy_str_noinline<Char>(buffer, end, out)};
+}
+
+template <unsigned BASE_BITS, typename Char, typename UInt>
+FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits,
+                               bool upper = false) -> Char* {
+  buffer += num_digits;
+  Char* end = buffer;
+  do {
+    const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
+    unsigned digit = static_cast<unsigned>(value & ((1 << BASE_BITS) - 1));
+    *--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 Char, typename It, typename UInt>
+inline auto format_uint(It out, UInt value, int num_digits, bool upper = false)
+    -> It {
+  if (auto ptr = to_pointer<Char>(out, to_unsigned(num_digits))) {
+    format_uint<BASE_BITS>(ptr, value, num_digits, upper);
+    return out;
+  }
+  // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
+  char buffer[num_bits<UInt>() / BASE_BITS + 1];
+  format_uint<BASE_BITS>(buffer, value, num_digits, upper);
+  return detail::copy_str_noinline<Char>(buffer, buffer + num_digits, out);
+}
+
+// A converter from UTF-8 to UTF-16.
+class utf8_to_utf16 {
+ private:
+  basic_memory_buffer<wchar_t> buffer_;
+
+ public:
+  FMT_API explicit utf8_to_utf16(string_view s);
+  operator basic_string_view<wchar_t>() const { return {&buffer_[0], size()}; }
+  auto size() const -> size_t { return buffer_.size() - 1; }
+  auto c_str() const -> const wchar_t* { return &buffer_[0]; }
+  auto str() const -> std::wstring { return {&buffer_[0], size()}; }
+};
+
+namespace dragonbox {
+
+// Type-specific information that Dragonbox uses.
+template <typename T, typename Enable = void> struct float_info;
+
+template <> struct float_info<float> {
+  using carrier_uint = uint32_t;
+  static const int exponent_bits = 8;
+  static const int kappa = 1;
+  static const int big_divisor = 100;
+  static const int small_divisor = 10;
+  static const int min_k = -31;
+  static const int max_k = 46;
+  static const int shorter_interval_tie_lower_threshold = -35;
+  static const int shorter_interval_tie_upper_threshold = -35;
+};
+
+template <> struct float_info<double> {
+  using carrier_uint = uint64_t;
+  static const int exponent_bits = 11;
+  static const int kappa = 2;
+  static const int big_divisor = 1000;
+  static const int small_divisor = 100;
+  static const int min_k = -292;
+  static const int max_k = 326;
+  static const int shorter_interval_tie_lower_threshold = -77;
+  static const int shorter_interval_tie_upper_threshold = -77;
+};
+
+// An 80- or 128-bit floating point number.
+template <typename T>
+struct float_info<T, enable_if_t<std::numeric_limits<T>::digits == 64 ||
+                                 std::numeric_limits<T>::digits == 113 ||
+                                 is_float128<T>::value>> {
+  using carrier_uint = detail::uint128_t;
+  static const int exponent_bits = 15;
+};
+
+// A double-double floating point number.
+template <typename T>
+struct float_info<T, enable_if_t<is_double_double<T>::value>> {
+  using carrier_uint = detail::uint128_t;
+};
+
+template <typename T> struct decimal_fp {
+  using significand_type = typename float_info<T>::carrier_uint;
+  significand_type significand;
+  int exponent;
+};
+
+template <typename T> FMT_API auto to_decimal(T x) noexcept -> decimal_fp<T>;
+}  // namespace dragonbox
+
+// Returns true iff Float has the implicit bit which is not stored.
+template <typename Float> constexpr bool has_implicit_bit() {
+  // An 80-bit FP number has a 64-bit significand an no implicit bit.
+  return std::numeric_limits<Float>::digits != 64;
+}
+
+// Returns the number of significand bits stored in Float. The implicit bit is
+// not counted since it is not stored.
+template <typename Float> constexpr int num_significand_bits() {
+  // std::numeric_limits may not support __float128.
+  return is_float128<Float>() ? 112
+                              : (std::numeric_limits<Float>::digits -
+                                 (has_implicit_bit<Float>() ? 1 : 0));
+}
+
+template <typename Float>
+constexpr auto exponent_mask() ->
+    typename dragonbox::float_info<Float>::carrier_uint {
+  using uint = typename dragonbox::float_info<Float>::carrier_uint;
+  return ((uint(1) << dragonbox::float_info<Float>::exponent_bits) - 1)
+         << num_significand_bits<Float>();
+}
+template <typename Float> constexpr auto exponent_bias() -> int {
+  // std::numeric_limits may not support __float128.
+  return is_float128<Float>() ? 16383
+                              : std::numeric_limits<Float>::max_exponent - 1;
+}
+
+// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
+template <typename Char, typename It>
+FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It {
+  FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
+  if (exp < 0) {
+    *it++ = static_cast<Char>('-');
+    exp = -exp;
+  } else {
+    *it++ = static_cast<Char>('+');
+  }
+  if (exp >= 100) {
+    const char* top = digits2(to_unsigned(exp / 100));
+    if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
+    *it++ = static_cast<Char>(top[1]);
+    exp %= 100;
+  }
+  const char* d = digits2(to_unsigned(exp));
+  *it++ = static_cast<Char>(d[0]);
+  *it++ = static_cast<Char>(d[1]);
+  return it;
+}
+
+// A floating-point number f * pow(2, e) where F is an unsigned type.
+template <typename F> struct basic_fp {
+  F f;
+  int e;
+
+  static constexpr const int num_significand_bits =
+      static_cast<int>(sizeof(F) * num_bits<unsigned char>());
+
+  constexpr basic_fp() : f(0), e(0) {}
+  constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
+
+  // Constructs fp from an IEEE754 floating-point number.
+  template <typename Float> FMT_CONSTEXPR basic_fp(Float n) { assign(n); }
+
+  // Assigns n to this and return true iff predecessor is closer than successor.
+  template <typename Float, FMT_ENABLE_IF(!is_double_double<Float>::value)>
+  FMT_CONSTEXPR auto assign(Float n) -> bool {
+    static_assert(std::numeric_limits<Float>::digits <= 113, "unsupported FP");
+    // Assume Float is in the format [sign][exponent][significand].
+    using carrier_uint = typename dragonbox::float_info<Float>::carrier_uint;
+    const auto num_float_significand_bits =
+        detail::num_significand_bits<Float>();
+    const auto implicit_bit = carrier_uint(1) << num_float_significand_bits;
+    const auto significand_mask = implicit_bit - 1;
+    auto u = bit_cast<carrier_uint>(n);
+    f = static_cast<F>(u & significand_mask);
+    auto biased_e = static_cast<int>((u & exponent_mask<Float>()) >>
+                                     num_float_significand_bits);
+    // The predecessor is closer if n is a normalized power of 2 (f == 0)
+    // other than the smallest normalized number (biased_e > 1).
+    auto is_predecessor_closer = f == 0 && biased_e > 1;
+    if (biased_e == 0)
+      biased_e = 1;  // Subnormals use biased exponent 1 (min exponent).
+    else if (has_implicit_bit<Float>())
+      f += static_cast<F>(implicit_bit);
+    e = biased_e - exponent_bias<Float>() - num_float_significand_bits;
+    if (!has_implicit_bit<Float>()) ++e;
+    return is_predecessor_closer;
+  }
+
+  template <typename Float, FMT_ENABLE_IF(is_double_double<Float>::value)>
+  FMT_CONSTEXPR auto assign(Float n) -> bool {
+    static_assert(std::numeric_limits<double>::is_iec559, "unsupported FP");
+    return assign(static_cast<double>(n));
+  }
+};
+
+using fp = basic_fp<unsigned long long>;
+
+// Normalizes the value converted from double and multiplied by (1 << SHIFT).
+template <int SHIFT = 0, typename F>
+FMT_CONSTEXPR basic_fp<F> normalize(basic_fp<F> value) {
+  // Handle subnormals.
+  const auto implicit_bit = F(1) << num_significand_bits<double>();
+  const auto shifted_implicit_bit = implicit_bit << SHIFT;
+  while ((value.f & shifted_implicit_bit) == 0) {
+    value.f <<= 1;
+    --value.e;
+  }
+  // Subtract 1 to account for hidden bit.
+  const auto offset = basic_fp<F>::num_significand_bits -
+                      num_significand_bits<double>() - SHIFT - 1;
+  value.f <<= offset;
+  value.e -= offset;
+  return value;
+}
+
+// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
+FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
+#if FMT_USE_INT128
+  auto product = static_cast<__uint128_t>(lhs) * rhs;
+  auto f = static_cast<uint64_t>(product >> 64);
+  return (static_cast<uint64_t>(product) & (1ULL << 63)) != 0 ? f + 1 : f;
+#else
+  // Multiply 32-bit parts of significands.
+  uint64_t mask = (1ULL << 32) - 1;
+  uint64_t a = lhs >> 32, b = lhs & mask;
+  uint64_t c = rhs >> 32, d = rhs & mask;
+  uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
+  // Compute mid 64-bit of result and round.
+  uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
+  return ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
+#endif
+}
+
+FMT_CONSTEXPR inline fp operator*(fp x, fp y) {
+  return {multiply(x.f, y.f), x.e + y.e + 64};
+}
+
+template <typename T = void> struct basic_data {
+  // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
+  // These are generated by support/compute-powers.py.
+  static constexpr uint64_t pow10_significands[87] = {
+      0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
+      0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
+      0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
+      0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
+      0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
+      0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
+      0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
+      0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
+      0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
+      0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
+      0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
+      0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
+      0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
+      0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
+      0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
+      0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
+      0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
+      0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
+      0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
+      0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
+      0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
+      0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
+      0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
+      0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
+      0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
+      0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
+      0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
+      0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
+      0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
+  };
+
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+#  pragma GCC diagnostic push
+#  pragma GCC diagnostic ignored "-Wnarrowing"
+#endif
+  // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
+  // to significands above.
+  static constexpr int16_t pow10_exponents[87] = {
+      -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
+      -927,  -901,  -874,  -847,  -821,  -794,  -768,  -741,  -715,  -688, -661,
+      -635,  -608,  -582,  -555,  -529,  -502,  -475,  -449,  -422,  -396, -369,
+      -343,  -316,  -289,  -263,  -236,  -210,  -183,  -157,  -130,  -103, -77,
+      -50,   -24,   3,     30,    56,    83,    109,   136,   162,   189,  216,
+      242,   269,   295,   322,   348,   375,   402,   428,   455,   481,  508,
+      534,   561,   588,   614,   641,   667,   694,   720,   747,   774,  800,
+      827,   853,   880,   907,   933,   960,   986,   1013,  1039,  1066};
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+#  pragma GCC diagnostic pop
+#endif
+
+  static constexpr uint64_t power_of_10_64[20] = {
+      1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL),
+      10000000000000000000ULL};
+};
+
+#if FMT_CPLUSPLUS < 201703L
+template <typename T> constexpr uint64_t basic_data<T>::pow10_significands[];
+template <typename T> constexpr int16_t basic_data<T>::pow10_exponents[];
+template <typename T> constexpr uint64_t basic_data<T>::power_of_10_64[];
+#endif
+
+// This is a struct rather than an alias to avoid shadowing warnings in gcc.
+struct data : basic_data<> {};
+
+// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
+// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
+FMT_CONSTEXPR inline fp get_cached_power(int min_exponent,
+                                         int& pow10_exponent) {
+  const int shift = 32;
+  // log10(2) = 0x0.4d104d427de7fbcc...
+  const int64_t significand = 0x4d104d427de7fbcc;
+  int index = static_cast<int>(
+      ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) +
+       ((int64_t(1) << shift) - 1))  // ceil
+      >> 32                          // arithmetic shift
+  );
+  // Decimal exponent of the first (smallest) cached power of 10.
+  const int first_dec_exp = -348;
+  // Difference between 2 consecutive decimal exponents in cached powers of 10.
+  const int dec_exp_step = 8;
+  index = (index - first_dec_exp - 1) / dec_exp_step + 1;
+  pow10_exponent = first_dec_exp + index * dec_exp_step;
+  // Using *(x + index) instead of x[index] avoids an issue with some compilers
+  // using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode).
+  return {*(data::pow10_significands + index),
+          *(data::pow10_exponents + index)};
+}
+
+#ifndef _MSC_VER
+#  define FMT_SNPRINTF snprintf
+#else
+FMT_API auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...) -> int;
+#  define FMT_SNPRINTF fmt_snprintf
+#endif  // _MSC_VER
+
+// Formats a floating-point number with snprintf using the hexfloat format.
+template <typename T>
+auto snprintf_float(T value, int precision, float_specs specs,
+                    buffer<char>& buf) -> int {
+  // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
+  FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
+  FMT_ASSERT(specs.format == float_format::hex, "");
+  static_assert(!std::is_same<T, float>::value, "");
+
+  // Build the format string.
+  char format[7];  // The longest format is "%#.*Le".
+  char* format_ptr = format;
+  *format_ptr++ = '%';
+  if (specs.showpoint) *format_ptr++ = '#';
+  if (precision >= 0) {
+    *format_ptr++ = '.';
+    *format_ptr++ = '*';
+  }
+  if (std::is_same<T, long double>()) *format_ptr++ = 'L';
+  *format_ptr++ = specs.upper ? 'A' : 'a';
+  *format_ptr = '\0';
+
+  // Format using snprintf.
+  auto offset = buf.size();
+  for (;;) {
+    auto begin = buf.data() + offset;
+    auto capacity = buf.capacity() - offset;
+    abort_fuzzing_if(precision > 100000);
+    // Suppress the warning about a nonliteral format string.
+    // Cannot use auto because of a bug in MinGW (#1532).
+    int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
+    int result = precision >= 0
+                     ? snprintf_ptr(begin, capacity, format, precision, value)
+                     : snprintf_ptr(begin, capacity, format, value);
+    if (result < 0) {
+      // The buffer will grow exponentially.
+      buf.try_reserve(buf.capacity() + 1);
+      continue;
+    }
+    auto size = to_unsigned(result);
+    // Size equal to capacity means that the last character was truncated.
+    if (size < capacity) {
+      buf.try_resize(size + offset);
+      return 0;
+    }
+    buf.try_reserve(size + offset + 1);  // Add 1 for the terminating '\0'.
+  }
+}
+
+template <typename T>
+using convert_float_result =
+    conditional_t<std::is_same<T, float>::value || sizeof(T) == sizeof(double),
+                  double, T>;
+
+template <typename T>
+constexpr auto convert_float(T value) -> convert_float_result<T> {
+  return static_cast<convert_float_result<T>>(value);
+}
+
+template <typename OutputIt, typename Char>
+FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n,
+                                     const fill_t<Char>& fill) -> OutputIt {
+  auto fill_size = fill.size();
+  if (fill_size == 1) return detail::fill_n(it, n, fill[0]);
+  auto data = fill.data();
+  for (size_t i = 0; i < n; ++i)
+    it = copy_str<Char>(data, data + fill_size, it);
+  return it;
+}
+
+// Writes the output of f, padded according to format specifications in specs.
+// size: output size in code units.
+// width: output display width in (terminal) column positions.
+template <align::type align = align::left, typename OutputIt, typename Char,
+          typename F>
+FMT_CONSTEXPR auto write_padded(OutputIt out,
+                                const basic_format_specs<Char>& specs,
+                                size_t size, size_t width, F&& f) -> OutputIt {
+  static_assert(align == align::left || align == align::right, "");
+  unsigned spec_width = to_unsigned(specs.width);
+  size_t padding = spec_width > width ? spec_width - width : 0;
+  // Shifts are encoded as string literals because static constexpr is not
+  // supported in constexpr functions.
+  auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01";
+  size_t left_padding = padding >> shifts[specs.align];
+  size_t right_padding = padding - left_padding;
+  auto it = reserve(out, size + padding * specs.fill.size());
+  if (left_padding != 0) it = fill(it, left_padding, specs.fill);
+  it = f(it);
+  if (right_padding != 0) it = fill(it, right_padding, specs.fill);
+  return base_iterator(out, it);
+}
+
+template <align::type align = align::left, typename OutputIt, typename Char,
+          typename F>
+constexpr auto write_padded(OutputIt out, const basic_format_specs<Char>& specs,
+                            size_t size, F&& f) -> OutputIt {
+  return write_padded<align>(out, specs, size, size, f);
+}
+
+template <align::type align = align::left, typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes,
+                               const basic_format_specs<Char>& specs)
+    -> OutputIt {
+  return write_padded<align>(
+      out, specs, bytes.size(), [bytes](reserve_iterator<OutputIt> it) {
+        const char* data = bytes.data();
+        return copy_str<Char>(data, data + bytes.size(), it);
+      });
+}
+
+template <typename Char, typename OutputIt, typename UIntPtr>
+auto write_ptr(OutputIt out, UIntPtr value,
+               const basic_format_specs<Char>* specs) -> OutputIt {
+  int num_digits = count_digits<4>(value);
+  auto size = to_unsigned(num_digits) + size_t(2);
+  auto write = [=](reserve_iterator<OutputIt> it) {
+    *it++ = static_cast<Char>('0');
+    *it++ = static_cast<Char>('x');
+    return format_uint<4, Char>(it, value, num_digits);
+  };
+  return specs ? write_padded<align::right>(out, *specs, size, write)
+               : base_iterator(out, write(reserve(out, size)));
+}
+
+// Returns true iff the code point cp is printable.
+FMT_API auto is_printable(uint32_t cp) -> bool;
+
+inline auto needs_escape(uint32_t cp) -> bool {
+  return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
+         !is_printable(cp);
+}
+
+template <typename Char> struct find_escape_result {
+  const Char* begin;
+  const Char* end;
+  uint32_t cp;
+};
+
+template <typename Char>
+using make_unsigned_char =
+    typename conditional_t<std::is_integral<Char>::value,
+                           std::make_unsigned<Char>,
+                           type_identity<uint32_t>>::type;
+
+template <typename Char>
+auto find_escape(const Char* begin, const Char* end)
+    -> find_escape_result<Char> {
+  for (; begin != end; ++begin) {
+    uint32_t cp = static_cast<make_unsigned_char<Char>>(*begin);
+    if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue;
+    if (needs_escape(cp)) return {begin, begin + 1, cp};
+  }
+  return {begin, nullptr, 0};
+}
+
+inline auto find_escape(const char* begin, const char* end)
+    -> find_escape_result<char> {
+  if (!is_utf8()) return find_escape<char>(begin, end);
+  auto result = find_escape_result<char>{end, nullptr, 0};
+  for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
+                     [&](uint32_t cp, string_view sv) {
+                       if (needs_escape(cp)) {
+                         result = {sv.begin(), sv.end(), cp};
+                         return false;
+                       }
+                       return true;
+                     });
+  return result;
+}
+
+#define FMT_STRING_IMPL(s, base, explicit)                                    \
+  [] {                                                                        \
+    /* Use the hidden visibility as a workaround for a GCC bug (#1973). */    \
+    /* Use a macro-like name to avoid shadowing warnings. */                  \
+    struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base {              \
+      using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t<decltype(s[0])>; \
+      FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit                                 \
+      operator fmt::basic_string_view<char_type>() const {                    \
+        return fmt::detail_exported::compile_string_to_view<char_type>(s);    \
+      }                                                                       \
+    };                                                                        \
+    return FMT_COMPILE_STRING();                                              \
+  }()
+
+/**
+  \rst
+  Constructs a compile-time format string from a string literal *s*.
+
+  **Example**::
+
+    // A compile-time error because 'd' is an invalid specifier for strings.
+    std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
+  \endrst
+ */
+#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, )
+
+template <size_t width, typename Char, typename OutputIt>
+auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt {
+  *out++ = static_cast<Char>('\\');
+  *out++ = static_cast<Char>(prefix);
+  Char buf[width];
+  fill_n(buf, width, static_cast<Char>('0'));
+  format_uint<4>(buf, cp, width);
+  return copy_str<Char>(buf, buf + width, out);
+}
+
+template <typename OutputIt, typename Char>
+auto write_escaped_cp(OutputIt out, const find_escape_result<Char>& escape)
+    -> OutputIt {
+  auto c = static_cast<Char>(escape.cp);
+  switch (escape.cp) {
+  case '\n':
+    *out++ = static_cast<Char>('\\');
+    c = static_cast<Char>('n');
+    break;
+  case '\r':
+    *out++ = static_cast<Char>('\\');
+    c = static_cast<Char>('r');
+    break;
+  case '\t':
+    *out++ = static_cast<Char>('\\');
+    c = static_cast<Char>('t');
+    break;
+  case '"':
+    FMT_FALLTHROUGH;
+  case '\'':
+    FMT_FALLTHROUGH;
+  case '\\':
+    *out++ = static_cast<Char>('\\');
+    break;
+  default:
+    if (is_utf8()) {
+      if (escape.cp < 0x100) {
+        return write_codepoint<2, Char>(out, 'x', escape.cp);
+      }
+      if (escape.cp < 0x10000) {
+        return write_codepoint<4, Char>(out, 'u', escape.cp);
+      }
+      if (escape.cp < 0x110000) {
+        return write_codepoint<8, Char>(out, 'U', escape.cp);
+      }
+    }
+    for (Char escape_char : basic_string_view<Char>(
+             escape.begin, to_unsigned(escape.end - escape.begin))) {
+      out = write_codepoint<2, Char>(out, 'x',
+                                     static_cast<uint32_t>(escape_char) & 0xFF);
+    }
+    return out;
+  }
+  *out++ = c;
+  return out;
+}
+
+template <typename Char, typename OutputIt>
+auto write_escaped_string(OutputIt out, basic_string_view<Char> str)
+    -> OutputIt {
+  *out++ = static_cast<Char>('"');
+  auto begin = str.begin(), end = str.end();
+  do {
+    auto escape = find_escape(begin, end);
+    out = copy_str<Char>(begin, escape.begin, out);
+    begin = escape.end;
+    if (!begin) break;
+    out = write_escaped_cp<OutputIt, Char>(out, escape);
+  } while (begin != end);
+  *out++ = static_cast<Char>('"');
+  return out;
+}
+
+template <typename Char, typename OutputIt>
+auto write_escaped_char(OutputIt out, Char v) -> OutputIt {
+  *out++ = static_cast<Char>('\'');
+  if ((needs_escape(static_cast<uint32_t>(v)) && v != static_cast<Char>('"')) ||
+      v == static_cast<Char>('\'')) {
+    out = write_escaped_cp(
+        out, find_escape_result<Char>{&v, &v + 1, static_cast<uint32_t>(v)});
+  } else {
+    *out++ = v;
+  }
+  *out++ = static_cast<Char>('\'');
+  return out;
+}
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write_char(OutputIt out, Char value,
+                              const basic_format_specs<Char>& specs)
+    -> OutputIt {
+  bool is_debug = specs.type == presentation_type::debug;
+  return write_padded(out, specs, 1, [=](reserve_iterator<OutputIt> it) {
+    if (is_debug) return write_escaped_char(it, value);
+    *it++ = value;
+    return it;
+  });
+}
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out, Char value,
+                         const basic_format_specs<Char>& specs,
+                         locale_ref loc = {}) -> OutputIt {
+  return check_char_specs(specs)
+             ? write_char(out, value, specs)
+             : write(out, static_cast<int>(value), specs, loc);
+}
+
+// Data for write_int that doesn't depend on output iterator type. It is used to
+// avoid template code bloat.
+template <typename Char> struct write_int_data {
+  size_t size;
+  size_t padding;
+
+  FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix,
+                               const basic_format_specs<Char>& specs)
+      : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) {
+    if (specs.align == align::numeric) {
+      auto width = to_unsigned(specs.width);
+      if (width > size) {
+        padding = width - size;
+        size = width;
+      }
+    } else if (specs.precision > num_digits) {
+      size = (prefix >> 24) + to_unsigned(specs.precision);
+      padding = to_unsigned(specs.precision - num_digits);
+    }
+  }
+};
+
+// Writes an integer in the format
+//   <left-padding><prefix><numeric-padding><digits><right-padding>
+// where <digits> are written by write_digits(it).
+// prefix contains chars in three lower bytes and the size in the fourth byte.
+template <typename OutputIt, typename Char, typename W>
+FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits,
+                                        unsigned prefix,
+                                        const basic_format_specs<Char>& specs,
+                                        W write_digits) -> OutputIt {
+  // Slightly faster check for specs.width == 0 && specs.precision == -1.
+  if ((specs.width | (specs.precision + 1)) == 0) {
+    auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24));
+    if (prefix != 0) {
+      for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
+        *it++ = static_cast<Char>(p & 0xff);
+    }
+    return base_iterator(out, write_digits(it));
+  }
+  auto data = write_int_data<Char>(num_digits, prefix, specs);
+  return write_padded<align::right>(
+      out, specs, data.size, [=](reserve_iterator<OutputIt> it) {
+        for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
+          *it++ = static_cast<Char>(p & 0xff);
+        it = detail::fill_n(it, data.padding, static_cast<Char>('0'));
+        return write_digits(it);
+      });
+}
+
+template <typename Char> class digit_grouping {
+ private:
+  thousands_sep_result<Char> sep_;
+
+  struct next_state {
+    std::string::const_iterator group;
+    int pos;
+  };
+  next_state initial_state() const { return {sep_.grouping.begin(), 0}; }
+
+  // Returns the next digit group separator position.
+  int next(next_state& state) const {
+    if (!sep_.thousands_sep) return max_value<int>();
+    if (state.group == sep_.grouping.end())
+      return state.pos += sep_.grouping.back();
+    if (*state.group <= 0 || *state.group == max_value<char>())
+      return max_value<int>();
+    state.pos += *state.group++;
+    return state.pos;
+  }
+
+ public:
+  explicit digit_grouping(locale_ref loc, bool localized = true) {
+    if (localized)
+      sep_ = thousands_sep<Char>(loc);
+    else
+      sep_.thousands_sep = Char();
+  }
+  explicit digit_grouping(thousands_sep_result<Char> sep) : sep_(sep) {}
+
+  Char separator() const { return sep_.thousands_sep; }
+
+  int count_separators(int num_digits) const {
+    int count = 0;
+    auto state = initial_state();
+    while (num_digits > next(state)) ++count;
+    return count;
+  }
+
+  // Applies grouping to digits and write the output to out.
+  template <typename Out, typename C>
+  Out apply(Out out, basic_string_view<C> digits) const {
+    auto num_digits = static_cast<int>(digits.size());
+    auto separators = basic_memory_buffer<int>();
+    separators.push_back(0);
+    auto state = initial_state();
+    while (int i = next(state)) {
+      if (i >= num_digits) break;
+      separators.push_back(i);
+    }
+    for (int i = 0, sep_index = static_cast<int>(separators.size() - 1);
+         i < num_digits; ++i) {
+      if (num_digits - i == separators[sep_index]) {
+        *out++ = separator();
+        --sep_index;
+      }
+      *out++ = static_cast<Char>(digits[to_unsigned(i)]);
+    }
+    return out;
+  }
+};
+
+template <typename OutputIt, typename UInt, typename Char>
+auto write_int_localized(OutputIt out, UInt value, unsigned prefix,
+                         const basic_format_specs<Char>& specs,
+                         const digit_grouping<Char>& grouping) -> OutputIt {
+  static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
+  int num_digits = count_digits(value);
+  char digits[40];
+  format_decimal(digits, value, num_digits);
+  unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits +
+                              grouping.count_separators(num_digits));
+  return write_padded<align::right>(
+      out, specs, size, size, [&](reserve_iterator<OutputIt> it) {
+        if (prefix != 0) {
+          char sign = static_cast<char>(prefix);
+          *it++ = static_cast<Char>(sign);
+        }
+        return grouping.apply(it, string_view(digits, to_unsigned(num_digits)));
+      });
+}
+
+template <typename OutputIt, typename UInt, typename Char>
+auto write_int_localized(OutputIt& out, UInt value, unsigned prefix,
+                         const basic_format_specs<Char>& specs, locale_ref loc)
+    -> bool {
+  auto grouping = digit_grouping<Char>(loc);
+  out = write_int_localized(out, value, prefix, specs, grouping);
+  return true;
+}
+
+FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
+  prefix |= prefix != 0 ? value << 8 : value;
+  prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
+}
+
+template <typename UInt> struct write_int_arg {
+  UInt abs_value;
+  unsigned prefix;
+};
+
+template <typename T>
+FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign)
+    -> write_int_arg<uint32_or_64_or_128_t<T>> {
+  auto prefix = 0u;
+  auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
+  if (is_negative(value)) {
+    prefix = 0x01000000 | '-';
+    abs_value = 0 - abs_value;
+  } else {
+    constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
+                                            0x1000000u | ' '};
+    prefix = prefixes[sign];
+  }
+  return {abs_value, prefix};
+}
+
+template <typename Char, typename OutputIt, typename T>
+FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
+                                        const basic_format_specs<Char>& specs,
+                                        locale_ref loc) -> OutputIt {
+  static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
+  auto abs_value = arg.abs_value;
+  auto prefix = arg.prefix;
+  switch (specs.type) {
+  case presentation_type::none:
+  case presentation_type::dec: {
+    if (specs.localized &&
+        write_int_localized(out, static_cast<uint64_or_128_t<T>>(abs_value),
+                            prefix, specs, loc)) {
+      return out;
+    }
+    auto num_digits = count_digits(abs_value);
+    return write_int(
+        out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
+          return format_decimal<Char>(it, abs_value, num_digits).end;
+        });
+  }
+  case presentation_type::hex_lower:
+  case presentation_type::hex_upper: {
+    bool upper = specs.type == presentation_type::hex_upper;
+    if (specs.alt)
+      prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0');
+    int num_digits = count_digits<4>(abs_value);
+    return write_int(
+        out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
+          return format_uint<4, Char>(it, abs_value, num_digits, upper);
+        });
+  }
+  case presentation_type::bin_lower:
+  case presentation_type::bin_upper: {
+    bool upper = specs.type == presentation_type::bin_upper;
+    if (specs.alt)
+      prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0');
+    int num_digits = count_digits<1>(abs_value);
+    return write_int(out, num_digits, prefix, specs,
+                     [=](reserve_iterator<OutputIt> it) {
+                       return format_uint<1, Char>(it, abs_value, num_digits);
+                     });
+  }
+  case presentation_type::oct: {
+    int num_digits = count_digits<3>(abs_value);
+    // Octal prefix '0' is counted as a digit, so only add it if precision
+    // is not greater than the number of digits.
+    if (specs.alt && specs.precision <= num_digits && abs_value != 0)
+      prefix_append(prefix, '0');
+    return write_int(out, num_digits, prefix, specs,
+                     [=](reserve_iterator<OutputIt> it) {
+                       return format_uint<3, Char>(it, abs_value, num_digits);
+                     });
+  }
+  case presentation_type::chr:
+    return write_char(out, static_cast<Char>(abs_value), specs);
+  default:
+    throw_format_error("invalid type specifier");
+  }
+  return out;
+}
+template <typename Char, typename OutputIt, typename T>
+FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(
+    OutputIt out, write_int_arg<T> arg, const basic_format_specs<Char>& specs,
+    locale_ref loc) -> OutputIt {
+  return write_int(out, arg, specs, loc);
+}
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_integral<T>::value &&
+                        !std::is_same<T, bool>::value &&
+                        std::is_same<OutputIt, buffer_appender<Char>>::value)>
+FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
+                                    const basic_format_specs<Char>& specs,
+                                    locale_ref loc) -> OutputIt {
+  return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs,
+                            loc);
+}
+// An inlined version of write used in format string compilation.
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_integral<T>::value &&
+                        !std::is_same<T, bool>::value &&
+                        !std::is_same<OutputIt, buffer_appender<Char>>::value)>
+FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
+                                    const basic_format_specs<Char>& specs,
+                                    locale_ref loc) -> OutputIt {
+  return write_int(out, make_write_int_arg(value, specs.sign), specs, loc);
+}
+
+// An output iterator that counts the number of objects written to it and
+// discards them.
+class counting_iterator {
+ private:
+  size_t count_;
+
+ public:
+  using iterator_category = std::output_iterator_tag;
+  using difference_type = std::ptrdiff_t;
+  using pointer = void;
+  using reference = void;
+  FMT_UNCHECKED_ITERATOR(counting_iterator);
+
+  struct value_type {
+    template <typename T> FMT_CONSTEXPR void operator=(const T&) {}
+  };
+
+  FMT_CONSTEXPR counting_iterator() : count_(0) {}
+
+  FMT_CONSTEXPR size_t count() const { return count_; }
+
+  FMT_CONSTEXPR counting_iterator& operator++() {
+    ++count_;
+    return *this;
+  }
+  FMT_CONSTEXPR counting_iterator operator++(int) {
+    auto it = *this;
+    ++*this;
+    return it;
+  }
+
+  FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it,
+                                                   difference_type n) {
+    it.count_ += static_cast<size_t>(n);
+    return it;
+  }
+
+  FMT_CONSTEXPR value_type operator*() const { return {}; }
+};
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s,
+                         const basic_format_specs<Char>& specs) -> OutputIt {
+  auto data = s.data();
+  auto size = s.size();
+  if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
+    size = code_point_index(s, to_unsigned(specs.precision));
+  bool is_debug = specs.type == presentation_type::debug;
+  size_t width = 0;
+  if (specs.width != 0) {
+    if (is_debug)
+      width = write_escaped_string(counting_iterator{}, s).count();
+    else
+      width = compute_width(basic_string_view<Char>(data, size));
+  }
+  return write_padded(out, specs, size, width,
+                      [=](reserve_iterator<OutputIt> it) {
+                        if (is_debug) return write_escaped_string(it, s);
+                        return copy_str<Char>(data, data + size, it);
+                      });
+}
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out,
+                         basic_string_view<type_identity_t<Char>> s,
+                         const basic_format_specs<Char>& specs, locale_ref)
+    -> OutputIt {
+  check_string_type_spec(specs.type);
+  return write(out, s, specs);
+}
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out, const Char* s,
+                         const basic_format_specs<Char>& specs, locale_ref)
+    -> OutputIt {
+  return check_cstring_type_spec(specs.type)
+             ? write(out, basic_string_view<Char>(s), specs, {})
+             : write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_integral<T>::value &&
+                        !std::is_same<T, bool>::value &&
+                        !std::is_same<T, Char>::value)>
+FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
+  auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
+  bool negative = is_negative(value);
+  // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
+  if (negative) abs_value = ~abs_value + 1;
+  int num_digits = count_digits(abs_value);
+  auto size = (negative ? 1 : 0) + static_cast<size_t>(num_digits);
+  auto it = reserve(out, size);
+  if (auto ptr = to_pointer<Char>(it, size)) {
+    if (negative) *ptr++ = static_cast<Char>('-');
+    format_decimal<Char>(ptr, abs_value, num_digits);
+    return out;
+  }
+  if (negative) *it++ = static_cast<Char>('-');
+  it = format_decimal<Char>(it, abs_value, num_digits).end;
+  return base_iterator(out, it);
+}
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan,
+                                     basic_format_specs<Char> specs,
+                                     const float_specs& fspecs) -> OutputIt {
+  auto str =
+      isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf");
+  constexpr size_t str_size = 3;
+  auto sign = fspecs.sign;
+  auto size = str_size + (sign ? 1 : 0);
+  // Replace '0'-padding with space for non-finite values.
+  const bool is_zero_fill =
+      specs.fill.size() == 1 && *specs.fill.data() == static_cast<Char>('0');
+  if (is_zero_fill) specs.fill[0] = static_cast<Char>(' ');
+  return write_padded(out, specs, size, [=](reserve_iterator<OutputIt> it) {
+    if (sign) *it++ = detail::sign<Char>(sign);
+    return copy_str<Char>(str, str + str_size, it);
+  });
+}
+
+// A decimal floating-point number significand * pow(10, exp).
+struct big_decimal_fp {
+  const char* significand;
+  int significand_size;
+  int exponent;
+};
+
+constexpr auto get_significand_size(const big_decimal_fp& f) -> int {
+  return f.significand_size;
+}
+template <typename T>
+inline auto get_significand_size(const dragonbox::decimal_fp<T>& f) -> int {
+  return count_digits(f.significand);
+}
+
+template <typename Char, typename OutputIt>
+constexpr auto write_significand(OutputIt out, const char* significand,
+                                 int significand_size) -> OutputIt {
+  return copy_str<Char>(significand, significand + significand_size, out);
+}
+template <typename Char, typename OutputIt, typename UInt>
+inline auto write_significand(OutputIt out, UInt significand,
+                              int significand_size) -> OutputIt {
+  return format_decimal<Char>(out, significand, significand_size).end;
+}
+template <typename Char, typename OutputIt, typename T, typename Grouping>
+FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
+                                       int significand_size, int exponent,
+                                       const Grouping& grouping) -> OutputIt {
+  if (!grouping.separator()) {
+    out = write_significand<Char>(out, significand, significand_size);
+    return detail::fill_n(out, exponent, static_cast<Char>('0'));
+  }
+  auto buffer = memory_buffer();
+  write_significand<char>(appender(buffer), significand, significand_size);
+  detail::fill_n(appender(buffer), exponent, '0');
+  return grouping.apply(out, string_view(buffer.data(), buffer.size()));
+}
+
+template <typename Char, typename UInt,
+          FMT_ENABLE_IF(std::is_integral<UInt>::value)>
+inline auto write_significand(Char* out, UInt significand, int significand_size,
+                              int integral_size, Char decimal_point) -> Char* {
+  if (!decimal_point)
+    return format_decimal(out, significand, significand_size).end;
+  out += significand_size + 1;
+  Char* end = out;
+  int floating_size = significand_size - integral_size;
+  for (int i = floating_size / 2; i > 0; --i) {
+    out -= 2;
+    copy2(out, digits2(static_cast<std::size_t>(significand % 100)));
+    significand /= 100;
+  }
+  if (floating_size % 2 != 0) {
+    *--out = static_cast<Char>('0' + significand % 10);
+    significand /= 10;
+  }
+  *--out = decimal_point;
+  format_decimal(out - integral_size, significand, integral_size);
+  return end;
+}
+
+template <typename OutputIt, typename UInt, typename Char,
+          FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<OutputIt>>::value)>
+inline auto write_significand(OutputIt out, UInt significand,
+                              int significand_size, int integral_size,
+                              Char decimal_point) -> OutputIt {
+  // Buffer is large enough to hold digits (digits10 + 1) and a decimal point.
+  Char buffer[digits10<UInt>() + 2];
+  auto end = write_significand(buffer, significand, significand_size,
+                               integral_size, decimal_point);
+  return detail::copy_str_noinline<Char>(buffer, end, out);
+}
+
+template <typename OutputIt, typename Char>
+FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand,
+                                     int significand_size, int integral_size,
+                                     Char decimal_point) -> OutputIt {
+  out = detail::copy_str_noinline<Char>(significand,
+                                        significand + integral_size, out);
+  if (!decimal_point) return out;
+  *out++ = decimal_point;
+  return detail::copy_str_noinline<Char>(significand + integral_size,
+                                         significand + significand_size, out);
+}
+
+template <typename OutputIt, typename Char, typename T, typename Grouping>
+FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
+                                       int significand_size, int integral_size,
+                                       Char decimal_point,
+                                       const Grouping& grouping) -> OutputIt {
+  if (!grouping.separator()) {
+    return write_significand(out, significand, significand_size, integral_size,
+                             decimal_point);
+  }
+  auto buffer = basic_memory_buffer<Char>();
+  write_significand(buffer_appender<Char>(buffer), significand,
+                    significand_size, integral_size, decimal_point);
+  grouping.apply(
+      out, basic_string_view<Char>(buffer.data(), to_unsigned(integral_size)));
+  return detail::copy_str_noinline<Char>(buffer.data() + integral_size,
+                                         buffer.end(), out);
+}
+
+template <typename OutputIt, typename DecimalFP, typename Char,
+          typename Grouping = digit_grouping<Char>>
+FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
+                                    const basic_format_specs<Char>& specs,
+                                    float_specs fspecs, locale_ref loc)
+    -> OutputIt {
+  auto significand = f.significand;
+  int significand_size = get_significand_size(f);
+  const Char zero = static_cast<Char>('0');
+  auto sign = fspecs.sign;
+  size_t size = to_unsigned(significand_size) + (sign ? 1 : 0);
+  using iterator = reserve_iterator<OutputIt>;
+
+  Char decimal_point =
+      fspecs.locale ? detail::decimal_point<Char>(loc) : static_cast<Char>('.');
+
+  int output_exp = f.exponent + significand_size - 1;
+  auto use_exp_format = [=]() {
+    if (fspecs.format == float_format::exp) return true;
+    if (fspecs.format != float_format::general) return false;
+    // Use the fixed notation if the exponent is in [exp_lower, exp_upper),
+    // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation.
+    const int exp_lower = -4, exp_upper = 16;
+    return output_exp < exp_lower ||
+           output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper);
+  };
+  if (use_exp_format()) {
+    int num_zeros = 0;
+    if (fspecs.showpoint) {
+      num_zeros = fspecs.precision - significand_size;
+      if (num_zeros < 0) num_zeros = 0;
+      size += to_unsigned(num_zeros);
+    } else if (significand_size == 1) {
+      decimal_point = Char();
+    }
+    auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp;
+    int exp_digits = 2;
+    if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3;
+
+    size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits);
+    char exp_char = fspecs.upper ? 'E' : 'e';
+    auto write = [=](iterator it) {
+      if (sign) *it++ = detail::sign<Char>(sign);
+      // Insert a decimal point after the first digit and add an exponent.
+      it = write_significand(it, significand, significand_size, 1,
+                             decimal_point);
+      if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero);
+      *it++ = static_cast<Char>(exp_char);
+      return write_exponent<Char>(output_exp, it);
+    };
+    return specs.width > 0 ? write_padded<align::right>(out, specs, size, write)
+                           : base_iterator(out, write(reserve(out, size)));
+  }
+
+  int exp = f.exponent + significand_size;
+  if (f.exponent >= 0) {
+    // 1234e5 -> 123400000[.0+]
+    size += to_unsigned(f.exponent);
+    int num_zeros = fspecs.precision - exp;
+    abort_fuzzing_if(num_zeros > 5000);
+    if (fspecs.showpoint) {
+      ++size;
+      if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1;
+      if (num_zeros > 0) size += to_unsigned(num_zeros);
+    }
+    auto grouping = Grouping(loc, fspecs.locale);
+    size += to_unsigned(grouping.count_separators(exp));
+    return write_padded<align::right>(out, specs, size, [&](iterator it) {
+      if (sign) *it++ = detail::sign<Char>(sign);
+      it = write_significand<Char>(it, significand, significand_size,
+                                   f.exponent, grouping);
+      if (!fspecs.showpoint) return it;
+      *it++ = decimal_point;
+      return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
+    });
+  } else if (exp > 0) {
+    // 1234e-2 -> 12.34[0+]
+    int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0;
+    size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0);
+    auto grouping = Grouping(loc, fspecs.locale);
+    size += to_unsigned(grouping.count_separators(significand_size));
+    return write_padded<align::right>(out, specs, size, [&](iterator it) {
+      if (sign) *it++ = detail::sign<Char>(sign);
+      it = write_significand(it, significand, significand_size, exp,
+                             decimal_point, grouping);
+      return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
+    });
+  }
+  // 1234e-6 -> 0.001234
+  int num_zeros = -exp;
+  if (significand_size == 0 && fspecs.precision >= 0 &&
+      fspecs.precision < num_zeros) {
+    num_zeros = fspecs.precision;
+  }
+  bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint;
+  size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros);
+  return write_padded<align::right>(out, specs, size, [&](iterator it) {
+    if (sign) *it++ = detail::sign<Char>(sign);
+    *it++ = zero;
+    if (!pointy) return it;
+    *it++ = decimal_point;
+    it = detail::fill_n(it, num_zeros, zero);
+    return write_significand<Char>(it, significand, significand_size);
+  });
+}
+
+template <typename Char> class fallback_digit_grouping {
+ public:
+  constexpr fallback_digit_grouping(locale_ref, bool) {}
+
+  constexpr Char separator() const { return Char(); }
+
+  constexpr int count_separators(int) const { return 0; }
+
+  template <typename Out, typename C>
+  constexpr Out apply(Out out, basic_string_view<C>) const {
+    return out;
+  }
+};
+
+template <typename OutputIt, typename DecimalFP, typename Char>
+FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f,
+                                 const basic_format_specs<Char>& specs,
+                                 float_specs fspecs, locale_ref loc)
+    -> OutputIt {
+  if (is_constant_evaluated()) {
+    return do_write_float<OutputIt, DecimalFP, Char,
+                          fallback_digit_grouping<Char>>(out, f, specs, fspecs,
+                                                         loc);
+  } else {
+    return do_write_float(out, f, specs, fspecs, loc);
+  }
+}
+
+template <typename T> constexpr bool isnan(T value) {
+  return !(value >= value);  // std::isnan doesn't support __float128.
+}
+
+template <typename T, typename Enable = void>
+struct has_isfinite : std::false_type {};
+
+template <typename T>
+struct has_isfinite<T, enable_if_t<sizeof(std::isfinite(T())) != 0>>
+    : std::true_type {};
+
+template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value&&
+                                        has_isfinite<T>::value)>
+FMT_CONSTEXPR20 bool isfinite(T value) {
+  constexpr T inf = T(std::numeric_limits<double>::infinity());
+  if (is_constant_evaluated())
+    return !detail::isnan(value) && value != inf && value != -inf;
+  return std::isfinite(value);
+}
+template <typename T, FMT_ENABLE_IF(!has_isfinite<T>::value)>
+FMT_CONSTEXPR bool isfinite(T value) {
+  T inf = T(std::numeric_limits<double>::infinity());
+  // std::isfinite doesn't support __float128.
+  return !detail::isnan(value) && value != inf && value != -inf;
+}
+
+template <typename T, FMT_ENABLE_IF(is_floating_point<T>::value)>
+FMT_INLINE FMT_CONSTEXPR bool signbit(T value) {
+  if (is_constant_evaluated()) {
+#ifdef __cpp_if_constexpr
+    if constexpr (std::numeric_limits<double>::is_iec559) {
+      auto bits = detail::bit_cast<uint64_t>(static_cast<double>(value));
+      return (bits >> (num_bits<uint64_t>() - 1)) != 0;
+    }
+#endif
+  }
+  return std::signbit(static_cast<double>(value));
+}
+
+enum class round_direction { unknown, up, down };
+
+// Given the divisor (normally a power of 10), the remainder = v % divisor for
+// some number v and the error, returns whether v should be rounded up, down, or
+// whether the rounding direction can't be determined due to error.
+// error should be less than divisor / 2.
+FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor,
+                                                         uint64_t remainder,
+                                                         uint64_t error) {
+  FMT_ASSERT(remainder < divisor, "");  // divisor - remainder won't overflow.
+  FMT_ASSERT(error < divisor, "");      // divisor - error won't overflow.
+  FMT_ASSERT(error < divisor - error, "");  // error * 2 won't overflow.
+  // Round down if (remainder + error) * 2 <= divisor.
+  if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
+    return round_direction::down;
+  // Round up if (remainder - error) * 2 >= divisor.
+  if (remainder >= error &&
+      remainder - error >= divisor - (remainder - error)) {
+    return round_direction::up;
+  }
+  return round_direction::unknown;
+}
+
+namespace digits {
+enum result {
+  more,  // Generate more digits.
+  done,  // Done generating digits.
+  error  // Digit generation cancelled due to an error.
+};
+}
+
+struct gen_digits_handler {
+  char* buf;
+  int size;
+  int precision;
+  int exp10;
+  bool fixed;
+
+  FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor,
+                                        uint64_t remainder, uint64_t error,
+                                        bool integral) {
+    FMT_ASSERT(remainder < divisor, "");
+    buf[size++] = digit;
+    if (!integral && error >= remainder) return digits::error;
+    if (size < precision) return digits::more;
+    if (!integral) {
+      // Check if error * 2 < divisor with overflow prevention.
+      // The check is not needed for the integral part because error = 1
+      // and divisor > (1 << 32) there.
+      if (error >= divisor || error >= divisor - error) return digits::error;
+    } else {
+      FMT_ASSERT(error == 1 && divisor > 2, "");
+    }
+    auto dir = get_round_direction(divisor, remainder, error);
+    if (dir != round_direction::up)
+      return dir == round_direction::down ? digits::done : digits::error;
+    ++buf[size - 1];
+    for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
+      buf[i] = '0';
+      ++buf[i - 1];
+    }
+    if (buf[0] > '9') {
+      buf[0] = '1';
+      if (fixed)
+        buf[size++] = '0';
+      else
+        ++exp10;
+    }
+    return digits::done;
+  }
+};
+
+inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) {
+  // Adjust fixed precision by exponent because it is relative to decimal
+  // point.
+  if (exp10 > 0 && precision > max_value<int>() - exp10)
+    FMT_THROW(format_error("number is too big"));
+  precision += exp10;
+}
+
+// Generates output using the Grisu digit-gen algorithm.
+// error: the size of the region (lower, upper) outside of which numbers
+// definitely do not round to value (Delta in Grisu3).
+FMT_INLINE FMT_CONSTEXPR20 auto grisu_gen_digits(fp value, uint64_t error,
+                                                 int& exp,
+                                                 gen_digits_handler& handler)
+    -> digits::result {
+  const fp one(1ULL << -value.e, value.e);
+  // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
+  // zero because it contains a product of two 64-bit numbers with MSB set (due
+  // to normalization) - 1, shifted right by at most 60 bits.
+  auto integral = static_cast<uint32_t>(value.f >> -one.e);
+  FMT_ASSERT(integral != 0, "");
+  FMT_ASSERT(integral == value.f >> -one.e, "");
+  // The fractional part of scaled value (p2 in Grisu) c = value % one.
+  uint64_t fractional = value.f & (one.f - 1);
+  exp = count_digits(integral);  // kappa in Grisu.
+  // Non-fixed formats require at least one digit and no precision adjustment.
+  if (handler.fixed) {
+    adjust_precision(handler.precision, exp + handler.exp10);
+    // Check if precision is satisfied just by leading zeros, e.g.
+    // format("{:.2f}", 0.001) gives "0.00" without generating any digits.
+    if (handler.precision <= 0) {
+      if (handler.precision < 0) return digits::done;
+      // Divide by 10 to prevent overflow.
+      uint64_t divisor = data::power_of_10_64[exp - 1] << -one.e;
+      auto dir = get_round_direction(divisor, value.f / 10, error * 10);
+      if (dir == round_direction::unknown) return digits::error;
+      handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0';
+      return digits::done;
+    }
+  }
+  // Generate digits for the integral part. This can produce up to 10 digits.
+  do {
+    uint32_t digit = 0;
+    auto divmod_integral = [&](uint32_t divisor) {
+      digit = integral / divisor;
+      integral %= divisor;
+    };
+    // This optimization by Milo Yip reduces the number of integer divisions by
+    // one per iteration.
+    switch (exp) {
+    case 10:
+      divmod_integral(1000000000);
+      break;
+    case 9:
+      divmod_integral(100000000);
+      break;
+    case 8:
+      divmod_integral(10000000);
+      break;
+    case 7:
+      divmod_integral(1000000);
+      break;
+    case 6:
+      divmod_integral(100000);
+      break;
+    case 5:
+      divmod_integral(10000);
+      break;
+    case 4:
+      divmod_integral(1000);
+      break;
+    case 3:
+      divmod_integral(100);
+      break;
+    case 2:
+      divmod_integral(10);
+      break;
+    case 1:
+      digit = integral;
+      integral = 0;
+      break;
+    default:
+      FMT_ASSERT(false, "invalid number of digits");
+    }
+    --exp;
+    auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
+    auto result = handler.on_digit(static_cast<char>('0' + digit),
+                                   data::power_of_10_64[exp] << -one.e,
+                                   remainder, error, true);
+    if (result != digits::more) return result;
+  } while (exp > 0);
+  // Generate digits for the fractional part.
+  for (;;) {
+    fractional *= 10;
+    error *= 10;
+    char digit = static_cast<char>('0' + (fractional >> -one.e));
+    fractional &= one.f - 1;
+    --exp;
+    auto result = handler.on_digit(digit, one.f, fractional, error, false);
+    if (result != digits::more) return result;
+  }
+}
+
+class bigint {
+ private:
+  // A bigint is stored as an array of bigits (big digits), with bigit at index
+  // 0 being the least significant one.
+  using bigit = uint32_t;
+  using double_bigit = uint64_t;
+  enum { bigits_capacity = 32 };
+  basic_memory_buffer<bigit, bigits_capacity> bigits_;
+  int exp_;
+
+  FMT_CONSTEXPR20 bigit operator[](int index) const {
+    return bigits_[to_unsigned(index)];
+  }
+  FMT_CONSTEXPR20 bigit& operator[](int index) {
+    return bigits_[to_unsigned(index)];
+  }
+
+  static constexpr const int bigit_bits = num_bits<bigit>();
+
+  friend struct formatter<bigint>;
+
+  FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) {
+    auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
+    (*this)[index] = static_cast<bigit>(result);
+    borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
+  }
+
+  FMT_CONSTEXPR20 void remove_leading_zeros() {
+    int num_bigits = static_cast<int>(bigits_.size()) - 1;
+    while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
+    bigits_.resize(to_unsigned(num_bigits + 1));
+  }
+
+  // Computes *this -= other assuming aligned bigints and *this >= other.
+  FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) {
+    FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
+    FMT_ASSERT(compare(*this, other) >= 0, "");
+    bigit borrow = 0;
+    int i = other.exp_ - exp_;
+    for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j)
+      subtract_bigits(i, other.bigits_[j], borrow);
+    while (borrow > 0) subtract_bigits(i, 0, borrow);
+    remove_leading_zeros();
+  }
+
+  FMT_CONSTEXPR20 void multiply(uint32_t value) {
+    const double_bigit wide_value = value;
+    bigit carry = 0;
+    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+      double_bigit result = bigits_[i] * wide_value + carry;
+      bigits_[i] = static_cast<bigit>(result);
+      carry = static_cast<bigit>(result >> bigit_bits);
+    }
+    if (carry != 0) bigits_.push_back(carry);
+  }
+
+  template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
+                                         std::is_same<UInt, uint128_t>::value)>
+  FMT_CONSTEXPR20 void multiply(UInt value) {
+    using half_uint =
+        conditional_t<std::is_same<UInt, uint128_t>::value, uint64_t, uint32_t>;
+    const int shift = num_bits<half_uint>() - bigit_bits;
+    const UInt lower = static_cast<half_uint>(value);
+    const UInt upper = value >> num_bits<half_uint>();
+    UInt carry = 0;
+    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+      UInt result = lower * bigits_[i] + static_cast<bigit>(carry);
+      carry = (upper * bigits_[i] << shift) + (result >> bigit_bits) +
+              (carry >> bigit_bits);
+      bigits_[i] = static_cast<bigit>(result);
+    }
+    while (carry != 0) {
+      bigits_.push_back(static_cast<bigit>(carry));
+      carry >>= bigit_bits;
+    }
+  }
+
+  template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
+                                         std::is_same<UInt, uint128_t>::value)>
+  FMT_CONSTEXPR20 void assign(UInt n) {
+    size_t num_bigits = 0;
+    do {
+      bigits_[num_bigits++] = static_cast<bigit>(n);
+      n >>= bigit_bits;
+    } while (n != 0);
+    bigits_.resize(num_bigits);
+    exp_ = 0;
+  }
+
+ public:
+  FMT_CONSTEXPR20 bigint() : exp_(0) {}
+  explicit bigint(uint64_t n) { assign(n); }
+
+  bigint(const bigint&) = delete;
+  void operator=(const bigint&) = delete;
+
+  FMT_CONSTEXPR20 void assign(const bigint& other) {
+    auto size = other.bigits_.size();
+    bigits_.resize(size);
+    auto data = other.bigits_.data();
+    std::copy(data, data + size, make_checked(bigits_.data(), size));
+    exp_ = other.exp_;
+  }
+
+  template <typename Int> FMT_CONSTEXPR20 void operator=(Int n) {
+    FMT_ASSERT(n > 0, "");
+    assign(uint64_or_128_t<Int>(n));
+  }
+
+  FMT_CONSTEXPR20 int num_bigits() const {
+    return static_cast<int>(bigits_.size()) + exp_;
+  }
+
+  FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) {
+    FMT_ASSERT(shift >= 0, "");
+    exp_ += shift / bigit_bits;
+    shift %= bigit_bits;
+    if (shift == 0) return *this;
+    bigit carry = 0;
+    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
+      bigit c = bigits_[i] >> (bigit_bits - shift);
+      bigits_[i] = (bigits_[i] << shift) + carry;
+      carry = c;
+    }
+    if (carry != 0) bigits_.push_back(carry);
+    return *this;
+  }
+
+  template <typename Int> FMT_CONSTEXPR20 bigint& operator*=(Int value) {
+    FMT_ASSERT(value > 0, "");
+    multiply(uint32_or_64_or_128_t<Int>(value));
+    return *this;
+  }
+
+  friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) {
+    int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
+    if (num_lhs_bigits != num_rhs_bigits)
+      return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
+    int i = static_cast<int>(lhs.bigits_.size()) - 1;
+    int j = static_cast<int>(rhs.bigits_.size()) - 1;
+    int end = i - j;
+    if (end < 0) end = 0;
+    for (; i >= end; --i, --j) {
+      bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
+      if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
+    }
+    if (i != j) return i > j ? 1 : -1;
+    return 0;
+  }
+
+  // Returns compare(lhs1 + lhs2, rhs).
+  friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2,
+                                         const bigint& rhs) {
+    auto minimum = [](int a, int b) { return a < b ? a : b; };
+    auto maximum = [](int a, int b) { return a > b ? a : b; };
+    int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits());
+    int num_rhs_bigits = rhs.num_bigits();
+    if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
+    if (max_lhs_bigits > num_rhs_bigits) return 1;
+    auto get_bigit = [](const bigint& n, int i) -> bigit {
+      return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
+    };
+    double_bigit borrow = 0;
+    int min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_);
+    for (int i = num_rhs_bigits - 1; i >= min_exp; --i) {
+      double_bigit sum =
+          static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
+      bigit rhs_bigit = get_bigit(rhs, i);
+      if (sum > rhs_bigit + borrow) return 1;
+      borrow = rhs_bigit + borrow - sum;
+      if (borrow > 1) return -1;
+      borrow <<= bigit_bits;
+    }
+    return borrow != 0 ? -1 : 0;
+  }
+
+  // Assigns pow(10, exp) to this bigint.
+  FMT_CONSTEXPR20 void assign_pow10(int exp) {
+    FMT_ASSERT(exp >= 0, "");
+    if (exp == 0) return *this = 1;
+    // Find the top bit.
+    int bitmask = 1;
+    while (exp >= bitmask) bitmask <<= 1;
+    bitmask >>= 1;
+    // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
+    // repeated squaring and multiplication.
+    *this = 5;
+    bitmask >>= 1;
+    while (bitmask != 0) {
+      square();
+      if ((exp & bitmask) != 0) *this *= 5;
+      bitmask >>= 1;
+    }
+    *this <<= exp;  // Multiply by pow(2, exp) by shifting.
+  }
+
+  FMT_CONSTEXPR20 void square() {
+    int num_bigits = static_cast<int>(bigits_.size());
+    int num_result_bigits = 2 * num_bigits;
+    basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
+    bigits_.resize(to_unsigned(num_result_bigits));
+    auto sum = uint128_t();
+    for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
+      // Compute bigit at position bigit_index of the result by adding
+      // cross-product terms n[i] * n[j] such that i + j == bigit_index.
+      for (int i = 0, j = bigit_index; j >= 0; ++i, --j) {
+        // Most terms are multiplied twice which can be optimized in the future.
+        sum += static_cast<double_bigit>(n[i]) * n[j];
+      }
+      (*this)[bigit_index] = static_cast<bigit>(sum);
+      sum >>= num_bits<bigit>();  // Compute the carry.
+    }
+    // Do the same for the top half.
+    for (int bigit_index = num_bigits; bigit_index < num_result_bigits;
+         ++bigit_index) {
+      for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
+        sum += static_cast<double_bigit>(n[i++]) * n[j--];
+      (*this)[bigit_index] = static_cast<bigit>(sum);
+      sum >>= num_bits<bigit>();
+    }
+    remove_leading_zeros();
+    exp_ *= 2;
+  }
+
+  // If this bigint has a bigger exponent than other, adds trailing zero to make
+  // exponents equal. This simplifies some operations such as subtraction.
+  FMT_CONSTEXPR20 void align(const bigint& other) {
+    int exp_difference = exp_ - other.exp_;
+    if (exp_difference <= 0) return;
+    int num_bigits = static_cast<int>(bigits_.size());
+    bigits_.resize(to_unsigned(num_bigits + exp_difference));
+    for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
+      bigits_[j] = bigits_[i];
+    std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
+    exp_ -= exp_difference;
+  }
+
+  // Divides this bignum by divisor, assigning the remainder to this and
+  // returning the quotient.
+  FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) {
+    FMT_ASSERT(this != &divisor, "");
+    if (compare(*this, divisor) < 0) return 0;
+    FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
+    align(divisor);
+    int quotient = 0;
+    do {
+      subtract_aligned(divisor);
+      ++quotient;
+    } while (compare(*this, divisor) >= 0);
+    return quotient;
+  }
+};
+
+// format_dragon flags.
+enum dragon {
+  predecessor_closer = 1,
+  fixup = 2,  // Run fixup to correct exp10 which can be off by one.
+  fixed = 4,
+};
+
+// Formats a floating-point number using a variation of the Fixed-Precision
+// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
+// https://fmt.dev/papers/p372-steele.pdf.
+FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
+                                          unsigned flags, int num_digits,
+                                          buffer<char>& buf, int& exp10) {
+  bigint numerator;    // 2 * R in (FPP)^2.
+  bigint denominator;  // 2 * S in (FPP)^2.
+  // lower and upper are differences between value and corresponding boundaries.
+  bigint lower;             // (M^- in (FPP)^2).
+  bigint upper_store;       // upper's value if different from lower.
+  bigint* upper = nullptr;  // (M^+ in (FPP)^2).
+  // Shift numerator and denominator by an extra bit or two (if lower boundary
+  // is closer) to make lower and upper integers. This eliminates multiplication
+  // by 2 during later computations.
+  bool is_predecessor_closer = (flags & dragon::predecessor_closer) != 0;
+  int shift = is_predecessor_closer ? 2 : 1;
+  if (value.e >= 0) {
+    numerator = value.f;
+    numerator <<= value.e + shift;
+    lower = 1;
+    lower <<= value.e;
+    if (is_predecessor_closer) {
+      upper_store = 1;
+      upper_store <<= value.e + 1;
+      upper = &upper_store;
+    }
+    denominator.assign_pow10(exp10);
+    denominator <<= shift;
+  } else if (exp10 < 0) {
+    numerator.assign_pow10(-exp10);
+    lower.assign(numerator);
+    if (is_predecessor_closer) {
+      upper_store.assign(numerator);
+      upper_store <<= 1;
+      upper = &upper_store;
+    }
+    numerator *= value.f;
+    numerator <<= shift;
+    denominator = 1;
+    denominator <<= shift - value.e;
+  } else {
+    numerator = value.f;
+    numerator <<= shift;
+    denominator.assign_pow10(exp10);
+    denominator <<= shift - value.e;
+    lower = 1;
+    if (is_predecessor_closer) {
+      upper_store = 1ULL << 1;
+      upper = &upper_store;
+    }
+  }
+  int even = static_cast<int>((value.f & 1) == 0);
+  if (!upper) upper = &lower;
+  if ((flags & dragon::fixup) != 0) {
+    if (add_compare(numerator, *upper, denominator) + even <= 0) {
+      --exp10;
+      numerator *= 10;
+      if (num_digits < 0) {
+        lower *= 10;
+        if (upper != &lower) *upper *= 10;
+      }
+    }
+    if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1);
+  }
+  // Invariant: value == (numerator / denominator) * pow(10, exp10).
+  if (num_digits < 0) {
+    // Generate the shortest representation.
+    num_digits = 0;
+    char* data = buf.data();
+    for (;;) {
+      int digit = numerator.divmod_assign(denominator);
+      bool low = compare(numerator, lower) - even < 0;  // numerator <[=] lower.
+      // numerator + upper >[=] pow10:
+      bool high = add_compare(numerator, *upper, denominator) + even > 0;
+      data[num_digits++] = static_cast<char>('0' + digit);
+      if (low || high) {
+        if (!low) {
+          ++data[num_digits - 1];
+        } else if (high) {
+          int result = add_compare(numerator, numerator, denominator);
+          // Round half to even.
+          if (result > 0 || (result == 0 && (digit % 2) != 0))
+            ++data[num_digits - 1];
+        }
+        buf.try_resize(to_unsigned(num_digits));
+        exp10 -= num_digits - 1;
+        return;
+      }
+      numerator *= 10;
+      lower *= 10;
+      if (upper != &lower) *upper *= 10;
+    }
+  }
+  // Generate the given number of digits.
+  exp10 -= num_digits - 1;
+  if (num_digits == 0) {
+    denominator *= 10;
+    auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
+    buf.push_back(digit);
+    return;
+  }
+  buf.try_resize(to_unsigned(num_digits));
+  for (int i = 0; i < num_digits - 1; ++i) {
+    int digit = numerator.divmod_assign(denominator);
+    buf[i] = static_cast<char>('0' + digit);
+    numerator *= 10;
+  }
+  int digit = numerator.divmod_assign(denominator);
+  auto result = add_compare(numerator, numerator, denominator);
+  if (result > 0 || (result == 0 && (digit % 2) != 0)) {
+    if (digit == 9) {
+      const auto overflow = '0' + 10;
+      buf[num_digits - 1] = overflow;
+      // Propagate the carry.
+      for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) {
+        buf[i] = '0';
+        ++buf[i - 1];
+      }
+      if (buf[0] == overflow) {
+        buf[0] = '1';
+        ++exp10;
+      }
+      return;
+    }
+    ++digit;
+  }
+  buf[num_digits - 1] = static_cast<char>('0' + digit);
+}
+
+template <typename Float>
+FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
+                                  buffer<char>& buf) -> int {
+  // float is passed as double to reduce the number of instantiations.
+  static_assert(!std::is_same<Float, float>::value, "");
+  FMT_ASSERT(value >= 0, "value is negative");
+  auto converted_value = convert_float(value);
+
+  const bool fixed = specs.format == float_format::fixed;
+  if (value <= 0) {  // <= instead of == to silence a warning.
+    if (precision <= 0 || !fixed) {
+      buf.push_back('0');
+      return 0;
+    }
+    buf.try_resize(to_unsigned(precision));
+    fill_n(buf.data(), precision, '0');
+    return -precision;
+  }
+
+  int exp = 0;
+  bool use_dragon = true;
+  unsigned dragon_flags = 0;
+  if (!is_fast_float<Float>()) {
+    const auto inv_log2_10 = 0.3010299956639812;  // 1 / log2(10)
+    using info = dragonbox::float_info<decltype(converted_value)>;
+    const auto f = basic_fp<typename info::carrier_uint>(converted_value);
+    // Compute exp, an approximate power of 10, such that
+    //   10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1).
+    // This is based on log10(value) == log2(value) / log2(10) and approximation
+    // of log2(value) by e + num_fraction_bits idea from double-conversion.
+    exp = static_cast<int>(
+        std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10));
+    dragon_flags = dragon::fixup;
+  } else if (!is_constant_evaluated() && precision < 0) {
+    // Use Dragonbox for the shortest format.
+    if (specs.binary32) {
+      auto dec = dragonbox::to_decimal(static_cast<float>(value));
+      write<char>(buffer_appender<char>(buf), dec.significand);
+      return dec.exponent;
+    }
+    auto dec = dragonbox::to_decimal(static_cast<double>(value));
+    write<char>(buffer_appender<char>(buf), dec.significand);
+    return dec.exponent;
+  } else {
+    // Use Grisu + Dragon4 for the given precision:
+    // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
+    const int min_exp = -60;  // alpha in Grisu.
+    int cached_exp10 = 0;     // K in Grisu.
+    fp normalized = normalize(fp(converted_value));
+    const auto cached_pow = get_cached_power(
+        min_exp - (normalized.e + fp::num_significand_bits), cached_exp10);
+    normalized = normalized * cached_pow;
+    gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
+    if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error &&
+        !is_constant_evaluated()) {
+      exp += handler.exp10;
+      buf.try_resize(to_unsigned(handler.size));
+      use_dragon = false;
+    } else {
+      exp += handler.size - cached_exp10 - 1;
+      precision = handler.precision;
+    }
+  }
+  if (use_dragon) {
+    auto f = basic_fp<uint128_t>();
+    bool is_predecessor_closer = specs.binary32
+                                     ? f.assign(static_cast<float>(value))
+                                     : f.assign(converted_value);
+    if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer;
+    if (fixed) dragon_flags |= dragon::fixed;
+    // Limit precision to the maximum possible number of significant digits in
+    // an IEEE754 double because we don't need to generate zeros.
+    const int max_double_digits = 767;
+    if (precision > max_double_digits) precision = max_double_digits;
+    format_dragon(f, dragon_flags, precision, buf, exp);
+  }
+  if (!fixed && !specs.showpoint) {
+    // Remove trailing zeros.
+    auto num_digits = buf.size();
+    while (num_digits > 0 && buf[num_digits - 1] == '0') {
+      --num_digits;
+      ++exp;
+    }
+    buf.try_resize(num_digits);
+  }
+  return exp;
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_floating_point<T>::value)>
+FMT_CONSTEXPR20 auto write(OutputIt out, T value,
+                           basic_format_specs<Char> specs, locale_ref loc = {})
+    -> OutputIt {
+  if (const_check(!is_supported_floating_point(value))) return out;
+  float_specs fspecs = parse_float_type_spec(specs);
+  fspecs.sign = specs.sign;
+  if (detail::signbit(value)) {  // value < 0 is false for NaN so use signbit.
+    fspecs.sign = sign::minus;
+    value = -value;
+  } else if (fspecs.sign == sign::minus) {
+    fspecs.sign = sign::none;
+  }
+
+  if (!detail::isfinite(value))
+    return write_nonfinite(out, detail::isnan(value), specs, fspecs);
+
+  if (specs.align == align::numeric && fspecs.sign) {
+    auto it = reserve(out, 1);
+    *it++ = detail::sign<Char>(fspecs.sign);
+    out = base_iterator(out, it);
+    fspecs.sign = sign::none;
+    if (specs.width != 0) --specs.width;
+  }
+
+  memory_buffer buffer;
+  if (fspecs.format == float_format::hex) {
+    if (fspecs.sign) buffer.push_back(detail::sign<char>(fspecs.sign));
+    snprintf_float(convert_float(value), specs.precision, fspecs, buffer);
+    return write_bytes<align::right>(out, {buffer.data(), buffer.size()},
+                                     specs);
+  }
+  int precision = specs.precision >= 0 || specs.type == presentation_type::none
+                      ? specs.precision
+                      : 6;
+  if (fspecs.format == float_format::exp) {
+    if (precision == max_value<int>())
+      throw_format_error("number is too big");
+    else
+      ++precision;
+  } else if (fspecs.format != float_format::fixed && precision == 0) {
+    precision = 1;
+  }
+  if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
+  int exp = format_float(convert_float(value), precision, fspecs, buffer);
+  fspecs.precision = precision;
+  auto f = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
+  return write_float(out, f, specs, fspecs, loc);
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_fast_float<T>::value)>
+FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
+  if (is_constant_evaluated())
+    return write(out, value, basic_format_specs<Char>());
+  if (const_check(!is_supported_floating_point(value))) return out;
+
+  auto fspecs = float_specs();
+  if (detail::signbit(value)) {
+    fspecs.sign = sign::minus;
+    value = -value;
+  }
+
+  constexpr auto specs = basic_format_specs<Char>();
+  using floaty = conditional_t<std::is_same<T, long double>::value, double, T>;
+  using uint = typename dragonbox::float_info<floaty>::carrier_uint;
+  uint mask = exponent_mask<floaty>();
+  if ((bit_cast<uint>(value) & mask) == mask)
+    return write_nonfinite(out, std::isnan(value), specs, fspecs);
+
+  auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
+  return write_float(out, dec, specs, fspecs, {});
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_floating_point<T>::value &&
+                        !is_fast_float<T>::value)>
+inline auto write(OutputIt out, T value) -> OutputIt {
+  return write(out, value, basic_format_specs<Char>());
+}
+
+template <typename Char, typename OutputIt>
+auto write(OutputIt out, monostate, basic_format_specs<Char> = {},
+           locale_ref = {}) -> OutputIt {
+  FMT_ASSERT(false, "");
+  return out;
+}
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> value)
+    -> OutputIt {
+  auto it = reserve(out, value.size());
+  it = copy_str_noinline<Char>(value.begin(), value.end(), it);
+  return base_iterator(out, it);
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(is_string<T>::value)>
+constexpr auto write(OutputIt out, const T& value) -> OutputIt {
+  return write<Char>(out, to_string_view(value));
+}
+
+// FMT_ENABLE_IF() condition separated to workaround an MSVC bug.
+template <
+    typename Char, typename OutputIt, typename T,
+    bool check =
+        std::is_enum<T>::value && !std::is_same<T, Char>::value &&
+        mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value !=
+            type::custom_type,
+    FMT_ENABLE_IF(check)>
+FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
+  return write<Char>(out, static_cast<underlying_t<T>>(value));
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(std::is_same<T, bool>::value)>
+FMT_CONSTEXPR auto write(OutputIt out, T value,
+                         const basic_format_specs<Char>& specs = {},
+                         locale_ref = {}) -> OutputIt {
+  return specs.type != presentation_type::none &&
+                 specs.type != presentation_type::string
+             ? write(out, value ? 1 : 0, specs, {})
+             : write_bytes(out, value ? "true" : "false", specs);
+}
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt {
+  auto it = reserve(out, 1);
+  *it++ = value;
+  return base_iterator(out, it);
+}
+
+template <typename Char, typename OutputIt>
+FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value)
+    -> OutputIt {
+  if (!value) {
+    throw_format_error("string pointer is null");
+  } else {
+    out = write(out, basic_string_view<Char>(value));
+  }
+  return out;
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(std::is_same<T, void>::value)>
+auto write(OutputIt out, const T* value,
+           const basic_format_specs<Char>& specs = {}, locale_ref = {})
+    -> OutputIt {
+  check_pointer_type_spec(specs.type, error_handler());
+  return write_ptr<Char>(out, bit_cast<uintptr_t>(value), &specs);
+}
+
+// A write overload that handles implicit conversions.
+template <typename Char, typename OutputIt, typename T,
+          typename Context = basic_format_context<OutputIt, Char>>
+FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t<
+    std::is_class<T>::value && !is_string<T>::value &&
+        !is_floating_point<T>::value && !std::is_same<T, Char>::value &&
+        !std::is_same<const T&,
+                      decltype(arg_mapper<Context>().map(value))>::value,
+    OutputIt> {
+  return write<Char>(out, arg_mapper<Context>().map(value));
+}
+
+template <typename Char, typename OutputIt, typename T,
+          typename Context = basic_format_context<OutputIt, Char>>
+FMT_CONSTEXPR auto write(OutputIt out, const T& value)
+    -> enable_if_t<mapped_type_constant<T, Context>::value == type::custom_type,
+                   OutputIt> {
+  using formatter_type =
+      conditional_t<has_formatter<T, Context>::value,
+                    typename Context::template formatter_type<T>,
+                    fallback_formatter<T, Char>>;
+  auto ctx = Context(out, {}, {});
+  return formatter_type().format(value, ctx);
+}
+
+// An argument visitor that formats the argument and writes it via the output
+// iterator. It's a class and not a generic lambda for compatibility with C++11.
+template <typename Char> struct default_arg_formatter {
+  using iterator = buffer_appender<Char>;
+  using context = buffer_context<Char>;
+
+  iterator out;
+  basic_format_args<context> args;
+  locale_ref loc;
+
+  template <typename T> auto operator()(T value) -> iterator {
+    return write<Char>(out, value);
+  }
+  auto operator()(typename basic_format_arg<context>::handle h) -> iterator {
+    basic_format_parse_context<Char> parse_ctx({});
+    context format_ctx(out, args, loc);
+    h.format(parse_ctx, format_ctx);
+    return format_ctx.out();
+  }
+};
+
+template <typename Char> struct arg_formatter {
+  using iterator = buffer_appender<Char>;
+  using context = buffer_context<Char>;
+
+  iterator out;
+  const basic_format_specs<Char>& specs;
+  locale_ref locale;
+
+  template <typename T>
+  FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator {
+    return detail::write(out, value, specs, locale);
+  }
+  auto operator()(typename basic_format_arg<context>::handle) -> iterator {
+    // User-defined types are handled separately because they require access
+    // to the parse context.
+    return out;
+  }
+};
+
+template <typename Char> struct custom_formatter {
+  basic_format_parse_context<Char>& parse_ctx;
+  buffer_context<Char>& ctx;
+
+  void operator()(
+      typename basic_format_arg<buffer_context<Char>>::handle h) const {
+    h.format(parse_ctx, ctx);
+  }
+  template <typename T> void operator()(T) const {}
+};
+
+template <typename T>
+using is_integer =
+    bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
+                  !std::is_same<T, char>::value &&
+                  !std::is_same<T, wchar_t>::value>;
+
+template <typename ErrorHandler> class width_checker {
+ public:
+  explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
+
+  template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
+  FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
+    if (is_negative(value)) handler_.on_error("negative width");
+    return static_cast<unsigned long long>(value);
+  }
+
+  template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
+  FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
+    handler_.on_error("width is not integer");
+    return 0;
+  }
+
+ private:
+  ErrorHandler& handler_;
+};
+
+template <typename ErrorHandler> class precision_checker {
+ public:
+  explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {}
+
+  template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
+  FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
+    if (is_negative(value)) handler_.on_error("negative precision");
+    return static_cast<unsigned long long>(value);
+  }
+
+  template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
+  FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
+    handler_.on_error("precision is not integer");
+    return 0;
+  }
+
+ private:
+  ErrorHandler& handler_;
+};
+
+template <template <typename> class Handler, typename FormatArg,
+          typename ErrorHandler>
+FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg, ErrorHandler eh) -> int {
+  unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
+  if (value > to_unsigned(max_value<int>())) eh.on_error("number is too big");
+  return static_cast<int>(value);
+}
+
+template <typename Context, typename ID>
+FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) ->
+    typename Context::format_arg {
+  auto arg = ctx.arg(id);
+  if (!arg) ctx.on_error("argument not found");
+  return arg;
+}
+
+// The standard format specifier handler with checking.
+template <typename Char> class specs_handler : public specs_setter<Char> {
+ private:
+  basic_format_parse_context<Char>& parse_context_;
+  buffer_context<Char>& context_;
+
+  // This is only needed for compatibility with gcc 4.4.
+  using format_arg = basic_format_arg<buffer_context<Char>>;
+
+  FMT_CONSTEXPR auto get_arg(auto_id) -> format_arg {
+    return detail::get_arg(context_, parse_context_.next_arg_id());
+  }
+
+  FMT_CONSTEXPR auto get_arg(int arg_id) -> format_arg {
+    parse_context_.check_arg_id(arg_id);
+    return detail::get_arg(context_, arg_id);
+  }
+
+  FMT_CONSTEXPR auto get_arg(basic_string_view<Char> arg_id) -> format_arg {
+    parse_context_.check_arg_id(arg_id);
+    return detail::get_arg(context_, arg_id);
+  }
+
+ public:
+  FMT_CONSTEXPR specs_handler(basic_format_specs<Char>& specs,
+                              basic_format_parse_context<Char>& parse_ctx,
+                              buffer_context<Char>& ctx)
+      : specs_setter<Char>(specs), parse_context_(parse_ctx), context_(ctx) {}
+
+  template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
+    this->specs_.width = get_dynamic_spec<width_checker>(
+        get_arg(arg_id), context_.error_handler());
+  }
+
+  template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
+    this->specs_.precision = get_dynamic_spec<precision_checker>(
+        get_arg(arg_id), context_.error_handler());
+  }
+
+  void on_error(const char* message) { context_.on_error(message); }
+};
+
+template <template <typename> class Handler, typename Context>
+FMT_CONSTEXPR void handle_dynamic_spec(int& value,
+                                       arg_ref<typename Context::char_type> ref,
+                                       Context& ctx) {
+  switch (ref.kind) {
+  case arg_id_kind::none:
+    break;
+  case arg_id_kind::index:
+    value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
+                                              ctx.error_handler());
+    break;
+  case arg_id_kind::name:
+    value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
+                                              ctx.error_handler());
+    break;
+  }
+}
+
+#if FMT_USE_USER_DEFINED_LITERALS
+template <typename Char> struct udl_formatter {
+  basic_string_view<Char> str;
+
+  template <typename... T>
+  auto operator()(T&&... args) const -> std::basic_string<Char> {
+    return vformat(str, fmt::make_format_args<buffer_context<Char>>(args...));
+  }
+};
+
+#  if FMT_USE_NONTYPE_TEMPLATE_ARGS
+template <typename T, typename Char, size_t N,
+          fmt::detail_exported::fixed_string<Char, N> Str>
+struct statically_named_arg : view {
+  static constexpr auto name = Str.data;
+
+  const T& value;
+  statically_named_arg(const T& v) : value(v) {}
+};
+
+template <typename T, typename Char, size_t N,
+          fmt::detail_exported::fixed_string<Char, N> Str>
+struct is_named_arg<statically_named_arg<T, Char, N, Str>> : std::true_type {};
+
+template <typename T, typename Char, size_t N,
+          fmt::detail_exported::fixed_string<Char, N> Str>
+struct is_statically_named_arg<statically_named_arg<T, Char, N, Str>>
+    : std::true_type {};
+
+template <typename Char, size_t N,
+          fmt::detail_exported::fixed_string<Char, N> Str>
+struct udl_arg {
+  template <typename T> auto operator=(T&& value) const {
+    return statically_named_arg<T, Char, N, Str>(std::forward<T>(value));
+  }
+};
+#  else
+template <typename Char> struct udl_arg {
+  const Char* str;
+
+  template <typename T> auto operator=(T&& value) const -> named_arg<Char, T> {
+    return {str, std::forward<T>(value)};
+  }
+};
+#  endif
+#endif  // FMT_USE_USER_DEFINED_LITERALS
+
+template <typename Locale, typename Char>
+auto vformat(const Locale& loc, basic_string_view<Char> format_str,
+             basic_format_args<buffer_context<type_identity_t<Char>>> args)
+    -> std::basic_string<Char> {
+  basic_memory_buffer<Char> buffer;
+  detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
+  return {buffer.data(), buffer.size()};
+}
+
+using format_func = void (*)(detail::buffer<char>&, int, const char*);
+
+FMT_API void format_error_code(buffer<char>& out, int error_code,
+                               string_view message) noexcept;
+
+FMT_API void report_error(format_func func, int error_code,
+                          const char* message) noexcept;
+FMT_END_DETAIL_NAMESPACE
+
+FMT_API auto vsystem_error(int error_code, string_view format_str,
+                           format_args args) -> std::system_error;
+
+/**
+ \rst
+ Constructs :class:`std::system_error` with a message formatted with
+ ``fmt::format(fmt, args...)``.
+  *error_code* is a system error code as given by ``errno``.
+
+ **Example**::
+
+   // This throws std::system_error with the description
+   //   cannot open file 'madeup': No such file or directory
+   // or similar (system message may vary).
+   const char* filename = "madeup";
+   std::FILE* file = std::fopen(filename, "r");
+   if (!file)
+     throw fmt::system_error(errno, "cannot open file '{}'", filename);
+ \endrst
+*/
+template <typename... T>
+auto system_error(int error_code, format_string<T...> fmt, T&&... args)
+    -> std::system_error {
+  return vsystem_error(error_code, fmt, fmt::make_format_args(args...));
+}
+
+/**
+  \rst
+  Formats an error message for an error returned by an operating system or a
+  language runtime, for example a file opening error, and writes it to *out*.
+  The format is the same as the one used by ``std::system_error(ec, message)``
+  where ``ec`` is ``std::error_code(error_code, std::generic_category()})``.
+  It is implementation-defined but normally looks like:
+
+  .. parsed-literal::
+     *<message>*: *<system-message>*
+
+  where *<message>* is the passed message and *<system-message>* is the system
+  message corresponding to the error code.
+  *error_code* is a system error code as given by ``errno``.
+  \endrst
+ */
+FMT_API void format_system_error(detail::buffer<char>& out, int error_code,
+                                 const char* message) noexcept;
+
+// Reports a system error without throwing an exception.
+// Can be used to report errors from destructors.
+FMT_API void report_system_error(int error_code, const char* message) noexcept;
+
+/** Fast integer formatter. */
+class format_int {
+ private:
+  // Buffer should be large enough to hold all digits (digits10 + 1),
+  // a sign and a null character.
+  enum { buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3 };
+  mutable char buffer_[buffer_size];
+  char* str_;
+
+  template <typename UInt> auto format_unsigned(UInt value) -> char* {
+    auto n = static_cast<detail::uint32_or_64_or_128_t<UInt>>(value);
+    return detail::format_decimal(buffer_, n, buffer_size - 1).begin;
+  }
+
+  template <typename Int> auto format_signed(Int value) -> char* {
+    auto abs_value = static_cast<detail::uint32_or_64_or_128_t<Int>>(value);
+    bool negative = value < 0;
+    if (negative) abs_value = 0 - abs_value;
+    auto begin = format_unsigned(abs_value);
+    if (negative) *--begin = '-';
+    return begin;
+  }
+
+ public:
+  explicit format_int(int value) : str_(format_signed(value)) {}
+  explicit format_int(long value) : str_(format_signed(value)) {}
+  explicit format_int(long long value) : str_(format_signed(value)) {}
+  explicit format_int(unsigned value) : str_(format_unsigned(value)) {}
+  explicit format_int(unsigned long value) : str_(format_unsigned(value)) {}
+  explicit format_int(unsigned long long value)
+      : str_(format_unsigned(value)) {}
+
+  /** Returns the number of characters written to the output buffer. */
+  auto size() const -> size_t {
+    return detail::to_unsigned(buffer_ - str_ + buffer_size - 1);
+  }
+
+  /**
+    Returns a pointer to the output buffer content. No terminating null
+    character is appended.
+   */
+  auto data() const -> const char* { return str_; }
+
+  /**
+    Returns a pointer to the output buffer content with terminating null
+    character appended.
+   */
+  auto c_str() const -> const char* {
+    buffer_[buffer_size - 1] = '\0';
+    return str_;
+  }
+
+  /**
+    \rst
+    Returns the content of the output buffer as an ``std::string``.
+    \endrst
+   */
+  auto str() const -> std::string { return std::string(str_, size()); }
+};
+
+template <typename T, typename Char>
+template <typename FormatContext>
+FMT_CONSTEXPR FMT_INLINE auto
+formatter<T, Char,
+          enable_if_t<detail::type_constant<T, Char>::value !=
+                      detail::type::custom_type>>::format(const T& val,
+                                                          FormatContext& ctx)
+    const -> decltype(ctx.out()) {
+  if (specs_.width_ref.kind != detail::arg_id_kind::none ||
+      specs_.precision_ref.kind != detail::arg_id_kind::none) {
+    auto specs = specs_;
+    detail::handle_dynamic_spec<detail::width_checker>(specs.width,
+                                                       specs.width_ref, ctx);
+    detail::handle_dynamic_spec<detail::precision_checker>(
+        specs.precision, specs.precision_ref, ctx);
+    return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
+  }
+  return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
+}
+
+template <typename Char>
+struct formatter<void*, Char> : formatter<const void*, Char> {
+  template <typename FormatContext>
+  auto format(void* val, FormatContext& ctx) const -> decltype(ctx.out()) {
+    return formatter<const void*, Char>::format(val, ctx);
+  }
+};
+
+template <typename Char, size_t N>
+struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {
+  template <typename FormatContext>
+  FMT_CONSTEXPR auto format(const Char* val, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    return formatter<basic_string_view<Char>, Char>::format(val, ctx);
+  }
+};
+
+// A formatter for types known only at run time such as variant alternatives.
+//
+// Usage:
+//   using variant = std::variant<int, std::string>;
+//   template <>
+//   struct formatter<variant>: dynamic_formatter<> {
+//     auto format(const variant& v, format_context& ctx) {
+//       return visit([&](const auto& val) {
+//           return dynamic_formatter<>::format(val, ctx);
+//       }, v);
+//     }
+//   };
+template <typename Char = char> class dynamic_formatter {
+ private:
+  detail::dynamic_format_specs<Char> specs_;
+  const Char* format_str_;
+
+  struct null_handler : detail::error_handler {
+    void on_align(align_t) {}
+    void on_sign(sign_t) {}
+    void on_hash() {}
+  };
+
+  template <typename Context> void handle_specs(Context& ctx) {
+    detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
+                                                       specs_.width_ref, ctx);
+    detail::handle_dynamic_spec<detail::precision_checker>(
+        specs_.precision, specs_.precision_ref, ctx);
+  }
+
+ public:
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    format_str_ = ctx.begin();
+    // Checks are deferred to formatting time when the argument type is known.
+    detail::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
+    return detail::parse_format_specs(ctx.begin(), ctx.end(), handler);
+  }
+
+  template <typename T, typename FormatContext>
+  auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
+    handle_specs(ctx);
+    detail::specs_checker<null_handler> checker(
+        null_handler(), detail::mapped_type_constant<T, FormatContext>::value);
+    checker.on_align(specs_.align);
+    if (specs_.sign != sign::none) checker.on_sign(specs_.sign);
+    if (specs_.alt) checker.on_hash();
+    if (specs_.precision >= 0) checker.end_precision();
+    return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
+  }
+};
+
+/**
+  \rst
+  Converts ``p`` to ``const void*`` for pointer formatting.
+
+  **Example**::
+
+    auto s = fmt::format("{}", fmt::ptr(p));
+  \endrst
+ */
+template <typename T> auto ptr(T p) -> const void* {
+  static_assert(std::is_pointer<T>::value, "");
+  return detail::bit_cast<const void*>(p);
+}
+template <typename T> auto ptr(const std::unique_ptr<T>& p) -> const void* {
+  return p.get();
+}
+template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
+  return p.get();
+}
+
+/**
+  \rst
+  Converts ``e`` to the underlying type.
+
+  **Example**::
+
+    enum class color { red, green, blue };
+    auto s = fmt::format("{}", fmt::underlying(color::red));
+  \endrst
+ */
+template <typename Enum>
+constexpr auto underlying(Enum e) noexcept -> underlying_t<Enum> {
+  return static_cast<underlying_t<Enum>>(e);
+}
+
+namespace enums {
+template <typename Enum, FMT_ENABLE_IF(std::is_enum<Enum>::value)>
+constexpr auto format_as(Enum e) noexcept -> underlying_t<Enum> {
+  return static_cast<underlying_t<Enum>>(e);
+}
+}  // namespace enums
+
+class bytes {
+ private:
+  string_view data_;
+  friend struct formatter<bytes>;
+
+ public:
+  explicit bytes(string_view data) : data_(data) {}
+};
+
+template <> struct formatter<bytes> {
+ private:
+  detail::dynamic_format_specs<char> specs_;
+
+ public:
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    using handler_type = detail::dynamic_specs_handler<ParseContext>;
+    detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
+                                                detail::type::string_type);
+    auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
+    detail::check_string_type_spec(specs_.type, ctx.error_handler());
+    return it;
+  }
+
+  template <typename FormatContext>
+  auto format(bytes b, FormatContext& ctx) -> decltype(ctx.out()) {
+    detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
+                                                       specs_.width_ref, ctx);
+    detail::handle_dynamic_spec<detail::precision_checker>(
+        specs_.precision, specs_.precision_ref, ctx);
+    return detail::write_bytes(ctx.out(), b.data_, specs_);
+  }
+};
+
+// group_digits_view is not derived from view because it copies the argument.
+template <typename T> struct group_digits_view { T value; };
+
+/**
+  \rst
+  Returns a view that formats an integer value using ',' as a locale-independent
+  thousands separator.
+
+  **Example**::
+
+    fmt::print("{}", fmt::group_digits(12345));
+    // Output: "12,345"
+  \endrst
+ */
+template <typename T> auto group_digits(T value) -> group_digits_view<T> {
+  return {value};
+}
+
+template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
+ private:
+  detail::dynamic_format_specs<char> specs_;
+
+ public:
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    using handler_type = detail::dynamic_specs_handler<ParseContext>;
+    detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
+                                                detail::type::int_type);
+    auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
+    detail::check_string_type_spec(specs_.type, ctx.error_handler());
+    return it;
+  }
+
+  template <typename FormatContext>
+  auto format(group_digits_view<T> t, FormatContext& ctx)
+      -> decltype(ctx.out()) {
+    detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
+                                                       specs_.width_ref, ctx);
+    detail::handle_dynamic_spec<detail::precision_checker>(
+        specs_.precision, specs_.precision_ref, ctx);
+    return detail::write_int_localized(
+        ctx.out(), static_cast<detail::uint64_or_128_t<T>>(t.value), 0, specs_,
+        detail::digit_grouping<char>({"\3", ','}));
+  }
+};
+
+template <typename It, typename Sentinel, typename Char = char>
+struct join_view : detail::view {
+  It begin;
+  Sentinel end;
+  basic_string_view<Char> sep;
+
+  join_view(It b, Sentinel e, basic_string_view<Char> s)
+      : begin(b), end(e), sep(s) {}
+};
+
+template <typename It, typename Sentinel, typename Char>
+struct formatter<join_view<It, Sentinel, Char>, Char> {
+ private:
+  using value_type =
+#ifdef __cpp_lib_ranges
+      std::iter_value_t<It>;
+#else
+      typename std::iterator_traits<It>::value_type;
+#endif
+  using context = buffer_context<Char>;
+  using mapper = detail::arg_mapper<context>;
+
+  template <typename T, FMT_ENABLE_IF(has_formatter<T, context>::value)>
+  static auto map(const T& value) -> const T& {
+    return value;
+  }
+  template <typename T, FMT_ENABLE_IF(!has_formatter<T, context>::value)>
+  static auto map(const T& value) -> decltype(mapper().map(value)) {
+    return mapper().map(value);
+  }
+
+  using formatter_type =
+      conditional_t<is_formattable<value_type, Char>::value,
+                    formatter<remove_cvref_t<decltype(map(
+                                  std::declval<const value_type&>()))>,
+                              Char>,
+                    detail::fallback_formatter<value_type, Char>>;
+
+  formatter_type value_formatter_;
+
+ public:
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    return value_formatter_.parse(ctx);
+  }
+
+  template <typename FormatContext>
+  auto format(const join_view<It, Sentinel, Char>& value,
+              FormatContext& ctx) const -> decltype(ctx.out()) {
+    auto it = value.begin;
+    auto out = ctx.out();
+    if (it != value.end) {
+      out = value_formatter_.format(map(*it), ctx);
+      ++it;
+      while (it != value.end) {
+        out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
+        ctx.advance_to(out);
+        out = value_formatter_.format(map(*it), ctx);
+        ++it;
+      }
+    }
+    return out;
+  }
+};
+
+/**
+  Returns a view that formats the iterator range `[begin, end)` with elements
+  separated by `sep`.
+ */
+template <typename It, typename Sentinel>
+auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
+  return {begin, end, sep};
+}
+
+/**
+  \rst
+  Returns a view that formats `range` with elements separated by `sep`.
+
+  **Example**::
+
+    std::vector<int> v = {1, 2, 3};
+    fmt::print("{}", fmt::join(v, ", "));
+    // Output: "1, 2, 3"
+
+  ``fmt::join`` applies passed format specifiers to the range elements::
+
+    fmt::print("{:02}", fmt::join(v, ", "));
+    // Output: "01, 02, 03"
+  \endrst
+ */
+template <typename Range>
+auto join(Range&& range, string_view sep)
+    -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>> {
+  return join(std::begin(range), std::end(range), sep);
+}
+
+/**
+  \rst
+  Converts *value* to ``std::string`` using the default format for type *T*.
+
+  **Example**::
+
+    #include <fmt/format.h>
+
+    std::string answer = fmt::to_string(42);
+  \endrst
+ */
+template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+inline auto to_string(const T& value) -> std::string {
+  auto result = std::string();
+  detail::write<char>(std::back_inserter(result), value);
+  return result;
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+FMT_NODISCARD inline auto to_string(T value) -> std::string {
+  // The buffer should be large enough to store the number including the sign
+  // or "false" for bool.
+  constexpr int max_size = detail::digits10<T>() + 2;
+  char buffer[max_size > 5 ? static_cast<unsigned>(max_size) : 5];
+  char* begin = buffer;
+  return std::string(begin, detail::write<char>(begin, value));
+}
+
+template <typename Char, size_t SIZE>
+FMT_NODISCARD auto to_string(const basic_memory_buffer<Char, SIZE>& buf)
+    -> std::basic_string<Char> {
+  auto size = buf.size();
+  detail::assume(size < std::basic_string<Char>().max_size());
+  return std::basic_string<Char>(buf.data(), size);
+}
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+template <typename Char>
+void vformat_to(
+    buffer<Char>& buf, basic_string_view<Char> fmt,
+    basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
+    locale_ref loc) {
+  // workaround for msvc bug regarding name-lookup in module
+  // link names into function scope
+  using detail::arg_formatter;
+  using detail::buffer_appender;
+  using detail::custom_formatter;
+  using detail::default_arg_formatter;
+  using detail::get_arg;
+  using detail::locale_ref;
+  using detail::parse_format_specs;
+  using detail::specs_checker;
+  using detail::specs_handler;
+  using detail::to_unsigned;
+  using detail::type;
+  using detail::write;
+  auto out = buffer_appender<Char>(buf);
+  if (fmt.size() == 2 && equal2(fmt.data(), "{}")) {
+    auto arg = args.get(0);
+    if (!arg) error_handler().on_error("argument not found");
+    visit_format_arg(default_arg_formatter<Char>{out, args, loc}, arg);
+    return;
+  }
+
+  struct format_handler : error_handler {
+    basic_format_parse_context<Char> parse_context;
+    buffer_context<Char> context;
+
+    format_handler(buffer_appender<Char> p_out, basic_string_view<Char> str,
+                   basic_format_args<buffer_context<Char>> p_args,
+                   locale_ref p_loc)
+        : parse_context(str), context(p_out, p_args, p_loc) {}
+
+    void on_text(const Char* begin, const Char* end) {
+      auto text = basic_string_view<Char>(begin, to_unsigned(end - begin));
+      context.advance_to(write<Char>(context.out(), text));
+    }
+
+    FMT_CONSTEXPR auto on_arg_id() -> int {
+      return parse_context.next_arg_id();
+    }
+    FMT_CONSTEXPR auto on_arg_id(int id) -> int {
+      return parse_context.check_arg_id(id), id;
+    }
+    FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
+      int arg_id = context.arg_id(id);
+      if (arg_id < 0) on_error("argument not found");
+      return arg_id;
+    }
+
+    FMT_INLINE void on_replacement_field(int id, const Char*) {
+      auto arg = get_arg(context, id);
+      context.advance_to(visit_format_arg(
+          default_arg_formatter<Char>{context.out(), context.args(),
+                                      context.locale()},
+          arg));
+    }
+
+    auto on_format_specs(int id, const Char* begin, const Char* end)
+        -> const Char* {
+      auto arg = get_arg(context, id);
+      if (arg.type() == type::custom_type) {
+        parse_context.advance_to(parse_context.begin() +
+                                 (begin - &*parse_context.begin()));
+        visit_format_arg(custom_formatter<Char>{parse_context, context}, arg);
+        return parse_context.begin();
+      }
+      auto specs = basic_format_specs<Char>();
+      specs_checker<specs_handler<Char>> handler(
+          specs_handler<Char>(specs, parse_context, context), arg.type());
+      begin = parse_format_specs(begin, end, handler);
+      if (begin == end || *begin != '}')
+        on_error("missing '}' in format string");
+      auto f = arg_formatter<Char>{context.out(), specs, context.locale()};
+      context.advance_to(visit_format_arg(f, arg));
+      return begin;
+    }
+  };
+  detail::parse_format_string<false>(fmt, format_handler(out, fmt, args, loc));
+}
+
+#ifndef FMT_HEADER_ONLY
+extern template FMT_API auto thousands_sep_impl<char>(locale_ref)
+    -> thousands_sep_result<char>;
+extern template FMT_API auto thousands_sep_impl<wchar_t>(locale_ref)
+    -> thousands_sep_result<wchar_t>;
+extern template FMT_API auto decimal_point_impl(locale_ref) -> char;
+extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
+#endif  // FMT_HEADER_ONLY
+
+FMT_END_DETAIL_NAMESPACE
+
+#if FMT_USE_USER_DEFINED_LITERALS
+inline namespace literals {
+/**
+  \rst
+  User-defined literal equivalent of :func:`fmt::arg`.
+
+  **Example**::
+
+    using namespace fmt::literals;
+    fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
+  \endrst
+ */
+#  if FMT_USE_NONTYPE_TEMPLATE_ARGS
+template <detail_exported::fixed_string Str> constexpr auto operator""_a() {
+  using char_t = remove_cvref_t<decltype(Str.data[0])>;
+  return detail::udl_arg<char_t, sizeof(Str.data) / sizeof(char_t), Str>();
+}
+#  else
+constexpr auto operator"" _a(const char* s, size_t) -> detail::udl_arg<char> {
+  return {s};
+}
+#  endif
+}  // namespace literals
+#endif  // FMT_USE_USER_DEFINED_LITERALS
+
+template <typename Locale, FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
+inline auto vformat(const Locale& loc, string_view fmt, format_args args)
+    -> std::string {
+  return detail::vformat(loc, fmt, args);
+}
+
+template <typename Locale, typename... T,
+          FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
+inline auto format(const Locale& loc, format_string<T...> fmt, T&&... args)
+    -> std::string {
+  return vformat(loc, string_view(fmt), fmt::make_format_args(args...));
+}
+
+template <typename OutputIt, typename Locale,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&&
+                            detail::is_locale<Locale>::value)>
+auto vformat_to(OutputIt out, const Locale& loc, string_view fmt,
+                format_args args) -> OutputIt {
+  using detail::get_buffer;
+  auto&& buf = get_buffer<char>(out);
+  detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
+  return detail::get_iterator(buf);
+}
+
+template <typename OutputIt, typename Locale, typename... T,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&&
+                            detail::is_locale<Locale>::value)>
+FMT_INLINE auto format_to(OutputIt out, const Locale& loc,
+                          format_string<T...> fmt, T&&... args) -> OutputIt {
+  return vformat_to(out, loc, fmt, fmt::make_format_args(args...));
+}
+
+FMT_MODULE_EXPORT_END
+FMT_END_NAMESPACE
+
+#ifdef FMT_HEADER_ONLY
+#  define FMT_FUNC inline
+#  include "format-inl.h"
+#else
+#  define FMT_FUNC
+#endif
+
+#endif  // FMT_FORMAT_H_
diff --git a/include/spdlog/fmt/bundled/locale.h b/include/spdlog/fmt/bundled/locale.h
new file mode 100644
index 000000000..7571b5261
--- /dev/null
+++ b/include/spdlog/fmt/bundled/locale.h
@@ -0,0 +1,2 @@
+#include "xchar.h"
+#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead
diff --git a/include/spdlog/fmt/bundled/os.h b/include/spdlog/fmt/bundled/os.h
new file mode 100644
index 000000000..d82be1125
--- /dev/null
+++ b/include/spdlog/fmt/bundled/os.h
@@ -0,0 +1,478 @@
+// Formatting library for C++ - optional OS-specific functionality
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_OS_H_
+#define FMT_OS_H_
+
+#include <cerrno>
+#include <cstddef>
+#include <cstdio>
+#include <system_error>  // std::system_error
+
+#if defined __APPLE__ || defined(__FreeBSD__)
+#  include <xlocale.h>  // for LC_NUMERIC_MASK on OS X
+#endif
+
+#include "format.h"
+
+#ifndef FMT_USE_FCNTL
+// UWP doesn't provide _pipe.
+#  if FMT_HAS_INCLUDE("winapifamily.h")
+#    include <winapifamily.h>
+#  endif
+#  if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
+       defined(__linux__)) &&                              \
+      (!defined(WINAPI_FAMILY) ||                          \
+       (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
+#    include <fcntl.h>  // for O_RDONLY
+#    define FMT_USE_FCNTL 1
+#  else
+#    define FMT_USE_FCNTL 0
+#  endif
+#endif
+
+#ifndef FMT_POSIX
+#  if defined(_WIN32) && !defined(__MINGW32__)
+// Fix warnings about deprecated symbols.
+#    define FMT_POSIX(call) _##call
+#  else
+#    define FMT_POSIX(call) call
+#  endif
+#endif
+
+// Calls to system functions are wrapped in FMT_SYSTEM for testability.
+#ifdef FMT_SYSTEM
+#  define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
+#else
+#  define FMT_SYSTEM(call) ::call
+#  ifdef _WIN32
+// Fix warnings about deprecated symbols.
+#    define FMT_POSIX_CALL(call) ::_##call
+#  else
+#    define FMT_POSIX_CALL(call) ::call
+#  endif
+#endif
+
+// Retries the expression while it evaluates to error_result and errno
+// equals to EINTR.
+#ifndef _WIN32
+#  define FMT_RETRY_VAL(result, expression, error_result) \
+    do {                                                  \
+      (result) = (expression);                            \
+    } while ((result) == (error_result) && errno == EINTR)
+#else
+#  define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
+#endif
+
+#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
+
+FMT_BEGIN_NAMESPACE
+FMT_MODULE_EXPORT_BEGIN
+
+/**
+  \rst
+  A reference to a null-terminated string. It can be constructed from a C
+  string or ``std::string``.
+
+  You can use one of the following type aliases for common character types:
+
+  +---------------+-----------------------------+
+  | Type          | Definition                  |
+  +===============+=============================+
+  | cstring_view  | basic_cstring_view<char>    |
+  +---------------+-----------------------------+
+  | wcstring_view | basic_cstring_view<wchar_t> |
+  +---------------+-----------------------------+
+
+  This class is most useful as a parameter type to allow passing
+  different types of strings to a function, for example::
+
+    template <typename... Args>
+    std::string format(cstring_view format_str, const Args & ... args);
+
+    format("{}", 42);
+    format(std::string("{}"), 42);
+  \endrst
+ */
+template <typename Char> class basic_cstring_view {
+ private:
+  const Char* data_;
+
+ public:
+  /** Constructs a string reference object from a C string. */
+  basic_cstring_view(const Char* s) : data_(s) {}
+
+  /**
+    \rst
+    Constructs a string reference from an ``std::string`` object.
+    \endrst
+   */
+  basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
+
+  /** Returns the pointer to a C string. */
+  const Char* c_str() const { return data_; }
+};
+
+using cstring_view = basic_cstring_view<char>;
+using wcstring_view = basic_cstring_view<wchar_t>;
+
+template <typename Char> struct formatter<std::error_code, Char> {
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    return ctx.begin();
+  }
+
+  template <typename FormatContext>
+  FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    auto out = ctx.out();
+    out = detail::write_bytes(out, ec.category().name(),
+                              basic_format_specs<Char>());
+    out = detail::write<Char>(out, Char(':'));
+    out = detail::write<Char>(out, ec.value());
+    return out;
+  }
+};
+
+#ifdef _WIN32
+FMT_API const std::error_category& system_category() noexcept;
+
+FMT_BEGIN_DETAIL_NAMESPACE
+// A converter from UTF-16 to UTF-8.
+// It is only provided for Windows since other systems support UTF-8 natively.
+class utf16_to_utf8 {
+ private:
+  memory_buffer buffer_;
+
+ public:
+  utf16_to_utf8() {}
+  FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
+  operator string_view() const { return string_view(&buffer_[0], size()); }
+  size_t size() const { return buffer_.size() - 1; }
+  const char* c_str() const { return &buffer_[0]; }
+  std::string str() const { return std::string(&buffer_[0], size()); }
+
+  // Performs conversion returning a system error code instead of
+  // throwing exception on conversion error. This method may still throw
+  // in case of memory allocation error.
+  FMT_API int convert(basic_string_view<wchar_t> s);
+};
+
+FMT_API void format_windows_error(buffer<char>& out, int error_code,
+                                  const char* message) noexcept;
+FMT_END_DETAIL_NAMESPACE
+
+FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
+                                         format_args args);
+
+/**
+ \rst
+ Constructs a :class:`std::system_error` object with the description
+ of the form
+
+ .. parsed-literal::
+   *<message>*: *<system-message>*
+
+ where *<message>* is the formatted message and *<system-message>* is the
+ system message corresponding to the error code.
+ *error_code* is a Windows error code as given by ``GetLastError``.
+ If *error_code* is not a valid error code such as -1, the system message
+ will look like "error -1".
+
+ **Example**::
+
+   // This throws a system_error with the description
+   //   cannot open file 'madeup': The system cannot find the file specified.
+   // or similar (system message may vary).
+   const char *filename = "madeup";
+   LPOFSTRUCT of = LPOFSTRUCT();
+   HFILE file = OpenFile(filename, &of, OF_READ);
+   if (file == HFILE_ERROR) {
+     throw fmt::windows_error(GetLastError(),
+                              "cannot open file '{}'", filename);
+   }
+ \endrst
+*/
+template <typename... Args>
+std::system_error windows_error(int error_code, string_view message,
+                                const Args&... args) {
+  return vwindows_error(error_code, message, fmt::make_format_args(args...));
+}
+
+// Reports a Windows error without throwing an exception.
+// Can be used to report errors from destructors.
+FMT_API void report_windows_error(int error_code, const char* message) noexcept;
+#else
+inline const std::error_category& system_category() noexcept {
+  return std::system_category();
+}
+#endif  // _WIN32
+
+// std::system is not available on some platforms such as iOS (#2248).
+#ifdef __OSX__
+template <typename S, typename... Args, typename Char = char_t<S>>
+void say(const S& format_str, Args&&... args) {
+  std::system(format("say \"{}\"", format(format_str, args...)).c_str());
+}
+#endif
+
+// A buffered file.
+class buffered_file {
+ private:
+  FILE* file_;
+
+  friend class file;
+
+  explicit buffered_file(FILE* f) : file_(f) {}
+
+ public:
+  buffered_file(const buffered_file&) = delete;
+  void operator=(const buffered_file&) = delete;
+
+  // Constructs a buffered_file object which doesn't represent any file.
+  buffered_file() noexcept : file_(nullptr) {}
+
+  // Destroys the object closing the file it represents if any.
+  FMT_API ~buffered_file() noexcept;
+
+ public:
+  buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
+    other.file_ = nullptr;
+  }
+
+  buffered_file& operator=(buffered_file&& other) {
+    close();
+    file_ = other.file_;
+    other.file_ = nullptr;
+    return *this;
+  }
+
+  // Opens a file.
+  FMT_API buffered_file(cstring_view filename, cstring_view mode);
+
+  // Closes the file.
+  FMT_API void close();
+
+  // Returns the pointer to a FILE object representing this file.
+  FILE* get() const noexcept { return file_; }
+
+  FMT_API int descriptor() const;
+
+  void vprint(string_view format_str, format_args args) {
+    fmt::vprint(file_, format_str, args);
+  }
+
+  template <typename... Args>
+  inline void print(string_view format_str, const Args&... args) {
+    vprint(format_str, fmt::make_format_args(args...));
+  }
+};
+
+#if FMT_USE_FCNTL
+// A file. Closed file is represented by a file object with descriptor -1.
+// Methods that are not declared with noexcept may throw
+// fmt::system_error in case of failure. Note that some errors such as
+// closing the file multiple times will cause a crash on Windows rather
+// than an exception. You can get standard behavior by overriding the
+// invalid parameter handler with _set_invalid_parameter_handler.
+class FMT_API file {
+ private:
+  int fd_;  // File descriptor.
+
+  // Constructs a file object with a given descriptor.
+  explicit file(int fd) : fd_(fd) {}
+
+ public:
+  // Possible values for the oflag argument to the constructor.
+  enum {
+    RDONLY = FMT_POSIX(O_RDONLY),  // Open for reading only.
+    WRONLY = FMT_POSIX(O_WRONLY),  // Open for writing only.
+    RDWR = FMT_POSIX(O_RDWR),      // Open for reading and writing.
+    CREATE = FMT_POSIX(O_CREAT),   // Create if the file doesn't exist.
+    APPEND = FMT_POSIX(O_APPEND),  // Open in append mode.
+    TRUNC = FMT_POSIX(O_TRUNC)     // Truncate the content of the file.
+  };
+
+  // Constructs a file object which doesn't represent any file.
+  file() noexcept : fd_(-1) {}
+
+  // Opens a file and constructs a file object representing this file.
+  file(cstring_view path, int oflag);
+
+ public:
+  file(const file&) = delete;
+  void operator=(const file&) = delete;
+
+  file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
+
+  // Move assignment is not noexcept because close may throw.
+  file& operator=(file&& other) {
+    close();
+    fd_ = other.fd_;
+    other.fd_ = -1;
+    return *this;
+  }
+
+  // Destroys the object closing the file it represents if any.
+  ~file() noexcept;
+
+  // Returns the file descriptor.
+  int descriptor() const noexcept { return fd_; }
+
+  // Closes the file.
+  void close();
+
+  // Returns the file size. The size has signed type for consistency with
+  // stat::st_size.
+  long long size() const;
+
+  // Attempts to read count bytes from the file into the specified buffer.
+  size_t read(void* buffer, size_t count);
+
+  // Attempts to write count bytes from the specified buffer to the file.
+  size_t write(const void* buffer, size_t count);
+
+  // Duplicates a file descriptor with the dup function and returns
+  // the duplicate as a file object.
+  static file dup(int fd);
+
+  // Makes fd be the copy of this file descriptor, closing fd first if
+  // necessary.
+  void dup2(int fd);
+
+  // Makes fd be the copy of this file descriptor, closing fd first if
+  // necessary.
+  void dup2(int fd, std::error_code& ec) noexcept;
+
+  // Creates a pipe setting up read_end and write_end file objects for reading
+  // and writing respectively.
+  static void pipe(file& read_end, file& write_end);
+
+  // Creates a buffered_file object associated with this file and detaches
+  // this file object from the file.
+  buffered_file fdopen(const char* mode);
+};
+
+// Returns the memory page size.
+long getpagesize();
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+struct buffer_size {
+  buffer_size() = default;
+  size_t value = 0;
+  buffer_size operator=(size_t val) const {
+    auto bs = buffer_size();
+    bs.value = val;
+    return bs;
+  }
+};
+
+struct ostream_params {
+  int oflag = file::WRONLY | file::CREATE | file::TRUNC;
+  size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
+
+  ostream_params() {}
+
+  template <typename... T>
+  ostream_params(T... params, int new_oflag) : ostream_params(params...) {
+    oflag = new_oflag;
+  }
+
+  template <typename... T>
+  ostream_params(T... params, detail::buffer_size bs)
+      : ostream_params(params...) {
+    this->buffer_size = bs.value;
+  }
+
+// Intel has a bug that results in failure to deduce a constructor
+// for empty parameter packs.
+#  if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
+  ostream_params(int new_oflag) : oflag(new_oflag) {}
+  ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
+#  endif
+};
+
+FMT_END_DETAIL_NAMESPACE
+
+// Added {} below to work around default constructor error known to
+// occur in Xcode versions 7.2.1 and 8.2.1.
+constexpr detail::buffer_size buffer_size{};
+
+/** A fast output stream which is not thread-safe. */
+class FMT_API ostream final : private detail::buffer<char> {
+ private:
+  file file_;
+
+  void grow(size_t) override;
+
+  ostream(cstring_view path, const detail::ostream_params& params)
+      : file_(path, params.oflag) {
+    set(new char[params.buffer_size], params.buffer_size);
+  }
+
+ public:
+  ostream(ostream&& other)
+      : detail::buffer<char>(other.data(), other.size(), other.capacity()),
+        file_(std::move(other.file_)) {
+    other.clear();
+    other.set(nullptr, 0);
+  }
+  ~ostream() {
+    flush();
+    delete[] data();
+  }
+
+  void flush() {
+    if (size() == 0) return;
+    file_.write(data(), size());
+    clear();
+  }
+
+  template <typename... T>
+  friend ostream output_file(cstring_view path, T... params);
+
+  void close() {
+    flush();
+    file_.close();
+  }
+
+  /**
+    Formats ``args`` according to specifications in ``fmt`` and writes the
+    output to the file.
+   */
+  template <typename... T> void print(format_string<T...> fmt, T&&... args) {
+    vformat_to(detail::buffer_appender<char>(*this), fmt,
+               fmt::make_format_args(args...));
+  }
+};
+
+/**
+  \rst
+  Opens a file for writing. Supported parameters passed in *params*:
+
+  * ``<integer>``: Flags passed to `open
+    <https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
+    (``file::WRONLY | file::CREATE | file::TRUNC`` by default)
+  * ``buffer_size=<integer>``: Output buffer size
+
+  **Example**::
+
+    auto out = fmt::output_file("guide.txt");
+    out.print("Don't {}", "Panic");
+  \endrst
+ */
+template <typename... T>
+inline ostream output_file(cstring_view path, T... params) {
+  return {path, detail::ostream_params(params...)};
+}
+#endif  // FMT_USE_FCNTL
+
+FMT_MODULE_EXPORT_END
+FMT_END_NAMESPACE
+
+#endif  // FMT_OS_H_
diff --git a/include/spdlog/fmt/bundled/ostream.h b/include/spdlog/fmt/bundled/ostream.h
new file mode 100644
index 000000000..c3cdd4a61
--- /dev/null
+++ b/include/spdlog/fmt/bundled/ostream.h
@@ -0,0 +1,237 @@
+// Formatting library for C++ - std::ostream support
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_OSTREAM_H_
+#define FMT_OSTREAM_H_
+
+#include <fstream>
+#include <ostream>
+#if defined(_WIN32) && defined(__GLIBCXX__)
+#  include <ext/stdio_filebuf.h>
+#  include <ext/stdio_sync_filebuf.h>
+#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
+#  include <__std_stream>
+#endif
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+
+template <typename OutputIt, typename Char> class basic_printf_context;
+
+namespace detail {
+
+// Checks if T has a user-defined operator<<.
+template <typename T, typename Char, typename Enable = void>
+class is_streamable {
+ private:
+  template <typename U>
+  static auto test(int)
+      -> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
+                              << std::declval<U>()) != 0>;
+
+  template <typename> static auto test(...) -> std::false_type;
+
+  using result = decltype(test<T>(0));
+
+ public:
+  is_streamable() = default;
+
+  static const bool value = result::value;
+};
+
+// Formatting of built-in types and arrays is intentionally disabled because
+// it's handled by standard (non-ostream) formatters.
+template <typename T, typename Char>
+struct is_streamable<
+    T, Char,
+    enable_if_t<
+        std::is_arithmetic<T>::value || std::is_array<T>::value ||
+        std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
+        std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
+        std::is_same<T, std_string_view<Char>>::value ||
+        (std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
+    : std::false_type {};
+
+// Generate a unique explicit instantion in every translation unit using a tag
+// type in an anonymous namespace.
+namespace {
+struct file_access_tag {};
+}  // namespace
+template <class Tag, class BufType, FILE* BufType::*FileMemberPtr>
+class file_access {
+  friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
+};
+
+#if FMT_MSC_VERSION
+template class file_access<file_access_tag, std::filebuf,
+                           &std::filebuf::_Myfile>;
+auto get_file(std::filebuf&) -> FILE*;
+#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
+template class file_access<file_access_tag, std::__stdoutbuf<char>,
+                           &std::__stdoutbuf<char>::__file_>;
+auto get_file(std::__stdoutbuf<char>&) -> FILE*;
+#endif
+
+inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
+#if FMT_MSC_VERSION
+  if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
+    if (FILE* f = get_file(*buf)) return write_console(f, data);
+#elif defined(_WIN32) && defined(__GLIBCXX__)
+  auto* rdbuf = os.rdbuf();
+  FILE* c_file;
+  if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
+    c_file = fbuf->file();
+  else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
+    c_file = fbuf->file();
+  else
+    return false;
+  if (c_file) return write_console(c_file, data);
+#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
+  if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf()))
+    if (FILE* f = get_file(*buf)) return write_console(f, data);
+#else
+  ignore_unused(os, data);
+#endif
+  return false;
+}
+inline bool write_ostream_unicode(std::wostream&,
+                                  fmt::basic_string_view<wchar_t>) {
+  return false;
+}
+
+// Write the content of buf to os.
+// It is a separate function rather than a part of vprint to simplify testing.
+template <typename Char>
+void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
+  const Char* buf_data = buf.data();
+  using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
+  unsigned_streamsize size = buf.size();
+  unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
+  do {
+    unsigned_streamsize n = size <= max_size ? size : max_size;
+    os.write(buf_data, static_cast<std::streamsize>(n));
+    buf_data += n;
+    size -= n;
+  } while (size != 0);
+}
+
+template <typename Char, typename T>
+void format_value(buffer<Char>& buf, const T& value,
+                  locale_ref loc = locale_ref()) {
+  auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
+  auto&& output = std::basic_ostream<Char>(&format_buf);
+#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
+  if (loc) output.imbue(loc.get<std::locale>());
+#endif
+  output << value;
+  output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
+}
+
+template <typename T> struct streamed_view { const T& value; };
+
+}  // namespace detail
+
+// Formats an object of type T that has an overloaded ostream operator<<.
+template <typename Char>
+struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
+  void set_debug_format() = delete;
+
+  template <typename T, typename OutputIt>
+  auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
+      -> OutputIt {
+    auto buffer = basic_memory_buffer<Char>();
+    format_value(buffer, value, ctx.locale());
+    return formatter<basic_string_view<Char>, Char>::format(
+        {buffer.data(), buffer.size()}, ctx);
+  }
+};
+
+using ostream_formatter = basic_ostream_formatter<char>;
+
+template <typename T, typename Char>
+struct formatter<detail::streamed_view<T>, Char>
+    : basic_ostream_formatter<Char> {
+  template <typename OutputIt>
+  auto format(detail::streamed_view<T> view,
+              basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
+    return basic_ostream_formatter<Char>::format(view.value, ctx);
+  }
+};
+
+/**
+  \rst
+  Returns a view that formats `value` via an ostream ``operator<<``.
+
+  **Example**::
+
+    fmt::print("Current thread id: {}\n",
+               fmt::streamed(std::this_thread::get_id()));
+  \endrst
+ */
+template <typename T>
+auto streamed(const T& value) -> detail::streamed_view<T> {
+  return {value};
+}
+
+namespace detail {
+
+// Formats an object of type T that has an overloaded ostream operator<<.
+template <typename T, typename Char>
+struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
+    : basic_ostream_formatter<Char> {
+  using basic_ostream_formatter<Char>::format;
+};
+
+inline void vprint_directly(std::ostream& os, string_view format_str,
+                            format_args args) {
+  auto buffer = memory_buffer();
+  detail::vformat_to(buffer, format_str, args);
+  detail::write_buffer(os, buffer);
+}
+
+}  // namespace detail
+
+FMT_MODULE_EXPORT template <typename Char>
+void vprint(std::basic_ostream<Char>& os,
+            basic_string_view<type_identity_t<Char>> format_str,
+            basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+  auto buffer = basic_memory_buffer<Char>();
+  detail::vformat_to(buffer, format_str, args);
+  if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
+  detail::write_buffer(os, buffer);
+}
+
+/**
+  \rst
+  Prints formatted data to the stream *os*.
+
+  **Example**::
+
+    fmt::print(cerr, "Don't {}!", "panic");
+  \endrst
+ */
+FMT_MODULE_EXPORT template <typename... T>
+void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
+  const auto& vargs = fmt::make_format_args(args...);
+  if (detail::is_utf8())
+    vprint(os, fmt, vargs);
+  else
+    detail::vprint_directly(os, fmt, vargs);
+}
+
+FMT_MODULE_EXPORT
+template <typename... Args>
+void print(std::wostream& os,
+           basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
+           Args&&... args) {
+  vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
+}
+
+FMT_END_NAMESPACE
+
+#endif  // FMT_OSTREAM_H_
diff --git a/include/spdlog/fmt/bundled/printf.h b/include/spdlog/fmt/bundled/printf.h
new file mode 100644
index 000000000..70a592dc2
--- /dev/null
+++ b/include/spdlog/fmt/bundled/printf.h
@@ -0,0 +1,640 @@
+// Formatting library for C++ - legacy printf implementation
+//
+// Copyright (c) 2012 - 2016, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_PRINTF_H_
+#define FMT_PRINTF_H_
+
+#include <algorithm>  // std::max
+#include <limits>     // std::numeric_limits
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+FMT_MODULE_EXPORT_BEGIN
+
+template <typename T> struct printf_formatter { printf_formatter() = delete; };
+
+template <typename Char>
+class basic_printf_parse_context : public basic_format_parse_context<Char> {
+  using basic_format_parse_context<Char>::basic_format_parse_context;
+};
+
+template <typename OutputIt, typename Char> class basic_printf_context {
+ private:
+  OutputIt out_;
+  basic_format_args<basic_printf_context> args_;
+
+ public:
+  using char_type = Char;
+  using format_arg = basic_format_arg<basic_printf_context>;
+  using parse_context_type = basic_printf_parse_context<Char>;
+  template <typename T> using formatter_type = printf_formatter<T>;
+
+  /**
+    \rst
+    Constructs a ``printf_context`` object. References to the arguments are
+    stored in the context object so make sure they have appropriate lifetimes.
+    \endrst
+   */
+  basic_printf_context(OutputIt out,
+                       basic_format_args<basic_printf_context> args)
+      : out_(out), args_(args) {}
+
+  OutputIt out() { return out_; }
+  void advance_to(OutputIt it) { out_ = it; }
+
+  detail::locale_ref locale() { return {}; }
+
+  format_arg arg(int id) const { return args_.get(id); }
+
+  FMT_CONSTEXPR void on_error(const char* message) {
+    detail::error_handler().on_error(message);
+  }
+};
+
+FMT_BEGIN_DETAIL_NAMESPACE
+
+// Checks if a value fits in int - used to avoid warnings about comparing
+// signed and unsigned integers.
+template <bool IsSigned> struct int_checker {
+  template <typename T> static bool fits_in_int(T value) {
+    unsigned max = max_value<int>();
+    return value <= max;
+  }
+  static bool fits_in_int(bool) { return true; }
+};
+
+template <> struct int_checker<true> {
+  template <typename T> static bool fits_in_int(T value) {
+    return value >= (std::numeric_limits<int>::min)() &&
+           value <= max_value<int>();
+  }
+  static bool fits_in_int(int) { return true; }
+};
+
+class printf_precision_handler {
+ public:
+  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+  int operator()(T value) {
+    if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
+      FMT_THROW(format_error("number is too big"));
+    return (std::max)(static_cast<int>(value), 0);
+  }
+
+  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+  int operator()(T) {
+    FMT_THROW(format_error("precision is not integer"));
+    return 0;
+  }
+};
+
+// An argument visitor that returns true iff arg is a zero integer.
+class is_zero_int {
+ public:
+  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+  bool operator()(T value) {
+    return value == 0;
+  }
+
+  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+  bool operator()(T) {
+    return false;
+  }
+};
+
+template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
+
+template <> struct make_unsigned_or_bool<bool> { using type = bool; };
+
+template <typename T, typename Context> class arg_converter {
+ private:
+  using char_type = typename Context::char_type;
+
+  basic_format_arg<Context>& arg_;
+  char_type type_;
+
+ public:
+  arg_converter(basic_format_arg<Context>& arg, char_type type)
+      : arg_(arg), type_(type) {}
+
+  void operator()(bool value) {
+    if (type_ != 's') operator()<bool>(value);
+  }
+
+  template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
+  void operator()(U value) {
+    bool is_signed = type_ == 'd' || type_ == 'i';
+    using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
+    if (const_check(sizeof(target_type) <= sizeof(int))) {
+      // Extra casts are used to silence warnings.
+      if (is_signed) {
+        arg_ = detail::make_arg<Context>(
+            static_cast<int>(static_cast<target_type>(value)));
+      } else {
+        using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
+        arg_ = detail::make_arg<Context>(
+            static_cast<unsigned>(static_cast<unsigned_type>(value)));
+      }
+    } else {
+      if (is_signed) {
+        // glibc's printf doesn't sign extend arguments of smaller types:
+        //   std::printf("%lld", -42);  // prints "4294967254"
+        // but we don't have to do the same because it's a UB.
+        arg_ = detail::make_arg<Context>(static_cast<long long>(value));
+      } else {
+        arg_ = detail::make_arg<Context>(
+            static_cast<typename make_unsigned_or_bool<U>::type>(value));
+      }
+    }
+  }
+
+  template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
+  void operator()(U) {}  // No conversion needed for non-integral types.
+};
+
+// Converts an integer argument to T for printf, if T is an integral type.
+// If T is void, the argument is converted to corresponding signed or unsigned
+// type depending on the type specifier: 'd' and 'i' - signed, other -
+// unsigned).
+template <typename T, typename Context, typename Char>
+void convert_arg(basic_format_arg<Context>& arg, Char type) {
+  visit_format_arg(arg_converter<T, Context>(arg, type), arg);
+}
+
+// Converts an integer argument to char for printf.
+template <typename Context> class char_converter {
+ private:
+  basic_format_arg<Context>& arg_;
+
+ public:
+  explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
+
+  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+  void operator()(T value) {
+    arg_ = detail::make_arg<Context>(
+        static_cast<typename Context::char_type>(value));
+  }
+
+  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+  void operator()(T) {}  // No conversion needed for non-integral types.
+};
+
+// An argument visitor that return a pointer to a C string if argument is a
+// string or null otherwise.
+template <typename Char> struct get_cstring {
+  template <typename T> const Char* operator()(T) { return nullptr; }
+  const Char* operator()(const Char* s) { return s; }
+};
+
+// Checks if an argument is a valid printf width specifier and sets
+// left alignment if it is negative.
+template <typename Char> class printf_width_handler {
+ private:
+  using format_specs = basic_format_specs<Char>;
+
+  format_specs& specs_;
+
+ public:
+  explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
+
+  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+  unsigned operator()(T value) {
+    auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
+    if (detail::is_negative(value)) {
+      specs_.align = align::left;
+      width = 0 - width;
+    }
+    unsigned int_max = max_value<int>();
+    if (width > int_max) FMT_THROW(format_error("number is too big"));
+    return static_cast<unsigned>(width);
+  }
+
+  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+  unsigned operator()(T) {
+    FMT_THROW(format_error("width is not integer"));
+    return 0;
+  }
+};
+
+// The ``printf`` argument formatter.
+template <typename OutputIt, typename Char>
+class printf_arg_formatter : public arg_formatter<Char> {
+ private:
+  using base = arg_formatter<Char>;
+  using context_type = basic_printf_context<OutputIt, Char>;
+  using format_specs = basic_format_specs<Char>;
+
+  context_type& context_;
+
+  OutputIt write_null_pointer(bool is_string = false) {
+    auto s = this->specs;
+    s.type = presentation_type::none;
+    return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
+  }
+
+ public:
+  printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
+      : base{iter, s, locale_ref()}, context_(ctx) {}
+
+  OutputIt operator()(monostate value) { return base::operator()(value); }
+
+  template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
+  OutputIt operator()(T value) {
+    // MSVC2013 fails to compile separate overloads for bool and Char so use
+    // std::is_same instead.
+    if (std::is_same<T, Char>::value) {
+      format_specs fmt_specs = this->specs;
+      if (fmt_specs.type != presentation_type::none &&
+          fmt_specs.type != presentation_type::chr) {
+        return (*this)(static_cast<int>(value));
+      }
+      fmt_specs.sign = sign::none;
+      fmt_specs.alt = false;
+      fmt_specs.fill[0] = ' ';  // Ignore '0' flag for char types.
+      // align::numeric needs to be overwritten here since the '0' flag is
+      // ignored for non-numeric types
+      if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
+        fmt_specs.align = align::right;
+      return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
+    }
+    return base::operator()(value);
+  }
+
+  template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+  OutputIt operator()(T value) {
+    return base::operator()(value);
+  }
+
+  /** Formats a null-terminated C string. */
+  OutputIt operator()(const char* value) {
+    if (value) return base::operator()(value);
+    return write_null_pointer(this->specs.type != presentation_type::pointer);
+  }
+
+  /** Formats a null-terminated wide C string. */
+  OutputIt operator()(const wchar_t* value) {
+    if (value) return base::operator()(value);
+    return write_null_pointer(this->specs.type != presentation_type::pointer);
+  }
+
+  OutputIt operator()(basic_string_view<Char> value) {
+    return base::operator()(value);
+  }
+
+  /** Formats a pointer. */
+  OutputIt operator()(const void* value) {
+    return value ? base::operator()(value) : write_null_pointer();
+  }
+
+  /** Formats an argument of a custom (user-defined) type. */
+  OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
+    auto parse_ctx =
+        basic_printf_parse_context<Char>(basic_string_view<Char>());
+    handle.format(parse_ctx, context_);
+    return this->out;
+  }
+};
+
+template <typename Char>
+void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
+                 const Char* end) {
+  for (; it != end; ++it) {
+    switch (*it) {
+    case '-':
+      specs.align = align::left;
+      break;
+    case '+':
+      specs.sign = sign::plus;
+      break;
+    case '0':
+      specs.fill[0] = '0';
+      break;
+    case ' ':
+      if (specs.sign != sign::plus) {
+        specs.sign = sign::space;
+      }
+      break;
+    case '#':
+      specs.alt = true;
+      break;
+    default:
+      return;
+    }
+  }
+}
+
+template <typename Char, typename GetArg>
+int parse_header(const Char*& it, const Char* end,
+                 basic_format_specs<Char>& specs, GetArg get_arg) {
+  int arg_index = -1;
+  Char c = *it;
+  if (c >= '0' && c <= '9') {
+    // Parse an argument index (if followed by '$') or a width possibly
+    // preceded with '0' flag(s).
+    int value = parse_nonnegative_int(it, end, -1);
+    if (it != end && *it == '$') {  // value is an argument index
+      ++it;
+      arg_index = value != -1 ? value : max_value<int>();
+    } else {
+      if (c == '0') specs.fill[0] = '0';
+      if (value != 0) {
+        // Nonzero value means that we parsed width and don't need to
+        // parse it or flags again, so return now.
+        if (value == -1) FMT_THROW(format_error("number is too big"));
+        specs.width = value;
+        return arg_index;
+      }
+    }
+  }
+  parse_flags(specs, it, end);
+  // Parse width.
+  if (it != end) {
+    if (*it >= '0' && *it <= '9') {
+      specs.width = parse_nonnegative_int(it, end, -1);
+      if (specs.width == -1) FMT_THROW(format_error("number is too big"));
+    } else if (*it == '*') {
+      ++it;
+      specs.width = static_cast<int>(visit_format_arg(
+          detail::printf_width_handler<Char>(specs), get_arg(-1)));
+    }
+  }
+  return arg_index;
+}
+
+template <typename Char, typename Context>
+void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
+             basic_format_args<Context> args) {
+  using OutputIt = buffer_appender<Char>;
+  auto out = OutputIt(buf);
+  auto context = basic_printf_context<OutputIt, Char>(out, args);
+  auto parse_ctx = basic_printf_parse_context<Char>(format);
+
+  // Returns the argument with specified index or, if arg_index is -1, the next
+  // argument.
+  auto get_arg = [&](int arg_index) {
+    if (arg_index < 0)
+      arg_index = parse_ctx.next_arg_id();
+    else
+      parse_ctx.check_arg_id(--arg_index);
+    return detail::get_arg(context, arg_index);
+  };
+
+  const Char* start = parse_ctx.begin();
+  const Char* end = parse_ctx.end();
+  auto it = start;
+  while (it != end) {
+    if (!detail::find<false, Char>(it, end, '%', it)) {
+      it = end;  // detail::find leaves it == nullptr if it doesn't find '%'
+      break;
+    }
+    Char c = *it++;
+    if (it != end && *it == c) {
+      out = detail::write(
+          out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
+      start = ++it;
+      continue;
+    }
+    out = detail::write(out, basic_string_view<Char>(
+                                 start, detail::to_unsigned(it - 1 - start)));
+
+    basic_format_specs<Char> specs;
+    specs.align = align::right;
+
+    // Parse argument index, flags and width.
+    int arg_index = parse_header(it, end, specs, get_arg);
+    if (arg_index == 0) parse_ctx.on_error("argument not found");
+
+    // Parse precision.
+    if (it != end && *it == '.') {
+      ++it;
+      c = it != end ? *it : 0;
+      if ('0' <= c && c <= '9') {
+        specs.precision = parse_nonnegative_int(it, end, 0);
+      } else if (c == '*') {
+        ++it;
+        specs.precision = static_cast<int>(
+            visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
+      } else {
+        specs.precision = 0;
+      }
+    }
+
+    auto arg = get_arg(arg_index);
+    // For d, i, o, u, x, and X conversion specifiers, if a precision is
+    // specified, the '0' flag is ignored
+    if (specs.precision >= 0 && arg.is_integral())
+      specs.fill[0] =
+          ' ';  // Ignore '0' flag for non-numeric types or if '-' present.
+    if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
+      auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
+      auto str_end = str + specs.precision;
+      auto nul = std::find(str, str_end, Char());
+      arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
+          basic_string_view<Char>(
+              str, detail::to_unsigned(nul != str_end ? nul - str
+                                                      : specs.precision)));
+    }
+    if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
+      specs.alt = false;
+    if (specs.fill[0] == '0') {
+      if (arg.is_arithmetic() && specs.align != align::left)
+        specs.align = align::numeric;
+      else
+        specs.fill[0] = ' ';  // Ignore '0' flag for non-numeric types or if '-'
+                              // flag is also present.
+    }
+
+    // Parse length and convert the argument to the required type.
+    c = it != end ? *it++ : 0;
+    Char t = it != end ? *it : 0;
+    using detail::convert_arg;
+    switch (c) {
+    case 'h':
+      if (t == 'h') {
+        ++it;
+        t = it != end ? *it : 0;
+        convert_arg<signed char>(arg, t);
+      } else {
+        convert_arg<short>(arg, t);
+      }
+      break;
+    case 'l':
+      if (t == 'l') {
+        ++it;
+        t = it != end ? *it : 0;
+        convert_arg<long long>(arg, t);
+      } else {
+        convert_arg<long>(arg, t);
+      }
+      break;
+    case 'j':
+      convert_arg<intmax_t>(arg, t);
+      break;
+    case 'z':
+      convert_arg<size_t>(arg, t);
+      break;
+    case 't':
+      convert_arg<std::ptrdiff_t>(arg, t);
+      break;
+    case 'L':
+      // printf produces garbage when 'L' is omitted for long double, no
+      // need to do the same.
+      break;
+    default:
+      --it;
+      convert_arg<void>(arg, c);
+    }
+
+    // Parse type.
+    if (it == end) FMT_THROW(format_error("invalid format string"));
+    char type = static_cast<char>(*it++);
+    if (arg.is_integral()) {
+      // Normalize type.
+      switch (type) {
+      case 'i':
+      case 'u':
+        type = 'd';
+        break;
+      case 'c':
+        visit_format_arg(
+            detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
+            arg);
+        break;
+      }
+    }
+    specs.type = parse_presentation_type(type);
+    if (specs.type == presentation_type::none)
+      parse_ctx.on_error("invalid type specifier");
+
+    start = it;
+
+    // Format argument.
+    out = visit_format_arg(
+        detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
+  }
+  detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
+}
+FMT_END_DETAIL_NAMESPACE
+
+template <typename Char>
+using basic_printf_context_t =
+    basic_printf_context<detail::buffer_appender<Char>, Char>;
+
+using printf_context = basic_printf_context_t<char>;
+using wprintf_context = basic_printf_context_t<wchar_t>;
+
+using printf_args = basic_format_args<printf_context>;
+using wprintf_args = basic_format_args<wprintf_context>;
+
+/**
+  \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... T>
+inline auto make_printf_args(const T&... args)
+    -> format_arg_store<printf_context, T...> {
+  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... T>
+inline auto make_wprintf_args(const T&... args)
+    -> format_arg_store<wprintf_context, T...> {
+  return {args...};
+}
+
+template <typename S, typename Char = char_t<S>>
+inline auto vsprintf(
+    const S& fmt,
+    basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
+    -> std::basic_string<Char> {
+  basic_memory_buffer<Char> buffer;
+  vprintf(buffer, detail::to_string_view(fmt), args);
+  return to_string(buffer);
+}
+
+/**
+  \rst
+  Formats arguments and returns the result as a string.
+
+  **Example**::
+
+    std::string message = fmt::sprintf("The answer is %d", 42);
+  \endrst
+*/
+template <typename S, typename... T,
+          typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
+inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
+  using context = basic_printf_context_t<Char>;
+  return vsprintf(detail::to_string_view(fmt),
+                  fmt::make_format_args<context>(args...));
+}
+
+template <typename S, typename Char = char_t<S>>
+inline auto vfprintf(
+    std::FILE* f, const S& fmt,
+    basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
+    -> int {
+  basic_memory_buffer<Char> buffer;
+  vprintf(buffer, detail::to_string_view(fmt), args);
+  size_t size = buffer.size();
+  return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
+             ? -1
+             : static_cast<int>(size);
+}
+
+/**
+  \rst
+  Prints formatted data to the file *f*.
+
+  **Example**::
+
+    fmt::fprintf(stderr, "Don't %s!", "panic");
+  \endrst
+ */
+template <typename S, typename... T, typename Char = char_t<S>>
+inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
+  using context = basic_printf_context_t<Char>;
+  return vfprintf(f, detail::to_string_view(fmt),
+                  fmt::make_format_args<context>(args...));
+}
+
+template <typename S, typename Char = char_t<S>>
+inline auto vprintf(
+    const S& fmt,
+    basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
+    -> int {
+  return vfprintf(stdout, detail::to_string_view(fmt), args);
+}
+
+/**
+  \rst
+  Prints formatted data to ``stdout``.
+
+  **Example**::
+
+    fmt::printf("Elapsed time: %.2f seconds", 1.23);
+  \endrst
+ */
+template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
+inline auto printf(const S& fmt, const T&... args) -> int {
+  return vprintf(
+      detail::to_string_view(fmt),
+      fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
+}
+
+FMT_MODULE_EXPORT_END
+FMT_END_NAMESPACE
+
+#endif  // FMT_PRINTF_H_
diff --git a/include/spdlog/fmt/bundled/ranges.h b/include/spdlog/fmt/bundled/ranges.h
new file mode 100644
index 000000000..dea7d60dd
--- /dev/null
+++ b/include/spdlog/fmt/bundled/ranges.h
@@ -0,0 +1,722 @@
+// Formatting library for C++ - experimental range support
+//
+// 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 ranges, containers and types tuple interface.
+
+#ifndef FMT_RANGES_H_
+#define FMT_RANGES_H_
+
+#include <initializer_list>
+#include <tuple>
+#include <type_traits>
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+
+namespace detail {
+
+template <typename RangeT, typename OutputIterator>
+OutputIterator copy(const RangeT& range, OutputIterator out) {
+  for (auto it = range.begin(), end = range.end(); it != end; ++it)
+    *out++ = *it;
+  return out;
+}
+
+template <typename OutputIterator>
+OutputIterator copy(const char* str, OutputIterator out) {
+  while (*str) *out++ = *str++;
+  return out;
+}
+
+template <typename OutputIterator>
+OutputIterator copy(char ch, OutputIterator out) {
+  *out++ = ch;
+  return out;
+}
+
+template <typename OutputIterator>
+OutputIterator copy(wchar_t ch, OutputIterator out) {
+  *out++ = ch;
+  return out;
+}
+
+// Returns true if T has a std::string-like interface, like std::string_view.
+template <typename T> class is_std_string_like {
+  template <typename U>
+  static auto check(U* p)
+      -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
+  template <typename> static void check(...);
+
+ public:
+  static constexpr const bool value =
+      is_string<T>::value ||
+      std::is_convertible<T, std_string_view<char>>::value ||
+      !std::is_void<decltype(check<T>(nullptr))>::value;
+};
+
+template <typename Char>
+struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
+
+template <typename T> class is_map {
+  template <typename U> static auto check(U*) -> typename U::mapped_type;
+  template <typename> static void check(...);
+
+ public:
+#ifdef FMT_FORMAT_MAP_AS_LIST
+  static constexpr const bool value = false;
+#else
+  static constexpr const bool value =
+      !std::is_void<decltype(check<T>(nullptr))>::value;
+#endif
+};
+
+template <typename T> class is_set {
+  template <typename U> static auto check(U*) -> typename U::key_type;
+  template <typename> static void check(...);
+
+ public:
+#ifdef FMT_FORMAT_SET_AS_LIST
+  static constexpr const bool value = false;
+#else
+  static constexpr const bool value =
+      !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
+#endif
+};
+
+template <typename... Ts> struct conditional_helper {};
+
+template <typename T, typename _ = void> struct is_range_ : std::false_type {};
+
+#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
+
+#  define FMT_DECLTYPE_RETURN(val)  \
+    ->decltype(val) { return val; } \
+    static_assert(                  \
+        true, "")  // This makes it so that a semicolon is required after the
+                   // macro, which helps clang-format handle the formatting.
+
+// C array overload
+template <typename T, std::size_t N>
+auto range_begin(const T (&arr)[N]) -> const T* {
+  return arr;
+}
+template <typename T, std::size_t N>
+auto range_end(const T (&arr)[N]) -> const T* {
+  return arr + N;
+}
+
+template <typename T, typename Enable = void>
+struct has_member_fn_begin_end_t : std::false_type {};
+
+template <typename T>
+struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
+                                           decltype(std::declval<T>().end())>>
+    : std::true_type {};
+
+// Member function overload
+template <typename T>
+auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
+template <typename T>
+auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
+
+// ADL overload. Only participates in overload resolution if member functions
+// are not found.
+template <typename T>
+auto range_begin(T&& rng)
+    -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
+                   decltype(begin(static_cast<T&&>(rng)))> {
+  return begin(static_cast<T&&>(rng));
+}
+template <typename T>
+auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
+                                       decltype(end(static_cast<T&&>(rng)))> {
+  return end(static_cast<T&&>(rng));
+}
+
+template <typename T, typename Enable = void>
+struct has_const_begin_end : std::false_type {};
+template <typename T, typename Enable = void>
+struct has_mutable_begin_end : std::false_type {};
+
+template <typename T>
+struct has_const_begin_end<
+    T,
+    void_t<
+        decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
+        decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
+    : std::true_type {};
+
+template <typename T>
+struct has_mutable_begin_end<
+    T, void_t<decltype(detail::range_begin(std::declval<T>())),
+              decltype(detail::range_end(std::declval<T>())),
+              enable_if_t<std::is_copy_constructible<T>::value>>>
+    : std::true_type {};
+
+template <typename T>
+struct is_range_<T, void>
+    : std::integral_constant<bool, (has_const_begin_end<T>::value ||
+                                    has_mutable_begin_end<T>::value)> {};
+#  undef FMT_DECLTYPE_RETURN
+#endif
+
+// tuple_size and tuple_element check.
+template <typename T> class is_tuple_like_ {
+  template <typename U>
+  static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
+  template <typename> static void check(...);
+
+ public:
+  static constexpr const bool value =
+      !std::is_void<decltype(check<T>(nullptr))>::value;
+};
+
+// Check for integer_sequence
+#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
+template <typename T, T... N>
+using integer_sequence = std::integer_sequence<T, N...>;
+template <size_t... N> using index_sequence = std::index_sequence<N...>;
+template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
+#else
+template <typename T, T... N> struct integer_sequence {
+  using value_type = T;
+
+  static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
+};
+
+template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
+
+template <typename T, size_t N, T... Ns>
+struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
+template <typename T, T... Ns>
+struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
+
+template <size_t N>
+using make_index_sequence = make_integer_sequence<size_t, N>;
+#endif
+
+template <typename T>
+using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
+
+template <typename T, typename C, bool = is_tuple_like_<T>::value>
+class is_tuple_formattable_ {
+ public:
+  static constexpr const bool value = false;
+};
+template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
+  template <std::size_t... I>
+  static std::true_type check2(index_sequence<I...>,
+                               integer_sequence<bool, (I == I)...>);
+  static std::false_type check2(...);
+  template <std::size_t... I>
+  static decltype(check2(
+      index_sequence<I...>{},
+      integer_sequence<
+          bool, (is_formattable<typename std::tuple_element<I, T>::type,
+                                C>::value)...>{})) check(index_sequence<I...>);
+
+ public:
+  static constexpr const bool value =
+      decltype(check(tuple_index_sequence<T>{}))::value;
+};
+
+template <class Tuple, class F, size_t... Is>
+void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) noexcept {
+  using std::get;
+  // using free function get<I>(T) now.
+  const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
+  (void)_;  // blocks warnings
+}
+
+template <class T>
+FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
+    T const&) {
+  return {};
+}
+
+template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
+  const auto indexes = get_indexes(tup);
+  for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
+}
+
+#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
+// Older MSVC doesn't get the reference type correctly for arrays.
+template <typename R> struct range_reference_type_impl {
+  using type = decltype(*detail::range_begin(std::declval<R&>()));
+};
+
+template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
+  using type = T&;
+};
+
+template <typename T>
+using range_reference_type = typename range_reference_type_impl<T>::type;
+#else
+template <typename Range>
+using range_reference_type =
+    decltype(*detail::range_begin(std::declval<Range&>()));
+#endif
+
+// We don't use the Range's value_type for anything, but we do need the Range's
+// reference type, with cv-ref stripped.
+template <typename Range>
+using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
+
+template <typename Range>
+using uncvref_first_type =
+    remove_cvref_t<decltype(std::declval<range_reference_type<Range>>().first)>;
+
+template <typename Range>
+using uncvref_second_type = remove_cvref_t<
+    decltype(std::declval<range_reference_type<Range>>().second)>;
+
+template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
+  *out++ = ',';
+  *out++ = ' ';
+  return out;
+}
+
+template <typename Char, typename OutputIt>
+auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
+  return write_escaped_string(out, str);
+}
+
+template <typename Char, typename OutputIt, typename T,
+          FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
+inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
+  auto sv = std_string_view<Char>(str);
+  return write_range_entry<Char>(out, basic_string_view<Char>(sv));
+}
+
+template <typename Char, typename OutputIt, typename Arg,
+          FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
+OutputIt write_range_entry(OutputIt out, const Arg v) {
+  return write_escaped_char(out, v);
+}
+
+template <
+    typename Char, typename OutputIt, typename Arg,
+    FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
+                  !std::is_same<Arg, Char>::value)>
+OutputIt write_range_entry(OutputIt out, const Arg& v) {
+  return write<Char>(out, v);
+}
+
+}  // namespace detail
+
+template <typename T> struct is_tuple_like {
+  static constexpr const bool value =
+      detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
+};
+
+template <typename T, typename C> struct is_tuple_formattable {
+  static constexpr const bool value =
+      detail::is_tuple_formattable_<T, C>::value;
+};
+
+template <typename TupleT, typename Char>
+struct formatter<TupleT, Char,
+                 enable_if_t<fmt::is_tuple_like<TupleT>::value &&
+                             fmt::is_tuple_formattable<TupleT, Char>::value>> {
+ private:
+  basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
+  basic_string_view<Char> opening_bracket_ =
+      detail::string_literal<Char, '('>{};
+  basic_string_view<Char> closing_bracket_ =
+      detail::string_literal<Char, ')'>{};
+
+  // C++11 generic lambda for format().
+  template <typename FormatContext> struct format_each {
+    template <typename T> void operator()(const T& v) {
+      if (i > 0) out = detail::copy_str<Char>(separator, out);
+      out = detail::write_range_entry<Char>(out, v);
+      ++i;
+    }
+    int i;
+    typename FormatContext::iterator& out;
+    basic_string_view<Char> separator;
+  };
+
+ public:
+  FMT_CONSTEXPR formatter() {}
+
+  FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
+    separator_ = sep;
+  }
+
+  FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
+                                  basic_string_view<Char> close) {
+    opening_bracket_ = open;
+    closing_bracket_ = close;
+  }
+
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    return ctx.begin();
+  }
+
+  template <typename FormatContext = format_context>
+  auto format(const TupleT& values, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    auto out = ctx.out();
+    out = detail::copy_str<Char>(opening_bracket_, out);
+    detail::for_each(values, format_each<FormatContext>{0, out, separator_});
+    out = detail::copy_str<Char>(closing_bracket_, out);
+    return out;
+  }
+};
+
+template <typename T, typename Char> struct is_range {
+  static constexpr const bool value =
+      detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
+      !std::is_convertible<T, std::basic_string<Char>>::value &&
+      !std::is_convertible<T, detail::std_string_view<Char>>::value;
+};
+
+namespace detail {
+template <typename Context> struct range_mapper {
+  using mapper = arg_mapper<Context>;
+
+  template <typename T,
+            FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
+  static auto map(T&& value) -> T&& {
+    return static_cast<T&&>(value);
+  }
+  template <typename T,
+            FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
+  static auto map(T&& value)
+      -> decltype(mapper().map(static_cast<T&&>(value))) {
+    return mapper().map(static_cast<T&&>(value));
+  }
+};
+
+template <typename Char, typename Element>
+using range_formatter_type = conditional_t<
+    is_formattable<Element, Char>::value,
+    formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
+                  std::declval<Element>()))>,
+              Char>,
+    fallback_formatter<Element, Char>>;
+
+template <typename R>
+using maybe_const_range =
+    conditional_t<has_const_begin_end<R>::value, const R, R>;
+
+// Workaround a bug in MSVC 2015 and earlier.
+#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
+template <typename R, typename Char>
+struct is_formattable_delayed
+    : disjunction<
+          is_formattable<uncvref_type<maybe_const_range<R>>, Char>,
+          has_fallback_formatter<uncvref_type<maybe_const_range<R>>, Char>> {};
+#endif
+
+}  // namespace detail
+
+template <typename T, typename Char, typename Enable = void>
+struct range_formatter;
+
+template <typename T, typename Char>
+struct range_formatter<
+    T, Char,
+    enable_if_t<conjunction<
+        std::is_same<T, remove_cvref_t<T>>,
+        disjunction<is_formattable<T, Char>,
+                    detail::has_fallback_formatter<T, Char>>>::value>> {
+ private:
+  detail::range_formatter_type<Char, T> underlying_;
+  bool custom_specs_ = false;
+  basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
+  basic_string_view<Char> opening_bracket_ =
+      detail::string_literal<Char, '['>{};
+  basic_string_view<Char> closing_bracket_ =
+      detail::string_literal<Char, ']'>{};
+
+  template <class U>
+  FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, int)
+      -> decltype(u.set_debug_format()) {
+    u.set_debug_format();
+  }
+
+  template <class U>
+  FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
+
+  FMT_CONSTEXPR void maybe_set_debug_format() {
+    maybe_set_debug_format(underlying_, 0);
+  }
+
+ public:
+  FMT_CONSTEXPR range_formatter() {}
+
+  FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
+    return underlying_;
+  }
+
+  FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
+    separator_ = sep;
+  }
+
+  FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
+                                  basic_string_view<Char> close) {
+    opening_bracket_ = open;
+    closing_bracket_ = close;
+  }
+
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    auto it = ctx.begin();
+    auto end = ctx.end();
+    if (it == end || *it == '}') {
+      maybe_set_debug_format();
+      return it;
+    }
+
+    if (*it == 'n') {
+      set_brackets({}, {});
+      ++it;
+    }
+
+    if (*it == '}') {
+      maybe_set_debug_format();
+      return it;
+    }
+
+    if (*it != ':')
+      FMT_THROW(format_error("no other top-level range formatters supported"));
+
+    custom_specs_ = true;
+    ++it;
+    ctx.advance_to(it);
+    return underlying_.parse(ctx);
+  }
+
+  template <typename R, class FormatContext>
+  auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
+    detail::range_mapper<buffer_context<Char>> mapper;
+    auto out = ctx.out();
+    out = detail::copy_str<Char>(opening_bracket_, out);
+    int i = 0;
+    auto it = detail::range_begin(range);
+    auto end = detail::range_end(range);
+    for (; it != end; ++it) {
+      if (i > 0) out = detail::copy_str<Char>(separator_, out);
+      ;
+      ctx.advance_to(out);
+      out = underlying_.format(mapper.map(*it), ctx);
+      ++i;
+    }
+    out = detail::copy_str<Char>(closing_bracket_, out);
+    return out;
+  }
+};
+
+enum class range_format { disabled, map, set, sequence, string, debug_string };
+
+namespace detail {
+template <typename T> struct range_format_kind_ {
+  static constexpr auto value = std::is_same<range_reference_type<T>, T>::value
+                                    ? range_format::disabled
+                                : is_map<T>::value ? range_format::map
+                                : is_set<T>::value ? range_format::set
+                                                   : range_format::sequence;
+};
+
+template <range_format K, typename R, typename Char, typename Enable = void>
+struct range_default_formatter;
+
+template <range_format K>
+using range_format_constant = std::integral_constant<range_format, K>;
+
+template <range_format K, typename R, typename Char>
+struct range_default_formatter<
+    K, R, Char,
+    enable_if_t<(K == range_format::sequence || K == range_format::map ||
+                 K == range_format::set)>> {
+  using range_type = detail::maybe_const_range<R>;
+  range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
+
+  FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
+
+  FMT_CONSTEXPR void init(range_format_constant<range_format::set>) {
+    underlying_.set_brackets(detail::string_literal<Char, '{'>{},
+                             detail::string_literal<Char, '}'>{});
+  }
+
+  FMT_CONSTEXPR void init(range_format_constant<range_format::map>) {
+    underlying_.set_brackets(detail::string_literal<Char, '{'>{},
+                             detail::string_literal<Char, '}'>{});
+    underlying_.underlying().set_brackets({}, {});
+    underlying_.underlying().set_separator(
+        detail::string_literal<Char, ':', ' '>{});
+  }
+
+  FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
+
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    return underlying_.parse(ctx);
+  }
+
+  template <typename FormatContext>
+  auto format(range_type& range, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    return underlying_.format(range, ctx);
+  }
+};
+}  // namespace detail
+
+template <typename T, typename Char, typename Enable = void>
+struct range_format_kind
+    : conditional_t<
+          is_range<T, Char>::value, detail::range_format_kind_<T>,
+          std::integral_constant<range_format, range_format::disabled>> {};
+
+template <typename R, typename Char>
+struct formatter<
+    R, Char,
+    enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value !=
+                                          range_format::disabled>
+// Workaround a bug in MSVC 2015 and earlier.
+#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
+                            ,
+                            detail::is_formattable_delayed<R, Char>
+#endif
+                            >::value>>
+    : detail::range_default_formatter<range_format_kind<R, Char>::value, R,
+                                      Char> {
+};
+
+template <typename Char, typename... T> struct tuple_join_view : detail::view {
+  const std::tuple<T...>& tuple;
+  basic_string_view<Char> sep;
+
+  tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
+      : tuple(t), sep{s} {}
+};
+
+template <typename Char, typename... T>
+using tuple_arg_join = tuple_join_view<Char, T...>;
+
+// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
+// support in tuple_join. It is disabled by default because of issues with
+// the dynamic width and precision.
+#ifndef FMT_TUPLE_JOIN_SPECIFIERS
+#  define FMT_TUPLE_JOIN_SPECIFIERS 0
+#endif
+
+template <typename Char, typename... T>
+struct formatter<tuple_join_view<Char, T...>, Char> {
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
+  }
+
+  template <typename FormatContext>
+  auto format(const tuple_join_view<Char, T...>& value,
+              FormatContext& ctx) const -> typename FormatContext::iterator {
+    return do_format(value, ctx,
+                     std::integral_constant<size_t, sizeof...(T)>());
+  }
+
+ private:
+  std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
+
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
+                              std::integral_constant<size_t, 0>)
+      -> decltype(ctx.begin()) {
+    return ctx.begin();
+  }
+
+  template <typename ParseContext, size_t N>
+  FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
+                              std::integral_constant<size_t, N>)
+      -> decltype(ctx.begin()) {
+    auto end = ctx.begin();
+#if FMT_TUPLE_JOIN_SPECIFIERS
+    end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
+    if (N > 1) {
+      auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
+      if (end != end1)
+        FMT_THROW(format_error("incompatible format specs for tuple elements"));
+    }
+#endif
+    return end;
+  }
+
+  template <typename FormatContext>
+  auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
+                 std::integral_constant<size_t, 0>) const ->
+      typename FormatContext::iterator {
+    return ctx.out();
+  }
+
+  template <typename FormatContext, size_t N>
+  auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
+                 std::integral_constant<size_t, N>) const ->
+      typename FormatContext::iterator {
+    auto out = std::get<sizeof...(T) - N>(formatters_)
+                   .format(std::get<sizeof...(T) - N>(value.tuple), ctx);
+    if (N > 1) {
+      out = std::copy(value.sep.begin(), value.sep.end(), out);
+      ctx.advance_to(out);
+      return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
+    }
+    return out;
+  }
+};
+
+FMT_MODULE_EXPORT_BEGIN
+
+/**
+  \rst
+  Returns an object that formats `tuple` with elements separated by `sep`.
+
+  **Example**::
+
+    std::tuple<int, char> t = {1, 'a'};
+    fmt::print("{}", fmt::join(t, ", "));
+    // Output: "1, a"
+  \endrst
+ */
+template <typename... T>
+FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
+    -> tuple_join_view<char, T...> {
+  return {tuple, sep};
+}
+
+template <typename... T>
+FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
+                        basic_string_view<wchar_t> sep)
+    -> tuple_join_view<wchar_t, T...> {
+  return {tuple, sep};
+}
+
+/**
+  \rst
+  Returns an object that formats `initializer_list` with elements separated by
+  `sep`.
+
+  **Example**::
+
+    fmt::print("{}", fmt::join({1, 2, 3}, ", "));
+    // Output: "1, 2, 3"
+  \endrst
+ */
+template <typename T>
+auto join(std::initializer_list<T> list, string_view sep)
+    -> join_view<const T*, const T*> {
+  return join(std::begin(list), std::end(list), sep);
+}
+
+FMT_MODULE_EXPORT_END
+FMT_END_NAMESPACE
+
+#endif  // FMT_RANGES_H_
diff --git a/include/spdlog/fmt/bundled/std.h b/include/spdlog/fmt/bundled/std.h
new file mode 100644
index 000000000..41d2b2838
--- /dev/null
+++ b/include/spdlog/fmt/bundled/std.h
@@ -0,0 +1,171 @@
+// Formatting library for C++ - formatters for standard library types
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_STD_H_
+#define FMT_STD_H_
+
+#include <thread>
+#include <type_traits>
+#include <utility>
+
+#include "ostream.h"
+
+#if FMT_HAS_INCLUDE(<version>)
+#  include <version>
+#endif
+// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
+#if FMT_CPLUSPLUS >= 201703L
+#  if FMT_HAS_INCLUDE(<filesystem>)
+#    include <filesystem>
+#  endif
+#  if FMT_HAS_INCLUDE(<variant>)
+#    include <variant>
+#  endif
+#endif
+
+#ifdef __cpp_lib_filesystem
+FMT_BEGIN_NAMESPACE
+
+namespace detail {
+
+template <typename Char>
+void write_escaped_path(basic_memory_buffer<Char>& quoted,
+                        const std::filesystem::path& p) {
+  write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
+}
+#  ifdef _WIN32
+template <>
+inline void write_escaped_path<char>(basic_memory_buffer<char>& quoted,
+                                     const std::filesystem::path& p) {
+  auto s = p.u8string();
+  write_escaped_string<char>(
+      std::back_inserter(quoted),
+      string_view(reinterpret_cast<const char*>(s.c_str()), s.size()));
+}
+#  endif
+template <>
+inline void write_escaped_path<std::filesystem::path::value_type>(
+    basic_memory_buffer<std::filesystem::path::value_type>& quoted,
+    const std::filesystem::path& p) {
+  write_escaped_string<std::filesystem::path::value_type>(
+      std::back_inserter(quoted), p.native());
+}
+
+}  // namespace detail
+
+template <typename Char>
+struct formatter<std::filesystem::path, Char>
+    : formatter<basic_string_view<Char>> {
+  template <typename FormatContext>
+  auto format(const std::filesystem::path& p, FormatContext& ctx) const ->
+      typename FormatContext::iterator {
+    basic_memory_buffer<Char> quoted;
+    detail::write_escaped_path(quoted, p);
+    return formatter<basic_string_view<Char>>::format(
+        basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
+  }
+};
+FMT_END_NAMESPACE
+#endif
+
+FMT_BEGIN_NAMESPACE
+template <typename Char>
+struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
+FMT_END_NAMESPACE
+
+#ifdef __cpp_lib_variant
+FMT_BEGIN_NAMESPACE
+template <typename Char> struct formatter<std::monostate, Char> {
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    return ctx.begin();
+  }
+
+  template <typename FormatContext>
+  auto format(const std::monostate&, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    auto out = ctx.out();
+    out = detail::write<Char>(out, "monostate");
+    return out;
+  }
+};
+
+namespace detail {
+
+template <typename T>
+using variant_index_sequence =
+    std::make_index_sequence<std::variant_size<T>::value>;
+
+// variant_size and variant_alternative check.
+template <typename T, typename U = void>
+struct is_variant_like_ : std::false_type {};
+template <typename T>
+struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>>
+    : std::true_type {};
+
+// formattable element check
+template <typename T, typename C> class is_variant_formattable_ {
+  template <std::size_t... I>
+  static std::conjunction<
+      is_formattable<std::variant_alternative_t<I, T>, C>...>
+      check(std::index_sequence<I...>);
+
+ public:
+  static constexpr const bool value =
+      decltype(check(variant_index_sequence<T>{}))::value;
+};
+
+template <typename Char, typename OutputIt, typename T>
+auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
+  if constexpr (is_string<T>::value)
+    return write_escaped_string<Char>(out, detail::to_string_view(v));
+  else if constexpr (std::is_same_v<T, Char>)
+    return write_escaped_char(out, v);
+  else
+    return write<Char>(out, v);
+}
+
+}  // namespace detail
+
+template <typename T> struct is_variant_like {
+  static constexpr const bool value = detail::is_variant_like_<T>::value;
+};
+
+template <typename T, typename C> struct is_variant_formattable {
+  static constexpr const bool value =
+      detail::is_variant_formattable_<T, C>::value;
+};
+
+template <typename Variant, typename Char>
+struct formatter<
+    Variant, Char,
+    std::enable_if_t<std::conjunction_v<
+        is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
+  template <typename ParseContext>
+  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
+    return ctx.begin();
+  }
+
+  template <typename FormatContext>
+  auto format(const Variant& value, FormatContext& ctx) const
+      -> decltype(ctx.out()) {
+    auto out = ctx.out();
+
+    out = detail::write<Char>(out, "variant(");
+    std::visit(
+        [&](const auto& v) {
+          out = detail::write_variant_alternative<Char>(out, v);
+        },
+        value);
+    *out++ = ')';
+    return out;
+  }
+};
+FMT_END_NAMESPACE
+#endif
+
+#endif  // FMT_STD_H_
diff --git a/include/spdlog/fmt/bundled/xchar.h b/include/spdlog/fmt/bundled/xchar.h
new file mode 100644
index 000000000..3b5bc15ca
--- /dev/null
+++ b/include/spdlog/fmt/bundled/xchar.h
@@ -0,0 +1,229 @@
+// Formatting library for C++ - optional wchar_t and exotic character support
+//
+// Copyright (c) 2012 - present, Victor Zverovich
+// All rights reserved.
+//
+// For the license information refer to format.h.
+
+#ifndef FMT_XCHAR_H_
+#define FMT_XCHAR_H_
+
+#include <cwchar>
+
+#include "format.h"
+
+FMT_BEGIN_NAMESPACE
+namespace detail {
+template <typename T>
+using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
+}
+
+FMT_MODULE_EXPORT_BEGIN
+
+using wstring_view = basic_string_view<wchar_t>;
+using wformat_parse_context = basic_format_parse_context<wchar_t>;
+using wformat_context = buffer_context<wchar_t>;
+using wformat_args = basic_format_args<wformat_context>;
+using wmemory_buffer = basic_memory_buffer<wchar_t>;
+
+#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
+// Workaround broken conversion on older gcc.
+template <typename... Args> using wformat_string = wstring_view;
+inline auto runtime(wstring_view s) -> wstring_view { return s; }
+#else
+template <typename... Args>
+using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
+inline auto runtime(wstring_view s) -> basic_runtime<wchar_t> { return {{s}}; }
+#endif
+
+template <> struct is_char<wchar_t> : std::true_type {};
+template <> struct is_char<detail::char8_type> : std::true_type {};
+template <> struct is_char<char16_t> : std::true_type {};
+template <> struct is_char<char32_t> : std::true_type {};
+
+template <typename... Args>
+constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
+    const Args&... args) {
+  return {args...};
+}
+
+inline namespace literals {
+#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
+constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
+  return {s};
+}
+#endif
+}  // namespace literals
+
+template <typename It, typename Sentinel>
+auto join(It begin, Sentinel end, wstring_view sep)
+    -> join_view<It, Sentinel, wchar_t> {
+  return {begin, end, sep};
+}
+
+template <typename Range>
+auto join(Range&& range, wstring_view sep)
+    -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
+                 wchar_t> {
+  return join(std::begin(range), std::end(range), sep);
+}
+
+template <typename T>
+auto join(std::initializer_list<T> list, wstring_view sep)
+    -> join_view<const T*, const T*, wchar_t> {
+  return join(std::begin(list), std::end(list), sep);
+}
+
+template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+auto vformat(basic_string_view<Char> format_str,
+             basic_format_args<buffer_context<type_identity_t<Char>>> args)
+    -> std::basic_string<Char> {
+  basic_memory_buffer<Char> buffer;
+  detail::vformat_to(buffer, format_str, args);
+  return to_string(buffer);
+}
+
+template <typename... T>
+auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
+  return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
+}
+
+// Pass char_t as a default template parameter instead of using
+// std::basic_string<char_t<S>> to reduce the symbol size.
+template <typename S, typename... Args, typename Char = char_t<S>,
+          FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
+                        !std::is_same<Char, wchar_t>::value)>
+auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
+  return vformat(detail::to_string_view(format_str),
+                 fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+template <typename Locale, typename S, typename Char = char_t<S>,
+          FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
+                            detail::is_exotic_char<Char>::value)>
+inline auto vformat(
+    const Locale& loc, const S& format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args)
+    -> std::basic_string<Char> {
+  return detail::vformat(loc, detail::to_string_view(format_str), args);
+}
+
+template <typename Locale, typename S, typename... Args,
+          typename Char = char_t<S>,
+          FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
+                            detail::is_exotic_char<Char>::value)>
+inline auto format(const Locale& loc, const S& format_str, Args&&... args)
+    -> std::basic_string<Char> {
+  return detail::vformat(loc, detail::to_string_view(format_str),
+                         fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+template <typename OutputIt, typename S, typename Char = char_t<S>,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
+                            detail::is_exotic_char<Char>::value)>
+auto vformat_to(OutputIt out, const S& format_str,
+                basic_format_args<buffer_context<type_identity_t<Char>>> args)
+    -> OutputIt {
+  auto&& buf = detail::get_buffer<Char>(out);
+  detail::vformat_to(buf, detail::to_string_view(format_str), args);
+  return detail::get_iterator(buf);
+}
+
+template <typename OutputIt, typename S, typename... Args,
+          typename Char = char_t<S>,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
+                            detail::is_exotic_char<Char>::value)>
+inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt {
+  return vformat_to(out, detail::to_string_view(fmt),
+                    fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+template <typename Locale, typename S, typename OutputIt, typename... Args,
+          typename Char = char_t<S>,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
+                            detail::is_locale<Locale>::value&&
+                                detail::is_exotic_char<Char>::value)>
+inline auto vformat_to(
+    OutputIt out, const Locale& loc, const S& format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
+  auto&& buf = detail::get_buffer<Char>(out);
+  vformat_to(buf, detail::to_string_view(format_str), args,
+             detail::locale_ref(loc));
+  return detail::get_iterator(buf);
+}
+
+template <
+    typename OutputIt, typename Locale, typename S, typename... Args,
+    typename Char = char_t<S>,
+    bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
+        detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
+inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
+                      Args&&... args) ->
+    typename std::enable_if<enable, OutputIt>::type {
+  return vformat_to(out, loc, to_string_view(format_str),
+                    fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+template <typename OutputIt, typename Char, typename... Args,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
+                            detail::is_exotic_char<Char>::value)>
+inline auto vformat_to_n(
+    OutputIt out, size_t n, basic_string_view<Char> format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args)
+    -> format_to_n_result<OutputIt> {
+  detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
+                                                                           n);
+  detail::vformat_to(buf, format_str, args);
+  return {buf.out(), buf.count()};
+}
+
+template <typename OutputIt, typename S, typename... Args,
+          typename Char = char_t<S>,
+          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
+                            detail::is_exotic_char<Char>::value)>
+inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
+                        const Args&... args) -> format_to_n_result<OutputIt> {
+  return vformat_to_n(out, n, detail::to_string_view(fmt),
+                      fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+template <typename S, typename... Args, typename Char = char_t<S>,
+          FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
+inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
+  detail::counting_buffer<Char> buf;
+  detail::vformat_to(buf, detail::to_string_view(fmt),
+                     fmt::make_format_args<buffer_context<Char>>(args...));
+  return buf.count();
+}
+
+inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
+  wmemory_buffer buffer;
+  detail::vformat_to(buffer, fmt, args);
+  buffer.push_back(L'\0');
+  if (std::fputws(buffer.data(), f) == -1)
+    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
+}
+
+inline void vprint(wstring_view fmt, wformat_args args) {
+  vprint(stdout, fmt, args);
+}
+
+template <typename... T>
+void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
+  return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
+}
+
+template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
+  return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
+}
+
+/**
+  Converts *value* to ``std::wstring`` using the default format for type *T*.
+ */
+template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
+  return format(FMT_STRING(L"{}"), value);
+}
+FMT_MODULE_EXPORT_END
+FMT_END_NAMESPACE
+
+#endif  // FMT_XCHAR_H_
diff --git a/include/spdlog/fmt/chrono.h b/include/spdlog/fmt/chrono.h
new file mode 100644
index 000000000..83fad2ff9
--- /dev/null
+++ b/include/spdlog/fmt/chrono.h
@@ -0,0 +1,22 @@
+//
+// Copyright(c) 2016 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+//
+// include bundled or external copy of fmtlib's chrono support
+//
+
+#if !defined(SPDLOG_USE_STD_FORMAT)
+#    if !defined(SPDLOG_FMT_EXTERNAL)
+#        ifdef SPDLOG_HEADER_ONLY
+#            ifndef FMT_HEADER_ONLY
+#                define FMT_HEADER_ONLY
+#            endif
+#        endif
+#        include <spdlog/fmt/bundled/chrono.h>
+#    else
+#        include <fmt/chrono.h>
+#    endif
+#endif
diff --git a/include/spdlog/fmt/compile.h b/include/spdlog/fmt/compile.h
new file mode 100644
index 000000000..906e9f57a
--- /dev/null
+++ b/include/spdlog/fmt/compile.h
@@ -0,0 +1,22 @@
+//
+// Copyright(c) 2016 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+//
+// include bundled or external copy of fmtlib's compile-time support
+//
+
+#if !defined(SPDLOG_USE_STD_FORMAT)
+#    if !defined(SPDLOG_FMT_EXTERNAL)
+#        ifdef SPDLOG_HEADER_ONLY
+#            ifndef FMT_HEADER_ONLY
+#                define FMT_HEADER_ONLY
+#            endif
+#        endif
+#        include <spdlog/fmt/bundled/compile.h>
+#    else
+#        include <fmt/compile.h>
+#    endif
+#endif
diff --git a/include/spdlog/fmt/fmt.h b/include/spdlog/fmt/fmt.h
new file mode 100644
index 000000000..90fcae0f0
--- /dev/null
+++ b/include/spdlog/fmt/fmt.h
@@ -0,0 +1,33 @@
+//
+// Copyright(c) 2016-2018 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+
+//
+// Include a bundled header-only copy of fmtlib or an external one.
+// By default spdlog include its own copy.
+//
+
+#if defined(SPDLOG_USE_STD_FORMAT) // SPDLOG_USE_STD_FORMAT is defined - use std::format
+#    include <format>
+#elif !defined(SPDLOG_FMT_EXTERNAL)
+#    if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY)
+#        define FMT_HEADER_ONLY
+#    endif
+#    ifndef FMT_USE_WINDOWS_H
+#        define FMT_USE_WINDOWS_H 0
+#    endif
+// enable the 'n' flag in for backward compatibility with fmt 6.x
+#    define FMT_DEPRECATED_N_SPECIFIER
+// enable ostream formatting for backward compatibility with fmt 8.x
+#    define FMT_DEPRECATED_OSTREAM
+
+#    include <spdlog/fmt/bundled/core.h>
+#    include <spdlog/fmt/bundled/format.h>
+
+#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
+#    include <fmt/core.h>
+#    include <fmt/format.h>
+#endif
diff --git a/include/spdlog/fmt/ostr.h b/include/spdlog/fmt/ostr.h
new file mode 100644
index 000000000..758803417
--- /dev/null
+++ b/include/spdlog/fmt/ostr.h
@@ -0,0 +1,22 @@
+//
+// Copyright(c) 2016 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+//
+// include bundled or external copy of fmtlib's ostream support
+//
+
+#if !defined(SPDLOG_USE_STD_FORMAT)
+#    if !defined(SPDLOG_FMT_EXTERNAL)
+#        ifdef SPDLOG_HEADER_ONLY
+#            ifndef FMT_HEADER_ONLY
+#                define FMT_HEADER_ONLY
+#            endif
+#        endif
+#        include <spdlog/fmt/bundled/ostream.h>
+#    else
+#        include <fmt/ostream.h>
+#    endif
+#endif
diff --git a/include/spdlog/fmt/ranges.h b/include/spdlog/fmt/ranges.h
new file mode 100644
index 000000000..9103a5f6a
--- /dev/null
+++ b/include/spdlog/fmt/ranges.h
@@ -0,0 +1,22 @@
+//
+// Copyright(c) 2016 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+//
+// include bundled or external copy of fmtlib's ranges support
+//
+
+#if !defined(SPDLOG_USE_STD_FORMAT)
+#    if !defined(SPDLOG_FMT_EXTERNAL)
+#        ifdef SPDLOG_HEADER_ONLY
+#            ifndef FMT_HEADER_ONLY
+#                define FMT_HEADER_ONLY
+#            endif
+#        endif
+#        include <spdlog/fmt/bundled/ranges.h>
+#    else
+#        include <fmt/ranges.h>
+#    endif
+#endif
diff --git a/include/spdlog/fmt/std.h b/include/spdlog/fmt/std.h
new file mode 100644
index 000000000..0490cab0b
--- /dev/null
+++ b/include/spdlog/fmt/std.h
@@ -0,0 +1,23 @@
+//
+// Copyright(c) 2016 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+//
+// include bundled or external copy of fmtlib's std support (for formatting e.g. std::filesystem::path, std::thread::id, std::monostate,
+// std::variant, ...)
+//
+
+#if !defined(SPDLOG_USE_STD_FORMAT)
+#    if !defined(SPDLOG_FMT_EXTERNAL)
+#        ifdef SPDLOG_HEADER_ONLY
+#            ifndef FMT_HEADER_ONLY
+#                define FMT_HEADER_ONLY
+#            endif
+#        endif
+#        include <spdlog/fmt/bundled/std.h>
+#    else
+#        include <fmt/std.h>
+#    endif
+#endif
diff --git a/include/spdlog/fmt/xchar.h b/include/spdlog/fmt/xchar.h
new file mode 100644
index 000000000..9a766e5a0
--- /dev/null
+++ b/include/spdlog/fmt/xchar.h
@@ -0,0 +1,22 @@
+//
+// Copyright(c) 2016 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#pragma once
+//
+// include bundled or external copy of fmtlib's xchar support
+//
+
+#if !defined(SPDLOG_USE_STD_FORMAT)
+#    if !defined(SPDLOG_FMT_EXTERNAL)
+#        ifdef SPDLOG_HEADER_ONLY
+#            ifndef FMT_HEADER_ONLY
+#                define FMT_HEADER_ONLY
+#            endif
+#        endif
+#        include <spdlog/fmt/bundled/xchar.h>
+#    else
+#        include <fmt/xchar.h>
+#    endif
+#endif
diff --git a/include/spdlog/formatter.h b/include/spdlog/formatter.h
new file mode 100644
index 000000000..5086fb217
--- /dev/null
+++ b/include/spdlog/formatter.h
@@ -0,0 +1,18 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/fmt/fmt.h>
+#include <spdlog/details/log_msg.h>
+
+namespace spdlog {
+
+class formatter
+{
+public:
+    virtual ~formatter() = default;
+    virtual void format(const details::log_msg &msg, memory_buf_t &dest) = 0;
+    virtual std::unique_ptr<formatter> clone() const = 0;
+};
+} // namespace spdlog
diff --git a/include/spdlog/fwd.h b/include/spdlog/fwd.h
new file mode 100644
index 000000000..d25882572
--- /dev/null
+++ b/include/spdlog/fwd.h
@@ -0,0 +1,18 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+namespace spdlog {
+class logger;
+class formatter;
+
+namespace sinks {
+class sink;
+}
+
+namespace level {
+enum level_enum : int;
+}
+
+} // namespace spdlog
diff --git a/include/spdlog/logger-inl.h b/include/spdlog/logger-inl.h
new file mode 100644
index 000000000..411f2cb5a
--- /dev/null
+++ b/include/spdlog/logger-inl.h
@@ -0,0 +1,257 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/logger.h>
+#endif
+
+#include <spdlog/sinks/sink.h>
+#include <spdlog/details/backtracer.h>
+#include <spdlog/pattern_formatter.h>
+
+#include <cstdio>
+
+namespace spdlog {
+
+// public methods
+SPDLOG_INLINE logger::logger(const logger &other)
+    : name_(other.name_)
+    , sinks_(other.sinks_)
+    , level_(other.level_.load(std::memory_order_relaxed))
+    , flush_level_(other.flush_level_.load(std::memory_order_relaxed))
+    , custom_err_handler_(other.custom_err_handler_)
+    , tracer_(other.tracer_)
+{}
+
+SPDLOG_INLINE logger::logger(logger &&other) SPDLOG_NOEXCEPT : name_(std::move(other.name_)),
+                                                               sinks_(std::move(other.sinks_)),
+                                                               level_(other.level_.load(std::memory_order_relaxed)),
+                                                               flush_level_(other.flush_level_.load(std::memory_order_relaxed)),
+                                                               custom_err_handler_(std::move(other.custom_err_handler_)),
+                                                               tracer_(std::move(other.tracer_))
+
+{}
+
+SPDLOG_INLINE logger &logger::operator=(logger other) SPDLOG_NOEXCEPT
+{
+    this->swap(other);
+    return *this;
+}
+
+SPDLOG_INLINE void logger::swap(spdlog::logger &other) SPDLOG_NOEXCEPT
+{
+    name_.swap(other.name_);
+    sinks_.swap(other.sinks_);
+
+    // swap level_
+    auto other_level = other.level_.load();
+    auto my_level = level_.exchange(other_level);
+    other.level_.store(my_level);
+
+    // swap flush level_
+    other_level = other.flush_level_.load();
+    my_level = flush_level_.exchange(other_level);
+    other.flush_level_.store(my_level);
+
+    custom_err_handler_.swap(other.custom_err_handler_);
+    std::swap(tracer_, other.tracer_);
+}
+
+SPDLOG_INLINE void swap(logger &a, logger &b)
+{
+    a.swap(b);
+}
+
+SPDLOG_INLINE void logger::set_level(level::level_enum log_level)
+{
+    level_.store(log_level);
+}
+
+SPDLOG_INLINE level::level_enum logger::level() const
+{
+    return static_cast<level::level_enum>(level_.load(std::memory_order_relaxed));
+}
+
+SPDLOG_INLINE const std::string &logger::name() const
+{
+    return name_;
+}
+
+// set formatting for the sinks in this logger.
+// each sink will get a separate instance of the formatter object.
+SPDLOG_INLINE void logger::set_formatter(std::unique_ptr<formatter> f)
+{
+    for (auto it = sinks_.begin(); it != sinks_.end(); ++it)
+    {
+        if (std::next(it) == sinks_.end())
+        {
+            // last element - we can be move it.
+            (*it)->set_formatter(std::move(f));
+            break; // to prevent clang-tidy warning
+        }
+        else
+        {
+            (*it)->set_formatter(f->clone());
+        }
+    }
+}
+
+SPDLOG_INLINE void logger::set_pattern(std::string pattern, pattern_time_type time_type)
+{
+    auto new_formatter = details::make_unique<pattern_formatter>(std::move(pattern), time_type);
+    set_formatter(std::move(new_formatter));
+}
+
+// create new backtrace sink and move to it all our child sinks
+SPDLOG_INLINE void logger::enable_backtrace(size_t n_messages)
+{
+    tracer_.enable(n_messages);
+}
+
+// restore orig sinks and level and delete the backtrace sink
+SPDLOG_INLINE void logger::disable_backtrace()
+{
+    tracer_.disable();
+}
+
+SPDLOG_INLINE void logger::dump_backtrace()
+{
+    dump_backtrace_();
+}
+
+// flush functions
+SPDLOG_INLINE void logger::flush()
+{
+    flush_();
+}
+
+SPDLOG_INLINE void logger::flush_on(level::level_enum log_level)
+{
+    flush_level_.store(log_level);
+}
+
+SPDLOG_INLINE level::level_enum logger::flush_level() const
+{
+    return static_cast<level::level_enum>(flush_level_.load(std::memory_order_relaxed));
+}
+
+// sinks
+SPDLOG_INLINE const std::vector<sink_ptr> &logger::sinks() const
+{
+    return sinks_;
+}
+
+SPDLOG_INLINE std::vector<sink_ptr> &logger::sinks()
+{
+    return sinks_;
+}
+
+// error handler
+SPDLOG_INLINE void logger::set_error_handler(err_handler handler)
+{
+    custom_err_handler_ = std::move(handler);
+}
+
+// create new logger with same sinks and configuration.
+SPDLOG_INLINE std::shared_ptr<logger> logger::clone(std::string logger_name)
+{
+    auto cloned = std::make_shared<logger>(*this);
+    cloned->name_ = std::move(logger_name);
+    return cloned;
+}
+
+// protected methods
+SPDLOG_INLINE void logger::log_it_(const spdlog::details::log_msg &log_msg, bool log_enabled, bool traceback_enabled)
+{
+    if (log_enabled)
+    {
+        sink_it_(log_msg);
+    }
+    if (traceback_enabled)
+    {
+        tracer_.push_back(log_msg);
+    }
+}
+
+SPDLOG_INLINE void logger::sink_it_(const details::log_msg &msg)
+{
+    for (auto &sink : sinks_)
+    {
+        if (sink->should_log(msg.level))
+        {
+            SPDLOG_TRY
+            {
+                sink->log(msg);
+            }
+            SPDLOG_LOGGER_CATCH(msg.source)
+        }
+    }
+
+    if (should_flush_(msg))
+    {
+        flush_();
+    }
+}
+
+SPDLOG_INLINE void logger::flush_()
+{
+    for (auto &sink : sinks_)
+    {
+        SPDLOG_TRY
+        {
+            sink->flush();
+        }
+        SPDLOG_LOGGER_CATCH(source_loc())
+    }
+}
+
+SPDLOG_INLINE void logger::dump_backtrace_()
+{
+    using details::log_msg;
+    if (tracer_.enabled())
+    {
+        sink_it_(log_msg{name(), level::info, "****************** Backtrace Start ******************"});
+        tracer_.foreach_pop([this](const log_msg &msg) { this->sink_it_(msg); });
+        sink_it_(log_msg{name(), level::info, "****************** Backtrace End ********************"});
+    }
+}
+
+SPDLOG_INLINE bool logger::should_flush_(const details::log_msg &msg)
+{
+    auto flush_level = flush_level_.load(std::memory_order_relaxed);
+    return (msg.level >= flush_level) && (msg.level != level::off);
+}
+
+SPDLOG_INLINE void logger::err_handler_(const std::string &msg)
+{
+    if (custom_err_handler_)
+    {
+        custom_err_handler_(msg);
+    }
+    else
+    {
+        using std::chrono::system_clock;
+        static std::mutex mutex;
+        static std::chrono::system_clock::time_point last_report_time;
+        static size_t err_counter = 0;
+        std::lock_guard<std::mutex> lk{mutex};
+        auto now = system_clock::now();
+        err_counter++;
+        if (now - last_report_time < std::chrono::seconds(1))
+        {
+            return;
+        }
+        last_report_time = now;
+        auto tm_time = details::os::localtime(system_clock::to_time_t(now));
+        char date_buf[64];
+        std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
+#if defined(USING_R) && defined(R_R_H) // if in R environment
+        REprintf("[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
+#else
+        std::fprintf(stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
+#endif
+    }
+}
+} // namespace spdlog
diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h
new file mode 100644
index 000000000..1fafdabdb
--- /dev/null
+++ b/include/spdlog/logger.h
@@ -0,0 +1,428 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+// Thread safe logger (except for set_error_handler())
+// Has name, log level, vector of std::shared sink pointers and formatter
+// Upon each log write the logger:
+// 1. Checks if its log level is enough to log the message and if yes:
+// 2. Call the underlying sinks to do the job.
+// 3. Each sink use its own private copy of a formatter to format the message
+// and send to its destination.
+//
+// The use of private formatter per sink provides the opportunity to cache some
+// formatted data, and support for different format per sink.
+
+#include <spdlog/common.h>
+#include <spdlog/details/log_msg.h>
+#include <spdlog/details/backtracer.h>
+
+#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
+#    ifndef _WIN32
+#        error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
+#    endif
+#    include <spdlog/details/os.h>
+#endif
+
+#include <vector>
+
+#ifndef SPDLOG_NO_EXCEPTIONS
+#    define SPDLOG_LOGGER_CATCH(location)                                                                                                  \
+        catch (const std::exception &ex)                                                                                                   \
+        {                                                                                                                                  \
+            if (location.filename)                                                                                                         \
+            {                                                                                                                              \
+                err_handler_(fmt_lib::format(SPDLOG_FMT_STRING("{} [{}({})]"), ex.what(), location.filename, location.line));              \
+            }                                                                                                                              \
+            else                                                                                                                           \
+            {                                                                                                                              \
+                err_handler_(ex.what());                                                                                                   \
+            }                                                                                                                              \
+        }                                                                                                                                  \
+        catch (...)                                                                                                                        \
+        {                                                                                                                                  \
+            err_handler_("Rethrowing unknown exception in logger");                                                                        \
+            throw;                                                                                                                         \
+        }
+#else
+#    define SPDLOG_LOGGER_CATCH(location)
+#endif
+
+namespace spdlog {
+
+class SPDLOG_API logger
+{
+public:
+    // Empty logger
+    explicit logger(std::string name)
+        : name_(std::move(name))
+        , sinks_()
+    {}
+
+    // Logger with range on sinks
+    template<typename It>
+    logger(std::string name, It begin, It end)
+        : name_(std::move(name))
+        , sinks_(begin, end)
+    {}
+
+    // Logger with single sink
+    logger(std::string name, sink_ptr single_sink)
+        : logger(std::move(name), {std::move(single_sink)})
+    {}
+
+    // Logger with sinks init list
+    logger(std::string name, sinks_init_list sinks)
+        : logger(std::move(name), sinks.begin(), sinks.end())
+    {}
+
+    virtual ~logger() = default;
+
+    logger(const logger &other);
+    logger(logger &&other) SPDLOG_NOEXCEPT;
+    logger &operator=(logger other) SPDLOG_NOEXCEPT;
+    void swap(spdlog::logger &other) SPDLOG_NOEXCEPT;
+
+    template<typename... Args>
+    void log(source_loc loc, level::level_enum lvl, format_string_t<Args...> fmt, Args &&... args)
+    {
+        log_(loc, lvl, details::to_string_view(fmt), std::forward<Args>(args)...);
+    }
+
+    template<typename... Args>
+    void log(level::level_enum lvl, format_string_t<Args...> fmt, Args &&... args)
+    {
+        log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
+    }
+
+    template<typename T>
+    void log(level::level_enum lvl, const T &msg)
+    {
+        log(source_loc{}, lvl, msg);
+    }
+
+    // T cannot be statically converted to format string (including string_view/wstring_view)
+    template<class T, typename std::enable_if<!is_convertible_to_any_format_string<const T &>::value, int>::type = 0>
+    void log(source_loc loc, level::level_enum lvl, const T &msg)
+    {
+        log(loc, lvl, "{}", msg);
+    }
+
+    void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, string_view_t msg)
+    {
+        bool log_enabled = should_log(lvl);
+        bool traceback_enabled = tracer_.enabled();
+        if (!log_enabled && !traceback_enabled)
+        {
+            return;
+        }
+
+        details::log_msg log_msg(log_time, loc, name_, lvl, msg);
+        log_it_(log_msg, log_enabled, traceback_enabled);
+    }
+
+    void log(source_loc loc, level::level_enum lvl, string_view_t msg)
+    {
+        bool log_enabled = should_log(lvl);
+        bool traceback_enabled = tracer_.enabled();
+        if (!log_enabled && !traceback_enabled)
+        {
+            return;
+        }
+
+        details::log_msg log_msg(loc, name_, lvl, msg);
+        log_it_(log_msg, log_enabled, traceback_enabled);
+    }
+
+    void log(level::level_enum lvl, string_view_t msg)
+    {
+        log(source_loc{}, lvl, msg);
+    }
+
+    template<typename... Args>
+    void trace(format_string_t<Args...> fmt, Args &&... args)
+    {
+        log(level::trace, fmt, std::forward<Args>(args)...);
+    }
+
+    template<typename... Args>
+    void debug(format_string_t<Args...> fmt, Args &&... args)
+    {
+        log(level::debug, fmt, std::forward<Args>(args)...);
+    }
+
+    template<typename... Args>
+    void info(format_string_t<Args...> fmt, Args &&... args)
+    {
+        log(level::info, fmt, std::forward<Args>(args)...);
+    }
+
+    template<typename... Args>
+    void warn(format_string_t<Args...> fmt, Args &&... args)
+    {
+        log(level::warn, fmt, std::forward<Args>(args)...);
+    }
+
+    template<typename... Args>
+    void error(format_string_t<Args...> fmt, Args &&... args)
+    {
+        log(level::err, fmt, std::forward<Args>(args)...);
+    }
+
+    template<typename... Args>
+    void critical(format_string_t<Args...> fmt, Args &&... args)
+    {
+        log(level::critical, fmt, std::forward<Args>(args)...);
+    }
+
+#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
+    template<typename... Args>
+    void log(source_loc loc, level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&... args)
+    {
+        log_(loc, lvl, details::to_string_view(fmt), std::forward<Args>(args)...);
+    }
+
+    template<typename... Args>
+    void log(level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&... args)
+    {
+        log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
+    }
+
+    void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, wstring_view_t msg)
+    {
+        bool log_enabled = should_log(lvl);
+        bool traceback_enabled = tracer_.enabled();
+        if (!log_enabled && !traceback_enabled)
+        {
+            return;
+        }
+
+        memory_buf_t buf;
+        details::os::wstr_to_utf8buf(wstring_view_t(msg.data(), msg.size()), buf);
+        details::log_msg log_msg(log_time, loc, name_, lvl, string_view_t(buf.data(), buf.size()));
+        log_it_(log_msg, log_enabled, traceback_enabled);
+    }
+
+    void log(source_loc loc, level::level_enum lvl, wstring_view_t msg)
+    {
+        bool log_enabled = should_log(lvl);
+        bool traceback_enabled = tracer_.enabled();
+        if (!log_enabled && !traceback_enabled)
+        {
+            return;
+        }
+
+        memory_buf_t buf;
+        details::os::wstr_to_utf8buf(wstring_view_t(msg.data(), msg.size()), buf);
+        details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
+        log_it_(log_msg, log_enabled, traceback_enabled);
+    }
+
+    void log(level::level_enum lvl, wstring_view_t msg)
+    {
+        log(source_loc{}, lvl, msg);
+    }
+
+    template<typename... Args>
+    void trace(wformat_string_t<Args...> fmt, Args &&... args)
+    {
+        log(level::trace, fmt, std::forward<Args>(args)...);
+    }
+
+    template<typename... Args>
+    void debug(wformat_string_t<Args...> fmt, Args &&... args)
+    {
+        log(level::debug, fmt, std::forward<Args>(args)...);
+    }
+
+    template<typename... Args>
+    void info(wformat_string_t<Args...> fmt, Args &&... args)
+    {
+        log(level::info, fmt, std::forward<Args>(args)...);
+    }
+
+    template<typename... Args>
+    void warn(wformat_string_t<Args...> fmt, Args &&... args)
+    {
+        log(level::warn, fmt, std::forward<Args>(args)...);
+    }
+
+    template<typename... Args>
+    void error(wformat_string_t<Args...> fmt, Args &&... args)
+    {
+        log(level::err, fmt, std::forward<Args>(args)...);
+    }
+
+    template<typename... Args>
+    void critical(wformat_string_t<Args...> fmt, Args &&... args)
+    {
+        log(level::critical, fmt, std::forward<Args>(args)...);
+    }
+#endif
+
+    template<typename T>
+    void trace(const T &msg)
+    {
+        log(level::trace, msg);
+    }
+
+    template<typename T>
+    void debug(const T &msg)
+    {
+        log(level::debug, msg);
+    }
+
+    template<typename T>
+    void info(const T &msg)
+    {
+        log(level::info, msg);
+    }
+
+    template<typename T>
+    void warn(const T &msg)
+    {
+        log(level::warn, msg);
+    }
+
+    template<typename T>
+    void error(const T &msg)
+    {
+        log(level::err, msg);
+    }
+
+    template<typename T>
+    void critical(const T &msg)
+    {
+        log(level::critical, msg);
+    }
+
+    // return true logging is enabled for the given level.
+    bool should_log(level::level_enum msg_level) const
+    {
+        return msg_level >= level_.load(std::memory_order_relaxed);
+    }
+
+    // return true if backtrace logging is enabled.
+    bool should_backtrace() const
+    {
+        return tracer_.enabled();
+    }
+
+    void set_level(level::level_enum log_level);
+
+    level::level_enum level() const;
+
+    const std::string &name() const;
+
+    // set formatting for the sinks in this logger.
+    // each sink will get a separate instance of the formatter object.
+    void set_formatter(std::unique_ptr<formatter> f);
+
+    // set formatting for the sinks in this logger.
+    // equivalent to
+    //     set_formatter(make_unique<pattern_formatter>(pattern, time_type))
+    // Note: each sink will get a new instance of a formatter object, replacing the old one.
+    void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
+
+    // backtrace support.
+    // efficiently store all debug/trace messages in a circular buffer until needed for debugging.
+    void enable_backtrace(size_t n_messages);
+    void disable_backtrace();
+    void dump_backtrace();
+
+    // flush functions
+    void flush();
+    void flush_on(level::level_enum log_level);
+    level::level_enum flush_level() const;
+
+    // sinks
+    const std::vector<sink_ptr> &sinks() const;
+
+    std::vector<sink_ptr> &sinks();
+
+    // error handler
+    void set_error_handler(err_handler);
+
+    // create new logger with same sinks and configuration.
+    virtual std::shared_ptr<logger> clone(std::string logger_name);
+
+protected:
+    std::string name_;
+    std::vector<sink_ptr> sinks_;
+    spdlog::level_t level_{level::info};
+    spdlog::level_t flush_level_{level::off};
+    err_handler custom_err_handler_{nullptr};
+    details::backtracer tracer_;
+
+    // common implementation for after templated public api has been resolved
+    template<typename... Args>
+    void log_(source_loc loc, level::level_enum lvl, string_view_t fmt, Args &&... args)
+    {
+        bool log_enabled = should_log(lvl);
+        bool traceback_enabled = tracer_.enabled();
+        if (!log_enabled && !traceback_enabled)
+        {
+            return;
+        }
+        SPDLOG_TRY
+        {
+            memory_buf_t buf;
+#ifdef SPDLOG_USE_STD_FORMAT
+            fmt_lib::vformat_to(std::back_inserter(buf), fmt, fmt_lib::make_format_args(std::forward<Args>(args)...));
+#else
+            fmt::vformat_to(fmt::appender(buf), fmt, fmt::make_format_args(std::forward<Args>(args)...));
+#endif
+
+            details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
+            log_it_(log_msg, log_enabled, traceback_enabled);
+        }
+        SPDLOG_LOGGER_CATCH(loc)
+    }
+
+#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
+    template<typename... Args>
+    void log_(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args &&... args)
+    {
+        bool log_enabled = should_log(lvl);
+        bool traceback_enabled = tracer_.enabled();
+        if (!log_enabled && !traceback_enabled)
+        {
+            return;
+        }
+        SPDLOG_TRY
+        {
+            // format to wmemory_buffer and convert to utf8
+            wmemory_buf_t wbuf;
+            fmt_lib::vformat_to(
+                std::back_inserter(wbuf), fmt, fmt_lib::make_format_args<fmt_lib::wformat_context>(std::forward<Args>(args)...));
+
+            memory_buf_t buf;
+            details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);
+            details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
+            log_it_(log_msg, log_enabled, traceback_enabled);
+        }
+        SPDLOG_LOGGER_CATCH(loc)
+    }
+#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
+
+    // log the given message (if the given log level is high enough),
+    // and save backtrace (if backtrace is enabled).
+    void log_it_(const details::log_msg &log_msg, bool log_enabled, bool traceback_enabled);
+    virtual void sink_it_(const details::log_msg &msg);
+    virtual void flush_();
+    void dump_backtrace_();
+    bool should_flush_(const details::log_msg &msg);
+
+    // handle errors during logging.
+    // default handler prints the error to stderr at max rate of 1 message/sec.
+    void err_handler_(const std::string &msg);
+};
+
+void swap(logger &a, logger &b);
+
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "logger-inl.h"
+#endif
diff --git a/include/spdlog/pattern_formatter-inl.h b/include/spdlog/pattern_formatter-inl.h
new file mode 100644
index 000000000..01afbe6f0
--- /dev/null
+++ b/include/spdlog/pattern_formatter-inl.h
@@ -0,0 +1,1436 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/pattern_formatter.h>
+#endif
+
+#include <spdlog/details/fmt_helper.h>
+#include <spdlog/details/log_msg.h>
+#include <spdlog/details/os.h>
+#include <spdlog/fmt/fmt.h>
+#include <spdlog/formatter.h>
+
+#include <algorithm>
+#include <array>
+#include <chrono>
+#include <ctime>
+#include <cctype>
+#include <cstring>
+#include <iterator>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <utility>
+#include <vector>
+
+namespace spdlog {
+namespace details {
+
+///////////////////////////////////////////////////////////////////////
+// name & level pattern appender
+///////////////////////////////////////////////////////////////////////
+
+class scoped_padder
+{
+public:
+    scoped_padder(size_t wrapped_size, const padding_info &padinfo, memory_buf_t &dest)
+        : padinfo_(padinfo)
+        , dest_(dest)
+    {
+        remaining_pad_ = static_cast<long>(padinfo.width_) - static_cast<long>(wrapped_size);
+        if (remaining_pad_ <= 0)
+        {
+            return;
+        }
+
+        if (padinfo_.side_ == padding_info::pad_side::left)
+        {
+            pad_it(remaining_pad_);
+            remaining_pad_ = 0;
+        }
+        else if (padinfo_.side_ == padding_info::pad_side::center)
+        {
+            auto half_pad = remaining_pad_ / 2;
+            auto reminder = remaining_pad_ & 1;
+            pad_it(half_pad);
+            remaining_pad_ = half_pad + reminder; // for the right side
+        }
+    }
+
+    template<typename T>
+    static unsigned int count_digits(T n)
+    {
+        return fmt_helper::count_digits(n);
+    }
+
+    ~scoped_padder()
+    {
+        if (remaining_pad_ >= 0)
+        {
+            pad_it(remaining_pad_);
+        }
+        else if (padinfo_.truncate_)
+        {
+            long new_size = static_cast<long>(dest_.size()) + remaining_pad_;
+            dest_.resize(static_cast<size_t>(new_size));
+        }
+    }
+
+private:
+    void pad_it(long count)
+    {
+        fmt_helper::append_string_view(string_view_t(spaces_.data(), static_cast<size_t>(count)), dest_);
+    }
+
+    const padding_info &padinfo_;
+    memory_buf_t &dest_;
+    long remaining_pad_;
+    string_view_t spaces_{"                                                                ", 64};
+};
+
+struct null_scoped_padder
+{
+    null_scoped_padder(size_t /*wrapped_size*/, const padding_info & /*padinfo*/, memory_buf_t & /*dest*/) {}
+
+    template<typename T>
+    static unsigned int count_digits(T /* number */)
+    {
+        return 0;
+    }
+};
+
+template<typename ScopedPadder>
+class name_formatter final : public flag_formatter
+{
+public:
+    explicit name_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {}
+
+    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    {
+        ScopedPadder p(msg.logger_name.size(), padinfo_, dest);
+        fmt_helper::append_string_view(msg.logger_name, dest);
+    }
+};
+
+// log level appender
+template<typename ScopedPadder>
+class level_formatter final : public flag_formatter
+{
+public:
+    explicit level_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {}
+
+    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    {
+        const string_view_t &level_name = level::to_string_view(msg.level);
+        ScopedPadder p(level_name.size(), padinfo_, dest);
+        fmt_helper::append_string_view(level_name, dest);
+    }
+};
+
+// short log level appender
+template<typename ScopedPadder>
+class short_level_formatter final : public flag_formatter
+{
+public:
+    explicit short_level_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {}
+
+    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    {
+        string_view_t level_name{level::to_short_c_str(msg.level)};
+        ScopedPadder p(level_name.size(), padinfo_, dest);
+        fmt_helper::append_string_view(level_name, dest);
+    }
+};
+
+///////////////////////////////////////////////////////////////////////
+// Date time pattern appenders
+///////////////////////////////////////////////////////////////////////
+
+static const char *ampm(const tm &t)
+{
+    return t.tm_hour >= 12 ? "PM" : "AM";
+}
+
+static int to12h(const tm &t)
+{
+    return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
+}
+
+// Abbreviated weekday name
+static std::array<const char *, 7> days{{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}};
+
+template<typename ScopedPadder>
+class a_formatter final : public flag_formatter
+{
+public:
+    explicit a_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {}
+
+    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    {
+        string_view_t field_value{days[static_cast<size_t>(tm_time.tm_wday)]};
+        ScopedPadder p(field_value.size(), padinfo_, dest);
+        fmt_helper::append_string_view(field_value, dest);
+    }
+};
+
+// Full weekday name
+static std::array<const char *, 7> full_days{{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}};
+
+template<typename ScopedPadder>
+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, memory_buf_t &dest) override
+    {
+        string_view_t field_value{full_days[static_cast<size_t>(tm_time.tm_wday)]};
+        ScopedPadder p(field_value.size(), padinfo_, dest);
+        fmt_helper::append_string_view(field_value, dest);
+    }
+};
+
+// Abbreviated month
+static const std::array<const char *, 12> months{{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}};
+
+template<typename ScopedPadder>
+class b_formatter final : public flag_formatter
+{
+public:
+    explicit b_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {}
+
+    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    {
+        string_view_t field_value{months[static_cast<size_t>(tm_time.tm_mon)]};
+        ScopedPadder p(field_value.size(), padinfo_, dest);
+        fmt_helper::append_string_view(field_value, dest);
+    }
+};
+
+// Full month name
+static const std::array<const char *, 12> full_months{
+    {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}};
+
+template<typename ScopedPadder>
+class B_formatter final : public flag_formatter
+{
+public:
+    explicit B_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {}
+
+    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    {
+        string_view_t field_value{full_months[static_cast<size_t>(tm_time.tm_mon)]};
+        ScopedPadder p(field_value.size(), padinfo_, dest);
+        fmt_helper::append_string_view(field_value, dest);
+    }
+};
+
+// Date and time representation (Thu Aug 23 15:35:46 2014)
+template<typename ScopedPadder>
+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, memory_buf_t &dest) override
+    {
+        const size_t field_size = 24;
+        ScopedPadder p(field_size, padinfo_, dest);
+
+        fmt_helper::append_string_view(days[static_cast<size_t>(tm_time.tm_wday)], dest);
+        dest.push_back(' ');
+        fmt_helper::append_string_view(months[static_cast<size_t>(tm_time.tm_mon)], dest);
+        dest.push_back(' ');
+        fmt_helper::append_int(tm_time.tm_mday, dest);
+        dest.push_back(' ');
+        // time
+
+        fmt_helper::pad2(tm_time.tm_hour, 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_int(tm_time.tm_year + 1900, dest);
+    }
+};
+
+// year - 2 digit
+template<typename ScopedPadder>
+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, memory_buf_t &dest) override
+    {
+        const size_t field_size = 2;
+        ScopedPadder p(field_size, padinfo_, dest);
+        fmt_helper::pad2(tm_time.tm_year % 100, dest);
+    }
+};
+
+// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
+template<typename ScopedPadder>
+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, memory_buf_t &dest) override
+    {
+        const size_t field_size = 10;
+        ScopedPadder 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);
+        dest.push_back('/');
+        fmt_helper::pad2(tm_time.tm_year % 100, dest);
+    }
+};
+
+// year - 4 digit
+template<typename ScopedPadder>
+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, memory_buf_t &dest) override
+    {
+        const size_t field_size = 4;
+        ScopedPadder p(field_size, padinfo_, dest);
+        fmt_helper::append_int(tm_time.tm_year + 1900, dest);
+    }
+};
+
+// month 1-12
+template<typename ScopedPadder>
+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, memory_buf_t &dest) override
+    {
+        const size_t field_size = 2;
+        ScopedPadder p(field_size, padinfo_, dest);
+        fmt_helper::pad2(tm_time.tm_mon + 1, dest);
+    }
+};
+
+// day of month 1-31
+template<typename ScopedPadder>
+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, memory_buf_t &dest) override
+    {
+        const size_t field_size = 2;
+        ScopedPadder p(field_size, padinfo_, dest);
+        fmt_helper::pad2(tm_time.tm_mday, dest);
+    }
+};
+
+// hours in 24 format 0-23
+template<typename ScopedPadder>
+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, memory_buf_t &dest) override
+    {
+        const size_t field_size = 2;
+        ScopedPadder p(field_size, padinfo_, dest);
+        fmt_helper::pad2(tm_time.tm_hour, dest);
+    }
+};
+
+// hours in 12 format 1-12
+template<typename ScopedPadder>
+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, memory_buf_t &dest) override
+    {
+        const size_t field_size = 2;
+        ScopedPadder p(field_size, padinfo_, dest);
+        fmt_helper::pad2(to12h(tm_time), dest);
+    }
+};
+
+// minutes 0-59
+template<typename ScopedPadder>
+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, memory_buf_t &dest) override
+    {
+        const size_t field_size = 2;
+        ScopedPadder p(field_size, padinfo_, dest);
+        fmt_helper::pad2(tm_time.tm_min, dest);
+    }
+};
+
+// seconds 0-59
+template<typename ScopedPadder>
+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, memory_buf_t &dest) override
+    {
+        const size_t field_size = 2;
+        ScopedPadder p(field_size, padinfo_, dest);
+        fmt_helper::pad2(tm_time.tm_sec, dest);
+    }
+};
+
+// milliseconds
+template<typename ScopedPadder>
+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 &, memory_buf_t &dest) override
+    {
+        auto millis = fmt_helper::time_fraction<std::chrono::milliseconds>(msg.time);
+        const size_t field_size = 3;
+        ScopedPadder p(field_size, padinfo_, dest);
+        fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest);
+    }
+};
+
+// microseconds
+template<typename ScopedPadder>
+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 &, memory_buf_t &dest) override
+    {
+        auto micros = fmt_helper::time_fraction<std::chrono::microseconds>(msg.time);
+
+        const size_t field_size = 6;
+        ScopedPadder p(field_size, padinfo_, dest);
+        fmt_helper::pad6(static_cast<size_t>(micros.count()), dest);
+    }
+};
+
+// nanoseconds
+template<typename ScopedPadder>
+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 &, memory_buf_t &dest) override
+    {
+        auto ns = fmt_helper::time_fraction<std::chrono::nanoseconds>(msg.time);
+        const size_t field_size = 9;
+        ScopedPadder p(field_size, padinfo_, dest);
+        fmt_helper::pad9(static_cast<size_t>(ns.count()), dest);
+    }
+};
+
+// seconds since epoch
+template<typename ScopedPadder>
+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 &, memory_buf_t &dest) override
+    {
+        const size_t field_size = 10;
+        ScopedPadder 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);
+    }
+};
+
+// AM/PM
+template<typename ScopedPadder>
+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, memory_buf_t &dest) override
+    {
+        const size_t field_size = 2;
+        ScopedPadder p(field_size, padinfo_, dest);
+        fmt_helper::append_string_view(ampm(tm_time), dest);
+    }
+};
+
+// 12 hour clock 02:55:02 pm
+template<typename ScopedPadder>
+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, memory_buf_t &dest) override
+    {
+        const size_t field_size = 11;
+        ScopedPadder 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_string_view(ampm(tm_time), dest);
+    }
+};
+
+// 24-hour HH:MM time, equivalent to %H:%M
+template<typename ScopedPadder>
+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, memory_buf_t &dest) override
+    {
+        const size_t field_size = 5;
+        ScopedPadder p(field_size, padinfo_, dest);
+
+        fmt_helper::pad2(tm_time.tm_hour, dest);
+        dest.push_back(':');
+        fmt_helper::pad2(tm_time.tm_min, dest);
+    }
+};
+
+// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
+template<typename ScopedPadder>
+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, memory_buf_t &dest) override
+    {
+        const size_t field_size = 8;
+        ScopedPadder p(field_size, padinfo_, dest);
+
+        fmt_helper::pad2(tm_time.tm_hour, dest);
+        dest.push_back(':');
+        fmt_helper::pad2(tm_time.tm_min, dest);
+        dest.push_back(':');
+        fmt_helper::pad2(tm_time.tm_sec, dest);
+    }
+};
+
+// ISO 8601 offset from UTC in timezone (+-HH:MM)
+template<typename ScopedPadder>
+class z_formatter final : public flag_formatter
+{
+public:
+    explicit z_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {}
+
+    z_formatter() = default;
+    z_formatter(const z_formatter &) = delete;
+    z_formatter &operator=(const z_formatter &) = delete;
+
+    void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) override
+    {
+        const size_t field_size = 6;
+        ScopedPadder p(field_size, padinfo_, dest);
+
+        auto total_minutes = get_cached_offset(msg, tm_time);
+        bool is_negative = total_minutes < 0;
+        if (is_negative)
+        {
+            total_minutes = -total_minutes;
+            dest.push_back('-');
+        }
+        else
+        {
+            dest.push_back('+');
+        }
+
+        fmt_helper::pad2(total_minutes / 60, dest); // hours
+        dest.push_back(':');
+        fmt_helper::pad2(total_minutes % 60, dest); // minutes
+    }
+
+private:
+    log_clock::time_point last_update_{std::chrono::seconds(0)};
+    int offset_minutes_{0};
+
+    int get_cached_offset(const log_msg &msg, const std::tm &tm_time)
+    {
+        // refresh every 10 seconds
+        if (msg.time - last_update_ >= std::chrono::seconds(10))
+        {
+            offset_minutes_ = os::utc_minutes_offset(tm_time);
+            last_update_ = msg.time;
+        }
+        return offset_minutes_;
+    }
+};
+
+// Thread id
+template<typename ScopedPadder>
+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 &, memory_buf_t &dest) override
+    {
+        const auto field_size = ScopedPadder::count_digits(msg.thread_id);
+        ScopedPadder p(field_size, padinfo_, dest);
+        fmt_helper::append_int(msg.thread_id, dest);
+    }
+};
+
+// Current pid
+template<typename ScopedPadder>
+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 &, memory_buf_t &dest) override
+    {
+        const auto pid = static_cast<uint32_t>(details::os::pid());
+        auto field_size = ScopedPadder::count_digits(pid);
+        ScopedPadder p(field_size, padinfo_, dest);
+        fmt_helper::append_int(pid, dest);
+    }
+};
+
+template<typename ScopedPadder>
+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 &, memory_buf_t &dest) override
+    {
+        ScopedPadder p(msg.payload.size(), padinfo_, dest);
+        fmt_helper::append_string_view(msg.payload, dest);
+    }
+};
+
+class ch_formatter final : public flag_formatter
+{
+public:
+    explicit ch_formatter(char ch)
+        : ch_(ch)
+    {}
+
+    void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override
+    {
+        dest.push_back(ch_);
+    }
+
+private:
+    char ch_;
+};
+
+// aggregate user chars to display as is
+class aggregate_formatter final : public flag_formatter
+{
+public:
+    aggregate_formatter() = default;
+
+    void add_ch(char ch)
+    {
+        str_ += ch;
+    }
+    void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override
+    {
+        fmt_helper::append_string_view(str_, dest);
+    }
+
+private:
+    std::string str_;
+};
+
+// 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 &, memory_buf_t &dest) override
+    {
+        msg.color_range_start = dest.size();
+    }
+};
+
+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 &, memory_buf_t &dest) override
+    {
+        msg.color_range_end = dest.size();
+    }
+};
+
+// print source location
+template<typename ScopedPadder>
+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 &, memory_buf_t &dest) override
+    {
+        if (msg.source.empty())
+        {
+            ScopedPadder p(0, padinfo_, dest);
+            return;
+        }
+
+        size_t text_size;
+        if (padinfo_.enabled())
+        {
+            // calc text size for padding based on "filename:line"
+            text_size = std::char_traits<char>::length(msg.source.filename) + ScopedPadder::count_digits(msg.source.line) + 1;
+        }
+        else
+        {
+            text_size = 0;
+        }
+
+        ScopedPadder 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);
+    }
+};
+
+// print source filename
+template<typename ScopedPadder>
+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 &, memory_buf_t &dest) override
+    {
+        if (msg.source.empty())
+        {
+            ScopedPadder p(0, padinfo_, dest);
+            return;
+        }
+        size_t text_size = padinfo_.enabled() ? std::char_traits<char>::length(msg.source.filename) : 0;
+        ScopedPadder p(text_size, padinfo_, dest);
+        fmt_helper::append_string_view(msg.source.filename, dest);
+    }
+};
+
+template<typename ScopedPadder>
+class short_filename_formatter final : public flag_formatter
+{
+public:
+    explicit short_filename_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+    {}
+
+#ifdef _MSC_VER
+#    pragma warning(push)
+#    pragma warning(disable : 4127) // consider using 'if constexpr' instead
+#endif                              // _MSC_VER
+    static const char *basename(const char *filename)
+    {
+        // if the size is 2 (1 character + null terminator) we can use the more efficient strrchr
+        // the branch will be elided by optimizations
+        if (sizeof(os::folder_seps) == 2)
+        {
+            const char *rv = std::strrchr(filename, os::folder_seps[0]);
+            return rv != nullptr ? rv + 1 : filename;
+        }
+        else
+        {
+            const std::reverse_iterator<const char *> begin(filename + std::strlen(filename));
+            const std::reverse_iterator<const char *> end(filename);
+
+            const auto it = std::find_first_of(begin, end, std::begin(os::folder_seps), std::end(os::folder_seps) - 1);
+            return it != end ? it.base() : filename;
+        }
+    }
+#ifdef _MSC_VER
+#    pragma warning(pop)
+#endif // _MSC_VER
+
+    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    {
+        if (msg.source.empty())
+        {
+            ScopedPadder p(0, padinfo_, dest);
+            return;
+        }
+        auto filename = basename(msg.source.filename);
+        size_t text_size = padinfo_.enabled() ? std::char_traits<char>::length(filename) : 0;
+        ScopedPadder p(text_size, padinfo_, dest);
+        fmt_helper::append_string_view(filename, dest);
+    }
+};
+
+template<typename ScopedPadder>
+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 &, memory_buf_t &dest) override
+    {
+        if (msg.source.empty())
+        {
+            ScopedPadder p(0, padinfo_, dest);
+            return;
+        }
+
+        auto field_size = ScopedPadder::count_digits(msg.source.line);
+        ScopedPadder p(field_size, padinfo_, dest);
+        fmt_helper::append_int(msg.source.line, dest);
+    }
+};
+
+// print source funcname
+template<typename ScopedPadder>
+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 &, memory_buf_t &dest) override
+    {
+        if (msg.source.empty())
+        {
+            ScopedPadder p(0, padinfo_, dest);
+            return;
+        }
+        size_t text_size = padinfo_.enabled() ? std::char_traits<char>::length(msg.source.funcname) : 0;
+        ScopedPadder p(text_size, padinfo_, dest);
+        fmt_helper::append_string_view(msg.source.funcname, dest);
+    }
+};
+
+// print elapsed time since last message
+template<typename ScopedPadder, typename Units>
+class elapsed_formatter final : public flag_formatter
+{
+public:
+    using DurationUnits = Units;
+
+    explicit elapsed_formatter(padding_info padinfo)
+        : flag_formatter(padinfo)
+        , last_message_time_(log_clock::now())
+    {}
+
+    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    {
+        auto delta = (std::max)(msg.time - last_message_time_, log_clock::duration::zero());
+        auto delta_units = std::chrono::duration_cast<DurationUnits>(delta);
+        last_message_time_ = msg.time;
+        auto delta_count = static_cast<size_t>(delta_units.count());
+        auto n_digits = static_cast<size_t>(ScopedPadder::count_digits(delta_count));
+        ScopedPadder p(n_digits, padinfo_, dest);
+        fmt_helper::append_int(delta_count, dest);
+    }
+
+private:
+    log_clock::time_point last_message_time_;
+};
+
+// Full info formatter
+// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] [%s:%#] %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, memory_buf_t &dest) override
+    {
+        using std::chrono::duration_cast;
+        using std::chrono::milliseconds;
+        using std::chrono::seconds;
+
+        // cache the date/time part for the next second.
+        auto duration = msg.time.time_since_epoch();
+        auto secs = duration_cast<seconds>(duration);
+
+        if (cache_timestamp_ != secs || cached_datetime_.size() == 0)
+        {
+            cached_datetime_.clear();
+            cached_datetime_.push_back('[');
+            fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_);
+            cached_datetime_.push_back('-');
+
+            fmt_helper::pad2(tm_time.tm_mon + 1, cached_datetime_);
+            cached_datetime_.push_back('-');
+
+            fmt_helper::pad2(tm_time.tm_mday, cached_datetime_);
+            cached_datetime_.push_back(' ');
+
+            fmt_helper::pad2(tm_time.tm_hour, cached_datetime_);
+            cached_datetime_.push_back(':');
+
+            fmt_helper::pad2(tm_time.tm_min, cached_datetime_);
+            cached_datetime_.push_back(':');
+
+            fmt_helper::pad2(tm_time.tm_sec, cached_datetime_);
+            cached_datetime_.push_back('.');
+
+            cache_timestamp_ = secs;
+        }
+        dest.append(cached_datetime_.begin(), cached_datetime_.end());
+
+        auto millis = fmt_helper::time_fraction<milliseconds>(msg.time);
+        fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest);
+        dest.push_back(']');
+        dest.push_back(' ');
+
+        // append logger name if exists
+        if (msg.logger_name.size() > 0)
+        {
+            dest.push_back('[');
+            fmt_helper::append_string_view(msg.logger_name, dest);
+            dest.push_back(']');
+            dest.push_back(' ');
+        }
+
+        dest.push_back('[');
+        // wrap the level name with color
+        msg.color_range_start = dest.size();
+        // 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(' ');
+
+        // add source location if present
+        if (!msg.source.empty())
+        {
+            dest.push_back('[');
+            const char *filename = details::short_filename_formatter<details::null_scoped_padder>::basename(msg.source.filename);
+            fmt_helper::append_string_view(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:
+    std::chrono::seconds cache_timestamp_{0};
+    memory_buf_t cached_datetime_;
+};
+
+} // namespace details
+
+SPDLOG_INLINE pattern_formatter::pattern_formatter(
+    std::string pattern, pattern_time_type time_type, std::string eol, custom_flags custom_user_flags)
+    : pattern_(std::move(pattern))
+    , eol_(std::move(eol))
+    , pattern_time_type_(time_type)
+    , need_localtime_(false)
+    , last_log_secs_(0)
+    , custom_handlers_(std::move(custom_user_flags))
+{
+    std::memset(&cached_tm_, 0, sizeof(cached_tm_));
+    compile_pattern_(pattern_);
+}
+
+// use by default full formatter for if pattern is not given
+SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type, std::string eol)
+    : pattern_("%+")
+    , eol_(std::move(eol))
+    , pattern_time_type_(time_type)
+    , need_localtime_(true)
+    , last_log_secs_(0)
+{
+    std::memset(&cached_tm_, 0, sizeof(cached_tm_));
+    formatters_.push_back(details::make_unique<details::full_formatter>(details::padding_info{}));
+}
+
+SPDLOG_INLINE std::unique_ptr<formatter> pattern_formatter::clone() const
+{
+    custom_flags cloned_custom_formatters;
+    for (auto &it : custom_handlers_)
+    {
+        cloned_custom_formatters[it.first] = it.second->clone();
+    }
+    auto cloned = details::make_unique<pattern_formatter>(pattern_, pattern_time_type_, eol_, std::move(cloned_custom_formatters));
+    cloned->need_localtime(need_localtime_);
+#if defined(__GNUC__) && __GNUC__ < 5
+    return std::move(cloned);
+#else
+    return cloned;
+#endif
+}
+
+SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory_buf_t &dest)
+{
+    if (need_localtime_)
+    {
+        const auto secs = std::chrono::duration_cast<std::chrono::seconds>(msg.time.time_since_epoch());
+        if (secs != last_log_secs_)
+        {
+            cached_tm_ = get_time_(msg);
+            last_log_secs_ = secs;
+        }
+    }
+
+    for (auto &f : formatters_)
+    {
+        f->format(msg, cached_tm_, dest);
+    }
+    // write eol
+    details::fmt_helper::append_string_view(eol_, dest);
+}
+
+SPDLOG_INLINE void pattern_formatter::set_pattern(std::string pattern)
+{
+    pattern_ = std::move(pattern);
+    need_localtime_ = false;
+    compile_pattern_(pattern_);
+}
+
+SPDLOG_INLINE void pattern_formatter::need_localtime(bool need)
+{
+    need_localtime_ = need;
+}
+
+SPDLOG_INLINE std::tm pattern_formatter::get_time_(const details::log_msg &msg)
+{
+    if (pattern_time_type_ == pattern_time_type::local)
+    {
+        return details::os::localtime(log_clock::to_time_t(msg.time));
+    }
+    return details::os::gmtime(log_clock::to_time_t(msg.time));
+}
+
+template<typename Padder>
+SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_info padding)
+{
+    // process custom flags
+    auto it = custom_handlers_.find(flag);
+    if (it != custom_handlers_.end())
+    {
+        auto custom_handler = it->second->clone();
+        custom_handler->set_padding_info(padding);
+        formatters_.push_back(std::move(custom_handler));
+        return;
+    }
+
+    // process built-in flags
+    switch (flag)
+    {
+    case ('+'): // default formatter
+        formatters_.push_back(details::make_unique<details::full_formatter>(padding));
+        need_localtime_ = true;
+        break;
+
+    case 'n': // logger name
+        formatters_.push_back(details::make_unique<details::name_formatter<Padder>>(padding));
+        break;
+
+    case 'l': // level
+        formatters_.push_back(details::make_unique<details::level_formatter<Padder>>(padding));
+        break;
+
+    case 'L': // short level
+        formatters_.push_back(details::make_unique<details::short_level_formatter<Padder>>(padding));
+        break;
+
+    case ('t'): // thread id
+        formatters_.push_back(details::make_unique<details::t_formatter<Padder>>(padding));
+        break;
+
+    case ('v'): // the message text
+        formatters_.push_back(details::make_unique<details::v_formatter<Padder>>(padding));
+        break;
+
+    case ('a'): // weekday
+        formatters_.push_back(details::make_unique<details::a_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('A'): // short weekday
+        formatters_.push_back(details::make_unique<details::A_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('b'):
+    case ('h'): // month
+        formatters_.push_back(details::make_unique<details::b_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('B'): // short month
+        formatters_.push_back(details::make_unique<details::B_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('c'): // datetime
+        formatters_.push_back(details::make_unique<details::c_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('C'): // year 2 digits
+        formatters_.push_back(details::make_unique<details::C_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('Y'): // year 4 digits
+        formatters_.push_back(details::make_unique<details::Y_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('D'):
+    case ('x'): // datetime MM/DD/YY
+        formatters_.push_back(details::make_unique<details::D_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('m'): // month 1-12
+        formatters_.push_back(details::make_unique<details::m_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('d'): // day of month 1-31
+        formatters_.push_back(details::make_unique<details::d_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('H'): // hours 24
+        formatters_.push_back(details::make_unique<details::H_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('I'): // hours 12
+        formatters_.push_back(details::make_unique<details::I_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('M'): // minutes
+        formatters_.push_back(details::make_unique<details::M_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('S'): // seconds
+        formatters_.push_back(details::make_unique<details::S_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('e'): // milliseconds
+        formatters_.push_back(details::make_unique<details::e_formatter<Padder>>(padding));
+        break;
+
+    case ('f'): // microseconds
+        formatters_.push_back(details::make_unique<details::f_formatter<Padder>>(padding));
+        break;
+
+    case ('F'): // nanoseconds
+        formatters_.push_back(details::make_unique<details::F_formatter<Padder>>(padding));
+        break;
+
+    case ('E'): // seconds since epoch
+        formatters_.push_back(details::make_unique<details::E_formatter<Padder>>(padding));
+        break;
+
+    case ('p'): // am/pm
+        formatters_.push_back(details::make_unique<details::p_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('r'): // 12 hour clock 02:55:02 pm
+        formatters_.push_back(details::make_unique<details::r_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('R'): // 24-hour HH:MM time
+        formatters_.push_back(details::make_unique<details::R_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('T'):
+    case ('X'): // ISO 8601 time format (HH:MM:SS)
+        formatters_.push_back(details::make_unique<details::T_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('z'): // timezone
+        formatters_.push_back(details::make_unique<details::z_formatter<Padder>>(padding));
+        need_localtime_ = true;
+        break;
+
+    case ('P'): // pid
+        formatters_.push_back(details::make_unique<details::pid_formatter<Padder>>(padding));
+        break;
+
+    case ('^'): // color range start
+        formatters_.push_back(details::make_unique<details::color_start_formatter>(padding));
+        break;
+
+    case ('$'): // color range end
+        formatters_.push_back(details::make_unique<details::color_stop_formatter>(padding));
+        break;
+
+    case ('@'): // source location (filename:filenumber)
+        formatters_.push_back(details::make_unique<details::source_location_formatter<Padder>>(padding));
+        break;
+
+    case ('s'): // short source filename - without directory name
+        formatters_.push_back(details::make_unique<details::short_filename_formatter<Padder>>(padding));
+        break;
+
+    case ('g'): // full source filename
+        formatters_.push_back(details::make_unique<details::source_filename_formatter<Padder>>(padding));
+        break;
+
+    case ('#'): // source line number
+        formatters_.push_back(details::make_unique<details::source_linenum_formatter<Padder>>(padding));
+        break;
+
+    case ('!'): // source funcname
+        formatters_.push_back(details::make_unique<details::source_funcname_formatter<Padder>>(padding));
+        break;
+
+    case ('%'): // % char
+        formatters_.push_back(details::make_unique<details::ch_formatter>('%'));
+        break;
+
+    case ('u'): // elapsed time since last log message in nanos
+        formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::nanoseconds>>(padding));
+        break;
+
+    case ('i'): // elapsed time since last log message in micros
+        formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::microseconds>>(padding));
+        break;
+
+    case ('o'): // elapsed time since last log message in millis
+        formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::milliseconds>>(padding));
+        break;
+
+    case ('O'): // elapsed time since last log message in seconds
+        formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::seconds>>(padding));
+        break;
+
+    default: // Unknown flag appears as is
+        auto unknown_flag = details::make_unique<details::aggregate_formatter>();
+
+        if (!padding.truncate_)
+        {
+            unknown_flag->add_ch('%');
+            unknown_flag->add_ch(flag);
+            formatters_.push_back((std::move(unknown_flag)));
+        }
+        // fix issue #1617 (prev char was '!' and should have been treated as funcname flag instead of truncating flag)
+        // spdlog::set_pattern("[%10!] %v") => "[      main] some message"
+        // spdlog::set_pattern("[%3!!] %v") => "[mai] some message"
+        else
+        {
+            padding.truncate_ = false;
+            formatters_.push_back(details::make_unique<details::source_funcname_formatter<Padder>>(padding));
+            unknown_flag->add_ch(flag);
+            formatters_.push_back((std::move(unknown_flag)));
+        }
+
+        break;
+    }
+}
+
+// Extract given pad spec (e.g. %8X, %=8X, %-8!X, %8!X, %=8!X, %-8!X, %+8!X)
+// Advance the given it pass the end of the padding spec found (if any)
+// Return padding.
+SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end)
+{
+    using details::padding_info;
+    using details::scoped_padder;
+    const size_t max_width = 64;
+    if (it == end)
+    {
+        return padding_info{};
+    }
+
+    padding_info::pad_side side;
+    switch (*it)
+    {
+    case '-':
+        side = padding_info::pad_side::right;
+        ++it;
+        break;
+    case '=':
+        side = padding_info::pad_side::center;
+        ++it;
+        break;
+    default:
+        side = details::padding_info::pad_side::left;
+        break;
+    }
+
+    if (it == end || !std::isdigit(static_cast<unsigned char>(*it)))
+    {
+        return padding_info{}; // no padding if no digit found here
+    }
+
+    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;
+    }
+
+    // search for the optional truncate marker '!'
+    bool truncate;
+    if (it != end && *it == '!')
+    {
+        truncate = true;
+        ++it;
+    }
+    else
+    {
+        truncate = false;
+    }
+    return details::padding_info{std::min<size_t>(width, max_width), side, truncate};
+}
+
+SPDLOG_INLINE void pattern_formatter::compile_pattern_(const std::string &pattern)
+{
+    auto end = pattern.end();
+    std::unique_ptr<details::aggregate_formatter> user_chars;
+    formatters_.clear();
+    for (auto it = pattern.begin(); it != end; ++it)
+    {
+        if (*it == '%')
+        {
+            if (user_chars) // append user chars found so far
+            {
+                formatters_.push_back(std::move(user_chars));
+            }
+
+            auto padding = handle_padspec_(++it, end);
+
+            if (it != end)
+            {
+                if (padding.enabled())
+                {
+                    handle_flag_<details::scoped_padder>(*it, padding);
+                }
+                else
+                {
+                    handle_flag_<details::null_scoped_padder>(*it, padding);
+                }
+            }
+            else
+            {
+                break;
+            }
+        }
+        else // chars not following the % sign should be displayed as is
+        {
+            if (!user_chars)
+            {
+                user_chars = details::make_unique<details::aggregate_formatter>();
+            }
+            user_chars->add_ch(*it);
+        }
+    }
+    if (user_chars) // append raw chars found so far
+    {
+        formatters_.push_back(std::move(user_chars));
+    }
+}
+} // namespace spdlog
diff --git a/include/spdlog/pattern_formatter.h b/include/spdlog/pattern_formatter.h
new file mode 100644
index 000000000..acf1c5365
--- /dev/null
+++ b/include/spdlog/pattern_formatter.h
@@ -0,0 +1,128 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/common.h>
+#include <spdlog/details/log_msg.h>
+#include <spdlog/details/os.h>
+#include <spdlog/formatter.h>
+
+#include <chrono>
+#include <ctime>
+#include <memory>
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+
+namespace spdlog {
+namespace details {
+
+// padding information.
+struct padding_info
+{
+    enum class pad_side
+    {
+        left,
+        right,
+        center
+    };
+
+    padding_info() = default;
+    padding_info(size_t width, padding_info::pad_side side, bool truncate)
+        : width_(width)
+        , side_(side)
+        , truncate_(truncate)
+        , enabled_(true)
+    {}
+
+    bool enabled() const
+    {
+        return enabled_;
+    }
+    size_t width_ = 0;
+    pad_side side_ = pad_side::left;
+    bool truncate_ = false;
+    bool enabled_ = false;
+};
+
+class SPDLOG_API 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, memory_buf_t &dest) = 0;
+
+protected:
+    padding_info padinfo_;
+};
+
+} // namespace details
+
+class SPDLOG_API custom_flag_formatter : public details::flag_formatter
+{
+public:
+    virtual std::unique_ptr<custom_flag_formatter> clone() const = 0;
+
+    void set_padding_info(const details::padding_info &padding)
+    {
+        flag_formatter::padinfo_ = padding;
+    }
+};
+
+class SPDLOG_API pattern_formatter final : public formatter
+{
+public:
+    using custom_flags = std::unordered_map<char, std::unique_ptr<custom_flag_formatter>>;
+
+    explicit pattern_formatter(std::string pattern, pattern_time_type time_type = pattern_time_type::local,
+        std::string eol = spdlog::details::os::default_eol, custom_flags custom_user_flags = custom_flags());
+
+    // use default 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_formatter(const pattern_formatter &other) = delete;
+    pattern_formatter &operator=(const pattern_formatter &other) = delete;
+
+    std::unique_ptr<formatter> clone() const override;
+    void format(const details::log_msg &msg, memory_buf_t &dest) override;
+
+    template<typename T, typename... Args>
+    pattern_formatter &add_flag(char flag, Args &&... args)
+    {
+        custom_handlers_[flag] = details::make_unique<T>(std::forward<Args>(args)...);
+        return *this;
+    }
+    void set_pattern(std::string pattern);
+    void need_localtime(bool need = true);
+
+private:
+    std::string pattern_;
+    std::string eol_;
+    pattern_time_type pattern_time_type_;
+    bool need_localtime_;
+    std::tm cached_tm_;
+    std::chrono::seconds last_log_secs_;
+    std::vector<std::unique_ptr<details::flag_formatter>> formatters_;
+    custom_flags custom_handlers_;
+
+    std::tm get_time_(const details::log_msg &msg);
+    template<typename Padder>
+    void handle_flag_(char flag, details::padding_info padding);
+
+    // Extract given pad spec (e.g. %8X)
+    // Advance the given it pass the end of the padding spec found (if any)
+    // Return padding.
+    static details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end);
+
+    void compile_pattern_(const std::string &pattern);
+};
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "pattern_formatter-inl.h"
+#endif
diff --git a/include/spdlog/sinks/android_sink.h b/include/spdlog/sinks/android_sink.h
new file mode 100644
index 000000000..07dbeea8e
--- /dev/null
+++ b/include/spdlog/sinks/android_sink.h
@@ -0,0 +1,142 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifdef __ANDROID__
+
+#    include <spdlog/details/fmt_helper.h>
+#    include <spdlog/details/null_mutex.h>
+#    include <spdlog/details/os.h>
+#    include <spdlog/sinks/base_sink.h>
+#    include <spdlog/details/synchronous_factory.h>
+
+#    include <android/log.h>
+#    include <chrono>
+#    include <mutex>
+#    include <string>
+#    include <thread>
+#    include <type_traits>
+
+#    if !defined(SPDLOG_ANDROID_RETRIES)
+#        define SPDLOG_ANDROID_RETRIES 2
+#    endif
+
+namespace spdlog {
+namespace sinks {
+
+/*
+ * Android sink
+ * (logging using __android_log_write or __android_log_buf_write depending on the specified BufferID)
+ */
+template<typename Mutex, int BufferID = log_id::LOG_ID_MAIN>
+class android_sink final : public base_sink<Mutex>
+{
+public:
+    explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false)
+        : tag_(std::move(tag))
+        , use_raw_msg_(use_raw_msg)
+    {}
+
+protected:
+    void sink_it_(const details::log_msg &msg) override
+    {
+        const android_LogPriority priority = convert_to_android_(msg.level);
+        memory_buf_t formatted;
+        if (use_raw_msg_)
+        {
+            details::fmt_helper::append_string_view(msg.payload, formatted);
+        }
+        else
+        {
+            base_sink<Mutex>::formatter_->format(msg, formatted);
+        }
+        formatted.push_back('\0');
+        const char *msg_output = formatted.data();
+
+        // See system/core/liblog/logger_write.c for explanation of return value
+        int ret = android_log(priority, tag_.c_str(), msg_output);
+        int retry_count = 0;
+        while ((ret == -11 /*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES))
+        {
+            details::os::sleep_for_millis(5);
+            ret = android_log(priority, tag_.c_str(), msg_output);
+            retry_count++;
+        }
+
+        if (ret < 0)
+        {
+            throw_spdlog_ex("logging to Android failed", ret);
+        }
+    }
+
+    void flush_() override {}
+
+private:
+    // There might be liblog versions used, that do not support __android_log_buf_write. So we only compile and link against
+    // __android_log_buf_write, if user explicitely provides a non-default log buffer. Otherwise, when using the default log buffer, always
+    // log via __android_log_write.
+    template<int ID = BufferID>
+    typename std::enable_if<ID == static_cast<int>(log_id::LOG_ID_MAIN), int>::type android_log(int prio, const char *tag, const char *text)
+    {
+        return __android_log_write(prio, tag, text);
+    }
+
+    template<int ID = BufferID>
+    typename std::enable_if<ID != static_cast<int>(log_id::LOG_ID_MAIN), int>::type android_log(int prio, const char *tag, const char *text)
+    {
+        return __android_log_buf_write(ID, prio, tag, text);
+    }
+
+    static android_LogPriority convert_to_android_(spdlog::level::level_enum level)
+    {
+        switch (level)
+        {
+        case spdlog::level::trace:
+            return ANDROID_LOG_VERBOSE;
+        case spdlog::level::debug:
+            return ANDROID_LOG_DEBUG;
+        case spdlog::level::info:
+            return ANDROID_LOG_INFO;
+        case spdlog::level::warn:
+            return ANDROID_LOG_WARN;
+        case spdlog::level::err:
+            return ANDROID_LOG_ERROR;
+        case spdlog::level::critical:
+            return ANDROID_LOG_FATAL;
+        default:
+            return ANDROID_LOG_DEFAULT;
+        }
+    }
+
+    std::string tag_;
+    bool use_raw_msg_;
+};
+
+using android_sink_mt = android_sink<std::mutex>;
+using android_sink_st = android_sink<details::null_mutex>;
+
+template<int BufferId = log_id::LOG_ID_MAIN>
+using android_sink_buf_mt = android_sink<std::mutex, BufferId>;
+template<int BufferId = log_id::LOG_ID_MAIN>
+using android_sink_buf_st = android_sink<details::null_mutex, BufferId>;
+
+} // namespace sinks
+
+// Create and register android syslog logger
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> android_logger_mt(const std::string &logger_name, const std::string &tag = "spdlog")
+{
+    return Factory::template create<sinks::android_sink_mt>(logger_name, tag);
+}
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> android_logger_st(const std::string &logger_name, const std::string &tag = "spdlog")
+{
+    return Factory::template create<sinks::android_sink_st>(logger_name, tag);
+}
+
+} // namespace spdlog
+
+#endif // __ANDROID__
\ No newline at end of file
diff --git a/include/spdlog/sinks/ansicolor_sink-inl.h b/include/spdlog/sinks/ansicolor_sink-inl.h
new file mode 100644
index 000000000..b5848f2db
--- /dev/null
+++ b/include/spdlog/sinks/ansicolor_sink-inl.h
@@ -0,0 +1,145 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/sinks/ansicolor_sink.h>
+#endif
+
+#include <spdlog/pattern_formatter.h>
+#include <spdlog/details/os.h>
+
+namespace spdlog {
+namespace sinks {
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE ansicolor_sink<ConsoleMutex>::ansicolor_sink(FILE *target_file, color_mode mode)
+    : target_file_(target_file)
+    , mutex_(ConsoleMutex::mutex())
+    , formatter_(details::make_unique<spdlog::pattern_formatter>())
+
+{
+    set_color_mode(mode);
+    colors_[level::trace] = to_string_(white);
+    colors_[level::debug] = to_string_(cyan);
+    colors_[level::info] = to_string_(green);
+    colors_[level::warn] = to_string_(yellow_bold);
+    colors_[level::err] = to_string_(red_bold);
+    colors_[level::critical] = to_string_(bold_on_red);
+    colors_[level::off] = to_string_(reset);
+}
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color(level::level_enum color_level, string_view_t color)
+{
+    std::lock_guard<mutex_t> lock(mutex_);
+    colors_[static_cast<size_t>(color_level)] = to_string_(color);
+}
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
+{
+    // Wrap the originally formatted message in color codes.
+    // If color is not supported in the terminal, log as is instead.
+    std::lock_guard<mutex_t> lock(mutex_);
+    msg.color_range_start = 0;
+    msg.color_range_end = 0;
+    memory_buf_t formatted;
+    formatter_->format(msg, formatted);
+    if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
+    {
+        // before color range
+        print_range_(formatted, 0, msg.color_range_start);
+        // in color range
+        print_ccode_(colors_[static_cast<size_t>(msg.level)]);
+        print_range_(formatted, msg.color_range_start, msg.color_range_end);
+        print_ccode_(reset);
+        // after color range
+        print_range_(formatted, msg.color_range_end, formatted.size());
+    }
+    else // no color
+    {
+        print_range_(formatted, 0, formatted.size());
+    }
+    fflush(target_file_);
+}
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::flush()
+{
+    std::lock_guard<mutex_t> lock(mutex_);
+    fflush(target_file_);
+}
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_pattern(const std::string &pattern)
+{
+    std::lock_guard<mutex_t> lock(mutex_);
+    formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
+}
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
+{
+    std::lock_guard<mutex_t> lock(mutex_);
+    formatter_ = std::move(sink_formatter);
+}
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE bool ansicolor_sink<ConsoleMutex>::should_color()
+{
+    return should_do_colors_;
+}
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
+{
+    switch (mode)
+    {
+    case color_mode::always:
+        should_do_colors_ = true;
+        return;
+    case color_mode::automatic:
+        should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal();
+        return;
+    case color_mode::never:
+        should_do_colors_ = false;
+        return;
+    default:
+        should_do_colors_ = false;
+    }
+}
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_ccode_(const string_view_t &color_code)
+{
+    fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_);
+}
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
+{
+    fwrite(formatted.data() + start, sizeof(char), end - start, target_file_);
+}
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE std::string ansicolor_sink<ConsoleMutex>::to_string_(const string_view_t &sv)
+{
+    return std::string(sv.data(), sv.size());
+}
+
+// ansicolor_stdout_sink
+template<typename ConsoleMutex>
+SPDLOG_INLINE ansicolor_stdout_sink<ConsoleMutex>::ansicolor_stdout_sink(color_mode mode)
+    : ansicolor_sink<ConsoleMutex>(stdout, mode)
+{}
+
+// ansicolor_stderr_sink
+template<typename ConsoleMutex>
+SPDLOG_INLINE ansicolor_stderr_sink<ConsoleMutex>::ansicolor_stderr_sink(color_mode mode)
+    : ansicolor_sink<ConsoleMutex>(stderr, mode)
+{}
+
+} // namespace sinks
+} // namespace spdlog
diff --git a/include/spdlog/sinks/ansicolor_sink.h b/include/spdlog/sinks/ansicolor_sink.h
new file mode 100644
index 000000000..39d966bc9
--- /dev/null
+++ b/include/spdlog/sinks/ansicolor_sink.h
@@ -0,0 +1,118 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/details/console_globals.h>
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/sinks/sink.h>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <array>
+
+namespace spdlog {
+namespace sinks {
+
+/**
+ * This sink prefixes the output with an ANSI escape sequence color code
+ * depending on the severity
+ * of the message.
+ * If no color terminal detected, omit the escape codes.
+ */
+
+template<typename ConsoleMutex>
+class ansicolor_sink : public sink
+{
+public:
+    using mutex_t = typename ConsoleMutex::mutex_t;
+    ansicolor_sink(FILE *target_file, color_mode mode);
+    ~ansicolor_sink() override = default;
+
+    ansicolor_sink(const ansicolor_sink &other) = delete;
+    ansicolor_sink(ansicolor_sink &&other) = delete;
+
+    ansicolor_sink &operator=(const ansicolor_sink &other) = delete;
+    ansicolor_sink &operator=(ansicolor_sink &&other) = delete;
+
+    void set_color(level::level_enum color_level, string_view_t color);
+    void set_color_mode(color_mode mode);
+    bool should_color();
+
+    void log(const details::log_msg &msg) override;
+    void flush() override;
+    void set_pattern(const std::string &pattern) final;
+    void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
+
+    // Formatting codes
+    const string_view_t reset = "\033[m";
+    const string_view_t bold = "\033[1m";
+    const string_view_t dark = "\033[2m";
+    const string_view_t underline = "\033[4m";
+    const string_view_t blink = "\033[5m";
+    const string_view_t reverse = "\033[7m";
+    const string_view_t concealed = "\033[8m";
+    const string_view_t clear_line = "\033[K";
+
+    // Foreground colors
+    const string_view_t black = "\033[30m";
+    const string_view_t red = "\033[31m";
+    const string_view_t green = "\033[32m";
+    const string_view_t yellow = "\033[33m";
+    const string_view_t blue = "\033[34m";
+    const string_view_t magenta = "\033[35m";
+    const string_view_t cyan = "\033[36m";
+    const string_view_t white = "\033[37m";
+
+    /// Background colors
+    const string_view_t on_black = "\033[40m";
+    const string_view_t on_red = "\033[41m";
+    const string_view_t on_green = "\033[42m";
+    const string_view_t on_yellow = "\033[43m";
+    const string_view_t on_blue = "\033[44m";
+    const string_view_t on_magenta = "\033[45m";
+    const string_view_t on_cyan = "\033[46m";
+    const string_view_t on_white = "\033[47m";
+
+    /// Bold colors
+    const string_view_t yellow_bold = "\033[33m\033[1m";
+    const string_view_t red_bold = "\033[31m\033[1m";
+    const string_view_t bold_on_red = "\033[1m\033[41m";
+
+private:
+    FILE *target_file_;
+    mutex_t &mutex_;
+    bool should_do_colors_;
+    std::unique_ptr<spdlog::formatter> formatter_;
+    std::array<std::string, level::n_levels> colors_;
+    void print_ccode_(const string_view_t &color_code);
+    void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
+    static std::string to_string_(const string_view_t &sv);
+};
+
+template<typename ConsoleMutex>
+class ansicolor_stdout_sink : public ansicolor_sink<ConsoleMutex>
+{
+public:
+    explicit ansicolor_stdout_sink(color_mode mode = color_mode::automatic);
+};
+
+template<typename ConsoleMutex>
+class ansicolor_stderr_sink : public ansicolor_sink<ConsoleMutex>
+{
+public:
+    explicit ansicolor_stderr_sink(color_mode mode = color_mode::automatic);
+};
+
+using ansicolor_stdout_sink_mt = ansicolor_stdout_sink<details::console_mutex>;
+using ansicolor_stdout_sink_st = ansicolor_stdout_sink<details::console_nullmutex>;
+
+using ansicolor_stderr_sink_mt = ansicolor_stderr_sink<details::console_mutex>;
+using ansicolor_stderr_sink_st = ansicolor_stderr_sink<details::console_nullmutex>;
+
+} // namespace sinks
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "ansicolor_sink-inl.h"
+#endif
diff --git a/include/spdlog/sinks/base_sink-inl.h b/include/spdlog/sinks/base_sink-inl.h
new file mode 100644
index 000000000..421fdf9dd
--- /dev/null
+++ b/include/spdlog/sinks/base_sink-inl.h
@@ -0,0 +1,63 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/sinks/base_sink.h>
+#endif
+
+#include <spdlog/common.h>
+#include <spdlog/pattern_formatter.h>
+
+#include <memory>
+
+template<typename Mutex>
+SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::base_sink()
+    : formatter_{details::make_unique<spdlog::pattern_formatter>()}
+{}
+
+template<typename Mutex>
+SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::base_sink(std::unique_ptr<spdlog::formatter> formatter)
+    : formatter_{std::move(formatter)}
+{}
+
+template<typename Mutex>
+void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::log(const details::log_msg &msg)
+{
+    std::lock_guard<Mutex> lock(mutex_);
+    sink_it_(msg);
+}
+
+template<typename Mutex>
+void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::flush()
+{
+    std::lock_guard<Mutex> lock(mutex_);
+    flush_();
+}
+
+template<typename Mutex>
+void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_pattern(const std::string &pattern)
+{
+    std::lock_guard<Mutex> lock(mutex_);
+    set_pattern_(pattern);
+}
+
+template<typename Mutex>
+void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
+{
+    std::lock_guard<Mutex> lock(mutex_);
+    set_formatter_(std::move(sink_formatter));
+}
+
+template<typename Mutex>
+void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_pattern_(const std::string &pattern)
+{
+    set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
+}
+
+template<typename Mutex>
+void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter)
+{
+    formatter_ = std::move(sink_formatter);
+}
diff --git a/include/spdlog/sinks/base_sink.h b/include/spdlog/sinks/base_sink.h
new file mode 100644
index 000000000..2e795f592
--- /dev/null
+++ b/include/spdlog/sinks/base_sink.h
@@ -0,0 +1,52 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+//
+// base sink templated over a mutex (either dummy or real)
+// concrete implementation should override the sink_it_() and flush_()  methods.
+// locking is taken care of in this class - no locking needed by the
+// implementers..
+//
+
+#include <spdlog/common.h>
+#include <spdlog/details/log_msg.h>
+#include <spdlog/sinks/sink.h>
+
+namespace spdlog {
+namespace sinks {
+template<typename Mutex>
+class SPDLOG_API base_sink : public sink
+{
+public:
+    base_sink();
+    explicit base_sink(std::unique_ptr<spdlog::formatter> formatter);
+    ~base_sink() override = default;
+
+    base_sink(const base_sink &) = delete;
+    base_sink(base_sink &&) = delete;
+
+    base_sink &operator=(const base_sink &) = delete;
+    base_sink &operator=(base_sink &&) = delete;
+
+    void log(const details::log_msg &msg) final;
+    void flush() final;
+    void set_pattern(const std::string &pattern) final;
+    void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) final;
+
+protected:
+    // sink formatter
+    std::unique_ptr<spdlog::formatter> formatter_;
+    Mutex mutex_;
+
+    virtual void sink_it_(const details::log_msg &msg) = 0;
+    virtual void flush_() = 0;
+    virtual void set_pattern_(const std::string &pattern);
+    virtual void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter);
+};
+} // namespace sinks
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "base_sink-inl.h"
+#endif
diff --git a/include/spdlog/sinks/basic_file_sink-inl.h b/include/spdlog/sinks/basic_file_sink-inl.h
new file mode 100644
index 000000000..8d23f96df
--- /dev/null
+++ b/include/spdlog/sinks/basic_file_sink-inl.h
@@ -0,0 +1,44 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/sinks/basic_file_sink.h>
+#endif
+
+#include <spdlog/common.h>
+#include <spdlog/details/os.h>
+
+namespace spdlog {
+namespace sinks {
+
+template<typename Mutex>
+SPDLOG_INLINE basic_file_sink<Mutex>::basic_file_sink(const filename_t &filename, bool truncate, const file_event_handlers &event_handlers)
+    : file_helper_{event_handlers}
+{
+    file_helper_.open(filename, truncate);
+}
+
+template<typename Mutex>
+SPDLOG_INLINE const filename_t &basic_file_sink<Mutex>::filename() const
+{
+    return file_helper_.filename();
+}
+
+template<typename Mutex>
+SPDLOG_INLINE void basic_file_sink<Mutex>::sink_it_(const details::log_msg &msg)
+{
+    memory_buf_t formatted;
+    base_sink<Mutex>::formatter_->format(msg, formatted);
+    file_helper_.write(formatted);
+}
+
+template<typename Mutex>
+SPDLOG_INLINE void basic_file_sink<Mutex>::flush_()
+{
+    file_helper_.flush();
+}
+
+} // namespace sinks
+} // namespace spdlog
diff --git a/include/spdlog/sinks/basic_file_sink.h b/include/spdlog/sinks/basic_file_sink.h
new file mode 100644
index 000000000..aacc993bf
--- /dev/null
+++ b/include/spdlog/sinks/basic_file_sink.h
@@ -0,0 +1,60 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// 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/sinks/base_sink.h>
+#include <spdlog/details/synchronous_factory.h>
+
+#include <mutex>
+#include <string>
+
+namespace spdlog {
+namespace sinks {
+/*
+ * Trivial file sink with single file as target
+ */
+template<typename Mutex>
+class basic_file_sink final : public base_sink<Mutex>
+{
+public:
+    explicit basic_file_sink(const filename_t &filename, bool truncate = false, const file_event_handlers &event_handlers = {});
+    const filename_t &filename() const;
+
+protected:
+    void sink_it_(const details::log_msg &msg) override;
+    void flush_() override;
+
+private:
+    details::file_helper file_helper_;
+};
+
+using basic_file_sink_mt = basic_file_sink<std::mutex>;
+using basic_file_sink_st = basic_file_sink<details::null_mutex>;
+
+} // namespace sinks
+
+//
+// factory functions
+//
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> basic_logger_mt(
+    const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers &event_handlers = {})
+{
+    return Factory::template create<sinks::basic_file_sink_mt>(logger_name, filename, truncate, event_handlers);
+}
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> basic_logger_st(
+    const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers &event_handlers = {})
+{
+    return Factory::template create<sinks::basic_file_sink_st>(logger_name, filename, truncate, event_handlers);
+}
+
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "basic_file_sink-inl.h"
+#endif
diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h
new file mode 100644
index 000000000..f6f1bb1d7
--- /dev/null
+++ b/include/spdlog/sinks/daily_file_sink.h
@@ -0,0 +1,297 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/common.h>
+#include <spdlog/details/file_helper.h>
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/fmt/fmt.h>
+#include <spdlog/fmt/chrono.h>
+#include <spdlog/sinks/base_sink.h>
+#include <spdlog/details/os.h>
+#include <spdlog/details/circular_q.h>
+#include <spdlog/details/synchronous_factory.h>
+
+#include <chrono>
+#include <cstdio>
+#include <ctime>
+#include <mutex>
+#include <string>
+
+namespace spdlog {
+namespace sinks {
+
+/*
+ * Generator of daily log file names in format basename.YYYY-MM-DD.ext
+ */
+struct daily_filename_calculator
+{
+    // Create filename for the form basename.YYYY-MM-DD
+    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_extension(filename);
+        return fmt_lib::format(SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}")), basename, now_tm.tm_year + 1900,
+            now_tm.tm_mon + 1, now_tm.tm_mday, ext);
+    }
+};
+
+/*
+ * Generator of daily log file names with strftime format.
+ * Usages:
+ *    auto sink =  std::make_shared<spdlog::sinks::daily_file_format_sink_mt>("myapp-%Y-%m-%d:%H:%M:%S.log", hour, minute);"
+ *    auto logger = spdlog::daily_logger_format_mt("loggername, "myapp-%Y-%m-%d:%X.log", hour,  minute)"
+ *
+ */
+struct daily_filename_format_calculator
+{
+    static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
+    {
+#ifdef SPDLOG_USE_STD_FORMAT
+        // adapted from fmtlib: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/chrono.h#L522-L546
+
+        filename_t tm_format;
+        tm_format.append(filename);
+        // By appending an extra space we can distinguish an empty result that
+        // indicates insufficient buffer size from a guaranteed non-empty result
+        // https://github.com/fmtlib/fmt/issues/2238
+        tm_format.push_back(' ');
+
+        const size_t MIN_SIZE = 10;
+        filename_t buf;
+        buf.resize(MIN_SIZE);
+        for (;;)
+        {
+            size_t count = strftime(buf.data(), buf.size(), tm_format.c_str(), &now_tm);
+            if (count != 0)
+            {
+                // Remove the extra space.
+                buf.resize(count - 1);
+                break;
+            }
+            buf.resize(buf.size() * 2);
+        }
+
+        return buf;
+#else
+        // generate fmt datetime format string, e.g. {:%Y-%m-%d}.
+        filename_t fmt_filename = fmt::format(SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{{:{}}}")), filename);
+
+        // MSVC doesn't allow fmt::runtime(..) with wchar, with fmtlib versions < 9.1.x
+#    if defined(_MSC_VER) && defined(SPDLOG_WCHAR_FILENAMES) && FMT_VERSION < 90101
+        return fmt::format(fmt_filename, now_tm);
+#    else
+        return fmt::format(SPDLOG_FMT_RUNTIME(fmt_filename), now_tm);
+#    endif
+
+#endif
+    }
+
+private:
+#if defined __GNUC__
+#    pragma GCC diagnostic push
+#    pragma GCC diagnostic ignored "-Wformat-nonliteral"
+#endif
+
+    static size_t strftime(char *str, size_t count, const char *format, const std::tm *time)
+    {
+        return std::strftime(str, count, format, time);
+    }
+
+    static size_t strftime(wchar_t *str, size_t count, const wchar_t *format, const std::tm *time)
+    {
+        return std::wcsftime(str, count, format, time);
+    }
+
+#if defined(__GNUC__)
+#    pragma GCC diagnostic pop
+#endif
+};
+
+/*
+ * Rotating file sink based on date.
+ * If truncate != false , the created file will be truncated.
+ * If max_files > 0, retain only the last max_files and delete previous.
+ */
+template<typename Mutex, typename FileNameCalc = daily_filename_calculator>
+class daily_file_sink final : public base_sink<Mutex>
+{
+public:
+    // create daily file sink which rotates on given time
+    daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0,
+        const file_event_handlers &event_handlers = {})
+        : base_filename_(std::move(base_filename))
+        , rotation_h_(rotation_hour)
+        , rotation_m_(rotation_minute)
+        , file_helper_{event_handlers}
+        , truncate_(truncate)
+        , max_files_(max_files)
+        , filenames_q_()
+    {
+        if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
+        {
+            throw_spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
+        }
+
+        auto now = log_clock::now();
+        auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
+        file_helper_.open(filename, truncate_);
+        rotation_tp_ = next_rotation_tp_();
+
+        if (max_files_ > 0)
+        {
+            init_filenames_q_();
+        }
+    }
+
+    filename_t filename()
+    {
+        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+        return file_helper_.filename();
+    }
+
+protected:
+    void sink_it_(const details::log_msg &msg) override
+    {
+        auto time = msg.time;
+        bool should_rotate = time >= rotation_tp_;
+        if (should_rotate)
+        {
+            auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
+            file_helper_.open(filename, truncate_);
+            rotation_tp_ = next_rotation_tp_();
+        }
+        memory_buf_t formatted;
+        base_sink<Mutex>::formatter_->format(msg, formatted);
+        file_helper_.write(formatted);
+
+        // Do the cleaning only at the end because it might throw on failure.
+        if (should_rotate && max_files_ > 0)
+        {
+            delete_old_();
+        }
+    }
+
+    void flush_() override
+    {
+        file_helper_.flush();
+    }
+
+private:
+    void init_filenames_q_()
+    {
+        using details::os::path_exists;
+
+        filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
+        std::vector<filename_t> filenames;
+        auto now = log_clock::now();
+        while (filenames.size() < max_files_)
+        {
+            auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
+            if (!path_exists(filename))
+            {
+                break;
+            }
+            filenames.emplace_back(filename);
+            now -= std::chrono::hours(24);
+        }
+        for (auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
+        {
+            filenames_q_.push_back(std::move(*iter));
+        }
+    }
+
+    tm now_tm(log_clock::time_point tp)
+    {
+        time_t tnow = log_clock::to_time_t(tp);
+        return spdlog::details::os::localtime(tnow);
+    }
+
+    log_clock::time_point next_rotation_tp_()
+    {
+        auto now = log_clock::now();
+        tm date = now_tm(now);
+        date.tm_hour = rotation_h_;
+        date.tm_min = rotation_m_;
+        date.tm_sec = 0;
+        auto rotation_time = log_clock::from_time_t(std::mktime(&date));
+        if (rotation_time > now)
+        {
+            return rotation_time;
+        }
+        return {rotation_time + std::chrono::hours(24)};
+    }
+
+    // Delete the file N rotations ago.
+    // Throw spdlog_ex on failure to delete the old file.
+    void delete_old_()
+    {
+        using details::os::filename_to_str;
+        using details::os::remove_if_exists;
+
+        filename_t current_file = file_helper_.filename();
+        if (filenames_q_.full())
+        {
+            auto old_filename = std::move(filenames_q_.front());
+            filenames_q_.pop_front();
+            bool ok = remove_if_exists(old_filename) == 0;
+            if (!ok)
+            {
+                filenames_q_.push_back(std::move(current_file));
+                throw_spdlog_ex("Failed removing daily file " + filename_to_str(old_filename), errno);
+            }
+        }
+        filenames_q_.push_back(std::move(current_file));
+    }
+
+    filename_t base_filename_;
+    int rotation_h_;
+    int rotation_m_;
+    log_clock::time_point rotation_tp_;
+    details::file_helper file_helper_;
+    bool truncate_;
+    uint16_t max_files_;
+    details::circular_q<filename_t> filenames_q_;
+};
+
+using daily_file_sink_mt = daily_file_sink<std::mutex>;
+using daily_file_sink_st = daily_file_sink<details::null_mutex>;
+using daily_file_format_sink_mt = daily_file_sink<std::mutex, daily_filename_format_calculator>;
+using daily_file_format_sink_st = daily_file_sink<details::null_mutex, daily_filename_format_calculator>;
+
+} // namespace sinks
+
+//
+// factory functions
+//
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> daily_logger_mt(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0,
+    bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
+{
+    return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate, max_files, event_handlers);
+}
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> daily_logger_format_mt(const std::string &logger_name, const filename_t &filename, int hour = 0,
+    int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
+{
+    return Factory::template create<sinks::daily_file_format_sink_mt>(
+        logger_name, filename, hour, minute, truncate, max_files, event_handlers);
+}
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> daily_logger_st(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0,
+    bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
+{
+    return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files, event_handlers);
+}
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> daily_logger_format_st(const std::string &logger_name, const filename_t &filename, int hour = 0,
+    int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
+{
+    return Factory::template create<sinks::daily_file_format_sink_st>(
+        logger_name, filename, hour, minute, truncate, max_files, event_handlers);
+}
+} // namespace spdlog
diff --git a/include/spdlog/sinks/dist_sink.h b/include/spdlog/sinks/dist_sink.h
new file mode 100644
index 000000000..065048ad4
--- /dev/null
+++ b/include/spdlog/sinks/dist_sink.h
@@ -0,0 +1,97 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include "base_sink.h"
+#include <spdlog/details/log_msg.h>
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/pattern_formatter.h>
+
+#include <algorithm>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+// Distribution sink (mux). Stores a vector of sinks which get called when log
+// is called
+
+namespace spdlog {
+namespace sinks {
+
+template<typename Mutex>
+class dist_sink : public base_sink<Mutex>
+{
+public:
+    dist_sink() = default;
+    explicit dist_sink(std::vector<std::shared_ptr<sink>> sinks)
+        : sinks_(sinks)
+    {}
+
+    dist_sink(const dist_sink &) = delete;
+    dist_sink &operator=(const dist_sink &) = delete;
+
+    void add_sink(std::shared_ptr<sink> sink)
+    {
+        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+        sinks_.push_back(sink);
+    }
+
+    void remove_sink(std::shared_ptr<sink> sink)
+    {
+        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+        sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
+    }
+
+    void set_sinks(std::vector<std::shared_ptr<sink>> sinks)
+    {
+        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+        sinks_ = std::move(sinks);
+    }
+
+    std::vector<std::shared_ptr<sink>> &sinks()
+    {
+        return sinks_;
+    }
+
+protected:
+    void sink_it_(const details::log_msg &msg) override
+    {
+        for (auto &sub_sink : sinks_)
+        {
+            if (sub_sink->should_log(msg.level))
+            {
+                sub_sink->log(msg);
+            }
+        }
+    }
+
+    void flush_() override
+    {
+        for (auto &sub_sink : sinks_)
+        {
+            sub_sink->flush();
+        }
+    }
+
+    void set_pattern_(const std::string &pattern) override
+    {
+        set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
+    }
+
+    void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter) override
+    {
+        base_sink<Mutex>::formatter_ = std::move(sink_formatter);
+        for (auto &sub_sink : sinks_)
+        {
+            sub_sink->set_formatter(base_sink<Mutex>::formatter_->clone());
+        }
+    }
+    std::vector<std::shared_ptr<sink>> sinks_;
+};
+
+using dist_sink_mt = dist_sink<std::mutex>;
+using dist_sink_st = dist_sink<details::null_mutex>;
+
+} // namespace sinks
+} // namespace spdlog
diff --git a/include/spdlog/sinks/dup_filter_sink.h b/include/spdlog/sinks/dup_filter_sink.h
new file mode 100644
index 000000000..1a4fb3485
--- /dev/null
+++ b/include/spdlog/sinks/dup_filter_sink.h
@@ -0,0 +1,94 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include "dist_sink.h"
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/details/log_msg.h>
+
+#include <cstdio>
+#include <mutex>
+#include <string>
+#include <chrono>
+
+// Duplicate message removal sink.
+// Skip the message if previous one is identical and less than "max_skip_duration" have passed
+//
+// Example:
+//
+//     #include <spdlog/sinks/dup_filter_sink.h>
+//
+//     int main() {
+//         auto dup_filter = std::make_shared<dup_filter_sink_st>(std::chrono::seconds(5));
+//         dup_filter->add_sink(std::make_shared<stdout_color_sink_mt>());
+//         spdlog::logger l("logger", dup_filter);
+//         l.info("Hello");
+//         l.info("Hello");
+//         l.info("Hello");
+//         l.info("Different Hello");
+//     }
+//
+// Will produce:
+//       [2019-06-25 17:50:56.511] [logger] [info] Hello
+//       [2019-06-25 17:50:56.512] [logger] [info] Skipped 3 duplicate messages..
+//       [2019-06-25 17:50:56.512] [logger] [info] Different Hello
+
+namespace spdlog {
+namespace sinks {
+template<typename Mutex>
+class dup_filter_sink : public dist_sink<Mutex>
+{
+public:
+    template<class Rep, class Period>
+    explicit dup_filter_sink(std::chrono::duration<Rep, Period> max_skip_duration)
+        : max_skip_duration_{max_skip_duration}
+    {}
+
+protected:
+    std::chrono::microseconds max_skip_duration_;
+    log_clock::time_point last_msg_time_;
+    std::string last_msg_payload_;
+    size_t skip_counter_ = 0;
+
+    void sink_it_(const details::log_msg &msg) override
+    {
+        bool filtered = filter_(msg);
+        if (!filtered)
+        {
+            skip_counter_ += 1;
+            return;
+        }
+
+        // log the "skipped.." message
+        if (skip_counter_ > 0)
+        {
+            char buf[64];
+            auto msg_size = ::snprintf(buf, sizeof(buf), "Skipped %u duplicate messages..", static_cast<unsigned>(skip_counter_));
+            if (msg_size > 0 && static_cast<size_t>(msg_size) < sizeof(buf))
+            {
+                details::log_msg skipped_msg{msg.source, msg.logger_name, level::info, string_view_t{buf, static_cast<size_t>(msg_size)}};
+                dist_sink<Mutex>::sink_it_(skipped_msg);
+            }
+        }
+
+        // log current message
+        dist_sink<Mutex>::sink_it_(msg);
+        last_msg_time_ = msg.time;
+        skip_counter_ = 0;
+        last_msg_payload_.assign(msg.payload.data(), msg.payload.data() + msg.payload.size());
+    }
+
+    // return whether the log msg should be displayed (true) or skipped (false)
+    bool filter_(const details::log_msg &msg)
+    {
+        auto filter_duration = msg.time - last_msg_time_;
+        return (filter_duration > max_skip_duration_) || (msg.payload != last_msg_payload_);
+    }
+};
+
+using dup_filter_sink_mt = dup_filter_sink<std::mutex>;
+using dup_filter_sink_st = dup_filter_sink<details::null_mutex>;
+
+} // namespace sinks
+} // namespace spdlog
diff --git a/include/spdlog/sinks/hourly_file_sink.h b/include/spdlog/sinks/hourly_file_sink.h
new file mode 100644
index 000000000..33dd89483
--- /dev/null
+++ b/include/spdlog/sinks/hourly_file_sink.h
@@ -0,0 +1,204 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/common.h>
+#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/details/os.h>
+#include <spdlog/details/circular_q.h>
+#include <spdlog/details/synchronous_factory.h>
+
+#include <chrono>
+#include <cstdio>
+#include <ctime>
+#include <mutex>
+#include <string>
+
+namespace spdlog {
+namespace sinks {
+
+/*
+ * Generator of Hourly log file names in format basename.YYYY-MM-DD-HH.ext
+ */
+struct hourly_filename_calculator
+{
+    // Create filename for the form basename.YYYY-MM-DD-H
+    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_extension(filename);
+        return fmt_lib::format(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1,
+            now_tm.tm_mday, now_tm.tm_hour, ext);
+    }
+};
+
+/*
+ * Rotating file sink based on time.
+ * If truncate != false , the created file will be truncated.
+ * If max_files > 0, retain only the last max_files and delete previous.
+ */
+template<typename Mutex, typename FileNameCalc = hourly_filename_calculator>
+class hourly_file_sink final : public base_sink<Mutex>
+{
+public:
+    // create hourly file sink which rotates on given time
+    hourly_file_sink(
+        filename_t base_filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
+        : base_filename_(std::move(base_filename))
+        , file_helper_{event_handlers}
+        , truncate_(truncate)
+        , max_files_(max_files)
+        , filenames_q_()
+    {
+        auto now = log_clock::now();
+        auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
+        file_helper_.open(filename, truncate_);
+        remove_init_file_ = file_helper_.size() == 0;
+        rotation_tp_ = next_rotation_tp_();
+
+        if (max_files_ > 0)
+        {
+            init_filenames_q_();
+        }
+    }
+
+    filename_t filename()
+    {
+        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+        return file_helper_.filename();
+    }
+
+protected:
+    void sink_it_(const details::log_msg &msg) override
+    {
+        auto time = msg.time;
+        bool should_rotate = time >= rotation_tp_;
+        if (should_rotate)
+        {
+            if (remove_init_file_)
+            {
+                file_helper_.close();
+                details::os::remove(file_helper_.filename());
+            }
+            auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
+            file_helper_.open(filename, truncate_);
+            rotation_tp_ = next_rotation_tp_();
+        }
+        remove_init_file_ = false;
+        memory_buf_t formatted;
+        base_sink<Mutex>::formatter_->format(msg, formatted);
+        file_helper_.write(formatted);
+
+        // Do the cleaning only at the end because it might throw on failure.
+        if (should_rotate && max_files_ > 0)
+        {
+            delete_old_();
+        }
+    }
+
+    void flush_() override
+    {
+        file_helper_.flush();
+    }
+
+private:
+    void init_filenames_q_()
+    {
+        using details::os::path_exists;
+
+        filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
+        std::vector<filename_t> filenames;
+        auto now = log_clock::now();
+        while (filenames.size() < max_files_)
+        {
+            auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
+            if (!path_exists(filename))
+            {
+                break;
+            }
+            filenames.emplace_back(filename);
+            now -= std::chrono::hours(1);
+        }
+        for (auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
+        {
+            filenames_q_.push_back(std::move(*iter));
+        }
+    }
+
+    tm now_tm(log_clock::time_point tp)
+    {
+        time_t tnow = log_clock::to_time_t(tp);
+        return spdlog::details::os::localtime(tnow);
+    }
+
+    log_clock::time_point next_rotation_tp_()
+    {
+        auto now = log_clock::now();
+        tm date = now_tm(now);
+        date.tm_min = 0;
+        date.tm_sec = 0;
+        auto rotation_time = log_clock::from_time_t(std::mktime(&date));
+        if (rotation_time > now)
+        {
+            return rotation_time;
+        }
+        return {rotation_time + std::chrono::hours(1)};
+    }
+
+    // Delete the file N rotations ago.
+    // Throw spdlog_ex on failure to delete the old file.
+    void delete_old_()
+    {
+        using details::os::filename_to_str;
+        using details::os::remove_if_exists;
+
+        filename_t current_file = file_helper_.filename();
+        if (filenames_q_.full())
+        {
+            auto old_filename = std::move(filenames_q_.front());
+            filenames_q_.pop_front();
+            bool ok = remove_if_exists(old_filename) == 0;
+            if (!ok)
+            {
+                filenames_q_.push_back(std::move(current_file));
+                SPDLOG_THROW(spdlog_ex("Failed removing hourly file " + filename_to_str(old_filename), errno));
+            }
+        }
+        filenames_q_.push_back(std::move(current_file));
+    }
+
+    filename_t base_filename_;
+    log_clock::time_point rotation_tp_;
+    details::file_helper file_helper_;
+    bool truncate_;
+    uint16_t max_files_;
+    details::circular_q<filename_t> filenames_q_;
+    bool remove_init_file_;
+};
+
+using hourly_file_sink_mt = hourly_file_sink<std::mutex>;
+using hourly_file_sink_st = hourly_file_sink<details::null_mutex>;
+
+} // namespace sinks
+
+//
+// factory functions
+//
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> hourly_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false,
+    uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
+{
+    return Factory::template create<sinks::hourly_file_sink_mt>(logger_name, filename, truncate, max_files, event_handlers);
+}
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> hourly_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false,
+    uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
+{
+    return Factory::template create<sinks::hourly_file_sink_st>(logger_name, filename, truncate, max_files, event_handlers);
+}
+} // namespace spdlog
diff --git a/include/spdlog/sinks/mongo_sink.h b/include/spdlog/sinks/mongo_sink.h
new file mode 100644
index 000000000..6a8927f54
--- /dev/null
+++ b/include/spdlog/sinks/mongo_sink.h
@@ -0,0 +1,107 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+//
+// Custom sink for mongodb
+// Building and using requires mongocxx library.
+// For building mongocxx library check the url below
+// http://mongocxx.org/mongocxx-v3/installation/
+//
+
+#include "spdlog/common.h"
+#include "spdlog/details/log_msg.h"
+#include "spdlog/sinks/base_sink.h"
+#include <spdlog/details/synchronous_factory.h>
+
+#include <bsoncxx/builder/stream/document.hpp>
+#include <bsoncxx/types.hpp>
+#include <bsoncxx/view_or_value.hpp>
+
+#include <mongocxx/client.hpp>
+#include <mongocxx/instance.hpp>
+#include <mongocxx/uri.hpp>
+
+namespace spdlog {
+namespace sinks {
+template<typename Mutex>
+class mongo_sink : public base_sink<Mutex>
+{
+public:
+    mongo_sink(const std::string &db_name, const std::string &collection_name, const std::string &uri = "mongodb://localhost:27017")
+    try : mongo_sink(std::make_shared<mongocxx::instance>(), db_name, collection_name, uri)
+    {}
+    catch (const std::exception &e)
+    {
+        throw_spdlog_ex(fmt_lib::format("Error opening database: {}", e.what()));
+    }
+
+    mongo_sink(std::shared_ptr<mongocxx::instance> instance, const std::string &db_name, const std::string &collection_name,
+        const std::string &uri = "mongodb://localhost:27017")
+        : instance_(std::move(instance))
+        , db_name_(db_name)
+        , coll_name_(collection_name)
+    {
+        try
+        {
+            client_ = spdlog::details::make_unique<mongocxx::client>(mongocxx::uri{uri});
+        }
+        catch (const std::exception &e)
+        {
+            throw_spdlog_ex(fmt_lib::format("Error opening database: {}", e.what()));
+        }
+    }
+
+    ~mongo_sink()
+    {
+        flush_();
+    }
+
+protected:
+    void sink_it_(const details::log_msg &msg) override
+    {
+        using bsoncxx::builder::stream::document;
+        using bsoncxx::builder::stream::finalize;
+
+        if (client_ != nullptr)
+        {
+            auto doc = document{} << "timestamp" << bsoncxx::types::b_date(msg.time) << "level" << level::to_string_view(msg.level).data()
+                                  << "level_num" << msg.level << "message" << std::string(msg.payload.begin(), msg.payload.end())
+                                  << "logger_name" << std::string(msg.logger_name.begin(), msg.logger_name.end()) << "thread_id"
+                                  << static_cast<int>(msg.thread_id) << finalize;
+            client_->database(db_name_).collection(coll_name_).insert_one(doc.view());
+        }
+    }
+
+    void flush_() override {}
+
+private:
+    std::shared_ptr<mongocxx::instance> instance_;
+    std::string db_name_;
+    std::string coll_name_;
+    std::unique_ptr<mongocxx::client> client_ = nullptr;
+};
+
+#include "spdlog/details/null_mutex.h"
+#include <mutex>
+using mongo_sink_mt = mongo_sink<std::mutex>;
+using mongo_sink_st = mongo_sink<spdlog::details::null_mutex>;
+
+} // namespace sinks
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> mongo_logger_mt(const std::string &logger_name, const std::string &db_name,
+    const std::string &collection_name, const std::string &uri = "mongodb://localhost:27017")
+{
+    return Factory::template create<sinks::mongo_sink_mt>(logger_name, db_name, collection_name, uri);
+}
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> mongo_logger_st(const std::string &logger_name, const std::string &db_name,
+    const std::string &collection_name, const std::string &uri = "mongodb://localhost:27017")
+{
+    return Factory::template create<sinks::mongo_sink_st>(logger_name, db_name, collection_name, uri);
+}
+
+} // namespace spdlog
diff --git a/include/spdlog/sinks/msvc_sink.h b/include/spdlog/sinks/msvc_sink.h
new file mode 100644
index 000000000..09008b784
--- /dev/null
+++ b/include/spdlog/sinks/msvc_sink.h
@@ -0,0 +1,59 @@
+// Copyright(c) 2016 Alexander Dalshov & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+
+#if defined(_WIN32)
+
+#    include <spdlog/details/null_mutex.h>
+#    include <spdlog/sinks/base_sink.h>
+
+#    include <mutex>
+#    include <string>
+
+// Avoid including windows.h (https://stackoverflow.com/a/30741042)
+extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString);
+extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+
+namespace spdlog {
+namespace sinks {
+/*
+ * MSVC sink (logging using OutputDebugStringA)
+ */
+template<typename Mutex>
+class msvc_sink : public base_sink<Mutex>
+{
+public:
+    msvc_sink() = default;
+    msvc_sink(bool check_ebugger_present)
+        : check_debbugger_present_{check_ebugger_present} {};
+
+protected:
+    void sink_it_(const details::log_msg &msg) override
+    {
+        if (check_debbugger_present_ && !IsDebuggerPresent())
+        {
+            return;
+        }
+        memory_buf_t formatted;
+        base_sink<Mutex>::formatter_->format(msg, formatted);
+        formatted.push_back('\0'); // add a null terminator for OutputDebugStringA
+        OutputDebugStringA(formatted.data());
+    }
+
+    void flush_() override {}
+
+    bool check_debbugger_present_ = true;
+};
+
+using msvc_sink_mt = msvc_sink<std::mutex>;
+using msvc_sink_st = msvc_sink<details::null_mutex>;
+
+using windebug_sink_mt = msvc_sink_mt;
+using windebug_sink_st = msvc_sink_st;
+
+} // namespace sinks
+} // namespace spdlog
+
+#endif
diff --git a/include/spdlog/sinks/null_sink.h b/include/spdlog/sinks/null_sink.h
new file mode 100644
index 000000000..eb8328014
--- /dev/null
+++ b/include/spdlog/sinks/null_sink.h
@@ -0,0 +1,44 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/sinks/base_sink.h>
+#include <spdlog/details/synchronous_factory.h>
+
+#include <mutex>
+
+namespace spdlog {
+namespace sinks {
+
+template<typename Mutex>
+class null_sink : public base_sink<Mutex>
+{
+protected:
+    void sink_it_(const details::log_msg &) override {}
+    void flush_() override {}
+};
+
+using null_sink_mt = null_sink<details::null_mutex>;
+using null_sink_st = null_sink<details::null_mutex>;
+
+} // namespace sinks
+
+template<typename Factory = spdlog::synchronous_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 = spdlog::synchronous_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/include/spdlog/sinks/ostream_sink.h b/include/spdlog/sinks/ostream_sink.h
new file mode 100644
index 000000000..95c1e9620
--- /dev/null
+++ b/include/spdlog/sinks/ostream_sink.h
@@ -0,0 +1,50 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/sinks/base_sink.h>
+
+#include <mutex>
+#include <ostream>
+
+namespace spdlog {
+namespace sinks {
+template<typename Mutex>
+class ostream_sink final : public base_sink<Mutex>
+{
+public:
+    explicit ostream_sink(std::ostream &os, bool force_flush = false)
+        : ostream_(os)
+        , force_flush_(force_flush)
+    {}
+    ostream_sink(const ostream_sink &) = delete;
+    ostream_sink &operator=(const ostream_sink &) = delete;
+
+protected:
+    void sink_it_(const details::log_msg &msg) override
+    {
+        memory_buf_t formatted;
+        base_sink<Mutex>::formatter_->format(msg, formatted);
+        ostream_.write(formatted.data(), static_cast<std::streamsize>(formatted.size()));
+        if (force_flush_)
+        {
+            ostream_.flush();
+        }
+    }
+
+    void flush_() override
+    {
+        ostream_.flush();
+    }
+
+    std::ostream &ostream_;
+    bool force_flush_;
+};
+
+using ostream_sink_mt = ostream_sink<std::mutex>;
+using ostream_sink_st = ostream_sink<details::null_mutex>;
+
+} // namespace sinks
+} // namespace spdlog
diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h
new file mode 100644
index 000000000..31b49c609
--- /dev/null
+++ b/include/spdlog/sinks/qt_sinks.h
@@ -0,0 +1,102 @@
+// Copyright(c) 2015-present, Gabi Melman, mguludag and spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+//
+// Custom sink for QPlainTextEdit or QTextEdit and its childs(QTextBrowser...
+// etc) Building and using requires Qt library.
+//
+
+#include "spdlog/common.h"
+#include "spdlog/details/log_msg.h"
+#include "spdlog/details/synchronous_factory.h"
+#include "spdlog/sinks/base_sink.h"
+
+#include <QTextEdit>
+#include <QPlainTextEdit>
+
+//
+// qt_sink class
+//
+namespace spdlog {
+namespace sinks {
+template<typename Mutex>
+class qt_sink : public base_sink<Mutex>
+{
+public:
+    qt_sink(QObject *qt_object, const std::string &meta_method)
+    {
+        qt_object_ = qt_object;
+        meta_method_ = meta_method;
+    }
+
+    ~qt_sink()
+    {
+        flush_();
+    }
+
+protected:
+    void sink_it_(const details::log_msg &msg) override
+    {
+        memory_buf_t formatted;
+        base_sink<Mutex>::formatter_->format(msg, formatted);
+        string_view_t str = string_view_t(formatted.data(), formatted.size());
+        QMetaObject::invokeMethod(qt_object_, meta_method_.c_str(), Qt::AutoConnection,
+            Q_ARG(QString, QString::fromUtf8(str.data(), static_cast<int>(str.size())).trimmed()));
+    }
+
+    void flush_() override {}
+
+private:
+    QObject *qt_object_ = nullptr;
+    std::string meta_method_;
+};
+
+#include "spdlog/details/null_mutex.h"
+#include <mutex>
+using qt_sink_mt = qt_sink<std::mutex>;
+using qt_sink_st = qt_sink<spdlog::details::null_mutex>;
+} // namespace sinks
+
+//
+// Factory functions
+//
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> qt_logger_mt(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append")
+{
+    return Factory::template create<sinks::qt_sink_mt>(logger_name, qt_object, meta_method);
+}
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> qt_logger_st(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append")
+{
+    return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
+}
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> qt_logger_mt(
+    const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText")
+{
+    return Factory::template create<sinks::qt_sink_mt>(logger_name, qt_object, meta_method);
+}
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> qt_logger_st(
+    const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText")
+{
+    return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
+}
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> qt_logger_mt(const std::string &logger_name, QObject *qt_object, const std::string &meta_method)
+{
+    return Factory::template create<sinks::qt_sink_mt>(logger_name, qt_object, meta_method);
+}
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> qt_logger_st(const std::string &logger_name, QObject *qt_object, const std::string &meta_method)
+{
+    return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
+}
+} // namespace spdlog
diff --git a/include/spdlog/sinks/ringbuffer_sink.h b/include/spdlog/sinks/ringbuffer_sink.h
new file mode 100644
index 000000000..65e227aa5
--- /dev/null
+++ b/include/spdlog/sinks/ringbuffer_sink.h
@@ -0,0 +1,74 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include "spdlog/sinks/base_sink.h"
+#include "spdlog/details/circular_q.h"
+#include "spdlog/details/log_msg_buffer.h"
+#include "spdlog/details/null_mutex.h"
+
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace spdlog {
+namespace sinks {
+/*
+ * Ring buffer sink
+ */
+template<typename Mutex>
+class ringbuffer_sink final : public base_sink<Mutex>
+{
+public:
+    explicit ringbuffer_sink(size_t n_items)
+        : q_{n_items}
+    {}
+
+    std::vector<details::log_msg_buffer> last_raw(size_t lim = 0)
+    {
+        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+        auto items_available = q_.size();
+        auto n_items = lim > 0 ? (std::min)(lim, items_available) : items_available;
+        std::vector<details::log_msg_buffer> ret;
+        ret.reserve(n_items);
+        for (size_t i = (items_available - n_items); i < items_available; i++)
+        {
+            ret.push_back(q_.at(i));
+        }
+        return ret;
+    }
+
+    std::vector<std::string> last_formatted(size_t lim = 0)
+    {
+        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+        auto items_available = q_.size();
+        auto n_items = lim > 0 ? (std::min)(lim, items_available) : items_available;
+        std::vector<std::string> ret;
+        ret.reserve(n_items);
+        for (size_t i = (items_available - n_items); i < items_available; i++)
+        {
+            memory_buf_t formatted;
+            base_sink<Mutex>::formatter_->format(q_.at(i), formatted);
+            ret.push_back(std::move(SPDLOG_BUF_TO_STRING(formatted)));
+        }
+        return ret;
+    }
+
+protected:
+    void sink_it_(const details::log_msg &msg) override
+    {
+        q_.push_back(details::log_msg_buffer{msg});
+    }
+    void flush_() override {}
+
+private:
+    details::circular_q<details::log_msg_buffer> q_;
+};
+
+using ringbuffer_sink_mt = ringbuffer_sink<std::mutex>;
+using ringbuffer_sink_st = ringbuffer_sink<details::null_mutex>;
+
+} // namespace sinks
+
+} // namespace spdlog
diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h
new file mode 100644
index 000000000..cf8b9d5c6
--- /dev/null
+++ b/include/spdlog/sinks/rotating_file_sink-inl.h
@@ -0,0 +1,152 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/sinks/rotating_file_sink.h>
+#endif
+
+#include <spdlog/common.h>
+
+#include <spdlog/details/file_helper.h>
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/fmt/fmt.h>
+
+#include <cerrno>
+#include <chrono>
+#include <ctime>
+#include <mutex>
+#include <string>
+#include <tuple>
+
+namespace spdlog {
+namespace sinks {
+
+template<typename Mutex>
+SPDLOG_INLINE rotating_file_sink<Mutex>::rotating_file_sink(
+    filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open, const file_event_handlers &event_handlers)
+    : base_filename_(std::move(base_filename))
+    , max_size_(max_size)
+    , max_files_(max_files)
+    , file_helper_{event_handlers}
+{
+    if (max_size == 0)
+    {
+        throw_spdlog_ex("rotating sink constructor: max_size arg cannot be zero");
+    }
+
+    if (max_files > 200000)
+    {
+        throw_spdlog_ex("rotating sink constructor: max_files arg cannot exceed 200000");
+    }
+    file_helper_.open(calc_filename(base_filename_, 0));
+    current_size_ = file_helper_.size(); // expensive. called only once
+    if (rotate_on_open && current_size_ > 0)
+    {
+        rotate_();
+        current_size_ = 0;
+    }
+}
+
+// calc filename according to index and file extension if exists.
+// e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
+template<typename Mutex>
+SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::calc_filename(const filename_t &filename, std::size_t index)
+{
+    if (index == 0u)
+    {
+        return filename;
+    }
+
+    filename_t basename, ext;
+    std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
+    return fmt_lib::format(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
+}
+
+template<typename Mutex>
+SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::filename()
+{
+    std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+    return file_helper_.filename();
+}
+
+template<typename Mutex>
+SPDLOG_INLINE void rotating_file_sink<Mutex>::sink_it_(const details::log_msg &msg)
+{
+    memory_buf_t formatted;
+    base_sink<Mutex>::formatter_->format(msg, formatted);
+    auto new_size = current_size_ + formatted.size();
+
+    // rotate if the new estimated file size exceeds max size.
+    // rotate only if the real size > 0 to better deal with full disk (see issue #2261).
+    // we only check the real size when new_size > max_size_ because it is relatively expensive.
+    if (new_size > max_size_)
+    {
+        file_helper_.flush();
+        if (file_helper_.size() > 0)
+        {
+            rotate_();
+            new_size = formatted.size();
+        }
+    }
+    file_helper_.write(formatted);
+    current_size_ = new_size;
+}
+
+template<typename Mutex>
+SPDLOG_INLINE void rotating_file_sink<Mutex>::flush_()
+{
+    file_helper_.flush();
+}
+
+// Rotate files:
+// log.txt -> log.1.txt
+// log.1.txt -> log.2.txt
+// log.2.txt -> log.3.txt
+// log.3.txt -> delete
+template<typename Mutex>
+SPDLOG_INLINE void rotating_file_sink<Mutex>::rotate_()
+{
+    using details::os::filename_to_str;
+    using details::os::path_exists;
+
+    file_helper_.close();
+    for (auto i = max_files_; i > 0; --i)
+    {
+        filename_t src = calc_filename(base_filename_, i - 1);
+        if (!path_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.
+template<typename Mutex>
+SPDLOG_INLINE bool rotating_file_sink<Mutex>::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;
+}
+
+} // namespace sinks
+} // namespace spdlog
diff --git a/include/spdlog/sinks/rotating_file_sink.h b/include/spdlog/sinks/rotating_file_sink.h
new file mode 100644
index 000000000..ce0d7b1ef
--- /dev/null
+++ b/include/spdlog/sinks/rotating_file_sink.h
@@ -0,0 +1,81 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/sinks/base_sink.h>
+#include <spdlog/details/file_helper.h>
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/details/synchronous_factory.h>
+
+#include <chrono>
+#include <mutex>
+#include <string>
+
+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, bool rotate_on_open = false,
+        const file_event_handlers &event_handlers = {});
+    static filename_t calc_filename(const filename_t &filename, std::size_t index);
+    filename_t filename();
+
+protected:
+    void sink_it_(const details::log_msg &msg) override;
+    void flush_() override;
+
+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_();
+
+    // 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);
+
+    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 = spdlog::synchronous_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, bool rotate_on_open = false, const file_event_handlers &event_handlers = {})
+{
+    return Factory::template create<sinks::rotating_file_sink_mt>(
+        logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers);
+}
+
+template<typename Factory = spdlog::synchronous_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, bool rotate_on_open = false, const file_event_handlers &event_handlers = {})
+{
+    return Factory::template create<sinks::rotating_file_sink_st>(
+        logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers);
+}
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "rotating_file_sink-inl.h"
+#endif
diff --git a/include/spdlog/sinks/sink-inl.h b/include/spdlog/sinks/sink-inl.h
new file mode 100644
index 000000000..df07adda4
--- /dev/null
+++ b/include/spdlog/sinks/sink-inl.h
@@ -0,0 +1,25 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/sinks/sink.h>
+#endif
+
+#include <spdlog/common.h>
+
+SPDLOG_INLINE bool spdlog::sinks::sink::should_log(spdlog::level::level_enum msg_level) const
+{
+    return msg_level >= level_.load(std::memory_order_relaxed);
+}
+
+SPDLOG_INLINE void spdlog::sinks::sink::set_level(level::level_enum log_level)
+{
+    level_.store(log_level, std::memory_order_relaxed);
+}
+
+SPDLOG_INLINE spdlog::level::level_enum spdlog::sinks::sink::level() const
+{
+    return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed));
+}
diff --git a/include/spdlog/sinks/sink.h b/include/spdlog/sinks/sink.h
new file mode 100644
index 000000000..0a28cccc7
--- /dev/null
+++ b/include/spdlog/sinks/sink.h
@@ -0,0 +1,35 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/details/log_msg.h>
+#include <spdlog/formatter.h>
+
+namespace spdlog {
+
+namespace sinks {
+class SPDLOG_API sink
+{
+public:
+    virtual ~sink() = default;
+    virtual void log(const details::log_msg &msg) = 0;
+    virtual void flush() = 0;
+    virtual void set_pattern(const std::string &pattern) = 0;
+    virtual void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) = 0;
+
+    void set_level(level::level_enum log_level);
+    level::level_enum level() const;
+    bool should_log(level::level_enum msg_level) const;
+
+protected:
+    // sink log level - default is all
+    level_t level_{level::trace};
+};
+
+} // namespace sinks
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "sink-inl.h"
+#endif
diff --git a/include/spdlog/sinks/stdout_color_sinks-inl.h b/include/spdlog/sinks/stdout_color_sinks-inl.h
new file mode 100644
index 000000000..066df1826
--- /dev/null
+++ b/include/spdlog/sinks/stdout_color_sinks-inl.h
@@ -0,0 +1,38 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/sinks/stdout_color_sinks.h>
+#endif
+
+#include <spdlog/logger.h>
+#include <spdlog/common.h>
+
+namespace spdlog {
+
+template<typename Factory>
+SPDLOG_INLINE std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name, color_mode mode)
+{
+    return Factory::template create<sinks::stdout_color_sink_mt>(logger_name, mode);
+}
+
+template<typename Factory>
+SPDLOG_INLINE std::shared_ptr<logger> stdout_color_st(const std::string &logger_name, color_mode mode)
+{
+    return Factory::template create<sinks::stdout_color_sink_st>(logger_name, mode);
+}
+
+template<typename Factory>
+SPDLOG_INLINE std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name, color_mode mode)
+{
+    return Factory::template create<sinks::stderr_color_sink_mt>(logger_name, mode);
+}
+
+template<typename Factory>
+SPDLOG_INLINE std::shared_ptr<logger> stderr_color_st(const std::string &logger_name, color_mode mode)
+{
+    return Factory::template create<sinks::stderr_color_sink_st>(logger_name, mode);
+}
+} // namespace spdlog
diff --git a/include/spdlog/sinks/stdout_color_sinks.h b/include/spdlog/sinks/stdout_color_sinks.h
new file mode 100644
index 000000000..420b13ab4
--- /dev/null
+++ b/include/spdlog/sinks/stdout_color_sinks.h
@@ -0,0 +1,45 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifdef _WIN32
+#    include <spdlog/sinks/wincolor_sink.h>
+#else
+#    include <spdlog/sinks/ansicolor_sink.h>
+#endif
+
+#include <spdlog/details/synchronous_factory.h>
+
+namespace spdlog {
+namespace sinks {
+#ifdef _WIN32
+using stdout_color_sink_mt = wincolor_stdout_sink_mt;
+using stdout_color_sink_st = wincolor_stdout_sink_st;
+using stderr_color_sink_mt = wincolor_stderr_sink_mt;
+using stderr_color_sink_st = wincolor_stderr_sink_st;
+#else
+using stdout_color_sink_mt = ansicolor_stdout_sink_mt;
+using stdout_color_sink_st = ansicolor_stdout_sink_st;
+using stderr_color_sink_mt = ansicolor_stderr_sink_mt;
+using stderr_color_sink_st = ansicolor_stderr_sink_st;
+#endif
+} // namespace sinks
+
+template<typename Factory = spdlog::synchronous_factory>
+std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic);
+
+template<typename Factory = spdlog::synchronous_factory>
+std::shared_ptr<logger> stdout_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic);
+
+template<typename Factory = spdlog::synchronous_factory>
+std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic);
+
+template<typename Factory = spdlog::synchronous_factory>
+std::shared_ptr<logger> stderr_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic);
+
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "stdout_color_sinks-inl.h"
+#endif
diff --git a/include/spdlog/sinks/stdout_sinks-inl.h b/include/spdlog/sinks/stdout_sinks-inl.h
new file mode 100644
index 000000000..756734bf6
--- /dev/null
+++ b/include/spdlog/sinks/stdout_sinks-inl.h
@@ -0,0 +1,139 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/sinks/stdout_sinks.h>
+#endif
+
+#include <spdlog/details/console_globals.h>
+#include <spdlog/pattern_formatter.h>
+#include <memory>
+
+#ifdef _WIN32
+// under windows using fwrite to non-binary stream results in \r\r\n (see issue #1675)
+// so instead we use ::FileWrite
+#    include <spdlog/details/windows_include.h>
+
+#    ifndef _USING_V110_SDK71_ // fileapi.h doesn't exist in winxp
+#        include <fileapi.h>   // WriteFile (..)
+#    endif
+
+#    include <io.h>    // _get_osfhandle(..)
+#    include <stdio.h> // _fileno(..)
+#endif                 // WIN32
+
+namespace spdlog {
+
+namespace sinks {
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE stdout_sink_base<ConsoleMutex>::stdout_sink_base(FILE *file)
+    : mutex_(ConsoleMutex::mutex())
+    , file_(file)
+    , formatter_(details::make_unique<spdlog::pattern_formatter>())
+{
+#ifdef _WIN32
+    // get windows handle from the FILE* object
+
+    handle_ = reinterpret_cast<HANDLE>(::_get_osfhandle(::_fileno(file_)));
+
+    // don't throw to support cases where no console is attached,
+    // and let the log method to do nothing if (handle_ == INVALID_HANDLE_VALUE).
+    // throw only if non stdout/stderr target is requested (probably regular file and not console).
+    if (handle_ == INVALID_HANDLE_VALUE && file != stdout && file != stderr)
+    {
+        throw_spdlog_ex("spdlog::stdout_sink_base: _get_osfhandle() failed", errno);
+    }
+#endif // WIN32
+}
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &msg)
+{
+#ifdef _WIN32
+    if (handle_ == INVALID_HANDLE_VALUE)
+    {
+        return;
+    }
+    std::lock_guard<mutex_t> lock(mutex_);
+    memory_buf_t formatted;
+    formatter_->format(msg, formatted);
+    ::fflush(file_); // flush in case there is something in this file_ already
+    auto size = static_cast<DWORD>(formatted.size());
+    DWORD bytes_written = 0;
+    bool ok = ::WriteFile(handle_, formatted.data(), size, &bytes_written, nullptr) != 0;
+    if (!ok)
+    {
+        throw_spdlog_ex("stdout_sink_base: WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError()));
+    }
+#else
+    std::lock_guard<mutex_t> lock(mutex_);
+    memory_buf_t formatted;
+    formatter_->format(msg, formatted);
+    ::fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
+    ::fflush(file_); // flush every line to terminal
+#endif // WIN32
+}
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::flush()
+{
+    std::lock_guard<mutex_t> lock(mutex_);
+    fflush(file_);
+}
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::set_pattern(const std::string &pattern)
+{
+    std::lock_guard<mutex_t> lock(mutex_);
+    formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
+}
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
+{
+    std::lock_guard<mutex_t> lock(mutex_);
+    formatter_ = std::move(sink_formatter);
+}
+
+// stdout sink
+template<typename ConsoleMutex>
+SPDLOG_INLINE stdout_sink<ConsoleMutex>::stdout_sink()
+    : stdout_sink_base<ConsoleMutex>(stdout)
+{}
+
+// stderr sink
+template<typename ConsoleMutex>
+SPDLOG_INLINE stderr_sink<ConsoleMutex>::stderr_sink()
+    : stdout_sink_base<ConsoleMutex>(stderr)
+{}
+
+} // namespace sinks
+
+// factory methods
+template<typename Factory>
+SPDLOG_INLINE std::shared_ptr<logger> stdout_logger_mt(const std::string &logger_name)
+{
+    return Factory::template create<sinks::stdout_sink_mt>(logger_name);
+}
+
+template<typename Factory>
+SPDLOG_INLINE std::shared_ptr<logger> stdout_logger_st(const std::string &logger_name)
+{
+    return Factory::template create<sinks::stdout_sink_st>(logger_name);
+}
+
+template<typename Factory>
+SPDLOG_INLINE std::shared_ptr<logger> stderr_logger_mt(const std::string &logger_name)
+{
+    return Factory::template create<sinks::stderr_sink_mt>(logger_name);
+}
+
+template<typename Factory>
+SPDLOG_INLINE std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name)
+{
+    return Factory::template create<sinks::stderr_sink_st>(logger_name);
+}
+} // namespace spdlog
diff --git a/include/spdlog/sinks/stdout_sinks.h b/include/spdlog/sinks/stdout_sinks.h
new file mode 100644
index 000000000..6fdc0de34
--- /dev/null
+++ b/include/spdlog/sinks/stdout_sinks.h
@@ -0,0 +1,87 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/details/console_globals.h>
+#include <spdlog/details/synchronous_factory.h>
+#include <spdlog/sinks/sink.h>
+#include <cstdio>
+
+#ifdef _WIN32
+#    include <spdlog/details/windows_include.h>
+#endif
+
+namespace spdlog {
+
+namespace sinks {
+
+template<typename ConsoleMutex>
+class stdout_sink_base : public sink
+{
+public:
+    using mutex_t = typename ConsoleMutex::mutex_t;
+    explicit stdout_sink_base(FILE *file);
+    ~stdout_sink_base() override = default;
+
+    stdout_sink_base(const stdout_sink_base &other) = delete;
+    stdout_sink_base(stdout_sink_base &&other) = delete;
+
+    stdout_sink_base &operator=(const stdout_sink_base &other) = delete;
+    stdout_sink_base &operator=(stdout_sink_base &&other) = delete;
+
+    void log(const details::log_msg &msg) override;
+    void flush() override;
+    void set_pattern(const std::string &pattern) override;
+
+    void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
+
+protected:
+    mutex_t &mutex_;
+    FILE *file_;
+    std::unique_ptr<spdlog::formatter> formatter_;
+#ifdef _WIN32
+    HANDLE handle_;
+#endif // WIN32
+};
+
+template<typename ConsoleMutex>
+class stdout_sink : public stdout_sink_base<ConsoleMutex>
+{
+public:
+    stdout_sink();
+};
+
+template<typename ConsoleMutex>
+class stderr_sink : public stdout_sink_base<ConsoleMutex>
+{
+public:
+    stderr_sink();
+};
+
+using stdout_sink_mt = stdout_sink<details::console_mutex>;
+using stdout_sink_st = stdout_sink<details::console_nullmutex>;
+
+using stderr_sink_mt = stderr_sink<details::console_mutex>;
+using stderr_sink_st = stderr_sink<details::console_nullmutex>;
+
+} // namespace sinks
+
+// factory methods
+template<typename Factory = spdlog::synchronous_factory>
+std::shared_ptr<logger> stdout_logger_mt(const std::string &logger_name);
+
+template<typename Factory = spdlog::synchronous_factory>
+std::shared_ptr<logger> stdout_logger_st(const std::string &logger_name);
+
+template<typename Factory = spdlog::synchronous_factory>
+std::shared_ptr<logger> stderr_logger_mt(const std::string &logger_name);
+
+template<typename Factory = spdlog::synchronous_factory>
+std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name);
+
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "stdout_sinks-inl.h"
+#endif
diff --git a/include/spdlog/sinks/syslog_sink.h b/include/spdlog/sinks/syslog_sink.h
new file mode 100644
index 000000000..7c38fcb58
--- /dev/null
+++ b/include/spdlog/sinks/syslog_sink.h
@@ -0,0 +1,109 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/sinks/base_sink.h>
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/details/synchronous_factory.h>
+
+#include <array>
+#include <string>
+#include <syslog.h>
+
+namespace spdlog {
+namespace sinks {
+/**
+ * Sink that write to syslog using the `syscall()` library call.
+ */
+template<typename Mutex>
+class syslog_sink : public base_sink<Mutex>
+{
+
+public:
+    syslog_sink(std::string ident, int syslog_option, int syslog_facility, bool enable_formatting)
+        : enable_formatting_{enable_formatting}
+        , syslog_levels_{{/* spdlog::level::trace      */ LOG_DEBUG,
+              /* spdlog::level::debug      */ LOG_DEBUG,
+              /* spdlog::level::info       */ LOG_INFO,
+              /* spdlog::level::warn       */ LOG_WARNING,
+              /* spdlog::level::err        */ LOG_ERR,
+              /* spdlog::level::critical   */ LOG_CRIT,
+              /* spdlog::level::off        */ LOG_INFO}}
+        , ident_{std::move(ident)}
+    {
+        // set ident to be program name if empty
+        ::openlog(ident_.empty() ? nullptr : ident_.c_str(), syslog_option, syslog_facility);
+    }
+
+    ~syslog_sink() override
+    {
+        ::closelog();
+    }
+
+    syslog_sink(const syslog_sink &) = delete;
+    syslog_sink &operator=(const syslog_sink &) = delete;
+
+protected:
+    void sink_it_(const details::log_msg &msg) override
+    {
+        string_view_t payload;
+        memory_buf_t formatted;
+        if (enable_formatting_)
+        {
+            base_sink<Mutex>::formatter_->format(msg, formatted);
+            payload = string_view_t(formatted.data(), formatted.size());
+        }
+        else
+        {
+            payload = msg.payload;
+        }
+
+        size_t length = payload.size();
+        // limit to max int
+        if (length > static_cast<size_t>(std::numeric_limits<int>::max()))
+        {
+            length = static_cast<size_t>(std::numeric_limits<int>::max());
+        }
+
+        ::syslog(syslog_prio_from_level(msg), "%.*s", static_cast<int>(length), payload.data());
+    }
+
+    void flush_() override {}
+    bool enable_formatting_ = false;
+
+private:
+    using levels_array = std::array<int, 7>;
+    levels_array syslog_levels_;
+    // must store the ident because the man says openlog might use the pointer as
+    // is and not a string copy
+    const std::string ident_;
+
+    //
+    // Simply maps spdlog's log level to syslog priority level.
+    //
+    int syslog_prio_from_level(const details::log_msg &msg) const
+    {
+        return syslog_levels_.at(static_cast<levels_array::size_type>(msg.level));
+    }
+};
+
+using syslog_sink_mt = syslog_sink<std::mutex>;
+using syslog_sink_st = syslog_sink<details::null_mutex>;
+} // namespace sinks
+
+// Create and register a syslog logger
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> syslog_logger_mt(const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0,
+    int syslog_facility = LOG_USER, bool enable_formatting = false)
+{
+    return Factory::template create<sinks::syslog_sink_mt>(logger_name, syslog_ident, syslog_option, syslog_facility, enable_formatting);
+}
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> syslog_logger_st(const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0,
+    int syslog_facility = LOG_USER, bool enable_formatting = false)
+{
+    return Factory::template create<sinks::syslog_sink_st>(logger_name, syslog_ident, syslog_option, syslog_facility, enable_formatting);
+}
+} // namespace spdlog
diff --git a/include/spdlog/sinks/systemd_sink.h b/include/spdlog/sinks/systemd_sink.h
new file mode 100644
index 000000000..e1e97bffe
--- /dev/null
+++ b/include/spdlog/sinks/systemd_sink.h
@@ -0,0 +1,119 @@
+// Copyright(c) 2019 ZVYAGIN.Alexander@gmail.com
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/sinks/base_sink.h>
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/details/synchronous_factory.h>
+
+#include <array>
+#ifndef SD_JOURNAL_SUPPRESS_LOCATION
+#    define SD_JOURNAL_SUPPRESS_LOCATION
+#endif
+#include <systemd/sd-journal.h>
+
+namespace spdlog {
+namespace sinks {
+
+/**
+ * Sink that write to systemd journal using the `sd_journal_send()` library call.
+ */
+template<typename Mutex>
+class systemd_sink : public base_sink<Mutex>
+{
+public:
+    systemd_sink(std::string ident = "", bool enable_formatting = false)
+        : ident_{std::move(ident)}
+        , enable_formatting_{enable_formatting}
+        , syslog_levels_{{/* spdlog::level::trace      */ LOG_DEBUG,
+              /* spdlog::level::debug      */ LOG_DEBUG,
+              /* spdlog::level::info       */ LOG_INFO,
+              /* spdlog::level::warn       */ LOG_WARNING,
+              /* spdlog::level::err        */ LOG_ERR,
+              /* spdlog::level::critical   */ LOG_CRIT,
+              /* spdlog::level::off        */ LOG_INFO}}
+    {}
+
+    ~systemd_sink() override {}
+
+    systemd_sink(const systemd_sink &) = delete;
+    systemd_sink &operator=(const systemd_sink &) = delete;
+
+protected:
+    const std::string ident_;
+    bool enable_formatting_ = false;
+    using levels_array = std::array<int, 7>;
+    levels_array syslog_levels_;
+
+    void sink_it_(const details::log_msg &msg) override
+    {
+        int err;
+        string_view_t payload;
+        memory_buf_t formatted;
+        if (enable_formatting_)
+        {
+            base_sink<Mutex>::formatter_->format(msg, formatted);
+            payload = string_view_t(formatted.data(), formatted.size());
+        }
+        else
+        {
+            payload = msg.payload;
+        }
+
+        size_t length = payload.size();
+        // limit to max int
+        if (length > static_cast<size_t>(std::numeric_limits<int>::max()))
+        {
+            length = static_cast<size_t>(std::numeric_limits<int>::max());
+        }
+
+        const string_view_t syslog_identifier = ident_.empty() ? msg.logger_name : ident_;
+
+        // Do not send source location if not available
+        if (msg.source.empty())
+        {
+            // Note: function call inside '()' to avoid macro expansion
+            err = (sd_journal_send)("MESSAGE=%.*s", static_cast<int>(length), payload.data(), "PRIORITY=%d", syslog_level(msg.level),
+                "SYSLOG_IDENTIFIER=%.*s", static_cast<int>(syslog_identifier.size()), syslog_identifier.data(), nullptr);
+        }
+        else
+        {
+            err = (sd_journal_send)("MESSAGE=%.*s", static_cast<int>(length), payload.data(), "PRIORITY=%d", syslog_level(msg.level),
+                "SYSLOG_IDENTIFIER=%.*s", static_cast<int>(syslog_identifier.size()), syslog_identifier.data(), "CODE_FILE=%s",
+                msg.source.filename, "CODE_LINE=%d", msg.source.line, "CODE_FUNC=%s", msg.source.funcname, nullptr);
+        }
+
+        if (err)
+        {
+            throw_spdlog_ex("Failed writing to systemd", errno);
+        }
+    }
+
+    int syslog_level(level::level_enum l)
+    {
+        return syslog_levels_.at(static_cast<levels_array::size_type>(l));
+    }
+
+    void flush_() override {}
+};
+
+using systemd_sink_mt = systemd_sink<std::mutex>;
+using systemd_sink_st = systemd_sink<details::null_mutex>;
+} // namespace sinks
+
+// Create and register a syslog logger
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> systemd_logger_mt(
+    const std::string &logger_name, const std::string &ident = "", bool enable_formatting = false)
+{
+    return Factory::template create<sinks::systemd_sink_mt>(logger_name, ident, enable_formatting);
+}
+
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> systemd_logger_st(
+    const std::string &logger_name, const std::string &ident = "", bool enable_formatting = false)
+{
+    return Factory::template create<sinks::systemd_sink_st>(logger_name, ident, enable_formatting);
+}
+} // namespace spdlog
diff --git a/include/spdlog/sinks/tcp_sink.h b/include/spdlog/sinks/tcp_sink.h
new file mode 100644
index 000000000..e0efb31d9
--- /dev/null
+++ b/include/spdlog/sinks/tcp_sink.h
@@ -0,0 +1,81 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/common.h>
+#include <spdlog/sinks/base_sink.h>
+#include <spdlog/details/null_mutex.h>
+#ifdef _WIN32
+#    include <spdlog/details/tcp_client-windows.h>
+#else
+#    include <spdlog/details/tcp_client.h>
+#endif
+
+#include <mutex>
+#include <string>
+#include <chrono>
+#include <functional>
+
+#pragma once
+
+// Simple tcp client sink
+// Connects to remote address and send the formatted log.
+// Will attempt to reconnect if connection drops.
+// If more complicated behaviour is needed (i.e get responses), you can inherit it and override the sink_it_ method.
+
+namespace spdlog {
+namespace sinks {
+
+struct tcp_sink_config
+{
+    std::string server_host;
+    int server_port;
+    bool lazy_connect = false; // if true connect on first log call instead of on construction
+
+    tcp_sink_config(std::string host, int port)
+        : server_host{std::move(host)}
+        , server_port{port}
+    {}
+};
+
+template<typename Mutex>
+class tcp_sink : public spdlog::sinks::base_sink<Mutex>
+{
+public:
+    // connect to tcp host/port or throw if failed
+    // host can be hostname or ip address
+
+    explicit tcp_sink(tcp_sink_config sink_config)
+        : config_{std::move(sink_config)}
+    {
+        if (!config_.lazy_connect)
+        {
+            this->client_.connect(config_.server_host, config_.server_port);
+        }
+    }
+
+    ~tcp_sink() override = default;
+
+protected:
+    void sink_it_(const spdlog::details::log_msg &msg) override
+    {
+        spdlog::memory_buf_t formatted;
+        spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
+        if (!client_.is_connected())
+        {
+            client_.connect(config_.server_host, config_.server_port);
+        }
+        client_.send(formatted.data(), formatted.size());
+    }
+
+    void flush_() override {}
+    tcp_sink_config config_;
+    details::tcp_client client_;
+};
+
+using tcp_sink_mt = tcp_sink<std::mutex>;
+using tcp_sink_st = tcp_sink<spdlog::details::null_mutex>;
+
+} // namespace sinks
+} // namespace spdlog
diff --git a/include/spdlog/sinks/udp_sink.h b/include/spdlog/sinks/udp_sink.h
new file mode 100644
index 000000000..ccbce2be3
--- /dev/null
+++ b/include/spdlog/sinks/udp_sink.h
@@ -0,0 +1,74 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/common.h>
+#include <spdlog/sinks/base_sink.h>
+#include <spdlog/details/null_mutex.h>
+#ifdef _WIN32
+#    include <spdlog/details/udp_client-windows.h>
+#else
+#    include <spdlog/details/udp_client.h>
+#endif
+
+#include <mutex>
+#include <string>
+#include <chrono>
+#include <functional>
+
+// Simple udp client sink
+// Sends formatted log via udp
+
+namespace spdlog {
+namespace sinks {
+
+struct udp_sink_config
+{
+    std::string server_host;
+    uint16_t server_port;
+
+    udp_sink_config(std::string host, uint16_t port)
+        : server_host{std::move(host)}
+        , server_port{port}
+    {}
+};
+
+template<typename Mutex>
+class udp_sink : public spdlog::sinks::base_sink<Mutex>
+{
+public:
+    // host can be hostname or ip address
+    explicit udp_sink(udp_sink_config sink_config)
+        : client_{sink_config.server_host, sink_config.server_port}
+    {}
+
+    ~udp_sink() override = default;
+
+protected:
+    void sink_it_(const spdlog::details::log_msg &msg) override
+    {
+        spdlog::memory_buf_t formatted;
+        spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
+        client_.send(formatted.data(), formatted.size());
+    }
+
+    void flush_() override {}
+    details::udp_client client_;
+};
+
+using udp_sink_mt = udp_sink<std::mutex>;
+using udp_sink_st = udp_sink<spdlog::details::null_mutex>;
+
+} // namespace sinks
+
+//
+// factory functions
+//
+template<typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> udp_logger_mt(const std::string &logger_name, sinks::udp_sink_config skin_config)
+{
+    return Factory::template create<sinks::udp_sink_mt>(logger_name, skin_config);
+}
+
+} // namespace spdlog
diff --git a/include/spdlog/sinks/win_eventlog_sink.h b/include/spdlog/sinks/win_eventlog_sink.h
new file mode 100644
index 000000000..2f2aacb5c
--- /dev/null
+++ b/include/spdlog/sinks/win_eventlog_sink.h
@@ -0,0 +1,289 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+// Writing to Windows Event Log requires the registry entries below to be present, with the following modifications:
+// 1. <log_name>    should be replaced with your log name (e.g. your application name)
+// 2. <source_name> should be replaced with the specific source name and the key should be duplicated for
+//                  each source used in the application
+//
+// Since typically modifications of this kind require elevation, it's better to do it as a part of setup procedure.
+// The snippet below uses mscoree.dll as the message file as it exists on most of the Windows systems anyway and
+// happens to contain the needed resource.
+//
+// You can also specify a custom message file if needed.
+// Please refer to Event Log functions descriptions in MSDN for more details on custom message files.
+
+/*---------------------------------------------------------------------------------------
+
+Windows Registry Editor Version 5.00
+
+[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\<log_name>]
+
+[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\<log_name>\<source_name>]
+"TypesSupported"=dword:00000007
+"EventMessageFile"=hex(2):25,00,73,00,79,00,73,00,74,00,65,00,6d,00,72,00,6f,\
+  00,6f,00,74,00,25,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,\
+  5c,00,6d,00,73,00,63,00,6f,00,72,00,65,00,65,00,2e,00,64,00,6c,00,6c,00,00,\
+  00
+
+-----------------------------------------------------------------------------------------*/
+
+#pragma once
+
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/sinks/base_sink.h>
+
+#include <spdlog/details/windows_include.h>
+#include <winbase.h>
+
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace spdlog {
+namespace sinks {
+
+namespace win_eventlog {
+
+namespace internal {
+
+struct local_alloc_t
+{
+    HLOCAL hlocal_;
+
+    SPDLOG_CONSTEXPR local_alloc_t() SPDLOG_NOEXCEPT : hlocal_(nullptr) {}
+
+    local_alloc_t(local_alloc_t const &) = delete;
+    local_alloc_t &operator=(local_alloc_t const &) = delete;
+
+    ~local_alloc_t() SPDLOG_NOEXCEPT
+    {
+        if (hlocal_)
+        {
+            LocalFree(hlocal_);
+        }
+    }
+};
+
+/** Windows error */
+struct win32_error : public spdlog_ex
+{
+    /** Formats an error report line: "user-message: error-code (system message)" */
+    static std::string format(std::string const &user_message, DWORD error_code = GetLastError())
+    {
+        std::string system_message;
+
+        local_alloc_t format_message_result{};
+        auto format_message_succeeded =
+            ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
+                error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&format_message_result.hlocal_, 0, nullptr);
+
+        if (format_message_succeeded && format_message_result.hlocal_)
+        {
+            system_message = fmt_lib::format(" ({})", (LPSTR)format_message_result.hlocal_);
+        }
+
+        return fmt_lib::format("{}: {}{}", user_message, error_code, system_message);
+    }
+
+    explicit win32_error(std::string const &func_name, DWORD error = GetLastError())
+        : spdlog_ex(format(func_name, error))
+    {}
+};
+
+/** Wrapper for security identifiers (SID) on Windows */
+struct sid_t
+{
+    std::vector<char> buffer_;
+
+public:
+    sid_t() {}
+
+    /** creates a wrapped SID copy */
+    static sid_t duplicate_sid(PSID psid)
+    {
+        if (!::IsValidSid(psid))
+        {
+            throw_spdlog_ex("sid_t::sid_t(): invalid SID received");
+        }
+
+        auto const sid_length{::GetLengthSid(psid)};
+
+        sid_t result;
+        result.buffer_.resize(sid_length);
+        if (!::CopySid(sid_length, (PSID)result.as_sid(), psid))
+        {
+            SPDLOG_THROW(win32_error("CopySid"));
+        }
+
+        return result;
+    }
+
+    /** Retrieves pointer to the internal buffer contents as SID* */
+    SID *as_sid() const
+    {
+        return buffer_.empty() ? nullptr : (SID *)buffer_.data();
+    }
+
+    /** Get SID for the current user */
+    static sid_t get_current_user_sid()
+    {
+        /* create and init RAII holder for process token */
+        struct process_token_t
+        {
+            HANDLE token_handle_ = INVALID_HANDLE_VALUE;
+            explicit process_token_t(HANDLE process)
+            {
+                if (!::OpenProcessToken(process, TOKEN_QUERY, &token_handle_))
+                {
+                    SPDLOG_THROW(win32_error("OpenProcessToken"));
+                }
+            }
+
+            ~process_token_t()
+            {
+                ::CloseHandle(token_handle_);
+            }
+
+        } current_process_token(::GetCurrentProcess()); // GetCurrentProcess returns pseudohandle, no leak here!
+
+        // Get the required size, this is expected to fail with ERROR_INSUFFICIENT_BUFFER and return the token size
+        DWORD tusize = 0;
+        if (::GetTokenInformation(current_process_token.token_handle_, TokenUser, NULL, 0, &tusize))
+        {
+            SPDLOG_THROW(win32_error("GetTokenInformation should fail"));
+        }
+
+        // get user token
+        std::vector<unsigned char> buffer(static_cast<size_t>(tusize));
+        if (!::GetTokenInformation(current_process_token.token_handle_, TokenUser, (LPVOID)buffer.data(), tusize, &tusize))
+        {
+            SPDLOG_THROW(win32_error("GetTokenInformation"));
+        }
+
+        // create a wrapper of the SID data as stored in the user token
+        return sid_t::duplicate_sid(((TOKEN_USER *)buffer.data())->User.Sid);
+    }
+};
+
+struct eventlog
+{
+    static WORD get_event_type(details::log_msg const &msg)
+    {
+        switch (msg.level)
+        {
+        case level::trace:
+        case level::debug:
+            return EVENTLOG_SUCCESS;
+
+        case level::info:
+            return EVENTLOG_INFORMATION_TYPE;
+
+        case level::warn:
+            return EVENTLOG_WARNING_TYPE;
+
+        case level::err:
+        case level::critical:
+        case level::off:
+            return EVENTLOG_ERROR_TYPE;
+
+        default:
+            return EVENTLOG_INFORMATION_TYPE;
+        }
+    }
+
+    static WORD get_event_category(details::log_msg const &msg)
+    {
+        return (WORD)msg.level;
+    }
+};
+
+} // namespace internal
+
+/*
+ * Windows Event Log sink
+ */
+template<typename Mutex>
+class win_eventlog_sink : public base_sink<Mutex>
+{
+private:
+    HANDLE hEventLog_{NULL};
+    internal::sid_t current_user_sid_;
+    std::string source_;
+    WORD event_id_;
+
+    HANDLE event_log_handle()
+    {
+        if (!hEventLog_)
+        {
+            hEventLog_ = ::RegisterEventSourceA(nullptr, source_.c_str());
+            if (!hEventLog_ || hEventLog_ == (HANDLE)ERROR_ACCESS_DENIED)
+            {
+                SPDLOG_THROW(internal::win32_error("RegisterEventSource"));
+            }
+        }
+
+        return hEventLog_;
+    }
+
+protected:
+    void sink_it_(const details::log_msg &msg) override
+    {
+        using namespace internal;
+
+        bool succeeded;
+        memory_buf_t formatted;
+        base_sink<Mutex>::formatter_->format(msg, formatted);
+        formatted.push_back('\0');
+
+#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
+        wmemory_buf_t buf;
+        details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), buf);
+
+        LPCWSTR lp_wstr = buf.data();
+        succeeded = static_cast<bool>(::ReportEventW(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
+            current_user_sid_.as_sid(), 1, 0, &lp_wstr, nullptr));
+#else
+        LPCSTR lp_str = formatted.data();
+        succeeded = static_cast<bool>(::ReportEventA(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
+            current_user_sid_.as_sid(), 1, 0, &lp_str, nullptr));
+#endif
+
+        if (!succeeded)
+        {
+            SPDLOG_THROW(win32_error("ReportEvent"));
+        }
+    }
+
+    void flush_() override {}
+
+public:
+    win_eventlog_sink(std::string const &source, WORD event_id = 1000 /* according to mscoree.dll */)
+        : source_(source)
+        , event_id_(event_id)
+    {
+        try
+        {
+            current_user_sid_ = internal::sid_t::get_current_user_sid();
+        }
+        catch (...)
+        {
+            // get_current_user_sid() is unlikely to fail and if it does, we can still proceed without
+            // current_user_sid but in the event log the record will have no user name
+        }
+    }
+
+    ~win_eventlog_sink()
+    {
+        if (hEventLog_)
+            DeregisterEventSource(hEventLog_);
+    }
+};
+
+} // namespace win_eventlog
+
+using win_eventlog_sink_mt = win_eventlog::win_eventlog_sink<std::mutex>;
+using win_eventlog_sink_st = win_eventlog::win_eventlog_sink<details::null_mutex>;
+
+} // namespace sinks
+} // namespace spdlog
diff --git a/include/spdlog/sinks/wincolor_sink-inl.h b/include/spdlog/sinks/wincolor_sink-inl.h
new file mode 100644
index 000000000..8311929e4
--- /dev/null
+++ b/include/spdlog/sinks/wincolor_sink-inl.h
@@ -0,0 +1,175 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/sinks/wincolor_sink.h>
+#endif
+
+#include <spdlog/details/windows_include.h>
+#include <wincon.h>
+
+#include <spdlog/common.h>
+#include <spdlog/pattern_formatter.h>
+
+namespace spdlog {
+namespace sinks {
+template<typename ConsoleMutex>
+SPDLOG_INLINE wincolor_sink<ConsoleMutex>::wincolor_sink(void *out_handle, color_mode mode)
+    : out_handle_(out_handle)
+    , mutex_(ConsoleMutex::mutex())
+    , formatter_(details::make_unique<spdlog::pattern_formatter>())
+{
+
+    set_color_mode_impl(mode);
+    // set level colors
+    colors_[level::trace] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;     // white
+    colors_[level::debug] = FOREGROUND_GREEN | FOREGROUND_BLUE;                      // cyan
+    colors_[level::info] = FOREGROUND_GREEN;                                         // green
+    colors_[level::warn] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; // intense yellow
+    colors_[level::err] = FOREGROUND_RED | FOREGROUND_INTENSITY;                     // intense red
+    colors_[level::critical] =
+        BACKGROUND_RED | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; // intense white on red background
+    colors_[level::off] = 0;
+}
+
+template<typename ConsoleMutex>
+SPDLOG_INLINE wincolor_sink<ConsoleMutex>::~wincolor_sink()
+{
+    this->flush();
+}
+
+// change the color for the given level
+template<typename ConsoleMutex>
+void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color(level::level_enum level, std::uint16_t color)
+{
+    std::lock_guard<mutex_t> lock(mutex_);
+    colors_[static_cast<size_t>(level)] = color;
+}
+
+template<typename ConsoleMutex>
+void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
+{
+    if (out_handle_ == nullptr || out_handle_ == INVALID_HANDLE_VALUE)
+    {
+        return;
+    }
+
+    std::lock_guard<mutex_t> lock(mutex_);
+    msg.color_range_start = 0;
+    msg.color_range_end = 0;
+    memory_buf_t formatted;
+    formatter_->format(msg, formatted);
+    if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
+    {
+        // before color range
+        print_range_(formatted, 0, msg.color_range_start);
+        // in color range
+        auto orig_attribs = static_cast<WORD>(set_foreground_color_(colors_[static_cast<size_t>(msg.level)]));
+        print_range_(formatted, msg.color_range_start, msg.color_range_end);
+        // reset to orig colors
+        ::SetConsoleTextAttribute(static_cast<HANDLE>(out_handle_), orig_attribs);
+        print_range_(formatted, msg.color_range_end, formatted.size());
+    }
+    else // print without colors if color range is invalid (or color is disabled)
+    {
+        write_to_file_(formatted);
+    }
+}
+
+template<typename ConsoleMutex>
+void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::flush()
+{
+    // windows console always flushed?
+}
+
+template<typename ConsoleMutex>
+void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_pattern(const std::string &pattern)
+{
+    std::lock_guard<mutex_t> lock(mutex_);
+    formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
+}
+
+template<typename ConsoleMutex>
+void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
+{
+    std::lock_guard<mutex_t> lock(mutex_);
+    formatter_ = std::move(sink_formatter);
+}
+
+template<typename ConsoleMutex>
+void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
+{
+    std::lock_guard<mutex_t> lock(mutex_);
+    set_color_mode_impl(mode);
+}
+
+template<typename ConsoleMutex>
+void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode_impl(color_mode mode)
+{
+    if (mode == color_mode::automatic)
+    {
+        // should do colors only if out_handle_  points to actual console.
+        DWORD console_mode;
+        bool in_console = ::GetConsoleMode(static_cast<HANDLE>(out_handle_), &console_mode) != 0;
+        should_do_colors_ = in_console;
+    }
+    else
+    {
+        should_do_colors_ = mode == color_mode::always ? true : false;
+    }
+}
+
+// set foreground color and return the orig console attributes (for resetting later)
+template<typename ConsoleMutex>
+std::uint16_t SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_foreground_color_(std::uint16_t attribs)
+{
+    CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
+    if (!::GetConsoleScreenBufferInfo(static_cast<HANDLE>(out_handle_), &orig_buffer_info))
+    {
+        // just return white if failed getting console info
+        return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+    }
+
+    // change only the foreground bits (lowest 4 bits)
+    auto new_attribs = static_cast<WORD>(attribs) | (orig_buffer_info.wAttributes & 0xfff0);
+    auto ignored = ::SetConsoleTextAttribute(static_cast<HANDLE>(out_handle_), static_cast<WORD>(new_attribs));
+    (void)(ignored);
+    return static_cast<std::uint16_t>(orig_buffer_info.wAttributes); // return orig attribs
+}
+
+// print a range of formatted message to console
+template<typename ConsoleMutex>
+void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
+{
+    if (end > start)
+    {
+        auto size = static_cast<DWORD>(end - start);
+        auto ignored = ::WriteConsoleA(static_cast<HANDLE>(out_handle_), formatted.data() + start, size, nullptr, nullptr);
+        (void)(ignored);
+    }
+}
+
+template<typename ConsoleMutex>
+void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::write_to_file_(const memory_buf_t &formatted)
+{
+    auto size = static_cast<DWORD>(formatted.size());
+    DWORD bytes_written = 0;
+    auto ignored = ::WriteFile(static_cast<HANDLE>(out_handle_), formatted.data(), size, &bytes_written, nullptr);
+    (void)(ignored);
+}
+
+// wincolor_stdout_sink
+template<typename ConsoleMutex>
+SPDLOG_INLINE wincolor_stdout_sink<ConsoleMutex>::wincolor_stdout_sink(color_mode mode)
+    : wincolor_sink<ConsoleMutex>(::GetStdHandle(STD_OUTPUT_HANDLE), mode)
+{}
+
+// wincolor_stderr_sink
+template<typename ConsoleMutex>
+SPDLOG_INLINE wincolor_stderr_sink<ConsoleMutex>::wincolor_stderr_sink(color_mode mode)
+    : wincolor_sink<ConsoleMutex>(::GetStdHandle(STD_ERROR_HANDLE), mode)
+{}
+} // namespace sinks
+} // namespace spdlog
diff --git a/include/spdlog/sinks/wincolor_sink.h b/include/spdlog/sinks/wincolor_sink.h
new file mode 100644
index 000000000..9b030fc13
--- /dev/null
+++ b/include/spdlog/sinks/wincolor_sink.h
@@ -0,0 +1,85 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/common.h>
+#include <spdlog/details/console_globals.h>
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/sinks/sink.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <array>
+#include <cstdint>
+
+namespace spdlog {
+namespace sinks {
+/*
+ * Windows color console sink. Uses WriteConsoleA to write to the console with
+ * colors
+ */
+template<typename ConsoleMutex>
+class wincolor_sink : public sink
+{
+public:
+    wincolor_sink(void *out_handle, color_mode mode);
+    ~wincolor_sink() override;
+
+    wincolor_sink(const wincolor_sink &other) = delete;
+    wincolor_sink &operator=(const wincolor_sink &other) = delete;
+
+    // change the color for the given level
+    void set_color(level::level_enum level, std::uint16_t color);
+    void log(const details::log_msg &msg) final override;
+    void flush() final override;
+    void set_pattern(const std::string &pattern) override final;
+    void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override final;
+    void set_color_mode(color_mode mode);
+
+protected:
+    using mutex_t = typename ConsoleMutex::mutex_t;
+    void *out_handle_;
+    mutex_t &mutex_;
+    bool should_do_colors_;
+    std::unique_ptr<spdlog::formatter> formatter_;
+    std::array<std::uint16_t, level::n_levels> colors_;
+
+    // set foreground color and return the orig console attributes (for resetting later)
+    std::uint16_t set_foreground_color_(std::uint16_t attribs);
+
+    // print a range of formatted message to console
+    void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
+
+    // in case we are redirected to file (not in console mode)
+    void write_to_file_(const memory_buf_t &formatted);
+
+    void set_color_mode_impl(color_mode mode);
+};
+
+template<typename ConsoleMutex>
+class wincolor_stdout_sink : public wincolor_sink<ConsoleMutex>
+{
+public:
+    explicit wincolor_stdout_sink(color_mode mode = color_mode::automatic);
+};
+
+template<typename ConsoleMutex>
+class wincolor_stderr_sink : public wincolor_sink<ConsoleMutex>
+{
+public:
+    explicit wincolor_stderr_sink(color_mode mode = color_mode::automatic);
+};
+
+using wincolor_stdout_sink_mt = wincolor_stdout_sink<details::console_mutex>;
+using wincolor_stdout_sink_st = wincolor_stdout_sink<details::console_nullmutex>;
+
+using wincolor_stderr_sink_mt = wincolor_stderr_sink<details::console_mutex>;
+using wincolor_stderr_sink_st = wincolor_stderr_sink<details::console_nullmutex>;
+} // namespace sinks
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "wincolor_sink-inl.h"
+#endif
diff --git a/include/spdlog/spdlog-inl.h b/include/spdlog/spdlog-inl.h
new file mode 100644
index 000000000..708399c19
--- /dev/null
+++ b/include/spdlog/spdlog-inl.h
@@ -0,0 +1,120 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+#    include <spdlog/spdlog.h>
+#endif
+
+#include <spdlog/common.h>
+#include <spdlog/pattern_formatter.h>
+
+namespace spdlog {
+
+SPDLOG_INLINE void initialize_logger(std::shared_ptr<logger> logger)
+{
+    details::registry::instance().initialize_logger(std::move(logger));
+}
+
+SPDLOG_INLINE std::shared_ptr<logger> get(const std::string &name)
+{
+    return details::registry::instance().get(name);
+}
+
+SPDLOG_INLINE void set_formatter(std::unique_ptr<spdlog::formatter> formatter)
+{
+    details::registry::instance().set_formatter(std::move(formatter));
+}
+
+SPDLOG_INLINE void set_pattern(std::string pattern, pattern_time_type time_type)
+{
+    set_formatter(std::unique_ptr<spdlog::formatter>(new pattern_formatter(std::move(pattern), time_type)));
+}
+
+SPDLOG_INLINE void enable_backtrace(size_t n_messages)
+{
+    details::registry::instance().enable_backtrace(n_messages);
+}
+
+SPDLOG_INLINE void disable_backtrace()
+{
+    details::registry::instance().disable_backtrace();
+}
+
+SPDLOG_INLINE void dump_backtrace()
+{
+    default_logger_raw()->dump_backtrace();
+}
+
+SPDLOG_INLINE level::level_enum get_level()
+{
+    return default_logger_raw()->level();
+}
+
+SPDLOG_INLINE bool should_log(level::level_enum log_level)
+{
+    return default_logger_raw()->should_log(log_level);
+}
+
+SPDLOG_INLINE void set_level(level::level_enum log_level)
+{
+    details::registry::instance().set_level(log_level);
+}
+
+SPDLOG_INLINE void flush_on(level::level_enum log_level)
+{
+    details::registry::instance().flush_on(log_level);
+}
+
+SPDLOG_INLINE void set_error_handler(void (*handler)(const std::string &msg))
+{
+    details::registry::instance().set_error_handler(handler);
+}
+
+SPDLOG_INLINE void register_logger(std::shared_ptr<logger> logger)
+{
+    details::registry::instance().register_logger(std::move(logger));
+}
+
+SPDLOG_INLINE void apply_all(const std::function<void(std::shared_ptr<logger>)> &fun)
+{
+    details::registry::instance().apply_all(fun);
+}
+
+SPDLOG_INLINE void drop(const std::string &name)
+{
+    details::registry::instance().drop(name);
+}
+
+SPDLOG_INLINE void drop_all()
+{
+    details::registry::instance().drop_all();
+}
+
+SPDLOG_INLINE void shutdown()
+{
+    details::registry::instance().shutdown();
+}
+
+SPDLOG_INLINE void set_automatic_registration(bool automatic_registration)
+{
+    details::registry::instance().set_automatic_registration(automatic_registration);
+}
+
+SPDLOG_INLINE std::shared_ptr<spdlog::logger> default_logger()
+{
+    return details::registry::instance().default_logger();
+}
+
+SPDLOG_INLINE spdlog::logger *default_logger_raw()
+{
+    return details::registry::instance().get_default_raw();
+}
+
+SPDLOG_INLINE void set_default_logger(std::shared_ptr<spdlog::logger> default_logger)
+{
+    details::registry::instance().set_default_logger(std::move(default_logger));
+}
+
+} // namespace spdlog
diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h
new file mode 100644
index 000000000..ee83e8de3
--- /dev/null
+++ b/include/spdlog/spdlog.h
@@ -0,0 +1,354 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+// spdlog main header file.
+// see example.cpp for usage example
+
+#ifndef SPDLOG_H
+#define SPDLOG_H
+
+#pragma once
+
+#include <spdlog/common.h>
+#include <spdlog/details/registry.h>
+#include <spdlog/logger.h>
+#include <spdlog/version.h>
+#include <spdlog/details/synchronous_factory.h>
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace spdlog {
+
+using default_factory = synchronous_factory;
+
+// Create and register a logger with a templated sink type
+// The logger's level, formatter and flush level will be set according the
+// global settings.
+//
+// Example:
+//   spdlog::create<daily_file_sink_st>("logger_name", "dailylog_filename", 11, 59);
+template<typename Sink, typename... SinkArgs>
+inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... sink_args)
+{
+    return default_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
+}
+
+// Initialize and register a logger,
+// formatter and flush level will be set according the global settings.
+//
+// Useful for initializing manually created loggers with the global settings.
+//
+// Example:
+//   auto mylogger = std::make_shared<spdlog::logger>("mylogger", ...);
+//   spdlog::initialize_logger(mylogger);
+SPDLOG_API void initialize_logger(std::shared_ptr<logger> logger);
+
+// Return an existing logger or nullptr if a logger with such name doesn't
+// exist.
+// example: spdlog::get("my_logger")->info("hello {}", "world");
+SPDLOG_API std::shared_ptr<logger> get(const std::string &name);
+
+// Set global formatter. Each sink in each logger will get a clone of this object
+SPDLOG_API void set_formatter(std::unique_ptr<spdlog::formatter> formatter);
+
+// Set global format string.
+// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
+SPDLOG_API void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
+
+// enable global backtrace support
+SPDLOG_API void enable_backtrace(size_t n_messages);
+
+// disable global backtrace support
+SPDLOG_API void disable_backtrace();
+
+// call dump backtrace on default logger
+SPDLOG_API void dump_backtrace();
+
+// Get global logging level
+SPDLOG_API level::level_enum get_level();
+
+// Set global logging level
+SPDLOG_API void set_level(level::level_enum log_level);
+
+// Determine whether the default logger should log messages with a certain level
+SPDLOG_API bool should_log(level::level_enum lvl);
+
+// Set global flush level
+SPDLOG_API void flush_on(level::level_enum log_level);
+
+// Start/Restart a periodic flusher thread
+// Warning: Use only if all your loggers are thread safe!
+template<typename Rep, typename Period>
+inline void flush_every(std::chrono::duration<Rep, Period> interval)
+{
+    details::registry::instance().flush_every(interval);
+}
+
+// Set global error handler
+SPDLOG_API void set_error_handler(void (*handler)(const std::string &msg));
+
+// Register the given logger with the given name
+SPDLOG_API void register_logger(std::shared_ptr<logger> logger);
+
+// Apply a user defined function on all registered loggers
+// Example:
+// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});
+SPDLOG_API void apply_all(const std::function<void(std::shared_ptr<logger>)> &fun);
+
+// Drop the reference to the given logger
+SPDLOG_API void drop(const std::string &name);
+
+// Drop all references from the registry
+SPDLOG_API void drop_all();
+
+// stop any running threads started by spdlog and clean registry loggers
+SPDLOG_API void shutdown();
+
+// Automatic registration of loggers when using spdlog::create() or spdlog::create_async
+SPDLOG_API void set_automatic_registration(bool automatic_registration);
+
+// API for using default logger (stdout_color_mt),
+// e.g: spdlog::info("Message {}", 1);
+//
+// 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);
+//
+// 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.
+
+SPDLOG_API std::shared_ptr<spdlog::logger> default_logger();
+
+SPDLOG_API spdlog::logger *default_logger_raw();
+
+SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
+
+template<typename... Args>
+inline void log(source_loc source, level::level_enum lvl, format_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->log(source, lvl, fmt, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+inline void log(level::level_enum lvl, format_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+inline void trace(format_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->trace(fmt, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+inline void debug(format_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->debug(fmt, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+inline void info(format_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->info(fmt, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+inline void warn(format_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->warn(fmt, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+inline void error(format_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->error(fmt, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+inline void critical(format_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->critical(fmt, std::forward<Args>(args)...);
+}
+
+template<typename T>
+inline void log(source_loc source, level::level_enum lvl, const T &msg)
+{
+    default_logger_raw()->log(source, lvl, msg);
+}
+
+template<typename T>
+inline void log(level::level_enum lvl, const T &msg)
+{
+    default_logger_raw()->log(lvl, msg);
+}
+
+#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
+template<typename... Args>
+inline void log(source_loc source, level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->log(source, lvl, fmt, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+inline void log(level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+inline void trace(wformat_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->trace(fmt, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+inline void debug(wformat_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->debug(fmt, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+inline void info(wformat_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->info(fmt, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+inline void warn(wformat_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->warn(fmt, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+inline void error(wformat_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->error(fmt, std::forward<Args>(args)...);
+}
+
+template<typename... Args>
+inline void critical(wformat_string_t<Args...> fmt, Args &&... args)
+{
+    default_logger_raw()->critical(fmt, std::forward<Args>(args)...);
+}
+#endif
+
+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);
+}
+
+} // 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
+//
+
+#ifndef SPDLOG_NO_SOURCE_LOC
+#    define SPDLOG_LOGGER_CALL(logger, level, ...)                                                                                         \
+        (logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, level, __VA_ARGS__)
+#else
+#    define SPDLOG_LOGGER_CALL(logger, level, ...) (logger)->log(spdlog::source_loc{}, level, __VA_ARGS__)
+#endif
+
+#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_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_LOGGER_DEBUG(logger, ...) (void)0
+#    define SPDLOG_DEBUG(...) (void)0
+#endif
+
+#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_LOGGER_INFO(logger, ...) (void)0
+#    define SPDLOG_INFO(...) (void)0
+#endif
+
+#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
+
+#ifdef SPDLOG_HEADER_ONLY
+#    include "spdlog-inl.h"
+#endif
+
+#endif // SPDLOG_H
diff --git a/include/spdlog/stopwatch.h b/include/spdlog/stopwatch.h
new file mode 100644
index 000000000..5d1f2dc5e
--- /dev/null
+++ b/include/spdlog/stopwatch.h
@@ -0,0 +1,69 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/fmt/fmt.h>
+#include <chrono>
+
+// Stopwatch support for spdlog  (using std::chrono::steady_clock).
+// Displays elapsed seconds since construction as double.
+//
+// Usage:
+//
+// spdlog::stopwatch sw;
+// ...
+// spdlog::debug("Elapsed: {} seconds", sw);    =>  "Elapsed 0.005116733 seconds"
+// spdlog::info("Elapsed: {:.6} seconds", sw);  =>  "Elapsed 0.005163 seconds"
+//
+//
+// If other units are needed (e.g. millis instead of double), include "fmt/chrono.h" and use "duration_cast<..>(sw.elapsed())":
+//
+// #include <spdlog/fmt/chrono.h>
+//..
+// using std::chrono::duration_cast;
+// using std::chrono::milliseconds;
+// spdlog::info("Elapsed {}", duration_cast<milliseconds>(sw.elapsed())); => "Elapsed 5ms"
+
+namespace spdlog {
+class stopwatch
+{
+    using clock = std::chrono::steady_clock;
+    std::chrono::time_point<clock> start_tp_;
+
+public:
+    stopwatch()
+        : start_tp_{clock::now()}
+    {}
+
+    std::chrono::duration<double> elapsed() const
+    {
+        return std::chrono::duration<double>(clock::now() - start_tp_);
+    }
+
+    void reset()
+    {
+        start_tp_ = clock::now();
+    }
+};
+} // namespace spdlog
+
+// Support for fmt formatting  (e.g. "{:012.9}" or just "{}")
+namespace
+#ifdef SPDLOG_USE_STD_FORMAT
+    std
+#else
+    fmt
+#endif
+{
+
+template<>
+struct formatter<spdlog::stopwatch> : formatter<double>
+{
+    template<typename FormatContext>
+    auto format(const spdlog::stopwatch &sw, FormatContext &ctx) -> decltype(ctx.out())
+    {
+        return formatter<double>::format(sw.elapsed().count(), ctx);
+    }
+};
+} // namespace std
diff --git a/include/spdlog/tweakme.h b/include/spdlog/tweakme.h
new file mode 100644
index 000000000..5bcb5ff42
--- /dev/null
+++ b/include/spdlog/tweakme.h
@@ -0,0 +1,140 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Edit this file to squeeze more performance, and to customize supported
+// features
+//
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used.
+// This clock is less accurate - can be off by dozens of millis - depending on
+// the kernel HZ.
+// Uncomment to use it instead of the regular clock.
+//
+// #define SPDLOG_CLOCK_COARSE
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment if source location logging is not needed.
+// This will prevent spdlog from using __FILE__, __LINE__ and SPDLOG_FUNCTION
+//
+// #define SPDLOG_NO_SOURCE_LOC
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment if thread id logging is not needed (i.e. no %t in the log pattern).
+// This will prevent spdlog from querying the thread id on each log call.
+//
+// WARNING: If the log pattern contains thread id (i.e, %t) while this flag is
+// on, zero will be logged as thread id.
+//
+// #define SPDLOG_NO_THREAD_ID
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// 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_NO_TLS
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to avoid spdlog's usage of atomic log levels
+// Use only if your code never modifies a logger's log levels concurrently by
+// different threads.
+//
+// #define SPDLOG_NO_ATOMIC_LEVELS
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to enable usage of wchar_t for file names on Windows.
+//
+// #define SPDLOG_WCHAR_FILENAMES
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows)
+//
+// #define SPDLOG_EOL ";-)\n"
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to override default folder separators ("/" or "\\/" under
+// Linux/Windows). Each character in the string is treated as a different
+// separator.
+//
+// #define SPDLOG_FOLDER_SEPS "\\"
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to use your own copy of the fmt library instead of spdlog's copy.
+// In this case spdlog will try to include <fmt/format.h> so set your -I flag
+// accordingly.
+//
+// #define SPDLOG_FMT_EXTERNAL
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to use C++20 std::format instead of fmt.
+//
+// #define SPDLOG_USE_STD_FORMAT
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to enable wchar_t support (convert to utf8)
+//
+// #define SPDLOG_WCHAR_TO_UTF8_SUPPORT
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to prevent child processes from inheriting log file descriptors
+//
+// #define SPDLOG_PREVENT_CHILD_FD
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to customize level names (e.g. "MY TRACE")
+//
+// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", "MY ERROR", "MY CRITICAL", "OFF" }
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Uncomment to customize short level names (e.g. "MT")
+// These can be longer than one character.
+//
+// #define SPDLOG_SHORT_LEVEL_NAMES { "T", "D", "I", "W", "E", "C", "O" }
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// 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.
+//
+// #ifdef __PRETTY_FUNCTION__
+// # define SPDLOG_FUNCTION __PRETTY_FUNCTION__
+// #else
+// # define SPDLOG_FUNCTION __FUNCTION__
+// #endif
+///////////////////////////////////////////////////////////////////////////////
diff --git a/include/spdlog/version.h b/include/spdlog/version.h
new file mode 100644
index 000000000..5717beadd
--- /dev/null
+++ b/include/spdlog/version.h
@@ -0,0 +1,10 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#define SPDLOG_VER_MAJOR 1
+#define SPDLOG_VER_MINOR 11
+#define SPDLOG_VER_PATCH 0
+
+#define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH)
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index 201707d4c..9965c7526 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -1,47 +1,48 @@
 /*
- * PeTrack - Software for tracking pedestrians movement in videos
- * Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
+* PeTrack - Software for tracking pedestrians movement in videos
+* Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
 
 #include "logwindow.h"
+#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
 
 #include "ui_logwindow.h"
 
 LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
 {
-    mMainWindow = (class Petrack *) parent;
-    if(!ui)
-    {
-        mUi = new Ui::LogWindow();
-    }
-    else
-    {
-        mUi = ui;
-    }
-
-    mUi->setupUi(this);
-    mUi->logText->setReadOnly(true);
-}
+   mMainWindow = (class Petrack *) parent;
+   if(!ui)
+   {
+       mUi = new Ui::LogWindow();
+   }
+   else
+   {
+       mUi = ui;
+   }
+
+   mUi->setupUi(this);
+   mUi->logText->setReadOnly(true);
+
+   std::vector<spdlog::sink_ptr> sinks;
+   sinks.push_back(std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>());
+   sinks.push_back(std::make_shared<spdlog::sinks::qt_sink_mt>(mUi->logText, "appendPlainText"));
+
+   mLogger = std::make_shared<spdlog::logger>("Logging", std::begin(sinks), std::end(sinks));
+   mLogger->set_pattern("%! in %s line %#: %v");
+   spdlog::register_logger(mLogger);
 
-void LogWindow::appendLogText(std::string_view msg, const std::experimental::source_location location)
-{
-    std::stringstream result;
-    result << location.function_name() << " in " << splitFileName(location.file_name()) << " line " << location.line()
-           << ": " << msg;
-    std::cout << result.str() << std::endl;
-    mUi->logText->appendPlainText(QString::fromStdString(result.str()));
-    mUi->logText->verticalScrollBar()->setValue(mUi->logText->verticalScrollBar()->maximum());
 }
+
diff --git a/src/petrack.cpp b/src/petrack.cpp
index 8ab6a7ee0..d581956b7 100644
--- a/src/petrack.cpp
+++ b/src/petrack.cpp
@@ -167,8 +167,9 @@ Petrack::Petrack() :
 
     mLogWindow = new LogWindow(this, nullptr);
     mLogWindow->setWindowFlags(Qt::Window);
-    mLogWindow->setWindowTitle("Log");
-    mLogWindow->appendLogText("Test");
+    mLogWindow->setWindowTitle("Logging");
+    SPDLOG_LOGGER_INFO(spdlog::get("Logging"), "Test");
+
 
     mPlayerWidget = new Player(mAnimation, this);
 
-- 
GitLab


From 5d0b513af08311b9b34da2d10f22e23cc9a5c500 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Mon, 28 Nov 2022 16:26:51 +0100
Subject: [PATCH 06/22] formatting

---
 include/logwindow.h                           |   44 +-
 include/spdlog/async.h                        |   45 +-
 include/spdlog/async_logger-inl.h             |   41 +-
 include/spdlog/async_logger.h                 |   43 +-
 include/spdlog/cfg/argv.h                     |   34 +-
 include/spdlog/cfg/env.h                      |   18 +-
 include/spdlog/cfg/helpers-inl.h              |  183 +-
 include/spdlog/cfg/helpers.h                  |   33 +-
 include/spdlog/common-inl.h                   |   62 +-
 include/spdlog/common.h                       |  453 +-
 include/spdlog/details/backtracer-inl.h       |  102 +-
 include/spdlog/details/backtracer.h           |   47 +-
 include/spdlog/details/circular_q.h           |  207 +-
 include/spdlog/details/console_globals.h      |   40 +-
 include/spdlog/details/file_helper-inl.h      |  229 +-
 include/spdlog/details/file_helper.h          |   96 +-
 include/spdlog/details/fmt_helper.h           |  263 +-
 include/spdlog/details/log_msg-inl.h          |   59 +-
 include/spdlog/details/log_msg.h              |   47 +-
 include/spdlog/details/log_msg_buffer-inl.h   |   90 +-
 include/spdlog/details/log_msg_buffer.h       |   38 +-
 include/spdlog/details/mpmc_blocking_q.h      |  171 +-
 include/spdlog/details/null_mutex.h           |   48 +-
 include/spdlog/details/os-inl.h               |  929 +--
 include/spdlog/details/os.h                   |  144 +-
 include/spdlog/details/periodic_worker-inl.h  |   26 +-
 include/spdlog/details/periodic_worker.h      |   70 +-
 include/spdlog/details/registry-inl.h         |  416 +-
 include/spdlog/details/registry.h             |  147 +-
 include/spdlog/details/synchronous_factory.h  |    9 +-
 include/spdlog/details/tcp_client-windows.h   |  226 +-
 include/spdlog/details/tcp_client.h           |  190 +-
 include/spdlog/details/thread_pool-inl.h      |  222 +-
 include/spdlog/details/thread_pool.h          |  171 +-
 include/spdlog/details/udp_client-windows.h   |  149 +-
 include/spdlog/details/udp_client.h           |  123 +-
 include/spdlog/details/windows_include.h      |    4 +-
 include/spdlog/fmt/bin_to_hex.h               |  166 +-
 include/spdlog/fmt/bundled/args.h             |  404 +-
 include/spdlog/fmt/bundled/chrono.h           | 3897 ++++-----
 include/spdlog/fmt/bundled/color.h            | 1024 +--
 include/spdlog/fmt/bundled/compile.h          |  942 ++-
 include/spdlog/fmt/bundled/core.h             | 5052 ++++++------
 include/spdlog/fmt/bundled/format-inl.h       | 3194 ++++----
 include/spdlog/fmt/bundled/format.h           | 7012 +++++++++--------
 include/spdlog/fmt/bundled/os.h               |  579 +-
 include/spdlog/fmt/bundled/ostream.h          |  264 +-
 include/spdlog/fmt/bundled/printf.h           | 1056 +--
 include/spdlog/fmt/bundled/ranges.h           |  984 +--
 include/spdlog/fmt/bundled/std.h              |  210 +-
 include/spdlog/fmt/bundled/xchar.h            |  345 +-
 include/spdlog/fmt/chrono.h                   |   20 +-
 include/spdlog/fmt/compile.h                  |   20 +-
 include/spdlog/fmt/fmt.h                      |   26 +-
 include/spdlog/fmt/ostr.h                     |   20 +-
 include/spdlog/fmt/ranges.h                   |   20 +-
 include/spdlog/fmt/std.h                      |   24 +-
 include/spdlog/fmt/xchar.h                    |   20 +-
 include/spdlog/formatter.h                    |   11 +-
 include/spdlog/fwd.h                          |   13 +-
 include/spdlog/logger-inl.h                   |   88 +-
 include/spdlog/logger.h                       |  217 +-
 include/spdlog/pattern_formatter-inl.h        | 2080 +++--
 include/spdlog/pattern_formatter.h            |  125 +-
 include/spdlog/sinks/android_sink.h           |  202 +-
 include/spdlog/sinks/ansicolor_sink-inl.h     |  235 +-
 include/spdlog/sinks/ansicolor_sink.h         |  200 +-
 include/spdlog/sinks/base_sink-inl.h          |   35 +-
 include/spdlog/sinks/base_sink.h              |   64 +-
 include/spdlog/sinks/basic_file_sink-inl.h    |   59 +-
 include/spdlog/sinks/basic_file_sink.h        |   62 +-
 include/spdlog/sinks/daily_file_sink.h        |  470 +-
 include/spdlog/sinks/dist_sink.h              |  117 +-
 include/spdlog/sinks/dup_filter_sink.h        |   99 +-
 include/spdlog/sinks/hourly_file_sink.h       |  313 +-
 include/spdlog/sinks/mongo_sink.h             |  121 +-
 include/spdlog/sinks/msvc_sink.h              |   66 +-
 include/spdlog/sinks/null_sink.h              |   33 +-
 include/spdlog/sinks/ostream_sink.h           |   59 +-
 include/spdlog/sinks/qt_sinks.h               |  103 +-
 include/spdlog/sinks/ringbuffer_sink.h        |   91 +-
 include/spdlog/sinks/rotating_file_sink-inl.h |  228 +-
 include/spdlog/sinks/rotating_file_sink.h     |  105 +-
 include/spdlog/sinks/sink-inl.h               |    2 +-
 include/spdlog/sinks/sink.h                   |   40 +-
 include/spdlog/sinks/stdout_color_sinks-inl.h |   15 +-
 include/spdlog/sinks/stdout_color_sinks.h     |   36 +-
 include/spdlog/sinks/stdout_sinks-inl.h       |  167 +-
 include/spdlog/sinks/stdout_sinks.h           |   94 +-
 include/spdlog/sinks/syslog_sink.h            |  166 +-
 include/spdlog/sinks/systemd_sink.h           |  162 +-
 include/spdlog/sinks/tcp_sink.h               |   86 +-
 include/spdlog/sinks/udp_sink.h               |   73 +-
 include/spdlog/sinks/win_eventlog_sink.h      |  433 +-
 include/spdlog/sinks/wincolor_sink-inl.h      |  274 +-
 include/spdlog/sinks/wincolor_sink.h          |  135 +-
 include/spdlog/spdlog-inl.h                   |    5 +-
 include/spdlog/spdlog.h                       |  154 +-
 include/spdlog/stopwatch.h                    |   27 +-
 src/logwindow.cpp                             |   74 +-
 100 files changed, 19779 insertions(+), 17858 deletions(-)

diff --git a/include/logwindow.h b/include/logwindow.h
index 9b5350cea..028bf8df4 100644
--- a/include/logwindow.h
+++ b/include/logwindow.h
@@ -1,27 +1,27 @@
 /*
-* PeTrack - Software for tracking pedestrians movement in videos
-* Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
-*
-* This program is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation, either version 3 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program.  If not, see <https://www.gnu.org/licenses/>.
-*/
+ * PeTrack - Software for tracking pedestrians movement in videos
+ * Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
 
 #ifndef LOGWINDOW_H
 #define LOGWINDOW_H
 
+#include "spdlog//spdlog.h"
 #include "spdlog/logger.h"
 #include "spdlog/sinks/qt_sinks.h"
-#include "spdlog//spdlog.h"
 
 #include <QWidget>
 #include <experimental/source_location>
@@ -35,15 +35,15 @@ class LogWindow;
 
 class LogWindow : public QWidget
 {
-   Q_OBJECT
+    Q_OBJECT
 
 public:
-   LogWindow(QWidget *parent, Ui::LogWindow *mUi);
+    LogWindow(QWidget *parent, Ui::LogWindow *mUi);
 
 private:
-   Ui::LogWindow *mUi;
-   Petrack       *mMainWindow;
-   std::shared_ptr<spdlog::logger> mLogger;
+    Ui::LogWindow                  *mUi;
+    Petrack                        *mMainWindow;
+    std::shared_ptr<spdlog::logger> mLogger;
 };
 
 #endif // LOGWINDOW_H
diff --git a/include/spdlog/async.h b/include/spdlog/async.h
index d6e213498..fa47f7d59 100644
--- a/include/spdlog/async.h
+++ b/include/spdlog/async.h
@@ -14,67 +14,72 @@
 // This is because each message in the queue holds a shared_ptr to the
 // originating logger.
 
+#include <functional>
+#include <memory>
+#include <mutex>
 #include <spdlog/async_logger.h>
 #include <spdlog/details/registry.h>
 #include <spdlog/details/thread_pool.h>
 
-#include <memory>
-#include <mutex>
-#include <functional>
-
-namespace spdlog {
+namespace spdlog
+{
 
-namespace details {
-static const size_t default_async_q_size = 8192;
+namespace details
+{
+    static const size_t default_async_q_size = 8192;
 }
 
 // async logger factory - creates async loggers backed with thread pool.
 // if a global thread pool doesn't already exist, create it with default queue
 // size of 8192 items and single thread.
-template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
+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(std::string logger_name, SinkArgs &&... args)
+    template <typename Sink, typename... SinkArgs>
+    static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&...args)
     {
         auto &registry_inst = details::registry::instance();
 
         // create global thread pool if not already exists..
 
-        auto &mutex = registry_inst.tp_mutex();
+        auto                                 &mutex = registry_inst.tp_mutex();
         std::lock_guard<std::recursive_mutex> tp_lock(mutex);
-        auto tp = registry_inst.get_tp();
-        if (tp == nullptr)
+        auto                                  tp = registry_inst.get_tp();
+        if(tp == nullptr)
         {
             tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1U);
             registry_inst.set_tp(tp);
         }
 
         auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
-        auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy);
+        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;
     }
 };
 
-using async_factory = async_factory_impl<async_overflow_policy::block>;
+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(std::string logger_name, SinkArgs &&... sink_args)
+template <typename Sink, typename... SinkArgs>
+inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, 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(std::string logger_name, SinkArgs &&... sink_args)
+template <typename Sink, typename... SinkArgs>
+inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&...sink_args)
 {
     return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
 }
 
 // set global thread pool.
 inline void init_thread_pool(
-    size_t q_size, size_t thread_count, std::function<void()> on_thread_start, std::function<void()> on_thread_stop)
+    size_t                q_size,
+    size_t                thread_count,
+    std::function<void()> on_thread_start,
+    std::function<void()> on_thread_stop)
 {
     auto tp = std::make_shared<details::thread_pool>(q_size, thread_count, on_thread_start, on_thread_stop);
     details::registry::instance().set_tp(std::move(tp));
diff --git a/include/spdlog/async_logger-inl.h b/include/spdlog/async_logger-inl.h
index a1c27a59b..82bfd4ea7 100644
--- a/include/spdlog/async_logger-inl.h
+++ b/include/spdlog/async_logger-inl.h
@@ -4,29 +4,36 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/async_logger.h>
+#include <spdlog/async_logger.h>
 #endif
 
-#include <spdlog/sinks/sink.h>
-#include <spdlog/details/thread_pool.h>
-
 #include <memory>
+#include <spdlog/details/thread_pool.h>
+#include <spdlog/sinks/sink.h>
 #include <string>
 
 SPDLOG_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(), std::move(tp), overflow_policy)
-{}
+    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(), std::move(tp), overflow_policy)
+{
+}
 
 SPDLOG_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), {std::move(single_sink)}, std::move(tp), overflow_policy)
-{}
+    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), {std::move(single_sink)}, std::move(tp), overflow_policy)
+{
+}
 
 // send the log message to the thread pool
 SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg)
 {
-    if (auto pool_ptr = thread_pool_.lock())
+    if(auto pool_ptr = thread_pool_.lock())
     {
         pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
     }
@@ -39,7 +46,7 @@ SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg)
 // send flush request to the thread pool
 SPDLOG_INLINE void spdlog::async_logger::flush_()
 {
-    if (auto pool_ptr = thread_pool_.lock())
+    if(auto pool_ptr = thread_pool_.lock())
     {
         pool_ptr->post_flush(shared_from_this(), overflow_policy_);
     }
@@ -54,9 +61,9 @@ SPDLOG_INLINE void spdlog::async_logger::flush_()
 //
 SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg)
 {
-    for (auto &sink : sinks_)
+    for(auto &sink : sinks_)
     {
-        if (sink->should_log(msg.level))
+        if(sink->should_log(msg.level))
         {
             SPDLOG_TRY
             {
@@ -66,7 +73,7 @@ SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg
         }
     }
 
-    if (should_flush_(msg))
+    if(should_flush_(msg))
     {
         backend_flush_();
     }
@@ -74,7 +81,7 @@ SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg
 
 SPDLOG_INLINE void spdlog::async_logger::backend_flush_()
 {
-    for (auto &sink : sinks_)
+    for(auto &sink : sinks_)
     {
         SPDLOG_TRY
         {
@@ -86,7 +93,7 @@ SPDLOG_INLINE void spdlog::async_logger::backend_flush_()
 
 SPDLOG_INLINE std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name)
 {
-    auto cloned = std::make_shared<spdlog::async_logger>(*this);
+    auto cloned   = std::make_shared<spdlog::async_logger>(*this);
     cloned->name_ = std::move(new_name);
     return cloned;
 }
diff --git a/include/spdlog/async_logger.h b/include/spdlog/async_logger.h
index 91a93fcbe..7ec2fbb8a 100644
--- a/include/spdlog/async_logger.h
+++ b/include/spdlog/async_logger.h
@@ -16,7 +16,8 @@
 
 #include <spdlog/logger.h>
 
-namespace spdlog {
+namespace spdlog
+{
 
 // Async overflow policy - block by default.
 enum class async_overflow_policy
@@ -26,8 +27,9 @@ enum class async_overflow_policy
                    // add new item.
 };
 
-namespace details {
-class thread_pool;
+namespace details
+{
+    class thread_pool;
 }
 
 class SPDLOG_API async_logger final : public std::enable_shared_from_this<async_logger>, public logger
@@ -35,19 +37,28 @@ class SPDLOG_API async_logger final : public std::enable_shared_from_this<async_
     friend class details::thread_pool;
 
 public:
-    template<typename It>
-    async_logger(std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp,
-        async_overflow_policy overflow_policy = async_overflow_policy::block)
-        : logger(std::move(logger_name), begin, end)
-        , thread_pool_(std::move(tp))
-        , overflow_policy_(overflow_policy)
-    {}
+    template <typename It>
+    async_logger(
+        std::string                         logger_name,
+        It                                  begin,
+        It                                  end,
+        std::weak_ptr<details::thread_pool> tp,
+        async_overflow_policy               overflow_policy = async_overflow_policy::block) :
+        logger(std::move(logger_name), begin, end), thread_pool_(std::move(tp)), overflow_policy_(overflow_policy)
+    {
+    }
 
-    async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp,
-        async_overflow_policy overflow_policy = async_overflow_policy::block);
+    async_logger(
+        std::string                         logger_name,
+        sinks_init_list                     sinks_list,
+        std::weak_ptr<details::thread_pool> tp,
+        async_overflow_policy               overflow_policy = async_overflow_policy::block);
 
-    async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp,
-        async_overflow_policy overflow_policy = async_overflow_policy::block);
+    async_logger(
+        std::string                         logger_name,
+        sink_ptr                            single_sink,
+        std::weak_ptr<details::thread_pool> tp,
+        async_overflow_policy               overflow_policy = async_overflow_policy::block);
 
     std::shared_ptr<logger> clone(std::string new_name) override;
 
@@ -59,10 +70,10 @@ protected:
 
 private:
     std::weak_ptr<details::thread_pool> thread_pool_;
-    async_overflow_policy overflow_policy_;
+    async_overflow_policy               overflow_policy_;
 };
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "async_logger-inl.h"
+#include "async_logger-inl.h"
 #endif
diff --git a/include/spdlog/cfg/argv.h b/include/spdlog/cfg/argv.h
index 36d9f1c44..264f2a88a 100644
--- a/include/spdlog/cfg/argv.h
+++ b/include/spdlog/cfg/argv.h
@@ -17,28 +17,30 @@
 // turn off all logging except for logger1 and logger2:
 // example.exe "SPDLOG_LEVEL=off,logger1=debug,logger2=info"
 
-namespace spdlog {
-namespace cfg {
-
-// search for SPDLOG_LEVEL= in the args and use it to init the levels
-inline void load_argv_levels(int argc, const char **argv)
+namespace spdlog
+{
+namespace cfg
 {
-    const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
-    for (int i = 1; i < argc; i++)
+
+    // search for SPDLOG_LEVEL= in the args and use it to init the levels
+    inline void load_argv_levels(int argc, const char **argv)
     {
-        std::string arg = argv[i];
-        if (arg.find(spdlog_level_prefix) == 0)
+        const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
+        for(int i = 1; i < argc; i++)
         {
-            auto levels_string = arg.substr(spdlog_level_prefix.size());
-            helpers::load_levels(levels_string);
+            std::string arg = argv[i];
+            if(arg.find(spdlog_level_prefix) == 0)
+            {
+                auto levels_string = arg.substr(spdlog_level_prefix.size());
+                helpers::load_levels(levels_string);
+            }
         }
     }
-}
 
-inline void load_argv_levels(int argc, char **argv)
-{
-    load_argv_levels(argc, const_cast<const char **>(argv));
-}
+    inline void load_argv_levels(int argc, char **argv)
+    {
+        load_argv_levels(argc, const_cast<const char **>(argv));
+    }
 
 } // namespace cfg
 } // namespace spdlog
diff --git a/include/spdlog/cfg/env.h b/include/spdlog/cfg/env.h
index 1f39ebbb2..7102370df 100644
--- a/include/spdlog/cfg/env.h
+++ b/include/spdlog/cfg/env.h
@@ -3,8 +3,8 @@
 
 #pragma once
 #include <spdlog/cfg/helpers.h>
-#include <spdlog/details/registry.h>
 #include <spdlog/details/os.h>
+#include <spdlog/details/registry.h>
 
 //
 // Init levels and patterns from env variables SPDLOG_LEVEL
@@ -23,16 +23,18 @@
 // turn off all logging except for logger1 and logger2:
 // export SPDLOG_LEVEL="off,logger1=debug,logger2=info"
 
-namespace spdlog {
-namespace cfg {
-inline void load_env_levels()
+namespace spdlog
+{
+namespace cfg
 {
-    auto env_val = details::os::getenv("SPDLOG_LEVEL");
-    if (!env_val.empty())
+    inline void load_env_levels()
     {
-        helpers::load_levels(env_val);
+        auto env_val = details::os::getenv("SPDLOG_LEVEL");
+        if(!env_val.empty())
+        {
+            helpers::load_levels(env_val);
+        }
     }
-}
 
 } // namespace cfg
 } // namespace spdlog
diff --git a/include/spdlog/cfg/helpers-inl.h b/include/spdlog/cfg/helpers-inl.h
index 675a13af1..48129777e 100644
--- a/include/spdlog/cfg/helpers-inl.h
+++ b/include/spdlog/cfg/helpers-inl.h
@@ -4,117 +4,122 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/cfg/helpers.h>
+#include <spdlog/cfg/helpers.h>
 #endif
 
-#include <spdlog/spdlog.h>
+#include <algorithm>
 #include <spdlog/details/os.h>
 #include <spdlog/details/registry.h>
-
-#include <algorithm>
+#include <spdlog/spdlog.h>
+#include <sstream>
 #include <string>
 #include <utility>
-#include <sstream>
 
-namespace spdlog {
-namespace cfg {
-namespace helpers {
-
-// inplace convert to lowercase
-inline std::string &to_lower_(std::string &str)
-{
-    std::transform(
-        str.begin(), str.end(), str.begin(), [](char ch) { return static_cast<char>((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch); });
-    return str;
-}
-
-// inplace trim spaces
-inline std::string &trim_(std::string &str)
+namespace spdlog
 {
-    const char *spaces = " \n\r\t";
-    str.erase(str.find_last_not_of(spaces) + 1);
-    str.erase(0, str.find_first_not_of(spaces));
-    return str;
-}
-
-// return (name,value) trimmed pair from given "name=value" string.
-// return empty string on missing parts
-// "key=val" => ("key", "val")
-// " key  =  val " => ("key", "val")
-// "key=" => ("key", "")
-// "val" => ("", "val")
-
-inline std::pair<std::string, std::string> extract_kv_(char sep, const std::string &str)
+namespace cfg
 {
-    auto n = str.find(sep);
-    std::string k, v;
-    if (n == std::string::npos)
-    {
-        v = str;
-    }
-    else
+    namespace helpers
     {
-        k = str.substr(0, n);
-        v = str.substr(n + 1);
-    }
-    return std::make_pair(trim_(k), trim_(v));
-}
 
-// return vector of key/value pairs from sequence of "K1=V1,K2=V2,.."
-// "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
-inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str)
-{
-    std::string token;
-    std::istringstream token_stream(str);
-    std::unordered_map<std::string, std::string> rv{};
-    while (std::getline(token_stream, token, ','))
-    {
-        if (token.empty())
+        // inplace convert to lowercase
+        inline std::string &to_lower_(std::string &str)
         {
-            continue;
+            std::transform(
+                str.begin(),
+                str.end(),
+                str.begin(),
+                [](char ch) { return static_cast<char>((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch); });
+            return str;
         }
-        auto kv = extract_kv_('=', token);
-        rv[kv.first] = kv.second;
-    }
-    return rv;
-}
 
-SPDLOG_INLINE void load_levels(const std::string &input)
-{
-    if (input.empty() || input.size() > 512)
-    {
-        return;
-    }
+        // inplace trim spaces
+        inline std::string &trim_(std::string &str)
+        {
+            const char *spaces = " \n\r\t";
+            str.erase(str.find_last_not_of(spaces) + 1);
+            str.erase(0, str.find_first_not_of(spaces));
+            return str;
+        }
 
-    auto key_vals = extract_key_vals_(input);
-    std::unordered_map<std::string, level::level_enum> levels;
-    level::level_enum global_level = level::info;
-    bool global_level_found = false;
+        // return (name,value) trimmed pair from given "name=value" string.
+        // return empty string on missing parts
+        // "key=val" => ("key", "val")
+        // " key  =  val " => ("key", "val")
+        // "key=" => ("key", "")
+        // "val" => ("", "val")
 
-    for (auto &name_level : key_vals)
-    {
-        auto &logger_name = name_level.first;
-        auto level_name = to_lower_(name_level.second);
-        auto level = level::from_str(level_name);
-        // ignore unrecognized level names
-        if (level == level::off && level_name != "off")
+        inline std::pair<std::string, std::string> extract_kv_(char sep, const std::string &str)
         {
-            continue;
+            auto        n = str.find(sep);
+            std::string k, v;
+            if(n == std::string::npos)
+            {
+                v = str;
+            }
+            else
+            {
+                k = str.substr(0, n);
+                v = str.substr(n + 1);
+            }
+            return std::make_pair(trim_(k), trim_(v));
         }
-        if (logger_name.empty()) // no logger name indicate global level
+
+        // return vector of key/value pairs from sequence of "K1=V1,K2=V2,.."
+        // "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
+        inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str)
         {
-            global_level_found = true;
-            global_level = level;
+            std::string                                  token;
+            std::istringstream                           token_stream(str);
+            std::unordered_map<std::string, std::string> rv{};
+            while(std::getline(token_stream, token, ','))
+            {
+                if(token.empty())
+                {
+                    continue;
+                }
+                auto kv      = extract_kv_('=', token);
+                rv[kv.first] = kv.second;
+            }
+            return rv;
         }
-        else
+
+        SPDLOG_INLINE void load_levels(const std::string &input)
         {
-            levels[logger_name] = level;
-        }
-    }
+            if(input.empty() || input.size() > 512)
+            {
+                return;
+            }
 
-    details::registry::instance().set_levels(std::move(levels), global_level_found ? &global_level : nullptr);
-}
+            auto                                               key_vals = extract_key_vals_(input);
+            std::unordered_map<std::string, level::level_enum> levels;
+            level::level_enum                                  global_level       = level::info;
+            bool                                               global_level_found = false;
+
+            for(auto &name_level : key_vals)
+            {
+                auto &logger_name = name_level.first;
+                auto  level_name  = to_lower_(name_level.second);
+                auto  level       = level::from_str(level_name);
+                // ignore unrecognized level names
+                if(level == level::off && level_name != "off")
+                {
+                    continue;
+                }
+                if(logger_name.empty()) // no logger name indicate global level
+                {
+                    global_level_found = true;
+                    global_level       = level;
+                }
+                else
+                {
+                    levels[logger_name] = level;
+                }
+            }
+
+            details::registry::instance().set_levels(std::move(levels), global_level_found ? &global_level : nullptr);
+        }
 
-} // namespace helpers
+    } // namespace helpers
 } // namespace cfg
 } // namespace spdlog
diff --git a/include/spdlog/cfg/helpers.h b/include/spdlog/cfg/helpers.h
index ab7584e05..5312c3dfb 100644
--- a/include/spdlog/cfg/helpers.h
+++ b/include/spdlog/cfg/helpers.h
@@ -6,24 +6,27 @@
 #include <spdlog/common.h>
 #include <unordered_map>
 
-namespace spdlog {
-namespace cfg {
-namespace helpers {
-//
-// Init levels from given string
-//
-// Examples:
-//
-// set global level to debug: "debug"
-// turn off all logging except for logger1: "off,logger1=debug"
-// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
-//
-SPDLOG_API void load_levels(const std::string &txt);
-} // namespace helpers
+namespace spdlog
+{
+namespace cfg
+{
+    namespace helpers
+    {
+        //
+        // Init levels from given string
+        //
+        // Examples:
+        //
+        // set global level to debug: "debug"
+        // turn off all logging except for logger1: "off,logger1=debug"
+        // turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
+        //
+        SPDLOG_API void load_levels(const std::string &txt);
+    } // namespace helpers
 
 } // namespace cfg
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "helpers-inl.h"
+#include "helpers-inl.h"
 #endif // SPDLOG_HEADER_ONLY
diff --git a/include/spdlog/common-inl.h b/include/spdlog/common-inl.h
index 728f98317..5ebcf69a5 100644
--- a/include/spdlog/common-inl.h
+++ b/include/spdlog/common-inl.h
@@ -4,54 +4,54 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/common.h>
+#include <spdlog/common.h>
 #endif
 
 #include <algorithm>
 #include <iterator>
 
-namespace spdlog {
-namespace level {
+namespace spdlog
+{
+namespace level
+{
 
 #if __cplusplus >= 201703L
-constexpr
+    constexpr
 #endif
-    static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
+        static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
 
-static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
+    static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
 
-SPDLOG_INLINE const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
-{
-    return level_string_views[l];
-}
-
-SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
-{
-    return short_level_names[l];
-}
-
-SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
-{
-    auto it = std::find(std::begin(level_string_views), std::end(level_string_views), name);
-    if (it != std::end(level_string_views))
-        return static_cast<level::level_enum>(std::distance(std::begin(level_string_views), it));
+    SPDLOG_INLINE const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
+    {
+        return level_string_views[l];
+    }
 
-    // check also for "warn" and "err" before giving up..
-    if (name == "warn")
+    SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
     {
-        return level::warn;
+        return short_level_names[l];
     }
-    if (name == "err")
+
+    SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
     {
-        return level::err;
+        auto it = std::find(std::begin(level_string_views), std::end(level_string_views), name);
+        if(it != std::end(level_string_views))
+            return static_cast<level::level_enum>(std::distance(std::begin(level_string_views), it));
+
+        // check also for "warn" and "err" before giving up..
+        if(name == "warn")
+        {
+            return level::warn;
+        }
+        if(name == "err")
+        {
+            return level::err;
+        }
+        return level::off;
     }
-    return level::off;
-}
 } // namespace level
 
-SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg)
-    : msg_(std::move(msg))
-{}
+SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg) : msg_(std::move(msg)) {}
 
 SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno)
 {
diff --git a/include/spdlog/common.h b/include/spdlog/common.h
index e69201a81..127d7a672 100644
--- a/include/spdlog/common.h
+++ b/include/spdlog/common.h
@@ -3,264 +3,274 @@
 
 #pragma once
 
-#include <spdlog/tweakme.h>
-#include <spdlog/details/null_mutex.h>
-
 #include <atomic>
 #include <chrono>
+#include <cstdio>
+#include <exception>
+#include <functional>
 #include <initializer_list>
 #include <memory>
-#include <exception>
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/tweakme.h>
 #include <string>
 #include <type_traits>
-#include <functional>
-#include <cstdio>
 
 #ifdef SPDLOG_USE_STD_FORMAT
-#    include <version>
-#    if __cpp_lib_format >= 202207L
-#        include <format>
-#    else
-#        include <string_view>
-#    endif
+#include <version>
+#if __cpp_lib_format >= 202207L
+#include <format>
+#else
+#include <string_view>
+#endif
 #endif
 
 #ifdef SPDLOG_COMPILED_LIB
-#    undef SPDLOG_HEADER_ONLY
-#    if defined(SPDLOG_SHARED_LIB)
-#        if defined(_WIN32)
-#            ifdef spdlog_EXPORTS
-#                define SPDLOG_API __declspec(dllexport)
-#            else // !spdlog_EXPORTS
-#                define SPDLOG_API __declspec(dllimport)
-#            endif
-#        else // !defined(_WIN32)
-#            define SPDLOG_API __attribute__((visibility("default")))
-#        endif
-#    else // !defined(SPDLOG_SHARED_LIB)
-#        define SPDLOG_API
-#    endif
-#    define SPDLOG_INLINE
+#undef SPDLOG_HEADER_ONLY
+#if defined(SPDLOG_SHARED_LIB)
+#if defined(_WIN32)
+#ifdef spdlog_EXPORTS
+#define SPDLOG_API __declspec(dllexport)
+#else // !spdlog_EXPORTS
+#define SPDLOG_API __declspec(dllimport)
+#endif
+#else // !defined(_WIN32)
+#define SPDLOG_API __attribute__((visibility("default")))
+#endif
+#else // !defined(SPDLOG_SHARED_LIB)
+#define SPDLOG_API
+#endif
+#define SPDLOG_INLINE
 #else // !defined(SPDLOG_COMPILED_LIB)
-#    define SPDLOG_API
-#    define SPDLOG_HEADER_ONLY
-#    define SPDLOG_INLINE inline
+#define SPDLOG_API
+#define SPDLOG_HEADER_ONLY
+#define SPDLOG_INLINE inline
 #endif // #ifdef SPDLOG_COMPILED_LIB
 
 #include <spdlog/fmt/fmt.h>
 
 #if !defined(SPDLOG_USE_STD_FORMAT) && FMT_VERSION >= 80000 // backward compatibility with fmt versions older than 8
-#    define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string)
-#    define SPDLOG_FMT_STRING(format_string) FMT_STRING(format_string)
-#    if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
-#        include <spdlog/fmt/xchar.h>
-#    endif
+#define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string)
+#define SPDLOG_FMT_STRING(format_string)  FMT_STRING(format_string)
+#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
+#include <spdlog/fmt/xchar.h>
+#endif
 #else
-#    define SPDLOG_FMT_RUNTIME(format_string) format_string
-#    define SPDLOG_FMT_STRING(format_string) format_string
+#define SPDLOG_FMT_RUNTIME(format_string) format_string
+#define SPDLOG_FMT_STRING(format_string)  format_string
 #endif
 
 // visual studio up to 2013 does not support noexcept nor constexpr
 #if defined(_MSC_VER) && (_MSC_VER < 1900)
-#    define SPDLOG_NOEXCEPT _NOEXCEPT
-#    define SPDLOG_CONSTEXPR
-#    define SPDLOG_CONSTEXPR_FUNC inline
+#define SPDLOG_NOEXCEPT _NOEXCEPT
+#define SPDLOG_CONSTEXPR
+#define SPDLOG_CONSTEXPR_FUNC inline
+#else
+#define SPDLOG_NOEXCEPT  noexcept
+#define SPDLOG_CONSTEXPR constexpr
+#if __cplusplus >= 201402L
+#define SPDLOG_CONSTEXPR_FUNC constexpr
 #else
-#    define SPDLOG_NOEXCEPT noexcept
-#    define SPDLOG_CONSTEXPR constexpr
-#    if __cplusplus >= 201402L
-#        define SPDLOG_CONSTEXPR_FUNC constexpr
-#    else
-#        define SPDLOG_CONSTEXPR_FUNC inline
-#    endif
+#define SPDLOG_CONSTEXPR_FUNC inline
+#endif
 #endif
 
 #if defined(__GNUC__) || defined(__clang__)
-#    define SPDLOG_DEPRECATED __attribute__((deprecated))
+#define SPDLOG_DEPRECATED __attribute__((deprecated))
 #elif defined(_MSC_VER)
-#    define SPDLOG_DEPRECATED __declspec(deprecated)
+#define SPDLOG_DEPRECATED __declspec(deprecated)
 #else
-#    define SPDLOG_DEPRECATED
+#define SPDLOG_DEPRECATED
 #endif
 
 // 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
+#if(defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
+#define SPDLOG_NO_TLS 1
+#endif
 #endif
 
 #ifndef SPDLOG_FUNCTION
-#    define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__)
+#define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__)
 #endif
 
 #ifdef SPDLOG_NO_EXCEPTIONS
-#    define SPDLOG_TRY
-#    define SPDLOG_THROW(ex)                                                                                                               \
-        do                                                                                                                                 \
-        {                                                                                                                                  \
-            printf("spdlog fatal error: %s\n", ex.what());                                                                                 \
-            std::abort();                                                                                                                  \
-        } while (0)
-#    define SPDLOG_CATCH_STD
+#define SPDLOG_TRY
+#define SPDLOG_THROW(ex)                                                                                               \
+    do                                                                                                                 \
+    {                                                                                                                  \
+        printf("spdlog fatal error: %s\n", ex.what());                                                                 \
+        std::abort();                                                                                                  \
+    } while(0)
+#define SPDLOG_CATCH_STD
 #else
-#    define SPDLOG_TRY try
-#    define SPDLOG_THROW(ex) throw(ex)
-#    define SPDLOG_CATCH_STD                                                                                                               \
-        catch (const std::exception &) {}
+#define SPDLOG_TRY       try
+#define SPDLOG_THROW(ex) throw(ex)
+#define SPDLOG_CATCH_STD                                                                                               \
+    catch(const std::exception &) {}
 #endif
 
-namespace spdlog {
+namespace spdlog
+{
 
 class formatter;
 
-namespace sinks {
-class sink;
+namespace sinks
+{
+    class sink;
 }
 
 #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
 using filename_t = std::wstring;
 // allow macro expansion to occur in SPDLOG_FILENAME_T
-#    define SPDLOG_FILENAME_T_INNER(s) L##s
-#    define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s)
+#define SPDLOG_FILENAME_T_INNER(s) L##s
+#define SPDLOG_FILENAME_T(s)       SPDLOG_FILENAME_T_INNER(s)
 #else
-using filename_t = std::string;
-#    define SPDLOG_FILENAME_T(s) s
+using filename_t  = std::string;
+#define SPDLOG_FILENAME_T(s) s
 #endif
 
-using log_clock = std::chrono::system_clock;
-using sink_ptr = std::shared_ptr<sinks::sink>;
+using log_clock       = std::chrono::system_clock;
+using sink_ptr        = std::shared_ptr<sinks::sink>;
 using sinks_init_list = std::initializer_list<sink_ptr>;
-using err_handler = std::function<void(const std::string &err_msg)>;
+using err_handler     = std::function<void(const std::string &err_msg)>;
 #ifdef SPDLOG_USE_STD_FORMAT
 namespace fmt_lib = std;
 
 using string_view_t = std::string_view;
-using memory_buf_t = std::string;
+using memory_buf_t  = std::string;
 
-template<typename... Args>
-#    if __cpp_lib_format >= 202207L
+template <typename... Args>
+#if __cpp_lib_format >= 202207L
 using format_string_t = std::format_string<Args...>;
-#    else
+#else
 using format_string_t = std::string_view;
-#    endif
+#endif
 
-template<class T, class Char = char>
-struct is_convertible_to_basic_format_string : std::integral_constant<bool, std::is_convertible<T, std::basic_string_view<Char>>::value>
-{};
+template <class T, class Char = char>
+struct is_convertible_to_basic_format_string
+    : std::integral_constant<bool, std::is_convertible<T, std::basic_string_view<Char>>::value>
+{
+};
 
-#    if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
+#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
 using wstring_view_t = std::wstring_view;
-using wmemory_buf_t = std::wstring;
+using wmemory_buf_t  = std::wstring;
 
-template<typename... Args>
-#        if __cpp_lib_format >= 202207L
+template <typename... Args>
+#if __cpp_lib_format >= 202207L
 using wformat_string_t = std::wformat_string<Args...>;
-#        else
+#else
 using wformat_string_t = std::wstring_view;
-#        endif
-#    endif
-#    define SPDLOG_BUF_TO_STRING(x) x
+#endif
+#endif
+#define SPDLOG_BUF_TO_STRING(x) x
 #else // use fmt lib instead of std::format
 namespace fmt_lib = fmt;
 
 using string_view_t = fmt::basic_string_view<char>;
-using memory_buf_t = fmt::basic_memory_buffer<char, 250>;
+using memory_buf_t  = fmt::basic_memory_buffer<char, 250>;
 
-template<typename... Args>
+template <typename... Args>
 using format_string_t = fmt::format_string<Args...>;
 
-template<class T>
+template <class T>
 using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
 
-// clang doesn't like SFINAE disabled constructor in std::is_convertible<> so have to repeat the condition from basic_format_string here,
-// in addition, fmt::basic_runtime<Char> is only convertible to basic_format_string<Char> but not basic_string_view<Char>
-template<class T, class Char = char>
-struct is_convertible_to_basic_format_string
-    : std::integral_constant<bool,
-          std::is_convertible<T, fmt::basic_string_view<Char>>::value || std::is_same<remove_cvref_t<T>, fmt::basic_runtime<Char>>::value>
-{};
+// clang doesn't like SFINAE disabled constructor in std::is_convertible<> so have to repeat the condition from
+// basic_format_string here, in addition, fmt::basic_runtime<Char> is only convertible to basic_format_string<Char> but
+// not basic_string_view<Char>
+template <class T, class Char = char>
+struct is_convertible_to_basic_format_string : std::integral_constant<
+                                                   bool,
+                                                   std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
+                                                       std::is_same<remove_cvref_t<T>, fmt::basic_runtime<Char>>::value>
+{
+};
 
-#    if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
+#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
 using wstring_view_t = fmt::basic_string_view<wchar_t>;
-using wmemory_buf_t = fmt::basic_memory_buffer<wchar_t, 250>;
+using wmemory_buf_t  = fmt::basic_memory_buffer<wchar_t, 250>;
 
-template<typename... Args>
+template <typename... Args>
 using wformat_string_t = fmt::wformat_string<Args...>;
-#    endif
-#    define SPDLOG_BUF_TO_STRING(x) fmt::to_string(x)
+#endif
+#define SPDLOG_BUF_TO_STRING(x) fmt::to_string(x)
 #endif
 
 #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
-#    ifndef _WIN32
-#        error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
-#    endif // _WIN32
-#endif     // SPDLOG_WCHAR_TO_UTF8_SUPPORT
-
-template<class T>
-struct is_convertible_to_any_format_string : std::integral_constant<bool, is_convertible_to_basic_format_string<T, char>::value ||
-                                                                              is_convertible_to_basic_format_string<T, wchar_t>::value>
-{};
+#ifndef _WIN32
+#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
+#endif // _WIN32
+#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
+
+template <class T>
+struct is_convertible_to_any_format_string : std::integral_constant<
+                                                 bool,
+                                                 is_convertible_to_basic_format_string<T, char>::value ||
+                                                     is_convertible_to_basic_format_string<T, wchar_t>::value>
+{
+};
 
 #if defined(SPDLOG_NO_ATOMIC_LEVELS)
 using level_t = details::null_atomic_int;
 #else
-using level_t = std::atomic<int>;
+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_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
+#define SPDLOG_LEVEL_OFF      6
 
 #if !defined(SPDLOG_ACTIVE_LEVEL)
-#    define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
+#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
 #endif
 
 // Log level enum
-namespace level {
-enum level_enum : int
+namespace level
 {
-    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,
-    n_levels
-};
-
-#define SPDLOG_LEVEL_NAME_TRACE spdlog::string_view_t("trace", 5)
-#define SPDLOG_LEVEL_NAME_DEBUG spdlog::string_view_t("debug", 5)
-#define SPDLOG_LEVEL_NAME_INFO spdlog::string_view_t("info", 4)
-#define SPDLOG_LEVEL_NAME_WARNING spdlog::string_view_t("warning", 7)
-#define SPDLOG_LEVEL_NAME_ERROR spdlog::string_view_t("error", 5)
+    enum level_enum : int
+    {
+        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,
+        n_levels
+    };
+
+#define SPDLOG_LEVEL_NAME_TRACE    spdlog::string_view_t("trace", 5)
+#define SPDLOG_LEVEL_NAME_DEBUG    spdlog::string_view_t("debug", 5)
+#define SPDLOG_LEVEL_NAME_INFO     spdlog::string_view_t("info", 4)
+#define SPDLOG_LEVEL_NAME_WARNING  spdlog::string_view_t("warning", 7)
+#define SPDLOG_LEVEL_NAME_ERROR    spdlog::string_view_t("error", 5)
 #define SPDLOG_LEVEL_NAME_CRITICAL spdlog::string_view_t("critical", 8)
-#define SPDLOG_LEVEL_NAME_OFF spdlog::string_view_t("off", 3)
+#define SPDLOG_LEVEL_NAME_OFF      spdlog::string_view_t("off", 3)
 
 #if !defined(SPDLOG_LEVEL_NAMES)
-#    define SPDLOG_LEVEL_NAMES                                                                                                             \
-        {                                                                                                                                  \
-            SPDLOG_LEVEL_NAME_TRACE, SPDLOG_LEVEL_NAME_DEBUG, SPDLOG_LEVEL_NAME_INFO, SPDLOG_LEVEL_NAME_WARNING, SPDLOG_LEVEL_NAME_ERROR,  \
-                SPDLOG_LEVEL_NAME_CRITICAL, SPDLOG_LEVEL_NAME_OFF                                                                          \
-        }
+#define SPDLOG_LEVEL_NAMES                                                                                             \
+    {                                                                                                                  \
+        SPDLOG_LEVEL_NAME_TRACE, SPDLOG_LEVEL_NAME_DEBUG, SPDLOG_LEVEL_NAME_INFO, SPDLOG_LEVEL_NAME_WARNING,           \
+            SPDLOG_LEVEL_NAME_ERROR, SPDLOG_LEVEL_NAME_CRITICAL, SPDLOG_LEVEL_NAME_OFF                                 \
+    }
 #endif
 
 #if !defined(SPDLOG_SHORT_LEVEL_NAMES)
 
-#    define SPDLOG_SHORT_LEVEL_NAMES                                                                                                       \
-        {                                                                                                                                  \
-            "T", "D", "I", "W", "E", "C", "O"                                                                                              \
-        }
+#define SPDLOG_SHORT_LEVEL_NAMES                                                                                       \
+    {                                                                                                                  \
+        "T", "D", "I", "W", "E", "C", "O"                                                                              \
+    }
 #endif
 
-SPDLOG_API const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
-SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
-SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
+    SPDLOG_API const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
+    SPDLOG_API const char          *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
+    SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
 
 } // namespace level
 
@@ -304,109 +314,102 @@ private:
 struct source_loc
 {
     SPDLOG_CONSTEXPR source_loc() = default;
-    SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in)
-        : filename{filename_in}
-        , line{line_in}
-        , funcname{funcname_in}
-    {}
-
-    SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT
+    SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in) :
+        filename{filename_in}, line{line_in}, funcname{funcname_in}
     {
-        return line == 0;
     }
-    const char *filename{nullptr};
-    int line{0};
-    const char *funcname{nullptr};
+
+    SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT { return line == 0; }
+    const char           *filename{nullptr};
+    int                   line{0};
+    const char           *funcname{nullptr};
 };
 
 struct file_event_handlers
 {
-    file_event_handlers()
-        : before_open(nullptr)
-        , after_open(nullptr)
-        , before_close(nullptr)
-        , after_close(nullptr)
-    {}
-
-    std::function<void(const filename_t &filename)> before_open;
+    file_event_handlers() : before_open(nullptr), after_open(nullptr), before_close(nullptr), after_close(nullptr) {}
+
+    std::function<void(const filename_t &filename)>                         before_open;
     std::function<void(const filename_t &filename, std::FILE *file_stream)> after_open;
     std::function<void(const filename_t &filename, std::FILE *file_stream)> before_close;
-    std::function<void(const filename_t &filename)> after_close;
+    std::function<void(const filename_t &filename)>                         after_close;
 };
 
-namespace details {
+namespace details
+{
 
-// to_string_view
+    // to_string_view
 
-SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(const memory_buf_t &buf) SPDLOG_NOEXCEPT
-{
-    return spdlog::string_view_t{buf.data(), buf.size()};
-}
+    SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(const memory_buf_t &buf) SPDLOG_NOEXCEPT
+    {
+        return spdlog::string_view_t{buf.data(), buf.size()};
+    }
 
-SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(spdlog::string_view_t str) SPDLOG_NOEXCEPT
-{
-    return str;
-}
+    SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(spdlog::string_view_t str) SPDLOG_NOEXCEPT
+    {
+        return str;
+    }
 
 #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
-SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(const wmemory_buf_t &buf) SPDLOG_NOEXCEPT
-{
-    return spdlog::wstring_view_t{buf.data(), buf.size()};
-}
+    SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(const wmemory_buf_t &buf) SPDLOG_NOEXCEPT
+    {
+        return spdlog::wstring_view_t{buf.data(), buf.size()};
+    }
 
-SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(spdlog::wstring_view_t str) SPDLOG_NOEXCEPT
-{
-    return str;
-}
+    SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(spdlog::wstring_view_t str) SPDLOG_NOEXCEPT
+    {
+        return str;
+    }
 #endif
 
 #ifndef SPDLOG_USE_STD_FORMAT
-template<typename T, typename... Args>
-inline fmt::basic_string_view<T> to_string_view(fmt::basic_format_string<T, Args...> fmt)
-{
-    return fmt;
-}
+    template <typename T, typename... Args>
+    inline fmt::basic_string_view<T> to_string_view(fmt::basic_format_string<T, Args...> fmt)
+    {
+        return fmt;
+    }
 #elif __cpp_lib_format >= 202207L
-template<typename T, typename... Args>
-SPDLOG_CONSTEXPR_FUNC std::basic_string_view<T> to_string_view(std::basic_format_string<T, Args...> fmt) SPDLOG_NOEXCEPT
-{
-    return fmt.get();
-}
+    template <typename T, typename... Args>
+    SPDLOG_CONSTEXPR_FUNC std::basic_string_view<T>
+                          to_string_view(std::basic_format_string<T, Args...> fmt) SPDLOG_NOEXCEPT
+    {
+        return fmt.get();
+    }
 #endif
 
-// make_unique support for pre c++14
+    // make_unique support for pre c++14
 
 #if __cplusplus >= 201402L // C++14 and beyond
-using std::enable_if_t;
-using std::make_unique;
+    using std::enable_if_t;
+    using std::make_unique;
 #else
-template<bool B, class T = void>
-using enable_if_t = typename std::enable_if<B, T>::type;
+    template <bool B, class T = void>
+    using enable_if_t = typename std::enable_if<B, T>::type;
 
-template<typename T, typename... Args>
-std::unique_ptr<T> make_unique(Args &&... args)
-{
-    static_assert(!std::is_array<T>::value, "arrays not supported");
-    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-}
+    template <typename T, typename... Args>
+    std::unique_ptr<T> make_unique(Args &&...args)
+    {
+                 static_assert(!std::is_array<T>::value, "arrays not supported");
+                 return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+    }
 #endif
 
-// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
-template<typename T, typename U, enable_if_t<!std::is_same<T, U>::value, int> = 0>
-constexpr T conditional_static_cast(U value)
-{
-    return static_cast<T>(value);
-}
+    // to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
+    template <typename T, typename U, enable_if_t<!std::is_same<T, U>::value, int> = 0>
+    constexpr T conditional_static_cast(U value)
+    {
+        return static_cast<T>(value);
+    }
 
-template<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>
-constexpr T conditional_static_cast(U value)
-{
-    return value;
-}
+    template <typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>
+    constexpr T conditional_static_cast(U value)
+    {
+        return value;
+    }
 
 } // namespace details
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "common-inl.h"
+#include "common-inl.h"
 #endif
diff --git a/include/spdlog/details/backtracer-inl.h b/include/spdlog/details/backtracer-inl.h
index 2621c8f7d..19f73fcca 100644
--- a/include/spdlog/details/backtracer-inl.h
+++ b/include/spdlog/details/backtracer-inl.h
@@ -4,66 +4,68 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/details/backtracer.h>
+#include <spdlog/details/backtracer.h>
 #endif
-namespace spdlog {
-namespace details {
-SPDLOG_INLINE backtracer::backtracer(const backtracer &other)
+namespace spdlog
 {
-    std::lock_guard<std::mutex> lock(other.mutex_);
-    enabled_ = other.enabled();
-    messages_ = other.messages_;
-}
-
-SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT
+namespace details
 {
-    std::lock_guard<std::mutex> lock(other.mutex_);
-    enabled_ = other.enabled();
-    messages_ = std::move(other.messages_);
-}
+    SPDLOG_INLINE backtracer::backtracer(const backtracer &other)
+    {
+        std::lock_guard<std::mutex> lock(other.mutex_);
+        enabled_  = other.enabled();
+        messages_ = other.messages_;
+    }
 
-SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other)
-{
-    std::lock_guard<std::mutex> lock(mutex_);
-    enabled_ = other.enabled();
-    messages_ = std::move(other.messages_);
-    return *this;
-}
+    SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT
+    {
+        std::lock_guard<std::mutex> lock(other.mutex_);
+        enabled_  = other.enabled();
+        messages_ = std::move(other.messages_);
+    }
 
-SPDLOG_INLINE void backtracer::enable(size_t size)
-{
-    std::lock_guard<std::mutex> lock{mutex_};
-    enabled_.store(true, std::memory_order_relaxed);
-    messages_ = circular_q<log_msg_buffer>{size};
-}
+    SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other)
+    {
+        std::lock_guard<std::mutex> lock(mutex_);
+        enabled_  = other.enabled();
+        messages_ = std::move(other.messages_);
+        return *this;
+    }
 
-SPDLOG_INLINE void backtracer::disable()
-{
-    std::lock_guard<std::mutex> lock{mutex_};
-    enabled_.store(false, std::memory_order_relaxed);
-}
+    SPDLOG_INLINE void backtracer::enable(size_t size)
+    {
+        std::lock_guard<std::mutex> lock{mutex_};
+        enabled_.store(true, std::memory_order_relaxed);
+        messages_ = circular_q<log_msg_buffer>{size};
+    }
 
-SPDLOG_INLINE bool backtracer::enabled() const
-{
-    return enabled_.load(std::memory_order_relaxed);
-}
+    SPDLOG_INLINE void backtracer::disable()
+    {
+        std::lock_guard<std::mutex> lock{mutex_};
+        enabled_.store(false, std::memory_order_relaxed);
+    }
 
-SPDLOG_INLINE void backtracer::push_back(const log_msg &msg)
-{
-    std::lock_guard<std::mutex> lock{mutex_};
-    messages_.push_back(log_msg_buffer{msg});
-}
+    SPDLOG_INLINE bool backtracer::enabled() const
+    {
+        return enabled_.load(std::memory_order_relaxed);
+    }
 
-// pop all items in the q and apply the given fun on each of them.
-SPDLOG_INLINE void backtracer::foreach_pop(std::function<void(const details::log_msg &)> fun)
-{
-    std::lock_guard<std::mutex> lock{mutex_};
-    while (!messages_.empty())
+    SPDLOG_INLINE void backtracer::push_back(const log_msg &msg)
+    {
+        std::lock_guard<std::mutex> lock{mutex_};
+        messages_.push_back(log_msg_buffer{msg});
+    }
+
+    // pop all items in the q and apply the given fun on each of them.
+    SPDLOG_INLINE void backtracer::foreach_pop(std::function<void(const details::log_msg &)> fun)
     {
-        auto &front_msg = messages_.front();
-        fun(front_msg);
-        messages_.pop_front();
+        std::lock_guard<std::mutex> lock{mutex_};
+        while(!messages_.empty())
+        {
+            auto &front_msg = messages_.front();
+            fun(front_msg);
+            messages_.pop_front();
+        }
     }
-}
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/backtracer.h b/include/spdlog/details/backtracer.h
index b336ee776..c7709289c 100644
--- a/include/spdlog/details/backtracer.h
+++ b/include/spdlog/details/backtracer.h
@@ -3,43 +3,44 @@
 
 #pragma once
 
-#include <spdlog/details/log_msg_buffer.h>
-#include <spdlog/details/circular_q.h>
-
 #include <atomic>
-#include <mutex>
 #include <functional>
+#include <mutex>
+#include <spdlog/details/circular_q.h>
+#include <spdlog/details/log_msg_buffer.h>
 
 // Store log messages in circular buffer.
 // Useful for storing debug data in case of error/warning happens.
 
-namespace spdlog {
-namespace details {
-class SPDLOG_API backtracer
+namespace spdlog
+{
+namespace details
 {
-    mutable std::mutex mutex_;
-    std::atomic<bool> enabled_{false};
-    circular_q<log_msg_buffer> messages_;
+    class SPDLOG_API backtracer
+    {
+        mutable std::mutex         mutex_;
+        std::atomic<bool>          enabled_{false};
+        circular_q<log_msg_buffer> messages_;
 
-public:
-    backtracer() = default;
-    backtracer(const backtracer &other);
+    public:
+        backtracer() = default;
+        backtracer(const backtracer &other);
 
-    backtracer(backtracer &&other) SPDLOG_NOEXCEPT;
-    backtracer &operator=(backtracer other);
+        backtracer(backtracer &&other) SPDLOG_NOEXCEPT;
+        backtracer &operator=(backtracer other);
 
-    void enable(size_t size);
-    void disable();
-    bool enabled() const;
-    void push_back(const log_msg &msg);
+        void enable(size_t size);
+        void disable();
+        bool enabled() const;
+        void push_back(const log_msg &msg);
 
-    // pop all items in the q and apply the given fun on each of them.
-    void foreach_pop(std::function<void(const details::log_msg &)> fun);
-};
+        // pop all items in the q and apply the given fun on each of them.
+        void foreach_pop(std::function<void(const details::log_msg &)> fun);
+    };
 
 } // namespace details
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "backtracer-inl.h"
+#include "backtracer-inl.h"
 #endif
diff --git a/include/spdlog/details/circular_q.h b/include/spdlog/details/circular_q.h
index e4fd5fd4a..1fd067a0b 100644
--- a/include/spdlog/details/circular_q.h
+++ b/include/spdlog/details/circular_q.h
@@ -4,143 +4,126 @@
 // circular q view of std::vector.
 #pragma once
 
-#include <vector>
 #include <cassert>
+#include <vector>
 
-namespace spdlog {
-namespace details {
-template<typename T>
-class circular_q
+namespace spdlog
 {
-    size_t max_items_ = 0;
-    typename std::vector<T>::size_type head_ = 0;
-    typename std::vector<T>::size_type tail_ = 0;
-    size_t overrun_counter_ = 0;
-    std::vector<T> v_;
-
-public:
-    using value_type = T;
-
-    // empty ctor - create a disabled queue with no elements allocated at all
-    circular_q() = default;
-
-    explicit circular_q(size_t max_items)
-        : max_items_(max_items + 1) // one item is reserved as marker for full q
-        , v_(max_items_)
-    {}
-
-    circular_q(const circular_q &) = default;
-    circular_q &operator=(const circular_q &) = default;
-
-    // move cannot be default,
-    // since we need to reset head_, tail_, etc to zero in the moved object
-    circular_q(circular_q &&other) SPDLOG_NOEXCEPT
+namespace details
+{
+    template <typename T>
+    class circular_q
     {
-        copy_moveable(std::move(other));
-    }
+        size_t                             max_items_       = 0;
+        typename std::vector<T>::size_type head_            = 0;
+        typename std::vector<T>::size_type tail_            = 0;
+        size_t                             overrun_counter_ = 0;
+        std::vector<T>                     v_;
+
+    public:
+        using value_type = T;
+
+        // empty ctor - create a disabled queue with no elements allocated at all
+        circular_q() = default;
+
+        explicit circular_q(size_t max_items) :
+            max_items_(max_items + 1) // one item is reserved as marker for full q
+            ,
+            v_(max_items_)
+        {
+        }
 
-    circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT
-    {
-        copy_moveable(std::move(other));
-        return *this;
-    }
+        circular_q(const circular_q &)            = default;
+        circular_q &operator=(const circular_q &) = default;
 
-    // push back, overrun (oldest) item if no room left
-    void push_back(T &&item)
-    {
-        if (max_items_ > 0)
+        // move cannot be default,
+        // since we need to reset head_, tail_, etc to zero in the moved object
+        circular_q(circular_q &&other) SPDLOG_NOEXCEPT { copy_moveable(std::move(other)); }
+
+        circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT
         {
-            v_[tail_] = std::move(item);
-            tail_ = (tail_ + 1) % max_items_;
+            copy_moveable(std::move(other));
+            return *this;
+        }
 
-            if (tail_ == head_) // overrun last item if full
+        // push back, overrun (oldest) item if no room left
+        void push_back(T &&item)
+        {
+            if(max_items_ > 0)
             {
-                head_ = (head_ + 1) % max_items_;
-                ++overrun_counter_;
+                v_[tail_] = std::move(item);
+                tail_     = (tail_ + 1) % max_items_;
+
+                if(tail_ == head_) // overrun last item if full
+                {
+                    head_ = (head_ + 1) % max_items_;
+                    ++overrun_counter_;
+                }
             }
         }
-    }
 
-    // Return reference to the front item.
-    // If there are no elements in the container, the behavior is undefined.
-    const T &front() const
-    {
-        return v_[head_];
-    }
+        // Return reference to the front item.
+        // If there are no elements in the container, the behavior is undefined.
+        const T &front() const { return v_[head_]; }
 
-    T &front()
-    {
-        return v_[head_];
-    }
+        T &front() { return v_[head_]; }
 
-    // Return number of elements actually stored
-    size_t size() const
-    {
-        if (tail_ >= head_)
+        // Return number of elements actually stored
+        size_t size() const
         {
-            return tail_ - head_;
+            if(tail_ >= head_)
+            {
+                return tail_ - head_;
+            }
+            else
+            {
+                return max_items_ - (head_ - tail_);
+            }
         }
-        else
+
+        // Return const reference to item by index.
+        // If index is out of range 0…size()-1, the behavior is undefined.
+        const T &at(size_t i) const
         {
-            return max_items_ - (head_ - tail_);
+            assert(i < size());
+            return v_[(head_ + i) % max_items_];
         }
-    }
 
-    // Return const reference to item by index.
-    // If index is out of range 0…size()-1, the behavior is undefined.
-    const T &at(size_t i) const
-    {
-        assert(i < size());
-        return v_[(head_ + i) % max_items_];
-    }
-
-    // Pop item from front.
-    // If there are no elements in the container, the behavior is undefined.
-    void pop_front()
-    {
-        head_ = (head_ + 1) % max_items_;
-    }
+        // Pop item from front.
+        // If there are no elements in the container, the behavior is undefined.
+        void pop_front() { head_ = (head_ + 1) % max_items_; }
 
-    bool empty() const
-    {
-        return tail_ == head_;
-    }
+        bool empty() const { return tail_ == head_; }
 
-    bool full() const
-    {
-        // head is ahead of the tail by 1
-        if (max_items_ > 0)
+        bool full() const
         {
-            return ((tail_ + 1) % max_items_) == head_;
+            // head is ahead of the tail by 1
+            if(max_items_ > 0)
+            {
+                return ((tail_ + 1) % max_items_) == head_;
+            }
+            return false;
         }
-        return false;
-    }
 
-    size_t overrun_counter() const
-    {
-        return overrun_counter_;
-    }
+        size_t overrun_counter() const { return overrun_counter_; }
 
-    void reset_overrun_counter()
-    {
-        overrun_counter_ = 0;
-    }
+        void reset_overrun_counter() { overrun_counter_ = 0; }
 
-private:
-    // copy from other&& and reset it to disabled state
-    void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT
-    {
-        max_items_ = other.max_items_;
-        head_ = other.head_;
-        tail_ = other.tail_;
-        overrun_counter_ = other.overrun_counter_;
-        v_ = std::move(other.v_);
-
-        // put &&other in disabled, but valid state
-        other.max_items_ = 0;
-        other.head_ = other.tail_ = 0;
-        other.overrun_counter_ = 0;
-    }
-};
+    private:
+        // copy from other&& and reset it to disabled state
+        void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT
+        {
+            max_items_       = other.max_items_;
+            head_            = other.head_;
+            tail_            = other.tail_;
+            overrun_counter_ = other.overrun_counter_;
+            v_               = std::move(other.v_);
+
+            // put &&other in disabled, but valid state
+            other.max_items_ = 0;
+            other.head_ = other.tail_ = 0;
+            other.overrun_counter_    = 0;
+        }
+    };
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/console_globals.h b/include/spdlog/details/console_globals.h
index 665201dd2..67295c6f8 100644
--- a/include/spdlog/details/console_globals.h
+++ b/include/spdlog/details/console_globals.h
@@ -3,30 +3,32 @@
 
 #pragma once
 
-#include <spdlog/details/null_mutex.h>
 #include <mutex>
+#include <spdlog/details/null_mutex.h>
 
-namespace spdlog {
-namespace details {
-
-struct console_mutex
+namespace spdlog
 {
-    using mutex_t = std::mutex;
-    static mutex_t &mutex()
+namespace details
+{
+
+    struct console_mutex
     {
-        static mutex_t s_mutex;
-        return s_mutex;
-    }
-};
+        using mutex_t = std::mutex;
+        static mutex_t &mutex()
+        {
+            static mutex_t s_mutex;
+            return s_mutex;
+        }
+    };
 
-struct console_nullmutex
-{
-    using mutex_t = null_mutex;
-    static mutex_t &mutex()
+    struct console_nullmutex
     {
-        static mutex_t s_mutex;
-        return s_mutex;
-    }
-};
+        using mutex_t = null_mutex;
+        static mutex_t &mutex()
+        {
+            static mutex_t s_mutex;
+            return s_mutex;
+        }
+    };
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/file_helper-inl.h b/include/spdlog/details/file_helper-inl.h
index d4528711e..ae4b9df30 100644
--- a/include/spdlog/details/file_helper-inl.h
+++ b/include/spdlog/details/file_helper-inl.h
@@ -4,169 +4,170 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/details/file_helper.h>
+#include <spdlog/details/file_helper.h>
 #endif
 
-#include <spdlog/details/os.h>
-#include <spdlog/common.h>
-
 #include <cerrno>
 #include <chrono>
 #include <cstdio>
+#include <spdlog/common.h>
+#include <spdlog/details/os.h>
 #include <string>
 #include <thread>
 #include <tuple>
 
-namespace spdlog {
-namespace details {
-
-SPDLOG_INLINE file_helper::file_helper(const file_event_handlers &event_handlers)
-    : event_handlers_(event_handlers)
-{}
-
-SPDLOG_INLINE file_helper::~file_helper()
+namespace spdlog
 {
-    close();
-}
-
-SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
+namespace details
 {
-    close();
-    filename_ = fname;
 
-    auto *mode = SPDLOG_FILENAME_T("ab");
-    auto *trunc_mode = SPDLOG_FILENAME_T("wb");
+    SPDLOG_INLINE file_helper::file_helper(const file_event_handlers &event_handlers) : event_handlers_(event_handlers)
+    {
+    }
 
-    if (event_handlers_.before_open)
+    SPDLOG_INLINE file_helper::~file_helper()
     {
-        event_handlers_.before_open(filename_);
+        close();
     }
-    for (int tries = 0; tries < open_tries_; ++tries)
+
+    SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
     {
-        // create containing folder if not exists already.
-        os::create_dir(os::dir_name(fname));
-        if (truncate)
+        close();
+        filename_ = fname;
+
+        auto *mode       = SPDLOG_FILENAME_T("ab");
+        auto *trunc_mode = SPDLOG_FILENAME_T("wb");
+
+        if(event_handlers_.before_open)
         {
-            // Truncate by opening-and-closing a tmp file in "wb" mode, always
-            // opening the actual log-we-write-to in "ab" mode, since that
-            // interacts more politely with eternal processes that might
-            // rotate/truncate the file underneath us.
-            std::FILE *tmp;
-            if (os::fopen_s(&tmp, fname, trunc_mode))
-            {
-                continue;
-            }
-            std::fclose(tmp);
+            event_handlers_.before_open(filename_);
         }
-        if (!os::fopen_s(&fd_, fname, mode))
+        for(int tries = 0; tries < open_tries_; ++tries)
         {
-            if (event_handlers_.after_open)
+            // create containing folder if not exists already.
+            os::create_dir(os::dir_name(fname));
+            if(truncate)
+            {
+                // Truncate by opening-and-closing a tmp file in "wb" mode, always
+                // opening the actual log-we-write-to in "ab" mode, since that
+                // interacts more politely with eternal processes that might
+                // rotate/truncate the file underneath us.
+                std::FILE *tmp;
+                if(os::fopen_s(&tmp, fname, trunc_mode))
+                {
+                    continue;
+                }
+                std::fclose(tmp);
+            }
+            if(!os::fopen_s(&fd_, fname, mode))
             {
-                event_handlers_.after_open(filename_, fd_);
+                if(event_handlers_.after_open)
+                {
+                    event_handlers_.after_open(filename_, fd_);
+                }
+                return;
             }
-            return;
+
+            details::os::sleep_for_millis(open_interval_);
         }
 
-        details::os::sleep_for_millis(open_interval_);
+        throw_spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing", errno);
     }
 
-    throw_spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing", errno);
-}
-
-SPDLOG_INLINE void file_helper::reopen(bool truncate)
-{
-    if (filename_.empty())
+    SPDLOG_INLINE void file_helper::reopen(bool truncate)
     {
-        throw_spdlog_ex("Failed re opening file - was not opened before");
+        if(filename_.empty())
+        {
+            throw_spdlog_ex("Failed re opening file - was not opened before");
+        }
+        this->open(filename_, truncate);
     }
-    this->open(filename_, truncate);
-}
 
-SPDLOG_INLINE void file_helper::flush()
-{
-    if (std::fflush(fd_) != 0)
+    SPDLOG_INLINE void file_helper::flush()
     {
-        throw_spdlog_ex("Failed flush to file " + os::filename_to_str(filename_), errno);
+        if(std::fflush(fd_) != 0)
+        {
+            throw_spdlog_ex("Failed flush to file " + os::filename_to_str(filename_), errno);
+        }
     }
-}
 
-SPDLOG_INLINE void file_helper::close()
-{
-    if (fd_ != nullptr)
+    SPDLOG_INLINE void file_helper::close()
     {
-        if (event_handlers_.before_close)
+        if(fd_ != nullptr)
         {
-            event_handlers_.before_close(filename_, fd_);
-        }
+            if(event_handlers_.before_close)
+            {
+                event_handlers_.before_close(filename_, fd_);
+            }
 
-        std::fclose(fd_);
-        fd_ = nullptr;
+            std::fclose(fd_);
+            fd_ = nullptr;
 
-        if (event_handlers_.after_close)
-        {
-            event_handlers_.after_close(filename_);
+            if(event_handlers_.after_close)
+            {
+                event_handlers_.after_close(filename_);
+            }
         }
     }
-}
 
-SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf)
-{
-    size_t msg_size = buf.size();
-    auto data = buf.data();
-    if (std::fwrite(data, 1, msg_size, fd_) != msg_size)
+    SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf)
     {
-        throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno);
+        size_t msg_size = buf.size();
+        auto   data     = buf.data();
+        if(std::fwrite(data, 1, msg_size, fd_) != msg_size)
+        {
+            throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno);
+        }
     }
-}
 
-SPDLOG_INLINE size_t file_helper::size() const
-{
-    if (fd_ == nullptr)
+    SPDLOG_INLINE size_t file_helper::size() const
     {
-        throw_spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_));
+        if(fd_ == nullptr)
+        {
+            throw_spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_));
+        }
+        return os::filesize(fd_);
     }
-    return os::filesize(fd_);
-}
-
-SPDLOG_INLINE const filename_t &file_helper::filename() const
-{
-    return filename_;
-}
-
-//
-// return file path and its extension:
-//
-// "mylog.txt" => ("mylog", ".txt")
-// "mylog" => ("mylog", "")
-// "mylog." => ("mylog.", "")
-// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
-//
-// the starting dot in filenames is ignored (hidden files):
-//
-// ".mylog" => (".mylog". "")
-// "my_folder/.mylog" => ("my_folder/.mylog", "")
-// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
-SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension(const filename_t &fname)
-{
-    auto ext_index = fname.rfind('.');
 
-    // no valid extension found - return whole path and empty string as
-    // extension
-    if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
+    SPDLOG_INLINE const filename_t &file_helper::filename() const
     {
-        return std::make_tuple(fname, filename_t());
+        return filename_;
     }
 
-    // treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
-    auto folder_index = fname.find_last_of(details::os::folder_seps_filename);
-    if (folder_index != filename_t::npos && folder_index >= ext_index - 1)
+    //
+    // return file path and its extension:
+    //
+    // "mylog.txt" => ("mylog", ".txt")
+    // "mylog" => ("mylog", "")
+    // "mylog." => ("mylog.", "")
+    // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
+    //
+    // the starting dot in filenames is ignored (hidden files):
+    //
+    // ".mylog" => (".mylog". "")
+    // "my_folder/.mylog" => ("my_folder/.mylog", "")
+    // "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
+    SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension(const filename_t &fname)
     {
-        return std::make_tuple(fname, filename_t());
-    }
+        auto ext_index = fname.rfind('.');
+
+        // no valid extension found - return whole path and empty string as
+        // extension
+        if(ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
+        {
+            return std::make_tuple(fname, filename_t());
+        }
+
+        // treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
+        auto folder_index = fname.find_last_of(details::os::folder_seps_filename);
+        if(folder_index != filename_t::npos && folder_index >= ext_index - 1)
+        {
+            return std::make_tuple(fname, filename_t());
+        }
 
-    // finally - return a valid base and extension tuple
-    return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
-}
+        // finally - return a valid base and extension tuple
+        return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
+    }
 
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/file_helper.h b/include/spdlog/details/file_helper.h
index 0f5988b9e..b6b558ea3 100644
--- a/include/spdlog/details/file_helper.h
+++ b/include/spdlog/details/file_helper.h
@@ -6,56 +6,58 @@
 #include <spdlog/common.h>
 #include <tuple>
 
-namespace spdlog {
-namespace details {
-
-// Helper class for file sinks.
-// When failing to open a file, retry several times(5) with a delay interval(10 ms).
-// Throw spdlog_ex exception on errors.
-
-class SPDLOG_API file_helper
+namespace spdlog
 {
-public:
-    file_helper() = default;
-    explicit file_helper(const file_event_handlers &event_handlers);
-
-    file_helper(const file_helper &) = delete;
-    file_helper &operator=(const file_helper &) = delete;
-    ~file_helper();
-
-    void open(const filename_t &fname, bool truncate = false);
-    void reopen(bool truncate);
-    void flush();
-    void close();
-    void write(const memory_buf_t &buf);
-    size_t size() const;
-    const filename_t &filename() const;
-
-    //
-    // return file path and its extension:
-    //
-    // "mylog.txt" => ("mylog", ".txt")
-    // "mylog" => ("mylog", "")
-    // "mylog." => ("mylog.", "")
-    // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
-    //
-    // the starting dot in filenames is ignored (hidden files):
-    //
-    // ".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_extension(const filename_t &fname);
-
-private:
-    const int open_tries_ = 5;
-    const unsigned int open_interval_ = 10;
-    std::FILE *fd_{nullptr};
-    filename_t filename_;
-    file_event_handlers event_handlers_;
-};
+namespace details
+{
+
+    // Helper class for file sinks.
+    // When failing to open a file, retry several times(5) with a delay interval(10 ms).
+    // Throw spdlog_ex exception on errors.
+
+    class SPDLOG_API file_helper
+    {
+    public:
+        file_helper() = default;
+        explicit file_helper(const file_event_handlers &event_handlers);
+
+        file_helper(const file_helper &)            = delete;
+        file_helper &operator=(const file_helper &) = delete;
+        ~file_helper();
+
+        void              open(const filename_t &fname, bool truncate = false);
+        void              reopen(bool truncate);
+        void              flush();
+        void              close();
+        void              write(const memory_buf_t &buf);
+        size_t            size() const;
+        const filename_t &filename() const;
+
+        //
+        // return file path and its extension:
+        //
+        // "mylog.txt" => ("mylog", ".txt")
+        // "mylog" => ("mylog", "")
+        // "mylog." => ("mylog.", "")
+        // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
+        //
+        // the starting dot in filenames is ignored (hidden files):
+        //
+        // ".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_extension(const filename_t &fname);
+
+    private:
+        const int           open_tries_    = 5;
+        const unsigned int  open_interval_ = 10;
+        std::FILE          *fd_{nullptr};
+        filename_t          filename_;
+        file_event_handlers event_handlers_;
+    };
 } // namespace details
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "file_helper-inl.h"
+#include "file_helper-inl.h"
 #endif
diff --git a/include/spdlog/details/fmt_helper.h b/include/spdlog/details/fmt_helper.h
index d98671808..40560c721 100644
--- a/include/spdlog/details/fmt_helper.h
+++ b/include/spdlog/details/fmt_helper.h
@@ -3,162 +3,165 @@
 #pragma once
 
 #include <chrono>
-#include <type_traits>
 #include <iterator>
-#include <spdlog/fmt/fmt.h>
 #include <spdlog/common.h>
+#include <spdlog/fmt/fmt.h>
+#include <type_traits>
 
 #ifdef SPDLOG_USE_STD_FORMAT
-#    include <charconv>
-#    include <limits>
+#include <charconv>
+#include <limits>
 #endif
 
 // Some fmt helpers to efficiently format and pad ints and strings
-namespace spdlog {
-namespace details {
-namespace fmt_helper {
-
-inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest)
+namespace spdlog
+{
+namespace details
 {
-    auto *buf_ptr = view.data();
-    dest.append(buf_ptr, buf_ptr + view.size());
-}
+    namespace fmt_helper
+    {
+
+        inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest)
+        {
+            auto *buf_ptr = view.data();
+            dest.append(buf_ptr, buf_ptr + view.size());
+        }
 
 #ifdef SPDLOG_USE_STD_FORMAT
-template<typename T>
-inline void append_int(T n, memory_buf_t &dest)
-{
-    // Buffer should be large enough to hold all digits (digits10 + 1) and a sign
-    SPDLOG_CONSTEXPR const auto BUF_SIZE = std::numeric_limits<T>::digits10 + 2;
-    char buf[BUF_SIZE];
+        template <typename T>
+        inline void append_int(T n, memory_buf_t &dest)
+        {
+            // Buffer should be large enough to hold all digits (digits10 + 1) and a sign
+            SPDLOG_CONSTEXPR const auto BUF_SIZE = std::numeric_limits<T>::digits10 + 2;
+            char                        buf[BUF_SIZE];
 
-    auto [ptr, ec] = std::to_chars(buf, buf + BUF_SIZE, n, 10);
-    if (ec == std::errc())
-    {
-        dest.append(buf, ptr);
-    }
-    else
-    {
-        throw_spdlog_ex("Failed to format int", static_cast<int>(ec));
-    }
-}
+            auto [ptr, ec] = std::to_chars(buf, buf + BUF_SIZE, n, 10);
+            if(ec == std::errc())
+            {
+                dest.append(buf, ptr);
+            }
+            else
+            {
+                throw_spdlog_ex("Failed to format int", static_cast<int>(ec));
+            }
+        }
 #else
-template<typename T>
-inline void append_int(T n, memory_buf_t &dest)
-{
-    fmt::format_int i(n);
-    dest.append(i.data(), i.data() + i.size());
-}
+        template <typename T>
+        inline void append_int(T n, memory_buf_t &dest)
+        {
+            fmt::format_int i(n);
+            dest.append(i.data(), i.data() + i.size());
+        }
 #endif
 
-template<typename T>
-SPDLOG_CONSTEXPR_FUNC unsigned int count_digits_fallback(T n)
-{
-    // taken from fmt: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/format.h#L899-L912
-    unsigned 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
-        // "Three Optimization Tips for C++". See speed-test for a comparison.
-        if (n < 10)
-            return count;
-        if (n < 100)
-            return count + 1;
-        if (n < 1000)
-            return count + 2;
-        if (n < 10000)
-            return count + 3;
-        n /= 10000u;
-        count += 4;
-    }
-}
+        template <typename T>
+        SPDLOG_CONSTEXPR_FUNC unsigned int count_digits_fallback(T n)
+        {
+            // taken from fmt: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/format.h#L899-L912
+            unsigned 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
+                // "Three Optimization Tips for C++". See speed-test for a comparison.
+                if(n < 10)
+                    return count;
+                if(n < 100)
+                    return count + 1;
+                if(n < 1000)
+                    return count + 2;
+                if(n < 10000)
+                    return count + 3;
+                n /= 10000u;
+                count += 4;
+            }
+        }
 
-template<typename T>
-inline unsigned int count_digits(T n)
-{
-    using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
+        template <typename T>
+        inline unsigned int count_digits(T n)
+        {
+            using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
 #ifdef SPDLOG_USE_STD_FORMAT
-    return count_digits_fallback(static_cast<count_type>(n));
+            return count_digits_fallback(static_cast<count_type>(n));
 #else
-    return static_cast<unsigned int>(fmt::
+            return static_cast<unsigned int>(fmt::
 // fmt 7.0.0 renamed the internal namespace to detail.
 // See: https://github.com/fmtlib/fmt/issues/1538
-#    if FMT_VERSION < 70000
-            internal
-#    else
-            detail
-#    endif
-        ::count_digits(static_cast<count_type>(n)));
+#if FMT_VERSION < 70000
+                                                 internal
+#else
+                                                 detail
 #endif
-}
+                                             ::count_digits(static_cast<count_type>(n)));
+#endif
+        }
 
-inline void pad2(int n, memory_buf_t &dest)
-{
-    if (n >= 0 && n < 100) // 0-99
-    {
-        dest.push_back(static_cast<char>('0' + n / 10));
-        dest.push_back(static_cast<char>('0' + n % 10));
-    }
-    else // unlikely, but just in case, let fmt deal with it
-    {
-        fmt_lib::format_to(std::back_inserter(dest), SPDLOG_FMT_STRING("{:02}"), n);
-    }
-}
+        inline void pad2(int n, memory_buf_t &dest)
+        {
+            if(n >= 0 && n < 100) // 0-99
+            {
+                dest.push_back(static_cast<char>('0' + n / 10));
+                dest.push_back(static_cast<char>('0' + n % 10));
+            }
+            else // unlikely, but just in case, let fmt deal with it
+            {
+                fmt_lib::format_to(std::back_inserter(dest), SPDLOG_FMT_STRING("{:02}"), n);
+            }
+        }
 
-template<typename T>
-inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
-{
-    static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
-    for (auto digits = count_digits(n); digits < width; digits++)
-    {
-        dest.push_back('0');
-    }
-    append_int(n, dest);
-}
+        template <typename T>
+        inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
+        {
+            static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
+            for(auto digits = count_digits(n); digits < width; digits++)
+            {
+                dest.push_back('0');
+            }
+            append_int(n, dest);
+        }
 
-template<typename T>
-inline void pad3(T n, memory_buf_t &dest)
-{
-    static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T");
-    if (n < 1000)
-    {
-        dest.push_back(static_cast<char>(n / 100 + '0'));
-        n = n % 100;
-        dest.push_back(static_cast<char>((n / 10) + '0'));
-        dest.push_back(static_cast<char>((n % 10) + '0'));
-    }
-    else
-    {
-        append_int(n, dest);
-    }
-}
+        template <typename T>
+        inline void pad3(T n, memory_buf_t &dest)
+        {
+            static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T");
+            if(n < 1000)
+            {
+                dest.push_back(static_cast<char>(n / 100 + '0'));
+                n = n % 100;
+                dest.push_back(static_cast<char>((n / 10) + '0'));
+                dest.push_back(static_cast<char>((n % 10) + '0'));
+            }
+            else
+            {
+                append_int(n, dest);
+            }
+        }
 
-template<typename T>
-inline void pad6(T n, memory_buf_t &dest)
-{
-    pad_uint(n, 6, dest);
-}
+        template <typename T>
+        inline void pad6(T n, memory_buf_t &dest)
+        {
+            pad_uint(n, 6, dest);
+        }
 
-template<typename T>
-inline void pad9(T n, memory_buf_t &dest)
-{
-    pad_uint(n, 9, dest);
-}
+        template <typename T>
+        inline void pad9(T n, memory_buf_t &dest)
+        {
+            pad_uint(n, 9, dest);
+        }
 
-// return fraction of a second of the given time_point.
-// e.g.
-// fraction<std::milliseconds>(tp) -> will return the millis part of the second
-template<typename ToDuration>
-inline ToDuration time_fraction(log_clock::time_point tp)
-{
-    using std::chrono::duration_cast;
-    using std::chrono::seconds;
-    auto duration = tp.time_since_epoch();
-    auto secs = duration_cast<seconds>(duration);
-    return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
-}
+        // return fraction of a second of the given time_point.
+        // e.g.
+        // fraction<std::milliseconds>(tp) -> will return the millis part of the second
+        template <typename ToDuration>
+        inline ToDuration time_fraction(log_clock::time_point tp)
+        {
+            using std::chrono::duration_cast;
+            using std::chrono::seconds;
+            auto duration = tp.time_since_epoch();
+            auto secs     = duration_cast<seconds>(duration);
+            return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
+        }
 
-} // namespace fmt_helper
+    } // namespace fmt_helper
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/log_msg-inl.h b/include/spdlog/details/log_msg-inl.h
index c6e8a7e04..fb2ea9057 100644
--- a/include/spdlog/details/log_msg-inl.h
+++ b/include/spdlog/details/log_msg-inl.h
@@ -4,34 +4,49 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/details/log_msg.h>
+#include <spdlog/details/log_msg.h>
 #endif
 
 #include <spdlog/details/os.h>
 
-namespace spdlog {
-namespace details {
-
-SPDLOG_INLINE log_msg::log_msg(spdlog::log_clock::time_point log_time, spdlog::source_loc loc, string_view_t a_logger_name,
-    spdlog::level::level_enum lvl, spdlog::string_view_t msg)
-    : logger_name(a_logger_name)
-    , level(lvl)
-    , time(log_time)
+namespace spdlog
+{
+namespace details
+{
+
+    SPDLOG_INLINE log_msg::log_msg(
+        spdlog::log_clock::time_point log_time,
+        spdlog::source_loc            loc,
+        string_view_t                 a_logger_name,
+        spdlog::level::level_enum     lvl,
+        spdlog::string_view_t         msg) :
+        logger_name(a_logger_name),
+        level(lvl),
+        time(log_time)
 #ifndef SPDLOG_NO_THREAD_ID
-    , thread_id(os::thread_id())
+        ,
+        thread_id(os::thread_id())
 #endif
-    , source(loc)
-    , payload(msg)
-{}
-
-SPDLOG_INLINE log_msg::log_msg(
-    spdlog::source_loc loc, string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
-    : log_msg(os::now(), loc, a_logger_name, lvl, msg)
-{}
-
-SPDLOG_INLINE log_msg::log_msg(string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
-    : log_msg(os::now(), source_loc{}, a_logger_name, lvl, msg)
-{}
+        ,
+        source(loc),
+        payload(msg)
+    {
+    }
+
+    SPDLOG_INLINE log_msg::log_msg(
+        spdlog::source_loc        loc,
+        string_view_t             a_logger_name,
+        spdlog::level::level_enum lvl,
+        spdlog::string_view_t     msg) :
+        log_msg(os::now(), loc, a_logger_name, lvl, msg)
+    {
+    }
+
+    SPDLOG_INLINE
+    log_msg::log_msg(string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg) :
+        log_msg(os::now(), source_loc{}, a_logger_name, lvl, msg)
+    {
+    }
 
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/log_msg.h b/include/spdlog/details/log_msg.h
index fed51abdf..218ed72cf 100644
--- a/include/spdlog/details/log_msg.h
+++ b/include/spdlog/details/log_msg.h
@@ -6,32 +6,39 @@
 #include <spdlog/common.h>
 #include <string>
 
-namespace spdlog {
-namespace details {
-struct SPDLOG_API log_msg
+namespace spdlog
 {
-    log_msg() = default;
-    log_msg(log_clock::time_point log_time, source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
-    log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
-    log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
-    log_msg(const log_msg &other) = default;
-    log_msg &operator=(const log_msg &other) = default;
+namespace details
+{
+    struct SPDLOG_API log_msg
+    {
+        log_msg() = default;
+        log_msg(
+            log_clock::time_point log_time,
+            source_loc            loc,
+            string_view_t         logger_name,
+            level::level_enum     lvl,
+            string_view_t         msg);
+        log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
+        log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
+        log_msg(const log_msg &other)            = default;
+        log_msg &operator=(const log_msg &other) = default;
 
-    string_view_t logger_name;
-    level::level_enum level{level::off};
-    log_clock::time_point time;
-    size_t thread_id{0};
+        string_view_t         logger_name;
+        level::level_enum     level{level::off};
+        log_clock::time_point time;
+        size_t                thread_id{0};
 
-    // wrapping the formatted text with color (updated by pattern_formatter).
-    mutable size_t color_range_start{0};
-    mutable size_t color_range_end{0};
+        // 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;
-    string_view_t payload;
-};
+        source_loc    source;
+        string_view_t payload;
+    };
 } // namespace details
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "log_msg-inl.h"
+#include "log_msg-inl.h"
 #endif
diff --git a/include/spdlog/details/log_msg_buffer-inl.h b/include/spdlog/details/log_msg_buffer-inl.h
index 84d83dc2f..5852a0eb4 100644
--- a/include/spdlog/details/log_msg_buffer-inl.h
+++ b/include/spdlog/details/log_msg_buffer-inl.h
@@ -4,55 +4,57 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/details/log_msg_buffer.h>
+#include <spdlog/details/log_msg_buffer.h>
 #endif
 
-namespace spdlog {
-namespace details {
-
-SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg)
-    : log_msg{orig_msg}
-{
-    buffer.append(logger_name.begin(), logger_name.end());
-    buffer.append(payload.begin(), payload.end());
-    update_string_views();
-}
-
-SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
-    : log_msg{other}
-{
-    buffer.append(logger_name.begin(), logger_name.end());
-    buffer.append(payload.begin(), payload.end());
-    update_string_views();
-}
-
-SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT : log_msg{other}, buffer{std::move(other.buffer)}
-{
-    update_string_views();
-}
-
-SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other)
+namespace spdlog
 {
-    log_msg::operator=(other);
-    buffer.clear();
-    buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size());
-    update_string_views();
-    return *this;
-}
-
-SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT
+namespace details
 {
-    log_msg::operator=(other);
-    buffer = std::move(other.buffer);
-    update_string_views();
-    return *this;
-}
 
-SPDLOG_INLINE void log_msg_buffer::update_string_views()
-{
-    logger_name = string_view_t{buffer.data(), logger_name.size()};
-    payload = string_view_t{buffer.data() + logger_name.size(), payload.size()};
-}
+    SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg) : log_msg{orig_msg}
+    {
+        buffer.append(logger_name.begin(), logger_name.end());
+        buffer.append(payload.begin(), payload.end());
+        update_string_views();
+    }
+
+    SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other) : log_msg{other}
+    {
+        buffer.append(logger_name.begin(), logger_name.end());
+        buffer.append(payload.begin(), payload.end());
+        update_string_views();
+    }
+
+    SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT
+        : log_msg{other},
+          buffer{std::move(other.buffer)}
+    {
+        update_string_views();
+    }
+
+    SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other)
+    {
+        log_msg::operator=(other);
+        buffer.clear();
+        buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size());
+        update_string_views();
+        return *this;
+    }
+
+    SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT
+    {
+        log_msg::operator=(other);
+        buffer = std::move(other.buffer);
+        update_string_views();
+        return *this;
+    }
+
+    SPDLOG_INLINE void log_msg_buffer::update_string_views()
+    {
+        logger_name = string_view_t{buffer.data(), logger_name.size()};
+        payload     = string_view_t{buffer.data() + logger_name.size(), payload.size()};
+    }
 
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/log_msg_buffer.h b/include/spdlog/details/log_msg_buffer.h
index 810550656..46261b555 100644
--- a/include/spdlog/details/log_msg_buffer.h
+++ b/include/spdlog/details/log_msg_buffer.h
@@ -5,29 +5,31 @@
 
 #include <spdlog/details/log_msg.h>
 
-namespace spdlog {
-namespace details {
+namespace spdlog
+{
+namespace details
+{
 
-// Extend log_msg with internal buffer to store its payload.
-// This is needed since log_msg holds string_views that points to stack data.
+    // Extend log_msg with internal buffer to store its payload.
+    // This is needed since log_msg holds string_views that points to stack data.
 
-class SPDLOG_API log_msg_buffer : public log_msg
-{
-    memory_buf_t buffer;
-    void update_string_views();
-
-public:
-    log_msg_buffer() = default;
-    explicit log_msg_buffer(const log_msg &orig_msg);
-    log_msg_buffer(const log_msg_buffer &other);
-    log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
-    log_msg_buffer &operator=(const log_msg_buffer &other);
-    log_msg_buffer &operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
-};
+    class SPDLOG_API log_msg_buffer : public log_msg
+    {
+        memory_buf_t buffer;
+        void         update_string_views();
+
+    public:
+        log_msg_buffer() = default;
+        explicit log_msg_buffer(const log_msg &orig_msg);
+        log_msg_buffer(const log_msg_buffer &other);
+        log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
+        log_msg_buffer &operator=(const log_msg_buffer &other);
+        log_msg_buffer &operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
+    };
 
 } // namespace details
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "log_msg_buffer-inl.h"
+#include "log_msg_buffer-inl.h"
 #endif
diff --git a/include/spdlog/details/mpmc_blocking_q.h b/include/spdlog/details/mpmc_blocking_q.h
index 785180c18..471bc2bec 100644
--- a/include/spdlog/details/mpmc_blocking_q.h
+++ b/include/spdlog/details/mpmc_blocking_q.h
@@ -10,123 +10,122 @@
 // dequeue_for(..) - will block until the queue is not empty or timeout have
 // passed.
 
-#include <spdlog/details/circular_q.h>
-
 #include <condition_variable>
 #include <mutex>
+#include <spdlog/details/circular_q.h>
 
-namespace spdlog {
-namespace details {
-
-template<typename T>
-class mpmc_blocking_queue
+namespace spdlog
+{
+namespace details
 {
-public:
-    using item_type = T;
-    explicit mpmc_blocking_queue(size_t max_items)
-        : q_(max_items)
-    {}
 
-#ifndef __MINGW32__
-    // try to enqueue and block if no room left
-    void enqueue(T &&item)
+    template <typename T>
+    class mpmc_blocking_queue
     {
+    public:
+        using item_type = T;
+        explicit mpmc_blocking_queue(size_t max_items) : q_(max_items) {}
+
+#ifndef __MINGW32__
+        // try to enqueue and block if no room left
+        void enqueue(T &&item)
+        {
+            {
+                std::unique_lock<std::mutex> lock(queue_mutex_);
+                pop_cv_.wait(lock, [this] { return !this->q_.full(); });
+                q_.push_back(std::move(item));
+            }
+            push_cv_.notify_one();
+        }
+
+        // enqueue immediately. overrun oldest message in the queue if no room left.
+        void enqueue_nowait(T &&item)
+        {
+            {
+                std::unique_lock<std::mutex> lock(queue_mutex_);
+                q_.push_back(std::move(item));
+            }
+            push_cv_.notify_one();
+        }
+
+        // try to dequeue item. if no item found. wait up to timeout and try again
+        // Return true, if succeeded dequeue item, false otherwise
+        bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
+        {
+            {
+                std::unique_lock<std::mutex> lock(queue_mutex_);
+                if(!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
+                {
+                    return false;
+                }
+                popped_item = std::move(q_.front());
+                q_.pop_front();
+            }
+            pop_cv_.notify_one();
+            return true;
+        }
+
+#else
+        // apparently mingw deadlocks if the mutex is released before cv.notify_one(),
+        // so release the mutex at the very end each function.
+
+        // try to enqueue and block if no room left
+        void enqueue(T &&item)
         {
             std::unique_lock<std::mutex> lock(queue_mutex_);
             pop_cv_.wait(lock, [this] { return !this->q_.full(); });
             q_.push_back(std::move(item));
+            push_cv_.notify_one();
         }
-        push_cv_.notify_one();
-    }
 
-    // enqueue immediately. overrun oldest message in the queue if no room left.
-    void enqueue_nowait(T &&item)
-    {
+        // enqueue immediately. overrun oldest message in the queue if no room left.
+        void enqueue_nowait(T &&item)
         {
             std::unique_lock<std::mutex> lock(queue_mutex_);
             q_.push_back(std::move(item));
+            push_cv_.notify_one();
         }
-        push_cv_.notify_one();
-    }
 
-    // try to dequeue item. if no item found. wait up to timeout and try again
-    // Return true, if succeeded dequeue item, false otherwise
-    bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
-    {
+        // try to dequeue item. if no item found. wait up to timeout and try again
+        // Return true, if succeeded dequeue item, false otherwise
+        bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
         {
             std::unique_lock<std::mutex> lock(queue_mutex_);
-            if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
+            if(!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
             {
                 return false;
             }
             popped_item = std::move(q_.front());
             q_.pop_front();
+            pop_cv_.notify_one();
+            return true;
         }
-        pop_cv_.notify_one();
-        return true;
-    }
 
-#else
-    // apparently mingw deadlocks if the mutex is released before cv.notify_one(),
-    // so release the mutex at the very end each function.
+#endif
 
-    // try to enqueue and block if no room left
-    void enqueue(T &&item)
-    {
-        std::unique_lock<std::mutex> lock(queue_mutex_);
-        pop_cv_.wait(lock, [this] { return !this->q_.full(); });
-        q_.push_back(std::move(item));
-        push_cv_.notify_one();
-    }
-
-    // enqueue immediately. overrun oldest message in the queue if no room left.
-    void enqueue_nowait(T &&item)
-    {
-        std::unique_lock<std::mutex> lock(queue_mutex_);
-        q_.push_back(std::move(item));
-        push_cv_.notify_one();
-    }
-
-    // try to dequeue item. if no item found. wait up to timeout and try again
-    // Return true, if succeeded dequeue item, false otherwise
-    bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
-    {
-        std::unique_lock<std::mutex> lock(queue_mutex_);
-        if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
+        size_t overrun_counter()
         {
-            return false;
+            std::unique_lock<std::mutex> lock(queue_mutex_);
+            return q_.overrun_counter();
         }
-        popped_item = std::move(q_.front());
-        q_.pop_front();
-        pop_cv_.notify_one();
-        return true;
-    }
 
-#endif
-
-    size_t overrun_counter()
-    {
-        std::unique_lock<std::mutex> lock(queue_mutex_);
-        return q_.overrun_counter();
-    }
+        size_t size()
+        {
+            std::unique_lock<std::mutex> lock(queue_mutex_);
+            return q_.size();
+        }
 
-    size_t size()
-    {
-        std::unique_lock<std::mutex> lock(queue_mutex_);
-        return q_.size();
-    }
+        void reset_overrun_counter()
+        {
+            std::unique_lock<std::mutex> lock(queue_mutex_);
+            q_.reset_overrun_counter();
+        }
 
-    void reset_overrun_counter()
-    {
-        std::unique_lock<std::mutex> lock(queue_mutex_);
-        q_.reset_overrun_counter();
-    }
-
-private:
-    std::mutex queue_mutex_;
-    std::condition_variable push_cv_;
-    std::condition_variable pop_cv_;
-    spdlog::details::circular_q<T> q_;
-};
+    private:
+        std::mutex                     queue_mutex_;
+        std::condition_variable        push_cv_;
+        std::condition_variable        pop_cv_;
+        spdlog::details::circular_q<T> q_;
+    };
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/null_mutex.h b/include/spdlog/details/null_mutex.h
index 6550a7bf6..2b70596a7 100644
--- a/include/spdlog/details/null_mutex.h
+++ b/include/spdlog/details/null_mutex.h
@@ -7,39 +7,33 @@
 #include <utility>
 // null, no cost dummy "mutex" and dummy "atomic" int
 
-namespace spdlog {
-namespace details {
-struct null_mutex
+namespace spdlog
 {
-    void lock() const {}
-    void unlock() const {}
-};
-
-struct null_atomic_int
+namespace details
 {
-    int value;
-    null_atomic_int() = default;
-
-    explicit null_atomic_int(int new_value)
-        : value(new_value)
-    {}
-
-    int load(std::memory_order = std::memory_order_relaxed) const
+    struct null_mutex
     {
-        return value;
-    }
+        void lock() const {}
+        void unlock() const {}
+    };
 
-    void store(int new_value, std::memory_order = std::memory_order_relaxed)
+    struct null_atomic_int
     {
-        value = new_value;
-    }
+        int value;
+        null_atomic_int() = default;
 
-    int exchange(int new_value, std::memory_order = std::memory_order_relaxed)
-    {
-        std::swap(new_value, value);
-        return new_value; // return value before the call
-    }
-};
+        explicit null_atomic_int(int new_value) : value(new_value) {}
+
+        int load(std::memory_order = std::memory_order_relaxed) const { return value; }
+
+        void store(int new_value, std::memory_order = std::memory_order_relaxed) { value = new_value; }
+
+        int exchange(int new_value, std::memory_order = std::memory_order_relaxed)
+        {
+            std::swap(new_value, value);
+            return new_value; // return value before the call
+        }
+    };
 
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h
index b9bab53ce..1bc1ee840 100644
--- a/include/spdlog/details/os-inl.h
+++ b/include/spdlog/details/os-inl.h
@@ -4,603 +4,622 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/details/os.h>
+#include <spdlog/details/os.h>
 #endif
 
-#include <spdlog/common.h>
-
 #include <algorithm>
+#include <array>
 #include <chrono>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <ctime>
+#include <spdlog/common.h>
 #include <string>
-#include <thread>
-#include <array>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <thread>
 
 #ifdef _WIN32
 
-#    include <io.h>      // _get_osfhandle and _isatty support
-#    include <process.h> //  _get_pid support
-#    include <spdlog/details/windows_include.h>
+#include <io.h>      // _get_osfhandle and _isatty support
+#include <process.h> //  _get_pid support
+#include <spdlog/details/windows_include.h>
 
-#    ifdef __MINGW32__
-#        include <share.h>
-#    endif
+#ifdef __MINGW32__
+#include <share.h>
+#endif
 
-#    if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
-#        include <limits>
-#    endif
+#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
+#include <limits>
+#endif
 
-#    include <direct.h> // for _mkdir/_wmkdir
+#include <direct.h> // for _mkdir/_wmkdir
 
 #else // unix
 
-#    include <fcntl.h>
-#    include <unistd.h>
+#include <fcntl.h>
+#include <unistd.h>
 
-#    ifdef __linux__
-#        include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
+#ifdef __linux__
+#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
 
-#    elif defined(_AIX)
-#        include <pthread.h> // for pthread_getthrds_np
+#elif defined(_AIX)
+#include <pthread.h> // for pthread_getthrds_np
 
-#    elif defined(__DragonFly__) || defined(__FreeBSD__)
-#        include <pthread_np.h> // for pthread_getthreadid_np
+#elif defined(__DragonFly__) || defined(__FreeBSD__)
+#include <pthread_np.h> // for pthread_getthreadid_np
 
-#    elif defined(__NetBSD__)
-#        include <lwp.h> // for _lwp_self
+#elif defined(__NetBSD__)
+#include <lwp.h> // for _lwp_self
 
-#    elif defined(__sun)
-#        include <thread.h> // for thr_self
-#    endif
+#elif defined(__sun)
+#include <thread.h> // for thr_self
+#endif
 
 #endif // unix
 
-#ifndef __has_feature          // Clang - feature checking macros.
-#    define __has_feature(x) 0 // Compatibility with non-clang compilers.
+#ifndef __has_feature      // Clang - feature checking macros.
+#define __has_feature(x) 0 // Compatibility with non-clang compilers.
 #endif
 
-namespace spdlog {
-namespace details {
-namespace os {
-
-SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
+namespace spdlog
+{
+namespace details
 {
+    namespace os
+    {
 
+        SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
+        {
 #if defined __linux__ && defined SPDLOG_CLOCK_COARSE
-    timespec ts;
-    ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
-    return std::chrono::time_point<log_clock, typename log_clock::duration>(
-        std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
+            timespec ts;
+            ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
+            return std::chrono::time_point<log_clock, typename log_clock::duration>(
+                std::chrono::duration_cast<typename log_clock::duration>(
+                    std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
 
 #else
-    return log_clock::now();
+            return log_clock::now();
 #endif
-}
-SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
-{
-
+        }
+        SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
+        {
 #ifdef _WIN32
-    std::tm tm;
-    ::localtime_s(&tm, &time_tt);
+            std::tm tm;
+            ::localtime_s(&tm, &time_tt);
 #else
-    std::tm tm;
-    ::localtime_r(&time_tt, &tm);
+            std::tm tm;
+            ::localtime_r(&time_tt, &tm);
 #endif
-    return tm;
-}
-
-SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT
-{
-    std::time_t now_t = ::time(nullptr);
-    return localtime(now_t);
-}
+            return tm;
+        }
 
-SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
-{
+        SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT
+        {
+            std::time_t now_t = ::time(nullptr);
+            return localtime(now_t);
+        }
 
+        SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
+        {
 #ifdef _WIN32
-    std::tm tm;
-    ::gmtime_s(&tm, &time_tt);
+            std::tm tm;
+            ::gmtime_s(&tm, &time_tt);
 #else
-    std::tm tm;
-    ::gmtime_r(&time_tt, &tm);
+            std::tm tm;
+            ::gmtime_r(&time_tt, &tm);
 #endif
-    return tm;
-}
-
-SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
-{
-    std::time_t now_t = ::time(nullptr);
-    return gmtime(now_t);
-}
+            return tm;
+        }
 
-// fopen_s on non windows for writing
-SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
-{
-#ifdef _WIN32
-#    ifdef SPDLOG_WCHAR_FILENAMES
-    *fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
-#    else
-    *fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
-#    endif
-#    if defined(SPDLOG_PREVENT_CHILD_FD)
-    if (*fp != nullptr)
-    {
-        auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
-        if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
+        SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
         {
-            ::fclose(*fp);
-            *fp = nullptr;
+            std::time_t now_t = ::time(nullptr);
+            return gmtime(now_t);
         }
-    }
-#    endif
+
+        // fopen_s on non windows for writing
+        SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
+        {
+#ifdef _WIN32
+#ifdef SPDLOG_WCHAR_FILENAMES
+            *fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
+#else
+            *fp          = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
+#endif
+#if defined(SPDLOG_PREVENT_CHILD_FD)
+            if(*fp != nullptr)
+            {
+                auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
+                if(!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
+                {
+                    ::fclose(*fp);
+                    *fp = nullptr;
+                }
+            }
+#endif
 #else // unix
-#    if defined(SPDLOG_PREVENT_CHILD_FD)
-    const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
-    const int fd = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
-    if (fd == -1)
-    {
-        return true;
-    }
-    *fp = ::fdopen(fd, mode.c_str());
-    if (*fp == nullptr)
-    {
-        ::close(fd);
-    }
-#    else
-    *fp = ::fopen((filename.c_str()), mode.c_str());
-#    endif
+#if defined(SPDLOG_PREVENT_CHILD_FD)
+            const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
+            const int fd        = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
+            if(fd == -1)
+            {
+                return true;
+            }
+            *fp = ::fdopen(fd, mode.c_str());
+            if(*fp == nullptr)
+            {
+                ::close(fd);
+            }
+#else
+            *fp            = ::fopen((filename.c_str()), mode.c_str());
+#endif
 #endif
 
-    return *fp == nullptr;
-}
+            return *fp == nullptr;
+        }
 
-SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT
-{
+        SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT
+        {
 #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
-    return ::_wremove(filename.c_str());
+            return ::_wremove(filename.c_str());
 #else
-    return std::remove(filename.c_str());
+            return std::remove(filename.c_str());
 #endif
-}
+        }
 
-SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT
-{
-    return path_exists(filename) ? remove(filename) : 0;
-}
+        SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT
+        {
+            return path_exists(filename) ? remove(filename) : 0;
+        }
 
-SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
-{
+        SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
+        {
 #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
-    return ::_wrename(filename1.c_str(), filename2.c_str());
+            return ::_wrename(filename1.c_str(), filename2.c_str());
 #else
-    return std::rename(filename1.c_str(), filename2.c_str());
+            return std::rename(filename1.c_str(), filename2.c_str());
 #endif
-}
+        }
 
-// Return true if path exists (file or directory)
-SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT
-{
+        // Return true if path exists (file or directory)
+        SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT
+        {
 #ifdef _WIN32
-#    ifdef SPDLOG_WCHAR_FILENAMES
-    auto attribs = ::GetFileAttributesW(filename.c_str());
-#    else
-    auto attribs = ::GetFileAttributesA(filename.c_str());
-#    endif
-    return attribs != INVALID_FILE_ATTRIBUTES;
+#ifdef SPDLOG_WCHAR_FILENAMES
+            auto attribs = ::GetFileAttributesW(filename.c_str());
+#else
+            auto attribs = ::GetFileAttributesA(filename.c_str());
+#endif
+            return attribs != INVALID_FILE_ATTRIBUTES;
 #else // common linux/unix all have the stat system call
-    struct stat buffer;
-    return (::stat(filename.c_str(), &buffer) == 0);
+            struct stat buffer;
+            return (::stat(filename.c_str(), &buffer) == 0);
 #endif
-}
+        }
 
 #ifdef _MSC_VER
 // avoid warning about unreachable statement at the end of filesize()
-#    pragma warning(push)
-#    pragma warning(disable : 4702)
+#pragma warning(push)
+#pragma warning(disable : 4702)
 #endif
 
-// Return file size according to open FILE* object
-SPDLOG_INLINE size_t filesize(FILE *f)
-{
-    if (f == nullptr)
-    {
-        throw_spdlog_ex("Failed getting file size. fd is null");
-    }
+        // Return file size according to open FILE* object
+        SPDLOG_INLINE size_t filesize(FILE *f)
+        {
+            if(f == nullptr)
+            {
+                throw_spdlog_ex("Failed getting file size. fd is null");
+            }
 #if defined(_WIN32) && !defined(__CYGWIN__)
-    int fd = ::_fileno(f);
-#    if defined(_WIN64) // 64 bits
-    __int64 ret = ::_filelengthi64(fd);
-    if (ret >= 0)
-    {
-        return static_cast<size_t>(ret);
-    }
-
-#    else // windows 32 bits
-    long ret = ::_filelength(fd);
-    if (ret >= 0)
-    {
-        return static_cast<size_t>(ret);
-    }
-#    endif
+            int fd = ::_fileno(f);
+#if defined(_WIN64) // 64 bits
+            __int64 ret = ::_filelengthi64(fd);
+            if(ret >= 0)
+            {
+                return static_cast<size_t>(ret);
+            }
+
+#else // windows 32 bits
+            long ret     = ::_filelength(fd);
+            if(ret >= 0)
+            {
+                return static_cast<size_t>(ret);
+            }
+#endif
 
 #else // unix
 // OpenBSD and AIX doesn't compile with :: before the fileno(..)
-#    if defined(__OpenBSD__) || defined(_AIX)
-    int fd = fileno(f);
-#    else
-    int fd = ::fileno(f);
-#    endif
+#if defined(__OpenBSD__) || defined(_AIX)
+            int fd = fileno(f);
+#else
+            int         fd = ::fileno(f);
+#endif
 // 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
-#    if (defined(__linux__) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64))
-    struct stat64 st;
-    if (::fstat64(fd, &st) == 0)
-    {
-        return static_cast<size_t>(st.st_size);
-    }
-#    else // other unix or linux 32 bits or cygwin
-    struct stat st;
-    if (::fstat(fd, &st) == 0)
-    {
-        return static_cast<size_t>(st.st_size);
-    }
-#    endif
+#if(defined(__linux__) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64))
+            struct stat64 st;
+            if(::fstat64(fd, &st) == 0)
+            {
+                return static_cast<size_t>(st.st_size);
+            }
+#else // other unix or linux 32 bits or cygwin
+            struct stat st;
+            if(::fstat(fd, &st) == 0)
+            {
+                return static_cast<size_t>(st.st_size);
+            }
+#endif
 #endif
-    throw_spdlog_ex("Failed getting file size from fd", errno);
-    return 0; // will not be reached.
-}
+            throw_spdlog_ex("Failed getting file size from fd", errno);
+            return 0; // will not be reached.
+        }
 
 #ifdef _MSC_VER
-#    pragma warning(pop)
+#pragma warning(pop)
 #endif
 
-// Return utc offset in minutes or throw spdlog_ex on failure
-SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
-{
-
+        // Return utc offset in minutes or throw spdlog_ex on failure
+        SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
+        {
 #ifdef _WIN32
-#    if _WIN32_WINNT < _WIN32_WINNT_WS08
-    TIME_ZONE_INFORMATION tzinfo;
-    auto rv = ::GetTimeZoneInformation(&tzinfo);
-#    else
-    DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
-    auto rv = ::GetDynamicTimeZoneInformation(&tzinfo);
-#    endif
-    if (rv == TIME_ZONE_ID_INVALID)
-        throw_spdlog_ex("Failed getting timezone info. ", errno);
-
-    int offset = -tzinfo.Bias;
-    if (tm.tm_isdst)
-    {
-        offset -= tzinfo.DaylightBias;
-    }
-    else
-    {
-        offset -= tzinfo.StandardBias;
-    }
-    return offset;
+#if _WIN32_WINNT < _WIN32_WINNT_WS08
+            TIME_ZONE_INFORMATION tzinfo;
+            auto                  rv = ::GetTimeZoneInformation(&tzinfo);
+#else
+            DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
+            auto                          rv = ::GetDynamicTimeZoneInformation(&tzinfo);
+#endif
+            if(rv == TIME_ZONE_ID_INVALID)
+                throw_spdlog_ex("Failed getting timezone info. ", errno);
+
+            int offset = -tzinfo.Bias;
+            if(tm.tm_isdst)
+            {
+                offset -= tzinfo.DaylightBias;
+            }
+            else
+            {
+                offset -= tzinfo.StandardBias;
+            }
+            return offset;
 #else
 
-#    if defined(sun) || defined(__sun) || defined(_AIX) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE))
-    // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
-    struct helper
-    {
-        static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime())
-        {
-            int local_year = localtm.tm_year + (1900 - 1);
-            int gmt_year = gmtm.tm_year + (1900 - 1);
-
-            long int days = (
-                // difference in day of year
-                localtm.tm_yday -
-                gmtm.tm_yday
-
-                // + intervening leap days
-                + ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
-                ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
-
-                // + difference in years * 365 */
-                + static_cast<long int>(local_year - gmt_year) * 365);
-
-            long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
-            long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
-            long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
-
-            return secs;
-        }
-    };
-
-    auto offset_seconds = helper::calculate_gmt_offset(tm);
-#    else
-    auto offset_seconds = tm.tm_gmtoff;
-#    endif
+#if defined(sun) || defined(__sun) || defined(_AIX) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE))
+            // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
+            struct helper
+            {
+                static long int calculate_gmt_offset(
+                    const std::tm &localtm = details::os::localtime(),
+                    const std::tm &gmtm    = details::os::gmtime())
+                {
+                    int local_year = localtm.tm_year + (1900 - 1);
+                    int gmt_year   = gmtm.tm_year + (1900 - 1);
+
+                    long int days = (
+                        // difference in day of year
+                        localtm.tm_yday -
+                        gmtm.tm_yday
+
+                        // + intervening leap days
+                        + ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
+                        ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
+
+                        // + difference in years * 365 */
+                        + static_cast<long int>(local_year - gmt_year) * 365);
+
+                    long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
+                    long int mins  = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
+                    long int secs  = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
+
+                    return secs;
+                }
+            };
+
+            auto offset_seconds = helper::calculate_gmt_offset(tm);
+#else
+            auto offset_seconds = tm.tm_gmtoff;
+#endif
 
-    return static_cast<int>(offset_seconds / 60);
+            return static_cast<int>(offset_seconds / 60);
 #endif
-}
+        }
 
-// Return current thread id as size_t
-// It exists because the std::this_thread::get_id() is much slower(especially
-// under VS 2013)
-SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT
-{
+        // Return current thread id as size_t
+        // It exists because the std::this_thread::get_id() is much slower(especially
+        // under VS 2013)
+        SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT
+        {
 #ifdef _WIN32
-    return static_cast<size_t>(::GetCurrentThreadId());
+            return static_cast<size_t>(::GetCurrentThreadId());
 #elif defined(__linux__)
-#    if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
-#        define SYS_gettid __NR_gettid
-#    endif
-    return static_cast<size_t>(::syscall(SYS_gettid));
+#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
+#define SYS_gettid __NR_gettid
+#endif
+            return static_cast<size_t>(::syscall(SYS_gettid));
 #elif defined(_AIX)
-    struct __pthrdsinfo buf;
-    int reg_size = 0;
-    pthread_t pt = pthread_self();
-    int retval = pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_TID, &buf, sizeof(buf), NULL, &reg_size);
-    int tid = (!retval) ? buf.__pi_tid : 0;
-    return static_cast<size_t>(tid);
+            struct __pthrdsinfo buf;
+            int                 reg_size = 0;
+            pthread_t           pt       = pthread_self();
+            int retval = pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_TID, &buf, sizeof(buf), NULL, &reg_size);
+            int tid    = (!retval) ? buf.__pi_tid : 0;
+            return static_cast<size_t>(tid);
 #elif defined(__DragonFly__) || defined(__FreeBSD__)
-    return static_cast<size_t>(::pthread_getthreadid_np());
+            return static_cast<size_t>(::pthread_getthreadid_np());
 #elif defined(__NetBSD__)
-    return static_cast<size_t>(::_lwp_self());
+            return static_cast<size_t>(::_lwp_self());
 #elif defined(__OpenBSD__)
-    return static_cast<size_t>(::getthrid());
+            return static_cast<size_t>(::getthrid());
 #elif defined(__sun)
-    return static_cast<size_t>(::thr_self());
+            return static_cast<size_t>(::thr_self());
 #elif __APPLE__
-    uint64_t tid;
-    pthread_threadid_np(nullptr, &tid);
-    return static_cast<size_t>(tid);
+            uint64_t tid;
+            pthread_threadid_np(nullptr, &tid);
+            return static_cast<size_t>(tid);
 #else // Default to standard C++11 (other Unix)
-    return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
+            return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
 #endif
-}
+        }
 
-// Return current thread id as size_t (from thread local storage)
-SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT
-{
+        // Return current thread id as size_t (from thread local storage)
+        SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT
+        {
 #if defined(SPDLOG_NO_TLS)
-    return _thread_id();
+            return _thread_id();
 #else // cache thread id in tls
-    static thread_local const size_t tid = _thread_id();
-    return tid;
+            static thread_local const size_t tid = _thread_id();
+            return tid;
 #endif
-}
+        }
 
-// This is avoid msvc issue in sleep_for that happens if the clock changes.
-// See https://github.com/gabime/spdlog/issues/609
-SPDLOG_INLINE void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT
-{
+        // This is avoid msvc issue in sleep_for that happens if the clock changes.
+        // See https://github.com/gabime/spdlog/issues/609
+        SPDLOG_INLINE void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT
+        {
 #if defined(_WIN32)
-    ::Sleep(milliseconds);
+            ::Sleep(milliseconds);
 #else
-    std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
+            std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
 #endif
-}
+        }
 
 // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
 #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
-SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
-{
-    memory_buf_t buf;
-    wstr_to_utf8buf(filename, buf);
-    return SPDLOG_BUF_TO_STRING(buf);
-}
+        SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
+        {
+            memory_buf_t buf;
+            wstr_to_utf8buf(filename, buf);
+            return SPDLOG_BUF_TO_STRING(buf);
+        }
 #else
-SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
-{
-    return filename;
-}
+        SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
+        {
+            return filename;
+        }
 #endif
 
-SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT
-{
-
+        SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT
+        {
 #ifdef _WIN32
-    return conditional_static_cast<int>(::GetCurrentProcessId());
+            return conditional_static_cast<int>(::GetCurrentProcessId());
 #else
-    return conditional_static_cast<int>(::getpid());
+            return conditional_static_cast<int>(::getpid());
 #endif
-}
+        }
 
-// Determine if the terminal supports colors
-// Based on: https://github.com/agauniyal/rang/
-SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
-{
+        // Determine if the terminal supports colors
+        // Based on: https://github.com/agauniyal/rang/
+        SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
+        {
 #ifdef _WIN32
-    return true;
+            return true;
 #else
 
-    static const bool result = []() {
-        const char *env_colorterm_p = std::getenv("COLORTERM");
-        if (env_colorterm_p != nullptr)
-        {
-            return true;
+            static const bool result = []()
+            {
+                const char *env_colorterm_p = std::getenv("COLORTERM");
+                if(env_colorterm_p != nullptr)
+                {
+                    return true;
+                }
+
+                static constexpr std::array<const char *, 16> terms = {
+                    {"ansi",
+                     "color",
+                     "console",
+                     "cygwin",
+                     "gnome",
+                     "konsole",
+                     "kterm",
+                     "linux",
+                     "msys",
+                     "putty",
+                     "rxvt",
+                     "screen",
+                     "vt100",
+                     "xterm",
+                     "alacritty",
+                     "vt102"}};
+
+                const char *env_term_p = std::getenv("TERM");
+                if(env_term_p == nullptr)
+                {
+                    return false;
+                }
+
+                return std::any_of(
+                    terms.begin(),
+                    terms.end(),
+                    [&](const char *term) { return std::strstr(env_term_p, term) != nullptr; });
+            }();
+
+            return result;
+#endif
         }
 
-        static constexpr std::array<const char *, 16> terms = {{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux",
-            "msys", "putty", "rxvt", "screen", "vt100", "xterm", "alacritty", "vt102"}};
-
-        const char *env_term_p = std::getenv("TERM");
-        if (env_term_p == nullptr)
+        // Determine if the terminal attached
+        // Source: https://github.com/agauniyal/rang/
+        SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
         {
-            return false;
-        }
-
-        return std::any_of(terms.begin(), terms.end(), [&](const char *term) { return std::strstr(env_term_p, term) != nullptr; });
-    }();
-
-    return result;
-#endif
-}
-
-// Determine if the terminal attached
-// Source: https://github.com/agauniyal/rang/
-SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
-{
-
 #ifdef _WIN32
-    return ::_isatty(_fileno(file)) != 0;
+            return ::_isatty(_fileno(file)) != 0;
 #else
-    return ::isatty(fileno(file)) != 0;
+            return ::isatty(fileno(file)) != 0;
 #endif
-}
-
-#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
-SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
-{
-    if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 2 - 1)
-    {
-        throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
-    }
-
-    int wstr_size = static_cast<int>(wstr.size());
-    if (wstr_size == 0)
-    {
-        target.resize(0);
-        return;
-    }
-
-    int result_size = static_cast<int>(target.capacity());
-    if ((wstr_size + 1) * 2 > result_size)
-    {
-        result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
-    }
-
-    if (result_size > 0)
-    {
-        target.resize(result_size);
-        result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(), result_size, NULL, NULL);
+        }
 
-        if (result_size > 0)
+#if(defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
+        SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
         {
-            target.resize(result_size);
-            return;
+            if(wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 2 - 1)
+            {
+                throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
+            }
+
+            int wstr_size = static_cast<int>(wstr.size());
+            if(wstr_size == 0)
+            {
+                target.resize(0);
+                return;
+            }
+
+            int result_size = static_cast<int>(target.capacity());
+            if((wstr_size + 1) * 2 > result_size)
+            {
+                result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
+            }
+
+            if(result_size > 0)
+            {
+                target.resize(result_size);
+                result_size =
+                    ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(), result_size, NULL, NULL);
+
+                if(result_size > 0)
+                {
+                    target.resize(result_size);
+                    return;
+                }
+            }
+
+            throw_spdlog_ex(fmt_lib::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
         }
-    }
-
-    throw_spdlog_ex(fmt_lib::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
-}
 
-SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target)
-{
-    if (str.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) - 1)
-    {
-        throw_spdlog_ex("UTF-8 string is too big to be converted to UTF-16");
-    }
-
-    int str_size = static_cast<int>(str.size());
-    if (str_size == 0)
-    {
-        target.resize(0);
-        return;
-    }
-
-    int result_size = static_cast<int>(target.capacity());
-    if (str_size + 1 > result_size)
-    {
-        result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0);
-    }
-
-    if (result_size > 0)
-    {
-        target.resize(result_size);
-        result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, target.data(), result_size);
-
-        if (result_size > 0)
+        SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target)
         {
-            target.resize(result_size);
-            return;
+            if(str.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) - 1)
+            {
+                throw_spdlog_ex("UTF-8 string is too big to be converted to UTF-16");
+            }
+
+            int str_size = static_cast<int>(str.size());
+            if(str_size == 0)
+            {
+                target.resize(0);
+                return;
+            }
+
+            int result_size = static_cast<int>(target.capacity());
+            if(str_size + 1 > result_size)
+            {
+                result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0);
+            }
+
+            if(result_size > 0)
+            {
+                target.resize(result_size);
+                result_size = ::MultiByteToWideChar(
+                    CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, target.data(), result_size);
+
+                if(result_size > 0)
+                {
+                    target.resize(result_size);
+                    return;
+                }
+            }
+
+            throw_spdlog_ex(fmt_lib::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError()));
         }
-    }
-
-    throw_spdlog_ex(fmt_lib::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError()));
-}
 #endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
 
-// return true on success
-static SPDLOG_INLINE bool mkdir_(const filename_t &path)
-{
+        // return true on success
+        static SPDLOG_INLINE bool mkdir_(const filename_t &path)
+        {
 #ifdef _WIN32
-#    ifdef SPDLOG_WCHAR_FILENAMES
-    return ::_wmkdir(path.c_str()) == 0;
-#    else
-    return ::_mkdir(path.c_str()) == 0;
-#    endif
+#ifdef SPDLOG_WCHAR_FILENAMES
+            return ::_wmkdir(path.c_str()) == 0;
 #else
-    return ::mkdir(path.c_str(), mode_t(0755)) == 0;
+            return ::_mkdir(path.c_str()) == 0;
 #endif
-}
-
-// create the given directory - and all directories leading to it
-// return true on success or if the directory already exists
-SPDLOG_INLINE bool create_dir(const filename_t &path)
-{
-    if (path_exists(path))
-    {
-        return true;
-    }
-
-    if (path.empty())
-    {
-        return false;
-    }
+#else
+            return ::mkdir(path.c_str(), mode_t(0755)) == 0;
+#endif
+        }
 
-    size_t search_offset = 0;
-    do
-    {
-        auto token_pos = path.find_first_of(folder_seps_filename, search_offset);
-        // treat the entire path as a folder if no folder separator not found
-        if (token_pos == filename_t::npos)
+        // create the given directory - and all directories leading to it
+        // return true on success or if the directory already exists
+        SPDLOG_INLINE bool create_dir(const filename_t &path)
         {
-            token_pos = path.size();
-        }
+            if(path_exists(path))
+            {
+                return true;
+            }
+
+            if(path.empty())
+            {
+                return false;
+            }
+
+            size_t search_offset = 0;
+            do
+            {
+                auto token_pos = path.find_first_of(folder_seps_filename, search_offset);
+                // treat the entire path as a folder if no folder separator not found
+                if(token_pos == filename_t::npos)
+                {
+                    token_pos = path.size();
+                }
+
+                auto subdir = path.substr(0, token_pos);
+
+                if(!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir))
+                {
+                    return false; // return error if failed creating dir
+                }
+                search_offset = token_pos + 1;
+            } while(search_offset < path.size());
 
-        auto subdir = path.substr(0, token_pos);
+            return true;
+        }
 
-        if (!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir))
+        // Return directory name from given path or empty string
+        // "abc/file" => "abc"
+        // "abc/" => "abc"
+        // "abc" => ""
+        // "abc///" => "abc//"
+        SPDLOG_INLINE filename_t dir_name(const filename_t &path)
         {
-            return false; // return error if failed creating dir
+            auto pos = path.find_last_of(folder_seps_filename);
+            return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
         }
-        search_offset = token_pos + 1;
-    } while (search_offset < path.size());
-
-    return true;
-}
-
-// Return directory name from given path or empty string
-// "abc/file" => "abc"
-// "abc/" => "abc"
-// "abc" => ""
-// "abc///" => "abc//"
-SPDLOG_INLINE filename_t dir_name(const filename_t &path)
-{
-    auto pos = path.find_last_of(folder_seps_filename);
-    return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
-}
-
-std::string SPDLOG_INLINE getenv(const char *field)
-{
 
+        std::string SPDLOG_INLINE getenv(const char *field)
+        {
 #if defined(_MSC_VER)
-#    if defined(__cplusplus_winrt)
-    return std::string{}; // not supported under uwp
-#    else
-    size_t len = 0;
-    char buf[128];
-    bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
-    return ok ? buf : std::string{};
-#    endif
+#if defined(__cplusplus_winrt)
+            return std::string{}; // not supported under uwp
+#else
+            size_t len = 0;
+            char   buf[128];
+            bool   ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
+            return ok ? buf : std::string{};
+#endif
 #else // revert to getenv
-    char *buf = ::getenv(field);
-    return buf ? buf : std::string{};
+            char *buf = ::getenv(field);
+            return buf ? buf : std::string{};
 #endif
-}
+        }
 
-} // namespace os
+    } // namespace os
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/os.h b/include/spdlog/details/os.h
index b154bc473..35a5fa4b8 100644
--- a/include/spdlog/details/os.h
+++ b/include/spdlog/details/os.h
@@ -3,116 +3,120 @@
 
 #pragma once
 
-#include <spdlog/common.h>
 #include <ctime> // std::time_t
+#include <spdlog/common.h>
 
-namespace spdlog {
-namespace details {
-namespace os {
+namespace spdlog
+{
+namespace details
+{
+    namespace os
+    {
 
-SPDLOG_API spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
+        SPDLOG_API spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
 
-SPDLOG_API std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
+        SPDLOG_API std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
 
-SPDLOG_API std::tm localtime() SPDLOG_NOEXCEPT;
+        SPDLOG_API std::tm localtime() SPDLOG_NOEXCEPT;
 
-SPDLOG_API std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
+        SPDLOG_API std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
 
-SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
+        SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
 
 // eol definition
 #if !defined(SPDLOG_EOL)
-#    ifdef _WIN32
-#        define SPDLOG_EOL "\r\n"
-#    else
-#        define SPDLOG_EOL "\n"
-#    endif
+#ifdef _WIN32
+#define SPDLOG_EOL "\r\n"
+#else
+#define SPDLOG_EOL "\n"
+#endif
 #endif
 
-SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
+        SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
 
 // folder separator
 #if !defined(SPDLOG_FOLDER_SEPS)
-#    ifdef _WIN32
-#        define SPDLOG_FOLDER_SEPS "\\/"
-#    else
-#        define SPDLOG_FOLDER_SEPS "/"
-#    endif
+#ifdef _WIN32
+#define SPDLOG_FOLDER_SEPS "\\/"
+#else
+#define SPDLOG_FOLDER_SEPS "/"
+#endif
 #endif
 
-SPDLOG_CONSTEXPR static const char folder_seps[] = SPDLOG_FOLDER_SEPS;
-SPDLOG_CONSTEXPR static const filename_t::value_type folder_seps_filename[] = SPDLOG_FILENAME_T(SPDLOG_FOLDER_SEPS);
+        SPDLOG_CONSTEXPR static const char                   folder_seps[] = SPDLOG_FOLDER_SEPS;
+        SPDLOG_CONSTEXPR static const filename_t::value_type folder_seps_filename[] =
+            SPDLOG_FILENAME_T(SPDLOG_FOLDER_SEPS);
 
-// fopen_s on non windows for writing
-SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
+        // fopen_s on non windows for writing
+        SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
 
-// Remove filename. return 0 on success
-SPDLOG_API int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
+        // Remove filename. return 0 on success
+        SPDLOG_API int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
 
-// Remove file if exists. return 0 on success
-// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
-SPDLOG_API int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
+        // Remove file if exists. return 0 on success
+        // Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
+        SPDLOG_API int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
 
-SPDLOG_API int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
+        SPDLOG_API int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
 
-// Return if file exists.
-SPDLOG_API bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
+        // Return if file exists.
+        SPDLOG_API bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
 
-// Return file size according to open FILE* object
-SPDLOG_API size_t filesize(FILE *f);
+        // Return file size according to open FILE* object
+        SPDLOG_API size_t filesize(FILE *f);
 
-// Return utc offset in minutes or throw spdlog_ex on failure
-SPDLOG_API int utc_minutes_offset(const std::tm &tm = details::os::localtime());
+        // Return utc offset in minutes or throw spdlog_ex on failure
+        SPDLOG_API int utc_minutes_offset(const std::tm &tm = details::os::localtime());
 
-// Return current thread id as size_t
-// It exists because the std::this_thread::get_id() is much slower(especially
-// under VS 2013)
-SPDLOG_API size_t _thread_id() SPDLOG_NOEXCEPT;
+        // Return current thread id as size_t
+        // It exists because the std::this_thread::get_id() is much slower(especially
+        // under VS 2013)
+        SPDLOG_API size_t _thread_id() SPDLOG_NOEXCEPT;
 
-// Return current thread id as size_t (from thread local storage)
-SPDLOG_API size_t thread_id() SPDLOG_NOEXCEPT;
+        // Return current thread id as size_t (from thread local storage)
+        SPDLOG_API size_t thread_id() SPDLOG_NOEXCEPT;
 
-// This is avoid msvc issue in sleep_for that happens if the clock changes.
-// See https://github.com/gabime/spdlog/issues/609
-SPDLOG_API void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT;
+        // This is avoid msvc issue in sleep_for that happens if the clock changes.
+        // See https://github.com/gabime/spdlog/issues/609
+        SPDLOG_API void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT;
 
-SPDLOG_API std::string filename_to_str(const filename_t &filename);
+        SPDLOG_API std::string filename_to_str(const filename_t &filename);
 
-SPDLOG_API int pid() SPDLOG_NOEXCEPT;
+        SPDLOG_API int pid() SPDLOG_NOEXCEPT;
 
-// Determine if the terminal supports colors
-// Source: https://github.com/agauniyal/rang/
-SPDLOG_API bool is_color_terminal() SPDLOG_NOEXCEPT;
+        // Determine if the terminal supports colors
+        // Source: https://github.com/agauniyal/rang/
+        SPDLOG_API bool is_color_terminal() SPDLOG_NOEXCEPT;
 
-// Determine if the terminal attached
-// Source: https://github.com/agauniyal/rang/
-SPDLOG_API bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
+        // Determine if the terminal attached
+        // Source: https://github.com/agauniyal/rang/
+        SPDLOG_API bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
 
-#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
-SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
+#if(defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
+        SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
 
-SPDLOG_API void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target);
+        SPDLOG_API void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target);
 #endif
 
-// Return directory name from given path or empty string
-// "abc/file" => "abc"
-// "abc/" => "abc"
-// "abc" => ""
-// "abc///" => "abc//"
-SPDLOG_API filename_t dir_name(const filename_t &path);
+        // Return directory name from given path or empty string
+        // "abc/file" => "abc"
+        // "abc/" => "abc"
+        // "abc" => ""
+        // "abc///" => "abc//"
+        SPDLOG_API filename_t dir_name(const filename_t &path);
 
-// Create a dir from the given path.
-// Return true if succeeded or if this dir already exists.
-SPDLOG_API bool create_dir(const filename_t &path);
+        // Create a dir from the given path.
+        // Return true if succeeded or if this dir already exists.
+        SPDLOG_API bool create_dir(const filename_t &path);
 
-// non thread safe, cross platform getenv/getenv_s
-// return empty string if field not found
-SPDLOG_API std::string getenv(const char *field);
+        // non thread safe, cross platform getenv/getenv_s
+        // return empty string if field not found
+        SPDLOG_API std::string getenv(const char *field);
 
-} // namespace os
+    } // namespace os
 } // namespace details
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "os-inl.h"
+#include "os-inl.h"
 #endif
diff --git a/include/spdlog/details/periodic_worker-inl.h b/include/spdlog/details/periodic_worker-inl.h
index 520a2b339..2751bcf89 100644
--- a/include/spdlog/details/periodic_worker-inl.h
+++ b/include/spdlog/details/periodic_worker-inl.h
@@ -4,25 +4,27 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/details/periodic_worker.h>
+#include <spdlog/details/periodic_worker.h>
 #endif
 
-namespace spdlog {
-namespace details {
-
-// stop the worker thread and join it
-SPDLOG_INLINE periodic_worker::~periodic_worker()
+namespace spdlog
+{
+namespace details
 {
-    if (worker_thread_.joinable())
+
+    // stop the worker thread and join it
+    SPDLOG_INLINE periodic_worker::~periodic_worker()
     {
+        if(worker_thread_.joinable())
         {
-            std::lock_guard<std::mutex> lock(mutex_);
-            active_ = false;
+            {
+                std::lock_guard<std::mutex> lock(mutex_);
+                active_ = false;
+            }
+            cv_.notify_one();
+            worker_thread_.join();
         }
-        cv_.notify_one();
-        worker_thread_.join();
     }
-}
 
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/periodic_worker.h b/include/spdlog/details/periodic_worker.h
index d7d69b28c..8eb6ac337 100644
--- a/include/spdlog/details/periodic_worker.h
+++ b/include/spdlog/details/periodic_worker.h
@@ -14,47 +14,51 @@
 #include <functional>
 #include <mutex>
 #include <thread>
-namespace spdlog {
-namespace details {
-
-class SPDLOG_API periodic_worker
+namespace spdlog
+{
+namespace details
 {
-public:
-    template<typename Rep, typename Period>
-    periodic_worker(const std::function<void()> &callback_fun, std::chrono::duration<Rep, Period> interval)
+
+    class SPDLOG_API periodic_worker
     {
-        active_ = (interval > std::chrono::duration<Rep, Period>::zero());
-        if (!active_)
+    public:
+        template <typename Rep, typename Period>
+        periodic_worker(const std::function<void()> &callback_fun, std::chrono::duration<Rep, Period> interval)
         {
-            return;
-        }
-
-        worker_thread_ = std::thread([this, callback_fun, interval]() {
-            for (;;)
+            active_ = (interval > std::chrono::duration<Rep, Period>::zero());
+            if(!active_)
             {
-                std::unique_lock<std::mutex> lock(this->mutex_);
-                if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; }))
-                {
-                    return; // active_ == false, so exit this thread
-                }
-                callback_fun();
+                return;
             }
-        });
-    }
-    periodic_worker(const periodic_worker &) = delete;
-    periodic_worker &operator=(const periodic_worker &) = delete;
-    // stop the worker thread and join it
-    ~periodic_worker();
 
-private:
-    bool active_;
-    std::thread worker_thread_;
-    std::mutex mutex_;
-    std::condition_variable cv_;
-};
+            worker_thread_ = std::thread(
+                [this, callback_fun, interval]()
+                {
+                    for(;;)
+                    {
+                        std::unique_lock<std::mutex> lock(this->mutex_);
+                        if(this->cv_.wait_for(lock, interval, [this] { return !this->active_; }))
+                        {
+                            return; // active_ == false, so exit this thread
+                        }
+                        callback_fun();
+                    }
+                });
+        }
+        periodic_worker(const periodic_worker &)            = delete;
+        periodic_worker &operator=(const periodic_worker &) = delete;
+        // stop the worker thread and join it
+        ~periodic_worker();
+
+    private:
+        bool                    active_;
+        std::thread             worker_thread_;
+        std::mutex              mutex_;
+        std::condition_variable cv_;
+    };
 } // namespace details
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "periodic_worker-inl.h"
+#include "periodic_worker-inl.h"
 #endif
diff --git a/include/spdlog/details/registry-inl.h b/include/spdlog/details/registry-inl.h
index e6ecc9b08..8b9dbf98d 100644
--- a/include/spdlog/details/registry-inl.h
+++ b/include/spdlog/details/registry-inl.h
@@ -4,7 +4,7 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/details/registry.h>
+#include <spdlog/details/registry.h>
 #endif
 
 #include <spdlog/common.h>
@@ -14,11 +14,11 @@
 
 #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
+#ifdef _WIN32
+#include <spdlog/sinks/wincolor_sink.h>
+#else
+#include <spdlog/sinks/ansicolor_sink.h>
+#endif
 #endif // SPDLOG_DISABLE_DEFAULT_LOGGER
 
 #include <chrono>
@@ -27,280 +27,280 @@
 #include <string>
 #include <unordered_map>
 
-namespace spdlog {
-namespace details {
-
-SPDLOG_INLINE registry::registry()
-    : formatter_(new pattern_formatter())
+namespace spdlog
+{
+namespace details
 {
 
+    SPDLOG_INLINE registry::registry() : 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
+        // 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_;
+        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
-}
-
-SPDLOG_INLINE registry::~registry() = default;
-
-SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger)
-{
-    std::lock_guard<std::mutex> lock(logger_map_mutex_);
-    register_logger_(std::move(new_logger));
-}
+    }
 
-SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger)
-{
-    std::lock_guard<std::mutex> lock(logger_map_mutex_);
-    new_logger->set_formatter(formatter_->clone());
+    SPDLOG_INLINE registry::~registry() = default;
 
-    if (err_handler_)
+    SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger)
     {
-        new_logger->set_error_handler(err_handler_);
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        register_logger_(std::move(new_logger));
     }
 
-    // set new level according to previously configured level or default level
-    auto it = log_levels_.find(new_logger->name());
-    auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
-    new_logger->set_level(new_level);
+    SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger)
+    {
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        new_logger->set_formatter(formatter_->clone());
+
+        if(err_handler_)
+        {
+            new_logger->set_error_handler(err_handler_);
+        }
 
-    new_logger->flush_on(flush_level_);
+        // set new level according to previously configured level or default level
+        auto it        = log_levels_.find(new_logger->name());
+        auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
+        new_logger->set_level(new_level);
 
-    if (backtrace_n_messages_ > 0)
-    {
-        new_logger->enable_backtrace(backtrace_n_messages_);
+        new_logger->flush_on(flush_level_);
+
+        if(backtrace_n_messages_ > 0)
+        {
+            new_logger->enable_backtrace(backtrace_n_messages_);
+        }
+
+        if(automatic_registration_)
+        {
+            register_logger_(std::move(new_logger));
+        }
     }
 
-    if (automatic_registration_)
+    SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_name)
     {
-        register_logger_(std::move(new_logger));
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        auto                        found = loggers_.find(logger_name);
+        return found == loggers_.end() ? nullptr : found->second;
     }
-}
 
-SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_name)
-{
-    std::lock_guard<std::mutex> lock(logger_map_mutex_);
-    auto found = loggers_.find(logger_name);
-    return found == loggers_.end() ? nullptr : found->second;
-}
-
-SPDLOG_INLINE std::shared_ptr<logger> registry::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.
-SPDLOG_INLINE logger *registry::get_default_raw()
-{
-    return default_logger_.get();
-}
+    SPDLOG_INLINE std::shared_ptr<logger> registry::default_logger()
+    {
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        return default_logger_;
+    }
 
-// set default logger.
-// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
-SPDLOG_INLINE void registry::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)
+    // 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.
+    SPDLOG_INLINE logger *registry::get_default_raw()
     {
-        loggers_.erase(default_logger_->name());
+        return default_logger_.get();
     }
-    if (new_default_logger != nullptr)
+
+    // set default logger.
+    // default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
+    SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger)
     {
-        loggers_[new_default_logger->name()] = 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);
     }
-    default_logger_ = std::move(new_default_logger);
-}
 
-SPDLOG_INLINE void registry::set_tp(std::shared_ptr<thread_pool> tp)
-{
-    std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
-    tp_ = std::move(tp);
-}
+    SPDLOG_INLINE void registry::set_tp(std::shared_ptr<thread_pool> tp)
+    {
+        std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
+        tp_ = std::move(tp);
+    }
 
-SPDLOG_INLINE std::shared_ptr<thread_pool> registry::get_tp()
-{
-    std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
-    return tp_;
-}
+    SPDLOG_INLINE std::shared_ptr<thread_pool> registry::get_tp()
+    {
+        std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
+        return tp_;
+    }
 
-// Set global formatter. Each sink in each logger will get a clone of this object
-SPDLOG_INLINE void registry::set_formatter(std::unique_ptr<formatter> formatter)
-{
-    std::lock_guard<std::mutex> lock(logger_map_mutex_);
-    formatter_ = std::move(formatter);
-    for (auto &l : loggers_)
+    // Set global formatter. Each sink in each logger will get a clone of this object
+    SPDLOG_INLINE void registry::set_formatter(std::unique_ptr<formatter> formatter)
     {
-        l.second->set_formatter(formatter_->clone());
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        formatter_ = std::move(formatter);
+        for(auto &l : loggers_)
+        {
+            l.second->set_formatter(formatter_->clone());
+        }
     }
-}
 
-SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages)
-{
-    std::lock_guard<std::mutex> lock(logger_map_mutex_);
-    backtrace_n_messages_ = n_messages;
+    SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages)
+    {
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        backtrace_n_messages_ = n_messages;
 
-    for (auto &l : loggers_)
+        for(auto &l : loggers_)
+        {
+            l.second->enable_backtrace(n_messages);
+        }
+    }
+
+    SPDLOG_INLINE void registry::disable_backtrace()
     {
-        l.second->enable_backtrace(n_messages);
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        backtrace_n_messages_ = 0;
+        for(auto &l : loggers_)
+        {
+            l.second->disable_backtrace();
+        }
     }
-}
 
-SPDLOG_INLINE void registry::disable_backtrace()
-{
-    std::lock_guard<std::mutex> lock(logger_map_mutex_);
-    backtrace_n_messages_ = 0;
-    for (auto &l : loggers_)
+    SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
     {
-        l.second->disable_backtrace();
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        for(auto &l : loggers_)
+        {
+            l.second->set_level(log_level);
+        }
+        global_log_level_ = log_level;
     }
-}
 
-SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
-{
-    std::lock_guard<std::mutex> lock(logger_map_mutex_);
-    for (auto &l : loggers_)
+    SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
     {
-        l.second->set_level(log_level);
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        for(auto &l : loggers_)
+        {
+            l.second->flush_on(log_level);
+        }
+        flush_level_ = log_level;
     }
-    global_log_level_ = log_level;
-}
 
-SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
-{
-    std::lock_guard<std::mutex> lock(logger_map_mutex_);
-    for (auto &l : loggers_)
+    SPDLOG_INLINE void registry::set_error_handler(err_handler handler)
     {
-        l.second->flush_on(log_level);
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        for(auto &l : loggers_)
+        {
+            l.second->set_error_handler(handler);
+        }
+        err_handler_ = std::move(handler);
     }
-    flush_level_ = log_level;
-}
 
-SPDLOG_INLINE void registry::set_error_handler(err_handler handler)
-{
-    std::lock_guard<std::mutex> lock(logger_map_mutex_);
-    for (auto &l : loggers_)
+    SPDLOG_INLINE void registry::apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun)
     {
-        l.second->set_error_handler(handler);
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        for(auto &l : loggers_)
+        {
+            fun(l.second);
+        }
     }
-    err_handler_ = std::move(handler);
-}
 
-SPDLOG_INLINE void registry::apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun)
-{
-    std::lock_guard<std::mutex> lock(logger_map_mutex_);
-    for (auto &l : loggers_)
+    SPDLOG_INLINE void registry::flush_all()
     {
-        fun(l.second);
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        for(auto &l : loggers_)
+        {
+            l.second->flush();
+        }
     }
-}
 
-SPDLOG_INLINE void registry::flush_all()
-{
-    std::lock_guard<std::mutex> lock(logger_map_mutex_);
-    for (auto &l : loggers_)
+    SPDLOG_INLINE void registry::drop(const std::string &logger_name)
     {
-        l.second->flush();
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        loggers_.erase(logger_name);
+        if(default_logger_ && default_logger_->name() == logger_name)
+        {
+            default_logger_.reset();
+        }
     }
-}
 
-SPDLOG_INLINE void registry::drop(const std::string &logger_name)
-{
-    std::lock_guard<std::mutex> lock(logger_map_mutex_);
-    loggers_.erase(logger_name);
-    if (default_logger_ && default_logger_->name() == logger_name)
+    SPDLOG_INLINE void registry::drop_all()
     {
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        loggers_.clear();
         default_logger_.reset();
     }
-}
 
-SPDLOG_INLINE void registry::drop_all()
-{
-    std::lock_guard<std::mutex> lock(logger_map_mutex_);
-    loggers_.clear();
-    default_logger_.reset();
-}
-
-// clean all resources and threads started by the registry
-SPDLOG_INLINE void registry::shutdown()
-{
+    // clean all resources and threads started by the registry
+    SPDLOG_INLINE void registry::shutdown()
     {
-        std::lock_guard<std::mutex> lock(flusher_mutex_);
-        periodic_flusher_.reset();
+        {
+            std::lock_guard<std::mutex> lock(flusher_mutex_);
+            periodic_flusher_.reset();
+        }
+
+        drop_all();
+
+        {
+            std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
+            tp_.reset();
+        }
     }
 
-    drop_all();
+    SPDLOG_INLINE std::recursive_mutex &registry::tp_mutex()
+    {
+        return tp_mutex_;
+    }
 
+    SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registration)
     {
-        std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
-        tp_.reset();
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        automatic_registration_ = automatic_registration;
     }
-}
 
-SPDLOG_INLINE std::recursive_mutex &registry::tp_mutex()
-{
-    return tp_mutex_;
-}
+    SPDLOG_INLINE void registry::set_levels(log_levels levels, level::level_enum *global_level)
+    {
+        std::lock_guard<std::mutex> lock(logger_map_mutex_);
+        log_levels_                 = std::move(levels);
+        auto global_level_requested = global_level != nullptr;
+        global_log_level_           = global_level_requested ? *global_level : global_log_level_;
 
-SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registration)
-{
-    std::lock_guard<std::mutex> lock(logger_map_mutex_);
-    automatic_registration_ = automatic_registration;
-}
+        for(auto &logger : loggers_)
+        {
+            auto logger_entry = log_levels_.find(logger.first);
+            if(logger_entry != log_levels_.end())
+            {
+                logger.second->set_level(logger_entry->second);
+            }
+            else if(global_level_requested)
+            {
+                logger.second->set_level(*global_level);
+            }
+        }
+    }
 
-SPDLOG_INLINE void registry::set_levels(log_levels levels, level::level_enum *global_level)
-{
-    std::lock_guard<std::mutex> lock(logger_map_mutex_);
-    log_levels_ = std::move(levels);
-    auto global_level_requested = global_level != nullptr;
-    global_log_level_ = global_level_requested ? *global_level : global_log_level_;
+    SPDLOG_INLINE registry &registry::instance()
+    {
+        static registry s_instance;
+        return s_instance;
+    }
 
-    for (auto &logger : loggers_)
+    SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name)
     {
-        auto logger_entry = log_levels_.find(logger.first);
-        if (logger_entry != log_levels_.end())
-        {
-            logger.second->set_level(logger_entry->second);
-        }
-        else if (global_level_requested)
+        if(loggers_.find(logger_name) != loggers_.end())
         {
-            logger.second->set_level(*global_level);
+            throw_spdlog_ex("logger with name '" + logger_name + "' already exists");
         }
     }
-}
-
-SPDLOG_INLINE registry &registry::instance()
-{
-    static registry s_instance;
-    return s_instance;
-}
 
-SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name)
-{
-    if (loggers_.find(logger_name) != loggers_.end())
+    SPDLOG_INLINE void registry::register_logger_(std::shared_ptr<logger> new_logger)
     {
-        throw_spdlog_ex("logger with name '" + logger_name + "' already exists");
+        auto logger_name = new_logger->name();
+        throw_if_exists_(logger_name);
+        loggers_[logger_name] = std::move(new_logger);
     }
-}
-
-SPDLOG_INLINE void registry::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);
-}
 
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/registry.h b/include/spdlog/details/registry.h
index 6a4a5abce..2c00585f7 100644
--- a/include/spdlog/details/registry.h
+++ b/include/spdlog/details/registry.h
@@ -8,114 +8,115 @@
 // If user requests a non existing logger, nullptr will be returned
 // This class is thread safe
 
-#include <spdlog/common.h>
-#include <spdlog/details/periodic_worker.h>
-
 #include <chrono>
 #include <functional>
 #include <memory>
+#include <mutex>
+#include <spdlog/common.h>
+#include <spdlog/details/periodic_worker.h>
 #include <string>
 #include <unordered_map>
-#include <mutex>
 
-namespace spdlog {
+namespace spdlog
+{
 class logger;
 
-namespace details {
-class thread_pool;
-
-class SPDLOG_API registry
+namespace details
 {
-public:
-    using log_levels = std::unordered_map<std::string, level::level_enum>;
-    registry(const registry &) = delete;
-    registry &operator=(const registry &) = delete;
+    class thread_pool;
 
-    void register_logger(std::shared_ptr<logger> new_logger);
-    void initialize_logger(std::shared_ptr<logger> new_logger);
-    std::shared_ptr<logger> get(const std::string &logger_name);
-    std::shared_ptr<logger> default_logger();
+    class SPDLOG_API registry
+    {
+    public:
+        using log_levels                      = std::unordered_map<std::string, level::level_enum>;
+        registry(const registry &)            = delete;
+        registry &operator=(const registry &) = delete;
 
-    // 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();
+        void                    register_logger(std::shared_ptr<logger> new_logger);
+        void                    initialize_logger(std::shared_ptr<logger> new_logger);
+        std::shared_ptr<logger> get(const std::string &logger_name);
+        std::shared_ptr<logger> default_logger();
 
-    // 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);
+        // 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();
 
-    void set_tp(std::shared_ptr<thread_pool> tp);
+        // 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::shared_ptr<thread_pool> get_tp();
+        void set_tp(std::shared_ptr<thread_pool> tp);
 
-    // Set global formatter. Each sink in each logger will get a clone of this object
-    void set_formatter(std::unique_ptr<formatter> formatter);
+        std::shared_ptr<thread_pool> get_tp();
 
-    void enable_backtrace(size_t n_messages);
+        // Set global formatter. Each sink in each logger will get a clone of this object
+        void set_formatter(std::unique_ptr<formatter> formatter);
 
-    void disable_backtrace();
+        void enable_backtrace(size_t n_messages);
 
-    void set_level(level::level_enum log_level);
+        void disable_backtrace();
 
-    void flush_on(level::level_enum log_level);
+        void set_level(level::level_enum log_level);
 
-    template<typename Rep, typename Period>
-    void flush_every(std::chrono::duration<Rep, Period> interval)
-    {
-        std::lock_guard<std::mutex> lock(flusher_mutex_);
-        auto clbk = [this]() { this->flush_all(); };
-        periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
-    }
+        void flush_on(level::level_enum log_level);
+
+        template <typename Rep, typename Period>
+        void flush_every(std::chrono::duration<Rep, Period> interval)
+        {
+            std::lock_guard<std::mutex> lock(flusher_mutex_);
+            auto                        clbk = [this]() { this->flush_all(); };
+            periodic_flusher_                = details::make_unique<periodic_worker>(clbk, interval);
+        }
 
-    void set_error_handler(err_handler handler);
+        void set_error_handler(err_handler handler);
 
-    void apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun);
+        void apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun);
 
-    void flush_all();
+        void flush_all();
 
-    void drop(const std::string &logger_name);
+        void drop(const std::string &logger_name);
 
-    void drop_all();
+        void drop_all();
 
-    // clean all resources and threads started by the registry
-    void shutdown();
+        // clean all resources and threads started by the registry
+        void shutdown();
 
-    std::recursive_mutex &tp_mutex();
+        std::recursive_mutex &tp_mutex();
 
-    void set_automatic_registration(bool automatic_registration);
+        void set_automatic_registration(bool automatic_registration);
 
-    // set levels for all existing/future loggers. global_level can be null if should not set.
-    void set_levels(log_levels levels, level::level_enum *global_level);
+        // set levels for all existing/future loggers. global_level can be null if should not set.
+        void set_levels(log_levels levels, level::level_enum *global_level);
 
-    static registry &instance();
+        static registry &instance();
 
-private:
-    registry();
-    ~registry();
+    private:
+        registry();
+        ~registry();
 
-    void throw_if_exists_(const std::string &logger_name);
-    void register_logger_(std::shared_ptr<logger> new_logger);
-    bool set_level_from_cfg_(logger *logger);
-    std::mutex logger_map_mutex_, flusher_mutex_;
-    std::recursive_mutex tp_mutex_;
-    std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
-    log_levels log_levels_;
-    std::unique_ptr<formatter> formatter_;
-    spdlog::level::level_enum global_log_level_ = level::info;
-    level::level_enum flush_level_ = level::off;
-    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;
-    size_t backtrace_n_messages_ = 0;
-};
+        void                                                     throw_if_exists_(const std::string &logger_name);
+        void                                                     register_logger_(std::shared_ptr<logger> new_logger);
+        bool                                                     set_level_from_cfg_(logger *logger);
+        std::mutex                                               logger_map_mutex_, flusher_mutex_;
+        std::recursive_mutex                                     tp_mutex_;
+        std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
+        log_levels                                               log_levels_;
+        std::unique_ptr<formatter>                               formatter_;
+        spdlog::level::level_enum                                global_log_level_ = level::info;
+        level::level_enum                                        flush_level_      = level::off;
+        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;
+        size_t                                                   backtrace_n_messages_   = 0;
+    };
 
 } // namespace details
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "registry-inl.h"
+#include "registry-inl.h"
 #endif
diff --git a/include/spdlog/details/synchronous_factory.h b/include/spdlog/details/synchronous_factory.h
index 1f729ab93..732300c45 100644
--- a/include/spdlog/details/synchronous_factory.h
+++ b/include/spdlog/details/synchronous_factory.h
@@ -5,17 +5,18 @@
 
 #include "registry.h"
 
-namespace spdlog {
+namespace spdlog
+{
 
 // Default logger factory-  creates synchronous loggers
 class logger;
 
 struct synchronous_factory
 {
-    template<typename Sink, typename... SinkArgs>
-    static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args)
+    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 sink       = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
         auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink));
         details::registry::instance().initialize_logger(new_logger);
         return new_logger;
diff --git a/include/spdlog/details/tcp_client-windows.h b/include/spdlog/details/tcp_client-windows.h
index 9d3647aaa..d651df5e7 100644
--- a/include/spdlog/details/tcp_client-windows.h
+++ b/include/spdlog/details/tcp_client-windows.h
@@ -7,154 +7,154 @@
 // tcp client helper
 #include <spdlog/common.h>
 #include <spdlog/details/os.h>
-
-#include <winsock2.h>
-#include <windows.h>
-#include <ws2tcpip.h>
-#include <stdlib.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string>
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
 
 #pragma comment(lib, "Ws2_32.lib")
 #pragma comment(lib, "Mswsock.lib")
 #pragma comment(lib, "AdvApi32.lib")
 
-namespace spdlog {
-namespace details {
-class tcp_client
+namespace spdlog
 {
-    SOCKET socket_ = INVALID_SOCKET;
-
-    static void init_winsock_()
+namespace details
+{
+    class tcp_client
     {
-        WSADATA wsaData;
-        auto rv = WSAStartup(MAKEWORD(2, 2), &wsaData);
-        if (rv != 0)
+        SOCKET socket_ = INVALID_SOCKET;
+
+        static void init_winsock_()
         {
-            throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
+            WSADATA wsaData;
+            auto    rv = WSAStartup(MAKEWORD(2, 2), &wsaData);
+            if(rv != 0)
+            {
+                throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
+            }
         }
-    }
-
-    static void throw_winsock_error_(const std::string &msg, int last_error)
-    {
-        char buf[512];
-        ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error,
-            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL);
-
-        throw_spdlog_ex(fmt_lib::format("tcp_sink - {}: {}", msg, buf));
-    }
-
-public:
-    tcp_client()
-    {
-        init_winsock_();
-    }
-
-    ~tcp_client()
-    {
-        close();
-        ::WSACleanup();
-    }
 
-    bool is_connected() const
-    {
-        return socket_ != INVALID_SOCKET;
-    }
-
-    void close()
-    {
-        ::closesocket(socket_);
-        socket_ = INVALID_SOCKET;
-    }
+        static void throw_winsock_error_(const std::string &msg, int last_error)
+        {
+            char buf[512];
+            ::FormatMessageA(
+                FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                NULL,
+                last_error,
+                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                buf,
+                (sizeof(buf) / sizeof(char)),
+                NULL);
+
+            throw_spdlog_ex(fmt_lib::format("tcp_sink - {}: {}", msg, buf));
+        }
 
-    SOCKET fd() const
-    {
-        return socket_;
-    }
+    public:
+        tcp_client() { init_winsock_(); }
 
-    // try to connect or throw on failure
-    void connect(const std::string &host, int port)
-    {
-        if (is_connected())
+        ~tcp_client()
         {
             close();
+            ::WSACleanup();
         }
-        struct addrinfo hints
-        {};
-        ZeroMemory(&hints, sizeof(hints));
-
-        hints.ai_family = AF_INET;       // IPv4
-        hints.ai_socktype = SOCK_STREAM; // TCP
-        hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
-        hints.ai_protocol = 0;
-
-        auto port_str = std::to_string(port);
-        struct addrinfo *addrinfo_result;
-        auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
-        int last_error = 0;
-        if (rv != 0)
+
+        bool is_connected() const { return socket_ != INVALID_SOCKET; }
+
+        void close()
         {
-            last_error = ::WSAGetLastError();
-            WSACleanup();
-            throw_winsock_error_("getaddrinfo failed", last_error);
+            ::closesocket(socket_);
+            socket_ = INVALID_SOCKET;
         }
 
-        // Try each address until we successfully connect(2).
+        SOCKET fd() const { return socket_; }
 
-        for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
+        // try to connect or throw on failure
+        void connect(const std::string &host, int port)
         {
-            socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
-            if (socket_ == INVALID_SOCKET)
+            if(is_connected())
             {
-                last_error = ::WSAGetLastError();
-                WSACleanup();
-                continue;
+                close();
             }
-            if (::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen) == 0)
+            struct addrinfo hints
             {
-                break;
-            }
-            else
+            };
+            ZeroMemory(&hints, sizeof(hints));
+
+            hints.ai_family   = AF_INET;        // IPv4
+            hints.ai_socktype = SOCK_STREAM;    // TCP
+            hints.ai_flags    = AI_NUMERICSERV; // port passed as as numeric value
+            hints.ai_protocol = 0;
+
+            auto             port_str = std::to_string(port);
+            struct addrinfo *addrinfo_result;
+            auto             rv         = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
+            int              last_error = 0;
+            if(rv != 0)
             {
                 last_error = ::WSAGetLastError();
-                close();
+                WSACleanup();
+                throw_winsock_error_("getaddrinfo failed", last_error);
             }
-        }
-        ::freeaddrinfo(addrinfo_result);
-        if (socket_ == INVALID_SOCKET)
-        {
-            WSACleanup();
-            throw_winsock_error_("connect failed", last_error);
-        }
 
-        // set TCP_NODELAY
-        int enable_flag = 1;
-        ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
-    }
+            // Try each address until we successfully connect(2).
 
-    // Send exactly n_bytes of the given data.
-    // On error close the connection and throw.
-    void send(const char *data, size_t n_bytes)
-    {
-        size_t bytes_sent = 0;
-        while (bytes_sent < n_bytes)
-        {
-            const int send_flags = 0;
-            auto write_result = ::send(socket_, data + bytes_sent, (int)(n_bytes - bytes_sent), send_flags);
-            if (write_result == SOCKET_ERROR)
+            for(auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
             {
-                int last_error = ::WSAGetLastError();
-                close();
-                throw_winsock_error_("send failed", last_error);
+                socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+                if(socket_ == INVALID_SOCKET)
+                {
+                    last_error = ::WSAGetLastError();
+                    WSACleanup();
+                    continue;
+                }
+                if(::connect(socket_, rp->ai_addr, (int) rp->ai_addrlen) == 0)
+                {
+                    break;
+                }
+                else
+                {
+                    last_error = ::WSAGetLastError();
+                    close();
+                }
+            }
+            ::freeaddrinfo(addrinfo_result);
+            if(socket_ == INVALID_SOCKET)
+            {
+                WSACleanup();
+                throw_winsock_error_("connect failed", last_error);
             }
 
-            if (write_result == 0) // (probably should not happen but in any case..)
+            // set TCP_NODELAY
+            int enable_flag = 1;
+            ::setsockopt(
+                socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
+        }
+
+        // Send exactly n_bytes of the given data.
+        // On error close the connection and throw.
+        void send(const char *data, size_t n_bytes)
+        {
+            size_t bytes_sent = 0;
+            while(bytes_sent < n_bytes)
             {
-                break;
+                const int send_flags   = 0;
+                auto      write_result = ::send(socket_, data + bytes_sent, (int) (n_bytes - bytes_sent), send_flags);
+                if(write_result == SOCKET_ERROR)
+                {
+                    int last_error = ::WSAGetLastError();
+                    close();
+                    throw_winsock_error_("send failed", last_error);
+                }
+
+                if(write_result == 0) // (probably should not happen but in any case..)
+                {
+                    break;
+                }
+                bytes_sent += static_cast<size_t>(write_result);
             }
-            bytes_sent += static_cast<size_t>(write_result);
         }
-    }
-};
+    };
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/tcp_client.h b/include/spdlog/details/tcp_client.h
index 0daff0eb4..d7ae7ffdc 100644
--- a/include/spdlog/details/tcp_client.h
+++ b/include/spdlog/details/tcp_client.h
@@ -4,142 +4,136 @@
 #pragma once
 
 #ifdef _WIN32
-#    error include tcp_client-windows.h instead
+#error include tcp_client-windows.h instead
 #endif
 
 // tcp client helper
-#include <spdlog/common.h>
-#include <spdlog/details/os.h>
-
-#include <sys/socket.h>
 #include <arpa/inet.h>
-#include <unistd.h>
 #include <netdb.h>
 #include <netinet/tcp.h>
-
+#include <spdlog/common.h>
+#include <spdlog/details/os.h>
 #include <string>
+#include <sys/socket.h>
+#include <unistd.h>
 
-namespace spdlog {
-namespace details {
-class tcp_client
+namespace spdlog
 {
-    int socket_ = -1;
-
-public:
-    bool is_connected() const
+namespace details
+{
+    class tcp_client
     {
-        return socket_ != -1;
-    }
+        int socket_ = -1;
 
-    void close()
-    {
-        if (is_connected())
+    public:
+        bool is_connected() const { return socket_ != -1; }
+
+        void close()
         {
-            ::close(socket_);
-            socket_ = -1;
+            if(is_connected())
+            {
+                ::close(socket_);
+                socket_ = -1;
+            }
         }
-    }
 
-    int fd() const
-    {
-        return socket_;
-    }
+        int fd() const { return socket_; }
 
-    ~tcp_client()
-    {
-        close();
-    }
+        ~tcp_client() { close(); }
 
-    // try to connect or throw on failure
-    void connect(const std::string &host, int port)
-    {
-        close();
-        struct addrinfo hints
-        {};
-        memset(&hints, 0, sizeof(struct addrinfo));
-        hints.ai_family = AF_INET;       // IPv4
-        hints.ai_socktype = SOCK_STREAM; // TCP
-        hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
-        hints.ai_protocol = 0;
-
-        auto port_str = std::to_string(port);
-        struct addrinfo *addrinfo_result;
-        auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
-        if (rv != 0)
+        // try to connect or throw on failure
+        void connect(const std::string &host, int port)
         {
-            throw_spdlog_ex(fmt_lib::format("::getaddrinfo failed: {}", gai_strerror(rv)));
-        }
+            close();
+            struct addrinfo hints
+            {
+            };
+            memset(&hints, 0, sizeof(struct addrinfo));
+            hints.ai_family   = AF_INET;        // IPv4
+            hints.ai_socktype = SOCK_STREAM;    // TCP
+            hints.ai_flags    = AI_NUMERICSERV; // port passed as as numeric value
+            hints.ai_protocol = 0;
+
+            auto             port_str = std::to_string(port);
+            struct addrinfo *addrinfo_result;
+            auto             rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
+            if(rv != 0)
+            {
+                throw_spdlog_ex(fmt_lib::format("::getaddrinfo failed: {}", gai_strerror(rv)));
+            }
 
-        // Try each address until we successfully connect(2).
-        int last_errno = 0;
-        for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
-        {
+            // Try each address until we successfully connect(2).
+            int last_errno = 0;
+            for(auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
+            {
 #if defined(SOCK_CLOEXEC)
-            const int flags = SOCK_CLOEXEC;
+                const int flags = SOCK_CLOEXEC;
 #else
-            const int flags = 0;
+                const int flags      = 0;
 #endif
-            socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
-            if (socket_ == -1)
-            {
+                socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
+                if(socket_ == -1)
+                {
+                    last_errno = errno;
+                    continue;
+                }
+                rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
+                if(rv == 0)
+                {
+                    break;
+                }
                 last_errno = errno;
-                continue;
+                ::close(socket_);
+                socket_ = -1;
             }
-            rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
-            if (rv == 0)
+            ::freeaddrinfo(addrinfo_result);
+            if(socket_ == -1)
             {
-                break;
+                throw_spdlog_ex("::connect failed", last_errno);
             }
-            last_errno = errno;
-            ::close(socket_);
-            socket_ = -1;
-        }
-        ::freeaddrinfo(addrinfo_result);
-        if (socket_ == -1)
-        {
-            throw_spdlog_ex("::connect failed", last_errno);
-        }
 
-        // set TCP_NODELAY
-        int enable_flag = 1;
-        ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
+            // set TCP_NODELAY
+            int enable_flag = 1;
+            ::setsockopt(
+                socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
 
-        // prevent sigpipe on systems where MSG_NOSIGNAL is not available
+            // prevent sigpipe on systems where MSG_NOSIGNAL is not available
 #if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
-        ::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
+            ::setsockopt(
+                socket_, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
 #endif
 
 #if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
-#    error "tcp_sink would raise SIGPIPE since niether SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
+#error "tcp_sink would raise SIGPIPE since niether SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
 #endif
-    }
+        }
 
-    // Send exactly n_bytes of the given data.
-    // On error close the connection and throw.
-    void send(const char *data, size_t n_bytes)
-    {
-        size_t bytes_sent = 0;
-        while (bytes_sent < n_bytes)
+        // Send exactly n_bytes of the given data.
+        // On error close the connection and throw.
+        void send(const char *data, size_t n_bytes)
         {
+            size_t bytes_sent = 0;
+            while(bytes_sent < n_bytes)
+            {
 #if defined(MSG_NOSIGNAL)
-            const int send_flags = MSG_NOSIGNAL;
+                const int send_flags = MSG_NOSIGNAL;
 #else
-            const int send_flags = 0;
+                const int send_flags = 0;
 #endif
-            auto write_result = ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
-            if (write_result < 0)
-            {
-                close();
-                throw_spdlog_ex("write(2) failed", errno);
-            }
-
-            if (write_result == 0) // (probably should not happen but in any case..)
-            {
-                break;
+                auto write_result = ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
+                if(write_result < 0)
+                {
+                    close();
+                    throw_spdlog_ex("write(2) failed", errno);
+                }
+
+                if(write_result == 0) // (probably should not happen but in any case..)
+                {
+                    break;
+                }
+                bytes_sent += static_cast<size_t>(write_result);
             }
-            bytes_sent += static_cast<size_t>(write_result);
         }
-    }
-};
+    };
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/thread_pool-inl.h b/include/spdlog/details/thread_pool-inl.h
index 369f30fe0..cb944d483 100644
--- a/include/spdlog/details/thread_pool-inl.h
+++ b/include/spdlog/details/thread_pool-inl.h
@@ -4,138 +4,160 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/details/thread_pool.h>
+#include <spdlog/details/thread_pool.h>
 #endif
 
-#include <spdlog/common.h>
 #include <cassert>
+#include <spdlog/common.h>
 
-namespace spdlog {
-namespace details {
-
-SPDLOG_INLINE thread_pool::thread_pool(
-    size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start, std::function<void()> on_thread_stop)
-    : q_(q_max_items)
+namespace spdlog
 {
-    if (threads_n == 0 || threads_n > 1000)
-    {
-        throw_spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid "
-                        "range is 1-1000)");
-    }
-    for (size_t i = 0; i < threads_n; i++)
-    {
-        threads_.emplace_back([this, on_thread_start, on_thread_stop] {
-            on_thread_start();
-            this->thread_pool::worker_loop_();
-            on_thread_stop();
-        });
-    }
-}
-
-SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start)
-    : thread_pool(q_max_items, threads_n, on_thread_start, [] {})
-{}
-
-SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n)
-    : thread_pool(
-          q_max_items, threads_n, [] {}, [] {})
-{}
-
-// message all threads to terminate gracefully join them
-SPDLOG_INLINE thread_pool::~thread_pool()
+namespace details
 {
-    SPDLOG_TRY
+
+    SPDLOG_INLINE thread_pool::thread_pool(
+        size_t                q_max_items,
+        size_t                threads_n,
+        std::function<void()> on_thread_start,
+        std::function<void()> on_thread_stop) :
+        q_(q_max_items)
     {
-        for (size_t i = 0; i < threads_.size(); i++)
+        if(threads_n == 0 || threads_n > 1000)
         {
-            post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
+            throw_spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid "
+                            "range is 1-1000)");
         }
-
-        for (auto &t : threads_)
+        for(size_t i = 0; i < threads_n; i++)
         {
-            t.join();
+            threads_.emplace_back(
+                [this, on_thread_start, on_thread_stop]
+                {
+                    on_thread_start();
+                    this->thread_pool::worker_loop_();
+                    on_thread_stop();
+                });
         }
     }
-    SPDLOG_CATCH_STD
-}
-
-void SPDLOG_INLINE thread_pool::post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy)
-{
-    async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
-    post_async_msg_(std::move(async_m), overflow_policy);
-}
 
-void SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy)
-{
-    post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
-}
-
-size_t SPDLOG_INLINE thread_pool::overrun_counter()
-{
-    return q_.overrun_counter();
-}
-
-void SPDLOG_INLINE thread_pool::reset_overrun_counter()
-{
-    q_.reset_overrun_counter();
-}
-
-size_t SPDLOG_INLINE thread_pool::queue_size()
-{
-    return q_.size();
-}
+    SPDLOG_INLINE
+    thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start) :
+        thread_pool(q_max_items, threads_n, on_thread_start, [] {})
+    {
+    }
 
-void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
-{
-    if (overflow_policy == async_overflow_policy::block)
+    SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n) :
+        thread_pool(
+            q_max_items,
+            threads_n,
+            [] {},
+            [] {})
     {
-        q_.enqueue(std::move(new_msg));
     }
-    else
+
+    // message all threads to terminate gracefully join them
+    SPDLOG_INLINE thread_pool::~thread_pool()
     {
-        q_.enqueue_nowait(std::move(new_msg));
+        SPDLOG_TRY
+        {
+            for(size_t i = 0; i < threads_.size(); i++)
+            {
+                post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
+            }
+
+            for(auto &t : threads_)
+            {
+                t.join();
+            }
+        }
+        SPDLOG_CATCH_STD
     }
-}
 
-void SPDLOG_INLINE thread_pool::worker_loop_()
-{
-    while (process_next_msg_()) {}
-}
+    void SPDLOG_INLINE thread_pool::post_log(
+        async_logger_ptr      &&worker_ptr,
+        const details::log_msg &msg,
+        async_overflow_policy   overflow_policy)
+    {
+        async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
+        post_async_msg_(std::move(async_m), overflow_policy);
+    }
 
-// process next message in the queue
-// return true if this thread should still be active (while no terminate msg
-// was received)
-bool SPDLOG_INLINE thread_pool::process_next_msg_()
-{
-    async_msg incoming_async_msg;
-    bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10));
-    if (!dequeued)
+    void SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy)
     {
-        return true;
+        post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
     }
 
-    switch (incoming_async_msg.msg_type)
+    size_t SPDLOG_INLINE thread_pool::overrun_counter()
     {
-    case async_msg_type::log: {
-        incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg);
-        return true;
+        return q_.overrun_counter();
     }
-    case async_msg_type::flush: {
-        incoming_async_msg.worker_ptr->backend_flush_();
-        return true;
+
+    void SPDLOG_INLINE thread_pool::reset_overrun_counter()
+    {
+        q_.reset_overrun_counter();
     }
 
-    case async_msg_type::terminate: {
-        return false;
+    size_t SPDLOG_INLINE thread_pool::queue_size()
+    {
+        return q_.size();
     }
 
-    default: {
-        assert(false);
+    void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
+    {
+        if(overflow_policy == async_overflow_policy::block)
+        {
+            q_.enqueue(std::move(new_msg));
+        }
+        else
+        {
+            q_.enqueue_nowait(std::move(new_msg));
+        }
     }
+
+    void SPDLOG_INLINE thread_pool::worker_loop_()
+    {
+        while(process_next_msg_())
+        {
+        }
     }
 
-    return true;
-}
+    // process next message in the queue
+    // return true if this thread should still be active (while no terminate msg
+    // was received)
+    bool SPDLOG_INLINE thread_pool::process_next_msg_()
+    {
+        async_msg incoming_async_msg;
+        bool      dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10));
+        if(!dequeued)
+        {
+            return true;
+        }
+
+        switch(incoming_async_msg.msg_type)
+        {
+            case async_msg_type::log:
+            {
+                incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg);
+                return true;
+            }
+            case async_msg_type::flush:
+            {
+                incoming_async_msg.worker_ptr->backend_flush_();
+                return true;
+            }
+
+            case async_msg_type::terminate:
+            {
+                return false;
+            }
+
+            default:
+            {
+                assert(false);
+            }
+        }
+
+        return true;
+    }
 
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/thread_pool.h b/include/spdlog/details/thread_pool.h
index 52c569b80..280c72d0c 100644
--- a/include/spdlog/details/thread_pool.h
+++ b/include/spdlog/details/thread_pool.h
@@ -3,120 +3,121 @@
 
 #pragma once
 
+#include <chrono>
+#include <functional>
+#include <memory>
 #include <spdlog/details/log_msg_buffer.h>
 #include <spdlog/details/mpmc_blocking_q.h>
 #include <spdlog/details/os.h>
-
-#include <chrono>
-#include <memory>
 #include <thread>
 #include <vector>
-#include <functional>
 
-namespace spdlog {
+namespace spdlog
+{
 class async_logger;
 
-namespace details {
+namespace details
+{
 
-using async_logger_ptr = std::shared_ptr<spdlog::async_logger>;
+    using async_logger_ptr = std::shared_ptr<spdlog::async_logger>;
 
-enum class async_msg_type
-{
-    log,
-    flush,
-    terminate
-};
-
-// Async msg to move to/from the queue
-// Movable only. should never be copied
-struct async_msg : log_msg_buffer
-{
-    async_msg_type msg_type{async_msg_type::log};
-    async_logger_ptr worker_ptr;
+    enum class async_msg_type
+    {
+        log,
+        flush,
+        terminate
+    };
+
+    // Async msg to move to/from the queue
+    // Movable only. should never be copied
+    struct async_msg : log_msg_buffer
+    {
+        async_msg_type   msg_type{async_msg_type::log};
+        async_logger_ptr worker_ptr;
 
-    async_msg() = default;
-    ~async_msg() = default;
+        async_msg()  = default;
+        ~async_msg() = default;
 
-    // should only be moved in or out of the queue..
-    async_msg(const async_msg &) = delete;
+        // should only be moved in or out of the queue..
+        async_msg(const async_msg &) = delete;
 
 // support for vs2013 move
 #if defined(_MSC_VER) && _MSC_VER <= 1800
-    async_msg(async_msg &&other)
-        : log_msg_buffer(std::move(other))
-        , msg_type(other.msg_type)
-        , worker_ptr(std::move(other.worker_ptr))
-    {}
-
-    async_msg &operator=(async_msg &&other)
-    {
-        *static_cast<log_msg_buffer *>(this) = std::move(other);
-        msg_type = other.msg_type;
-        worker_ptr = std::move(other.worker_ptr);
-        return *this;
-    }
+        async_msg(async_msg &&other) :
+            log_msg_buffer(std::move(other)), msg_type(other.msg_type), worker_ptr(std::move(other.worker_ptr))
+        {
+        }
+
+        async_msg &operator=(async_msg &&other)
+        {
+            *static_cast<log_msg_buffer *>(this) = std::move(other);
+            msg_type                             = other.msg_type;
+            worker_ptr                           = std::move(other.worker_ptr);
+            return *this;
+        }
 #else // (_MSC_VER) && _MSC_VER <= 1800
-    async_msg(async_msg &&) = default;
-    async_msg &operator=(async_msg &&) = default;
+        async_msg(async_msg &&)            = default;
+        async_msg &operator=(async_msg &&) = default;
 #endif
 
-    // construct from log_msg with given type
-    async_msg(async_logger_ptr &&worker, async_msg_type the_type, const details::log_msg &m)
-        : log_msg_buffer{m}
-        , msg_type{the_type}
-        , worker_ptr{std::move(worker)}
-    {}
-
-    async_msg(async_logger_ptr &&worker, async_msg_type the_type)
-        : log_msg_buffer{}
-        , msg_type{the_type}
-        , worker_ptr{std::move(worker)}
-    {}
-
-    explicit async_msg(async_msg_type the_type)
-        : async_msg{nullptr, the_type}
-    {}
-};
-
-class SPDLOG_API thread_pool
-{
-public:
-    using item_type = async_msg;
-    using q_type = details::mpmc_blocking_queue<item_type>;
+        // construct from log_msg with given type
+        async_msg(async_logger_ptr &&worker, async_msg_type the_type, const details::log_msg &m) :
+            log_msg_buffer{m}, msg_type{the_type}, worker_ptr{std::move(worker)}
+        {
+        }
+
+        async_msg(async_logger_ptr &&worker, async_msg_type the_type) :
+            log_msg_buffer{}, msg_type{the_type}, worker_ptr{std::move(worker)}
+        {
+        }
+
+        explicit async_msg(async_msg_type the_type) : async_msg{nullptr, the_type} {}
+    };
+
+    class SPDLOG_API thread_pool
+    {
+    public:
+        using item_type = async_msg;
+        using q_type    = details::mpmc_blocking_queue<item_type>;
 
-    thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start, std::function<void()> on_thread_stop);
-    thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start);
-    thread_pool(size_t q_max_items, size_t threads_n);
+        thread_pool(
+            size_t                q_max_items,
+            size_t                threads_n,
+            std::function<void()> on_thread_start,
+            std::function<void()> on_thread_stop);
+        thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start);
+        thread_pool(size_t q_max_items, size_t threads_n);
 
-    // message all threads to terminate gracefully and join them
-    ~thread_pool();
+        // message all threads to terminate gracefully and join them
+        ~thread_pool();
 
-    thread_pool(const thread_pool &) = delete;
-    thread_pool &operator=(thread_pool &&) = delete;
+        thread_pool(const thread_pool &)       = delete;
+        thread_pool &operator=(thread_pool &&) = delete;
 
-    void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
-    void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
-    size_t overrun_counter();
-    void reset_overrun_counter();
-    size_t queue_size();
+        void
+        post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
+        void   post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
+        size_t overrun_counter();
+        void   reset_overrun_counter();
+        size_t queue_size();
 
-private:
-    q_type q_;
+    private:
+        q_type q_;
 
-    std::vector<std::thread> threads_;
+        std::vector<std::thread> threads_;
 
-    void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy);
-    void worker_loop_();
+        void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy);
+        void worker_loop_();
 
-    // process next message in the queue
-    // return true if this thread should still be active (while no terminate msg
-    // was received)
-    bool process_next_msg_();
-};
+        // process next message in the queue
+        // return true if this thread should still be active (while no terminate msg
+        // was received)
+        bool process_next_msg_();
+    };
 
 } // namespace details
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "thread_pool-inl.h"
+#include "thread_pool-inl.h"
 #endif
diff --git a/include/spdlog/details/udp_client-windows.h b/include/spdlog/details/udp_client-windows.h
index 8e763356d..00dd37c6b 100644
--- a/include/spdlog/details/udp_client-windows.h
+++ b/include/spdlog/details/udp_client-windows.h
@@ -9,103 +9,110 @@
 #include <spdlog/common.h>
 #include <spdlog/details/os.h>
 #include <spdlog/details/windows_include.h>
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#include <stdlib.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string>
+#include <winsock2.h>
+#include <ws2tcpip.h>
 
 #pragma comment(lib, "Ws2_32.lib")
 #pragma comment(lib, "Mswsock.lib")
 #pragma comment(lib, "AdvApi32.lib")
 
-namespace spdlog {
-namespace details {
-class udp_client
+namespace spdlog
 {
-    static constexpr int TX_BUFFER_SIZE = 1024 * 10;
-    SOCKET socket_ = INVALID_SOCKET;
-    sockaddr_in addr_ = {0};
-
-    static void init_winsock_()
-    {
-        WSADATA wsaData;
-        auto rv = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
-        if (rv != 0)
-        {
-            throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
-        }
-    }
-
-    static void throw_winsock_error_(const std::string &msg, int last_error)
+namespace details
+{
+    class udp_client
     {
-        char buf[512];
-        ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error,
-            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL);
-
-        throw_spdlog_ex(fmt_lib::format("udp_sink - {}: {}", msg, buf));
-    }
+        static constexpr int TX_BUFFER_SIZE = 1024 * 10;
+        SOCKET               socket_        = INVALID_SOCKET;
+        sockaddr_in          addr_          = {0};
 
-    void cleanup_()
-    {
-        if (socket_ != INVALID_SOCKET)
+        static void init_winsock_()
         {
-            ::closesocket(socket_);
+            WSADATA wsaData;
+            auto    rv = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
+            if(rv != 0)
+            {
+                throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
+            }
         }
-        socket_ = INVALID_SOCKET;
-        ::WSACleanup();
-    }
-
-public:
-    udp_client(const std::string &host, uint16_t port)
-    {
-        init_winsock_();
 
-        addr_.sin_family = PF_INET;
-        addr_.sin_port = htons(port);
-        addr_.sin_addr.s_addr = INADDR_ANY;
-        if (InetPtonA(PF_INET, host.c_str(), &addr_.sin_addr.s_addr) != 1)
+        static void throw_winsock_error_(const std::string &msg, int last_error)
         {
-            int last_error = ::WSAGetLastError();
-            ::WSACleanup();
-            throw_winsock_error_("error: Invalid address!", last_error);
+            char buf[512];
+            ::FormatMessageA(
+                FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                NULL,
+                last_error,
+                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                buf,
+                (sizeof(buf) / sizeof(char)),
+                NULL);
+
+            throw_spdlog_ex(fmt_lib::format("udp_sink - {}: {}", msg, buf));
         }
 
-        socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
-        if (socket_ == INVALID_SOCKET)
+        void cleanup_()
         {
-            int last_error = ::WSAGetLastError();
+            if(socket_ != INVALID_SOCKET)
+            {
+                ::closesocket(socket_);
+            }
+            socket_ = INVALID_SOCKET;
             ::WSACleanup();
-            throw_winsock_error_("error: Create Socket failed", last_error);
         }
 
-        int option_value = TX_BUFFER_SIZE;
-        if (::setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<const char *>(&option_value), sizeof(option_value)) < 0)
+    public:
+        udp_client(const std::string &host, uint16_t port)
         {
-            int last_error = ::WSAGetLastError();
-            cleanup_();
-            throw_winsock_error_("error: setsockopt(SO_SNDBUF) Failed!", last_error);
+            init_winsock_();
+
+            addr_.sin_family      = PF_INET;
+            addr_.sin_port        = htons(port);
+            addr_.sin_addr.s_addr = INADDR_ANY;
+            if(InetPtonA(PF_INET, host.c_str(), &addr_.sin_addr.s_addr) != 1)
+            {
+                int last_error = ::WSAGetLastError();
+                ::WSACleanup();
+                throw_winsock_error_("error: Invalid address!", last_error);
+            }
+
+            socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
+            if(socket_ == INVALID_SOCKET)
+            {
+                int last_error = ::WSAGetLastError();
+                ::WSACleanup();
+                throw_winsock_error_("error: Create Socket failed", last_error);
+            }
+
+            int option_value = TX_BUFFER_SIZE;
+            if(::setsockopt(
+                   socket_,
+                   SOL_SOCKET,
+                   SO_SNDBUF,
+                   reinterpret_cast<const char *>(&option_value),
+                   sizeof(option_value)) < 0)
+            {
+                int last_error = ::WSAGetLastError();
+                cleanup_();
+                throw_winsock_error_("error: setsockopt(SO_SNDBUF) Failed!", last_error);
+            }
         }
-    }
 
-    ~udp_client()
-    {
-        cleanup_();
-    }
+        ~udp_client() { cleanup_(); }
 
-    SOCKET fd() const
-    {
-        return socket_;
-    }
+        SOCKET fd() const { return socket_; }
 
-    void send(const char *data, size_t n_bytes)
-    {
-        socklen_t tolen = sizeof(struct sockaddr);
-        if (::sendto(socket_, data, static_cast<int>(n_bytes), 0, (struct sockaddr *)&addr_, tolen) == -1)
+        void send(const char *data, size_t n_bytes)
         {
-            throw_spdlog_ex("sendto(2) failed", errno);
+            socklen_t tolen = sizeof(struct sockaddr);
+            if(::sendto(socket_, data, static_cast<int>(n_bytes), 0, (struct sockaddr *) &addr_, tolen) == -1)
+            {
+                throw_spdlog_ex("sendto(2) failed", errno);
+            }
         }
-    }
-};
+    };
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/udp_client.h b/include/spdlog/details/udp_client.h
index 2db5b89e7..eecdafe2d 100644
--- a/include/spdlog/details/udp_client.h
+++ b/include/spdlog/details/udp_client.h
@@ -7,88 +7,87 @@
 // Will throw on construction if the socket creation failed.
 
 #ifdef _WIN32
-#    error "include udp_client-windows.h instead"
+#error "include udp_client-windows.h instead"
 #endif
 
-#include <spdlog/common.h>
-#include <spdlog/details/os.h>
-
-#include <sys/socket.h>
-#include <netinet/in.h>
 #include <arpa/inet.h>
-#include <unistd.h>
 #include <netdb.h>
+#include <netinet/in.h>
 #include <netinet/udp.h>
-
+#include <spdlog/common.h>
+#include <spdlog/details/os.h>
 #include <string>
+#include <sys/socket.h>
+#include <unistd.h>
 
-namespace spdlog {
-namespace details {
-
-class udp_client
+namespace spdlog
+{
+namespace details
 {
-    static constexpr int TX_BUFFER_SIZE = 1024 * 10;
-    int socket_ = -1;
-    struct sockaddr_in sockAddr_;
-
-    void cleanup_()
-    {
-        if (socket_ != -1)
-        {
-            ::close(socket_);
-            socket_ = -1;
-        }
-    }
 
-public:
-    udp_client(const std::string &host, uint16_t port)
+    class udp_client
     {
-        socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
-        if (socket_ < 0)
-        {
-            throw_spdlog_ex("error: Create Socket Failed!");
-        }
+        static constexpr int TX_BUFFER_SIZE = 1024 * 10;
+        int                  socket_        = -1;
+        struct sockaddr_in   sockAddr_;
 
-        int option_value = TX_BUFFER_SIZE;
-        if (::setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<const char *>(&option_value), sizeof(option_value)) < 0)
+        void cleanup_()
         {
-            cleanup_();
-            throw_spdlog_ex("error: setsockopt(SO_SNDBUF) Failed!");
+            if(socket_ != -1)
+            {
+                ::close(socket_);
+                socket_ = -1;
+            }
         }
 
-        sockAddr_.sin_family = AF_INET;
-        sockAddr_.sin_port = htons(port);
-
-        if (::inet_aton(host.c_str(), &sockAddr_.sin_addr) == 0)
+    public:
+        udp_client(const std::string &host, uint16_t port)
         {
-            cleanup_();
-            throw_spdlog_ex("error: Invalid address!");
+            socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
+            if(socket_ < 0)
+            {
+                throw_spdlog_ex("error: Create Socket Failed!");
+            }
+
+            int option_value = TX_BUFFER_SIZE;
+            if(::setsockopt(
+                   socket_,
+                   SOL_SOCKET,
+                   SO_SNDBUF,
+                   reinterpret_cast<const char *>(&option_value),
+                   sizeof(option_value)) < 0)
+            {
+                cleanup_();
+                throw_spdlog_ex("error: setsockopt(SO_SNDBUF) Failed!");
+            }
+
+            sockAddr_.sin_family = AF_INET;
+            sockAddr_.sin_port   = htons(port);
+
+            if(::inet_aton(host.c_str(), &sockAddr_.sin_addr) == 0)
+            {
+                cleanup_();
+                throw_spdlog_ex("error: Invalid address!");
+            }
+
+            ::memset(sockAddr_.sin_zero, 0x00, sizeof(sockAddr_.sin_zero));
         }
 
-        ::memset(sockAddr_.sin_zero, 0x00, sizeof(sockAddr_.sin_zero));
-    }
-
-    ~udp_client()
-    {
-        cleanup_();
-    }
+        ~udp_client() { cleanup_(); }
 
-    int fd() const
-    {
-        return socket_;
-    }
+        int fd() const { return socket_; }
 
-    // Send exactly n_bytes of the given data.
-    // On error close the connection and throw.
-    void send(const char *data, size_t n_bytes)
-    {
-        ssize_t toslen = 0;
-        socklen_t tolen = sizeof(struct sockaddr);
-        if ((toslen = ::sendto(socket_, data, n_bytes, 0, (struct sockaddr *)&sockAddr_, tolen)) == -1)
+        // Send exactly n_bytes of the given data.
+        // On error close the connection and throw.
+        void send(const char *data, size_t n_bytes)
         {
-            throw_spdlog_ex("sendto(2) failed", errno);
+            ssize_t   toslen = 0;
+            socklen_t tolen  = sizeof(struct sockaddr);
+            if((toslen = ::sendto(socket_, data, n_bytes, 0, (struct sockaddr *) &sockAddr_, tolen)) == -1)
+            {
+                throw_spdlog_ex("sendto(2) failed", errno);
+            }
         }
-    }
-};
+    };
 } // namespace details
 } // namespace spdlog
diff --git a/include/spdlog/details/windows_include.h b/include/spdlog/details/windows_include.h
index a92390b9a..6a2f14f9c 100644
--- a/include/spdlog/details/windows_include.h
+++ b/include/spdlog/details/windows_include.h
@@ -1,11 +1,11 @@
 #pragma once
 
 #ifndef NOMINMAX
-#    define NOMINMAX // prevent windows redefining min/max
+#define NOMINMAX // prevent windows redefining min/max
 #endif
 
 #ifndef WIN32_LEAN_AND_MEAN
-#    define WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
 #endif
 
 #include <windows.h>
diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h
index 47fec05b9..c00099b84 100644
--- a/include/spdlog/fmt/bin_to_hex.h
+++ b/include/spdlog/fmt/bin_to_hex.h
@@ -9,13 +9,13 @@
 #include <spdlog/common.h>
 
 #if defined(__has_include)
-#    if __has_include(<version>)
-#        include <version>
-#    endif
+#if __has_include(<version>)
+#include <version>
+#endif
 #endif
 
 #if __cpp_lib_span >= 202002L
-#    include <span>
+#include <span>
 #endif
 
 //
@@ -36,42 +36,35 @@
 // logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
 // logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf), 16));
 
-namespace spdlog {
-namespace details {
-
-template<typename It>
-class dump_info
+namespace spdlog
 {
-public:
-    dump_info(It range_begin, It range_end, size_t size_per_line)
-        : begin_(range_begin)
-        , end_(range_end)
-        , size_per_line_(size_per_line)
-    {}
-
-    // do not use begin() and end() to avoid collision with fmt/ranges
-    It get_begin() const
-    {
-        return begin_;
-    }
-    It get_end() const
-    {
-        return end_;
-    }
-    size_t size_per_line() const
+namespace details
+{
+
+    template <typename It>
+    class dump_info
     {
-        return size_per_line_;
-    }
+    public:
+        dump_info(It range_begin, It range_end, size_t size_per_line) :
+            begin_(range_begin), end_(range_end), size_per_line_(size_per_line)
+        {
+        }
 
-private:
-    It begin_, end_;
-    size_t size_per_line_;
-};
+        // do not use begin() and end() to avoid collision with fmt/ranges
+        It     get_begin() const { return begin_; }
+        It     get_end() const { return end_; }
+        size_t size_per_line() const { return size_per_line_; }
+
+    private:
+        It     begin_, end_;
+        size_t size_per_line_;
+    };
 } // namespace details
 
 // create a dump_info that wraps the given container
-template<typename Container>
-inline details::dump_info<typename Container::const_iterator> to_hex(const Container &container, size_t size_per_line = 32)
+template <typename Container>
+inline details::dump_info<typename Container::const_iterator>
+to_hex(const Container &container, size_t size_per_line = 32)
 {
     static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
     using Iter = typename Container::const_iterator;
@@ -80,9 +73,9 @@ inline details::dump_info<typename Container::const_iterator> to_hex(const Conta
 
 #if __cpp_lib_span >= 202002L
 
-template<typename Value, size_t Extent>
-inline details::dump_info<typename std::span<Value, Extent>::iterator> to_hex(
-    const std::span<Value, Extent> &container, size_t size_per_line = 32)
+template <typename Value, size_t Extent>
+inline details::dump_info<typename std::span<Value, Extent>::iterator>
+to_hex(const std::span<Value, Extent> &container, size_t size_per_line = 32)
 {
     using Container = std::span<Value, Extent>;
     static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
@@ -93,7 +86,7 @@ inline details::dump_info<typename std::span<Value, Extent>::iterator> to_hex(
 #endif
 
 // create dump_info from ranges
-template<typename It>
+template <typename It>
 inline details::dump_info<It> to_hex(const It range_begin, const It range_end, size_t size_per_line = 32)
 {
     return details::dump_info<It>(range_begin, range_end, size_per_line);
@@ -108,45 +101,44 @@ namespace
     fmt
 #endif
 {
-
-template<typename T>
+template <typename T>
 struct formatter<spdlog::details::dump_info<T>, char>
 {
-    const char delimiter = ' ';
-    bool put_newlines = true;
-    bool put_delimiters = true;
-    bool use_uppercase = false;
-    bool put_positions = true; // position on start of each line
-    bool show_ascii = false;
+    const char delimiter      = ' ';
+    bool       put_newlines   = true;
+    bool       put_delimiters = true;
+    bool       use_uppercase  = false;
+    bool       put_positions  = true; // position on start of each line
+    bool       show_ascii     = false;
 
     // parse the format string flags
-    template<typename ParseContext>
+    template <typename ParseContext>
     SPDLOG_CONSTEXPR_FUNC auto parse(ParseContext &ctx) -> decltype(ctx.begin())
     {
         auto it = ctx.begin();
-        while (it != ctx.end() && *it != '}')
+        while(it != ctx.end() && *it != '}')
         {
-            switch (*it)
+            switch(*it)
             {
-            case 'X':
-                use_uppercase = true;
-                break;
-            case 's':
-                put_delimiters = false;
-                break;
-            case 'p':
-                put_positions = false;
-                break;
-            case 'n':
-                put_newlines = false;
-                show_ascii = false;
-                break;
-            case 'a':
-                if (put_newlines)
-                {
-                    show_ascii = true;
-                }
-                break;
+                case 'X':
+                    use_uppercase = true;
+                    break;
+                case 's':
+                    put_delimiters = false;
+                    break;
+                case 'p':
+                    put_positions = false;
+                    break;
+                case 'n':
+                    put_newlines = false;
+                    show_ascii   = false;
+                    break;
+                case 'a':
+                    if(put_newlines)
+                    {
+                        show_ascii = true;
+                    }
+                    break;
             }
 
             ++it;
@@ -155,12 +147,12 @@ struct formatter<spdlog::details::dump_info<T>, char>
     }
 
     // format the given bytes range as hex
-    template<typename FormatContext, typename Container>
+    template <typename FormatContext, typename Container>
     auto format(const spdlog::details::dump_info<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
     {
         SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
         SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
-        const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
+        const char                  *hex_chars = use_uppercase ? hex_upper : hex_lower;
 
 #if !defined(SPDLOG_USE_STD_FORMAT) && FMT_VERSION < 60000
         auto inserter = ctx.begin();
@@ -168,21 +160,21 @@ struct formatter<spdlog::details::dump_info<T>, char>
         auto inserter = ctx.out();
 #endif
 
-        int size_per_line = static_cast<int>(the_range.size_per_line());
+        int  size_per_line = static_cast<int>(the_range.size_per_line());
         auto start_of_line = the_range.get_begin();
-        for (auto i = the_range.get_begin(); i != the_range.get_end(); i++)
+        for(auto i = the_range.get_begin(); i != the_range.get_end(); i++)
         {
             auto ch = static_cast<unsigned char>(*i);
 
-            if (put_newlines && (i == the_range.get_begin() || i - start_of_line >= size_per_line))
+            if(put_newlines && (i == the_range.get_begin() || i - start_of_line >= size_per_line))
             {
-                if (show_ascii && i != the_range.get_begin())
+                if(show_ascii && i != the_range.get_begin())
                 {
                     *inserter++ = delimiter;
                     *inserter++ = delimiter;
-                    for (auto j = start_of_line; j < i; j++)
+                    for(auto j = start_of_line; j < i; j++)
                     {
-                        auto pc = static_cast<unsigned char>(*j);
+                        auto pc     = static_cast<unsigned char>(*j);
                         *inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
                     }
                 }
@@ -190,13 +182,13 @@ struct formatter<spdlog::details::dump_info<T>, char>
                 put_newline(inserter, static_cast<size_t>(i - the_range.get_begin()));
 
                 // put first byte without delimiter in front of it
-                *inserter++ = hex_chars[(ch >> 4) & 0x0f];
-                *inserter++ = hex_chars[ch & 0x0f];
+                *inserter++   = hex_chars[(ch >> 4) & 0x0f];
+                *inserter++   = hex_chars[ch & 0x0f];
                 start_of_line = i;
                 continue;
             }
 
-            if (put_delimiters)
+            if(put_delimiters)
             {
                 *inserter++ = delimiter;
             }
@@ -204,16 +196,16 @@ struct formatter<spdlog::details::dump_info<T>, char>
             *inserter++ = hex_chars[(ch >> 4) & 0x0f];
             *inserter++ = hex_chars[ch & 0x0f];
         }
-        if (show_ascii) // add ascii to last line
+        if(show_ascii) // add ascii to last line
         {
-            if (the_range.get_end() - the_range.get_begin() > size_per_line)
+            if(the_range.get_end() - the_range.get_begin() > size_per_line)
             {
                 auto blank_num = size_per_line - (the_range.get_end() - start_of_line);
-                while (blank_num-- > 0)
+                while(blank_num-- > 0)
                 {
                     *inserter++ = delimiter;
                     *inserter++ = delimiter;
-                    if (put_delimiters)
+                    if(put_delimiters)
                     {
                         *inserter++ = delimiter;
                     }
@@ -221,9 +213,9 @@ struct formatter<spdlog::details::dump_info<T>, char>
             }
             *inserter++ = delimiter;
             *inserter++ = delimiter;
-            for (auto j = start_of_line; j != the_range.get_end(); j++)
+            for(auto j = start_of_line; j != the_range.get_end(); j++)
             {
-                auto pc = static_cast<unsigned char>(*j);
+                auto pc     = static_cast<unsigned char>(*j);
                 *inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
             }
         }
@@ -231,7 +223,7 @@ struct formatter<spdlog::details::dump_info<T>, char>
     }
 
     // put newline(and position header)
-    template<typename It>
+    template <typename It>
     void put_newline(It inserter, std::size_t pos)
     {
 #ifdef _WIN32
@@ -239,7 +231,7 @@ struct formatter<spdlog::details::dump_info<T>, char>
 #endif
         *inserter++ = '\n';
 
-        if (put_positions)
+        if(put_positions)
         {
             spdlog::fmt_lib::format_to(inserter, SPDLOG_FMT_STRING("{:04X}: "), pos);
         }
diff --git a/include/spdlog/fmt/bundled/args.h b/include/spdlog/fmt/bundled/args.h
index a3966d140..fcbc63a09 100644
--- a/include/spdlog/fmt/bundled/args.h
+++ b/include/spdlog/fmt/bundled/args.h
@@ -8,57 +8,79 @@
 #ifndef FMT_ARGS_H_
 #define FMT_ARGS_H_
 
-#include <functional>  // std::reference_wrapper
-#include <memory>      // std::unique_ptr
-#include <vector>
-
 #include "core.h"
 
+#include <functional> // std::reference_wrapper
+#include <memory>     // std::unique_ptr
+#include <vector>
+
 FMT_BEGIN_NAMESPACE
 
-namespace detail {
+namespace detail
+{
 
-template <typename T> struct is_reference_wrapper : std::false_type {};
 template <typename T>
-struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
+struct is_reference_wrapper : std::false_type
+{
+};
+template <typename T>
+struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type
+{
+};
 
-template <typename T> const T& unwrap(const T& v) { return v; }
-template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
-  return static_cast<const T&>(v);
+template <typename T>
+const T &unwrap(const T &v)
+{
+    return v;
+}
+template <typename T>
+const T &unwrap(const std::reference_wrapper<T> &v)
+{
+    return static_cast<const T &>(v);
 }
 
-class dynamic_arg_list {
-  // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
-  // templates it doesn't complain about inability to deduce single translation
-  // unit for placing vtable. So storage_node_base is made a fake template.
-  template <typename = void> struct node {
-    virtual ~node() = default;
-    std::unique_ptr<node<>> next;
-  };
-
-  template <typename T> struct typed_node : node<> {
-    T value;
-
-    template <typename Arg>
-    FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
-
-    template <typename Char>
-    FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
-        : value(arg.data(), arg.size()) {}
-  };
-
-  std::unique_ptr<node<>> head_;
-
- public:
-  template <typename T, typename Arg> const T& push(const Arg& arg) {
-    auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
-    auto& value = new_node->value;
-    new_node->next = std::move(head_);
-    head_ = std::move(new_node);
-    return value;
-  }
+class dynamic_arg_list
+{
+    // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
+    // templates it doesn't complain about inability to deduce single translation
+    // unit for placing vtable. So storage_node_base is made a fake template.
+    template <typename = void>
+    struct node
+    {
+        virtual ~node() = default;
+        std::unique_ptr<node<>> next;
+    };
+
+    template <typename T>
+    struct typed_node : node<>
+    {
+        T value;
+
+        template <typename Arg>
+        FMT_CONSTEXPR typed_node(const Arg &arg) : value(arg)
+        {
+        }
+
+        template <typename Char>
+        FMT_CONSTEXPR typed_node(const basic_string_view<Char> &arg) : value(arg.data(), arg.size())
+        {
+        }
+    };
+
+    std::unique_ptr<node<>> head_;
+
+public:
+    template <typename T, typename Arg>
+    const T &push(const Arg &arg)
+    {
+        auto  new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
+        auto &value    = new_node->value;
+        new_node->next = std::move(head_);
+        head_          = std::move(new_node);
+        return value;
+    }
 };
-}  // namespace detail
+} // namespace detail
 
 /**
   \rst
@@ -77,158 +99,162 @@ class dynamic_format_arg_store
     : public basic_format_args<Context>
 #endif
 {
- private:
-  using char_type = typename Context::char_type;
-
-  template <typename T> struct need_copy {
-    static constexpr detail::type mapped_type =
-        detail::mapped_type_constant<T, Context>::value;
-
-    enum {
-      value = !(detail::is_reference_wrapper<T>::value ||
-                std::is_same<T, basic_string_view<char_type>>::value ||
-                std::is_same<T, detail::std_string_view<char_type>>::value ||
-                (mapped_type != detail::type::cstring_type &&
-                 mapped_type != detail::type::string_type &&
-                 mapped_type != detail::type::custom_type))
+private:
+    using char_type = typename Context::char_type;
+
+    template <typename T>
+    struct need_copy
+    {
+        static constexpr detail::type mapped_type = detail::mapped_type_constant<T, Context>::value;
+
+        enum
+        {
+            value =
+                !(detail::is_reference_wrapper<T>::value || std::is_same<T, basic_string_view<char_type>>::value ||
+                  std::is_same<T, detail::std_string_view<char_type>>::value ||
+                  (mapped_type != detail::type::cstring_type && mapped_type != detail::type::string_type &&
+                   mapped_type != detail::type::custom_type))
+        };
     };
-  };
-
-  template <typename T>
-  using stored_type = conditional_t<
-      std::is_convertible<T, std::basic_string<char_type>>::value &&
-          !detail::is_reference_wrapper<T>::value,
-      std::basic_string<char_type>, T>;
-
-  // Storage of basic_format_arg must be contiguous.
-  std::vector<basic_format_arg<Context>> data_;
-  std::vector<detail::named_arg_info<char_type>> named_info_;
-
-  // Storage of arguments not fitting into basic_format_arg must grow
-  // without relocation because items in data_ refer to it.
-  detail::dynamic_arg_list dynamic_args_;
-
-  friend class basic_format_args<Context>;
-
-  unsigned long long get_types() const {
-    return detail::is_unpacked_bit | data_.size() |
-           (named_info_.empty()
-                ? 0ULL
-                : static_cast<unsigned long long>(detail::has_named_args_bit));
-  }
-
-  const basic_format_arg<Context>* data() const {
-    return named_info_.empty() ? data_.data() : data_.data() + 1;
-  }
-
-  template <typename T> void emplace_arg(const T& arg) {
-    data_.emplace_back(detail::make_arg<Context>(arg));
-  }
-
-  template <typename T>
-  void emplace_arg(const detail::named_arg<char_type, T>& arg) {
-    if (named_info_.empty()) {
-      constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
-      data_.insert(data_.begin(), {zero_ptr, 0});
+
+    template <typename T>
+    using stored_type = conditional_t<
+        std::is_convertible<T, std::basic_string<char_type>>::value && !detail::is_reference_wrapper<T>::value,
+        std::basic_string<char_type>,
+        T>;
+
+    // Storage of basic_format_arg must be contiguous.
+    std::vector<basic_format_arg<Context>>         data_;
+    std::vector<detail::named_arg_info<char_type>> named_info_;
+
+    // Storage of arguments not fitting into basic_format_arg must grow
+    // without relocation because items in data_ refer to it.
+    detail::dynamic_arg_list dynamic_args_;
+
+    friend class basic_format_args<Context>;
+
+    unsigned long long get_types() const
+    {
+        return detail::is_unpacked_bit | data_.size() |
+               (named_info_.empty() ? 0ULL : static_cast<unsigned long long>(detail::has_named_args_bit));
     }
-    data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
-    auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
-      data->pop_back();
-    };
-    std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
-        guard{&data_, pop_one};
-    named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
-    data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
-    guard.release();
-  }
-
- public:
-  constexpr dynamic_format_arg_store() = default;
-
-  /**
-    \rst
-    Adds an argument into the dynamic store for later passing to a formatting
-    function.
-
-    Note that custom types and string types (but not string views) are copied
-    into the store dynamically allocating memory if necessary.
-
-    **Example**::
-
-      fmt::dynamic_format_arg_store<fmt::format_context> store;
-      store.push_back(42);
-      store.push_back("abc");
-      store.push_back(1.5f);
-      std::string result = fmt::vformat("{} and {} and {}", store);
-    \endrst
-  */
-  template <typename T> void push_back(const T& arg) {
-    if (detail::const_check(need_copy<T>::value))
-      emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
-    else
-      emplace_arg(detail::unwrap(arg));
-  }
-
-  /**
-    \rst
-    Adds a reference to the argument into the dynamic store for later passing to
-    a formatting function.
-
-    **Example**::
-
-      fmt::dynamic_format_arg_store<fmt::format_context> store;
-      char band[] = "Rolling Stones";
-      store.push_back(std::cref(band));
-      band[9] = 'c'; // Changing str affects the output.
-      std::string result = fmt::vformat("{}", store);
-      // result == "Rolling Scones"
-    \endrst
-  */
-  template <typename T> void push_back(std::reference_wrapper<T> arg) {
-    static_assert(
-        need_copy<T>::value,
-        "objects of built-in types and string views are always copied");
-    emplace_arg(arg.get());
-  }
-
-  /**
-    Adds named argument into the dynamic store for later passing to a formatting
-    function. ``std::reference_wrapper`` is supported to avoid copying of the
-    argument. The name is always copied into the store.
-  */
-  template <typename T>
-  void push_back(const detail::named_arg<char_type, T>& arg) {
-    const char_type* arg_name =
-        dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
-    if (detail::const_check(need_copy<T>::value)) {
-      emplace_arg(
-          fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
-    } else {
-      emplace_arg(fmt::arg(arg_name, arg.value));
+
+    const basic_format_arg<Context> *data() const { return named_info_.empty() ? data_.data() : data_.data() + 1; }
+
+    template <typename T>
+    void emplace_arg(const T &arg)
+    {
+        data_.emplace_back(detail::make_arg<Context>(arg));
+    }
+
+    template <typename T>
+    void emplace_arg(const detail::named_arg<char_type, T> &arg)
+    {
+        if(named_info_.empty())
+        {
+            constexpr const detail::named_arg_info<char_type> *zero_ptr{nullptr};
+            data_.insert(data_.begin(), {zero_ptr, 0});
+        }
+        data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
+        auto pop_one = [](std::vector<basic_format_arg<Context>> *data) { data->pop_back(); };
+        std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)> guard{&data_, pop_one};
+        named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
+        data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
+        guard.release();
+    }
+
+public:
+    constexpr dynamic_format_arg_store() = default;
+
+    /**
+      \rst
+      Adds an argument into the dynamic store for later passing to a formatting
+      function.
+
+      Note that custom types and string types (but not string views) are copied
+      into the store dynamically allocating memory if necessary.
+
+      **Example**::
+
+        fmt::dynamic_format_arg_store<fmt::format_context> store;
+        store.push_back(42);
+        store.push_back("abc");
+        store.push_back(1.5f);
+        std::string result = fmt::vformat("{} and {} and {}", store);
+      \endrst
+    */
+    template <typename T>
+    void push_back(const T &arg)
+    {
+        if(detail::const_check(need_copy<T>::value))
+            emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
+        else
+            emplace_arg(detail::unwrap(arg));
+    }
+
+    /**
+      \rst
+      Adds a reference to the argument into the dynamic store for later passing to
+      a formatting function.
+
+      **Example**::
+
+        fmt::dynamic_format_arg_store<fmt::format_context> store;
+        char band[] = "Rolling Stones";
+        store.push_back(std::cref(band));
+        band[9] = 'c'; // Changing str affects the output.
+        std::string result = fmt::vformat("{}", store);
+        // result == "Rolling Scones"
+      \endrst
+    */
+    template <typename T>
+    void push_back(std::reference_wrapper<T> arg)
+    {
+        static_assert(need_copy<T>::value, "objects of built-in types and string views are always copied");
+        emplace_arg(arg.get());
+    }
+
+    /**
+      Adds named argument into the dynamic store for later passing to a formatting
+      function. ``std::reference_wrapper`` is supported to avoid copying of the
+      argument. The name is always copied into the store.
+    */
+    template <typename T>
+    void push_back(const detail::named_arg<char_type, T> &arg)
+    {
+        const char_type *arg_name = dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
+        if(detail::const_check(need_copy<T>::value))
+        {
+            emplace_arg(fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
+        }
+        else
+        {
+            emplace_arg(fmt::arg(arg_name, arg.value));
+        }
+    }
+
+    /** Erase all elements from the store */
+    void clear()
+    {
+        data_.clear();
+        named_info_.clear();
+        dynamic_args_ = detail::dynamic_arg_list();
+    }
+
+    /**
+      \rst
+      Reserves space to store at least *new_cap* arguments including
+      *new_cap_named* named arguments.
+      \endrst
+    */
+    void reserve(size_t new_cap, size_t new_cap_named)
+    {
+        FMT_ASSERT(new_cap >= new_cap_named, "Set of arguments includes set of named arguments");
+        data_.reserve(new_cap);
+        named_info_.reserve(new_cap_named);
     }
-  }
-
-  /** Erase all elements from the store */
-  void clear() {
-    data_.clear();
-    named_info_.clear();
-    dynamic_args_ = detail::dynamic_arg_list();
-  }
-
-  /**
-    \rst
-    Reserves space to store at least *new_cap* arguments including
-    *new_cap_named* named arguments.
-    \endrst
-  */
-  void reserve(size_t new_cap, size_t new_cap_named) {
-    FMT_ASSERT(new_cap >= new_cap_named,
-               "Set of arguments includes set of named arguments");
-    data_.reserve(new_cap);
-    named_info_.reserve(new_cap_named);
-  }
 };
 
 FMT_END_NAMESPACE
 
-#endif  // FMT_ARGS_H_
+#endif // FMT_ARGS_H_
diff --git a/include/spdlog/fmt/bundled/chrono.h b/include/spdlog/fmt/bundled/chrono.h
index b112f76e9..3d76d3c89 100644
--- a/include/spdlog/fmt/bundled/chrono.h
+++ b/include/spdlog/fmt/bundled/chrono.h
@@ -8,37 +8,36 @@
 #ifndef FMT_CHRONO_H_
 #define FMT_CHRONO_H_
 
+#include "format.h"
+
 #include <algorithm>
 #include <chrono>
-#include <cmath>    // std::isfinite
-#include <cstring>  // std::memcpy
+#include <cmath>   // std::isfinite
+#include <cstring> // std::memcpy
 #include <ctime>
 #include <iterator>
 #include <locale>
 #include <ostream>
 #include <type_traits>
 
-#include "format.h"
-
 FMT_BEGIN_NAMESPACE
 
 // Enable tzset.
 #ifndef FMT_USE_TZSET
 // UWP doesn't provide _tzset.
-#  if FMT_HAS_INCLUDE("winapifamily.h")
-#    include <winapifamily.h>
-#  endif
-#  if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \
-                          (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
-#    define FMT_USE_TZSET 1
-#  else
-#    define FMT_USE_TZSET 0
-#  endif
+#if FMT_HAS_INCLUDE("winapifamily.h")
+#include <winapifamily.h>
+#endif
+#if defined(_WIN32) && (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
+#define FMT_USE_TZSET 1
+#else
+#define FMT_USE_TZSET 0
+#endif
 #endif
 
 // Enable safe chrono durations, unless explicitly disabled.
 #ifndef FMT_SAFE_DURATION_CAST
-#  define FMT_SAFE_DURATION_CAST 1
+#define FMT_SAFE_DURATION_CAST 1
 #endif
 #if FMT_SAFE_DURATION_CAST
 
@@ -48,77 +47,88 @@ FMT_BEGIN_NAMESPACE
 // See https://github.com/pauldreik/safe_duration_cast
 //
 // Copyright Paul Dreik 2019
-namespace safe_duration_cast {
-
-template <typename To, typename From,
-          FMT_ENABLE_IF(!std::is_same<From, To>::value &&
-                        std::numeric_limits<From>::is_signed ==
-                            std::numeric_limits<To>::is_signed)>
-FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
-  ec = 0;
-  using F = std::numeric_limits<From>;
-  using T = std::numeric_limits<To>;
-  static_assert(F::is_integer, "From must be integral");
-  static_assert(T::is_integer, "To must be integral");
-
-  // A and B are both signed, or both unsigned.
-  if (detail::const_check(F::digits <= T::digits)) {
-    // From fits in To without any problem.
-  } else {
-    // From does not always fit in To, resort to a dynamic check.
-    if (from < (T::min)() || from > (T::max)()) {
-      // outside range.
-      ec = 1;
-      return {};
-    }
-  }
-  return static_cast<To>(from);
+namespace safe_duration_cast
+{
+
+template <
+    typename To,
+    typename From,
+    FMT_ENABLE_IF(
+        !std::is_same<From, To>::value && std::numeric_limits<From>::is_signed == std::numeric_limits<To>::is_signed)>
+FMT_CONSTEXPR To lossless_integral_conversion(const From from, int &ec)
+{
+    ec      = 0;
+    using F = std::numeric_limits<From>;
+    using T = std::numeric_limits<To>;
+    static_assert(F::is_integer, "From must be integral");
+    static_assert(T::is_integer, "To must be integral");
+
+    // A and B are both signed, or both unsigned.
+    if(detail::const_check(F::digits <= T::digits))
+    {
+        // From fits in To without any problem.
+    }
+    else
+    {
+        // From does not always fit in To, resort to a dynamic check.
+        if(from < (T::min)() || from > (T::max)())
+        {
+            // outside range.
+            ec = 1;
+            return {};
+        }
+    }
+    return static_cast<To>(from);
 }
 
 /**
  * converts From to To, without loss. If the dynamic value of from
  * can't be converted to To without loss, ec is set.
  */
-template <typename To, typename From,
-          FMT_ENABLE_IF(!std::is_same<From, To>::value &&
-                        std::numeric_limits<From>::is_signed !=
-                            std::numeric_limits<To>::is_signed)>
-FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
-  ec = 0;
-  using F = std::numeric_limits<From>;
-  using T = std::numeric_limits<To>;
-  static_assert(F::is_integer, "From must be integral");
-  static_assert(T::is_integer, "To must be integral");
-
-  if (detail::const_check(F::is_signed && !T::is_signed)) {
-    // From may be negative, not allowed!
-    if (fmt::detail::is_negative(from)) {
-      ec = 1;
-      return {};
-    }
-    // From is positive. Can it always fit in To?
-    if (detail::const_check(F::digits > T::digits) &&
-        from > static_cast<From>(detail::max_value<To>())) {
-      ec = 1;
-      return {};
-    }
-  }
-
-  if (detail::const_check(!F::is_signed && T::is_signed &&
-                          F::digits >= T::digits) &&
-      from > static_cast<From>(detail::max_value<To>())) {
-    ec = 1;
-    return {};
-  }
-  return static_cast<To>(from);  // Lossless conversion.
+template <
+    typename To,
+    typename From,
+    FMT_ENABLE_IF(
+        !std::is_same<From, To>::value && std::numeric_limits<From>::is_signed != std::numeric_limits<To>::is_signed)>
+FMT_CONSTEXPR To lossless_integral_conversion(const From from, int &ec)
+{
+    ec      = 0;
+    using F = std::numeric_limits<From>;
+    using T = std::numeric_limits<To>;
+    static_assert(F::is_integer, "From must be integral");
+    static_assert(T::is_integer, "To must be integral");
+
+    if(detail::const_check(F::is_signed && !T::is_signed))
+    {
+        // From may be negative, not allowed!
+        if(fmt::detail::is_negative(from))
+        {
+            ec = 1;
+            return {};
+        }
+        // From is positive. Can it always fit in To?
+        if(detail::const_check(F::digits > T::digits) && from > static_cast<From>(detail::max_value<To>()))
+        {
+            ec = 1;
+            return {};
+        }
+    }
+
+    if(detail::const_check(!F::is_signed && T::is_signed && F::digits >= T::digits) &&
+       from > static_cast<From>(detail::max_value<To>()))
+    {
+        ec = 1;
+        return {};
+    }
+    return static_cast<To>(from); // Lossless conversion.
 }
 
-template <typename To, typename From,
-          FMT_ENABLE_IF(std::is_same<From, To>::value)>
-FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
-  ec = 0;
-  return from;
-}  // function
+template <typename To, typename From, FMT_ENABLE_IF(std::is_same<From, To>::value)>
+FMT_CONSTEXPR To lossless_integral_conversion(const From from, int &ec)
+{
+    ec = 0;
+    return from;
+} // function
 
 // clang-format off
 /**
@@ -134,312 +144,355 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
  * -Inf                             | -Inf
  */
 // clang-format on
-template <typename To, typename From,
-          FMT_ENABLE_IF(!std::is_same<From, To>::value)>
-FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
-  ec = 0;
-  using T = std::numeric_limits<To>;
-  static_assert(std::is_floating_point<From>::value, "From must be floating");
-  static_assert(std::is_floating_point<To>::value, "To must be floating");
-
-  // catch the only happy case
-  if (std::isfinite(from)) {
-    if (from >= T::lowest() && from <= (T::max)()) {
-      return static_cast<To>(from);
-    }
-    // not within range.
-    ec = 1;
-    return {};
-  }
-
-  // nan and inf will be preserved
-  return static_cast<To>(from);
-}  // function
-
-template <typename To, typename From,
-          FMT_ENABLE_IF(std::is_same<From, To>::value)>
-FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
-  ec = 0;
-  static_assert(std::is_floating_point<From>::value, "From must be floating");
-  return from;
+template <typename To, typename From, FMT_ENABLE_IF(!std::is_same<From, To>::value)>
+FMT_CONSTEXPR To safe_float_conversion(const From from, int &ec)
+{
+    ec      = 0;
+    using T = std::numeric_limits<To>;
+    static_assert(std::is_floating_point<From>::value, "From must be floating");
+    static_assert(std::is_floating_point<To>::value, "To must be floating");
+
+    // catch the only happy case
+    if(std::isfinite(from))
+    {
+        if(from >= T::lowest() && from <= (T::max)())
+        {
+            return static_cast<To>(from);
+        }
+        // not within range.
+        ec = 1;
+        return {};
+    }
+
+    // nan and inf will be preserved
+    return static_cast<To>(from);
+} // function
+
+template <typename To, typename From, FMT_ENABLE_IF(std::is_same<From, To>::value)>
+FMT_CONSTEXPR To safe_float_conversion(const From from, int &ec)
+{
+    ec = 0;
+    static_assert(std::is_floating_point<From>::value, "From must be floating");
+    return from;
 }
 
 /**
  * safe duration cast between integral durations
  */
-template <typename To, typename FromRep, typename FromPeriod,
-          FMT_ENABLE_IF(std::is_integral<FromRep>::value),
-          FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
-To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
-                      int& ec) {
-  using From = std::chrono::duration<FromRep, FromPeriod>;
-  ec = 0;
-  // the basic idea is that we need to convert from count() in the from type
-  // to count() in the To type, by multiplying it with this:
-  struct Factor
-      : std::ratio_divide<typename From::period, typename To::period> {};
-
-  static_assert(Factor::num > 0, "num must be positive");
-  static_assert(Factor::den > 0, "den must be positive");
-
-  // the conversion is like this: multiply from.count() with Factor::num
-  // /Factor::den and convert it to To::rep, all this without
-  // overflow/underflow. let's start by finding a suitable type that can hold
-  // both To, From and Factor::num
-  using IntermediateRep =
-      typename std::common_type<typename From::rep, typename To::rep,
-                                decltype(Factor::num)>::type;
-
-  // safe conversion to IntermediateRep
-  IntermediateRep count =
-      lossless_integral_conversion<IntermediateRep>(from.count(), ec);
-  if (ec) return {};
-  // multiply with Factor::num without overflow or underflow
-  if (detail::const_check(Factor::num != 1)) {
-    const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
-    if (count > max1) {
-      ec = 1;
-      return {};
-    }
-    const auto min1 =
-        (std::numeric_limits<IntermediateRep>::min)() / Factor::num;
-    if (!std::is_unsigned<IntermediateRep>::value && count < min1) {
-      ec = 1;
-      return {};
-    }
-    count *= Factor::num;
-  }
-
-  if (detail::const_check(Factor::den != 1)) count /= Factor::den;
-  auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
-  return ec ? To() : To(tocount);
+template <
+    typename To,
+    typename FromRep,
+    typename FromPeriod,
+    FMT_ENABLE_IF(std::is_integral<FromRep>::value),
+    FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
+To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from, int &ec)
+{
+    using From = std::chrono::duration<FromRep, FromPeriod>;
+    ec         = 0;
+    // the basic idea is that we need to convert from count() in the from type
+    // to count() in the To type, by multiplying it with this:
+    struct Factor : std::ratio_divide<typename From::period, typename To::period>
+    {
+    };
+
+    static_assert(Factor::num > 0, "num must be positive");
+    static_assert(Factor::den > 0, "den must be positive");
+
+    // the conversion is like this: multiply from.count() with Factor::num
+    // /Factor::den and convert it to To::rep, all this without
+    // overflow/underflow. let's start by finding a suitable type that can hold
+    // both To, From and Factor::num
+    using IntermediateRep =
+        typename std::common_type<typename From::rep, typename To::rep, decltype(Factor::num)>::type;
+
+    // safe conversion to IntermediateRep
+    IntermediateRep count = lossless_integral_conversion<IntermediateRep>(from.count(), ec);
+    if(ec)
+        return {};
+    // multiply with Factor::num without overflow or underflow
+    if(detail::const_check(Factor::num != 1))
+    {
+        const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
+        if(count > max1)
+        {
+            ec = 1;
+            return {};
+        }
+        const auto min1 = (std::numeric_limits<IntermediateRep>::min)() / Factor::num;
+        if(!std::is_unsigned<IntermediateRep>::value && count < min1)
+        {
+            ec = 1;
+            return {};
+        }
+        count *= Factor::num;
+    }
+
+    if(detail::const_check(Factor::den != 1))
+        count /= Factor::den;
+    auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
+    return ec ? To() : To(tocount);
 }
 
 /**
  * safe duration_cast between floating point durations
  */
-template <typename To, typename FromRep, typename FromPeriod,
-          FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
-          FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
-To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
-                      int& ec) {
-  using From = std::chrono::duration<FromRep, FromPeriod>;
-  ec = 0;
-  if (std::isnan(from.count())) {
-    // nan in, gives nan out. easy.
-    return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
-  }
-  // maybe we should also check if from is denormal, and decide what to do about
-  // it.
-
-  // +-inf should be preserved.
-  if (std::isinf(from.count())) {
-    return To{from.count()};
-  }
-
-  // the basic idea is that we need to convert from count() in the from type
-  // to count() in the To type, by multiplying it with this:
-  struct Factor
-      : std::ratio_divide<typename From::period, typename To::period> {};
-
-  static_assert(Factor::num > 0, "num must be positive");
-  static_assert(Factor::den > 0, "den must be positive");
-
-  // the conversion is like this: multiply from.count() with Factor::num
-  // /Factor::den and convert it to To::rep, all this without
-  // overflow/underflow. let's start by finding a suitable type that can hold
-  // both To, From and Factor::num
-  using IntermediateRep =
-      typename std::common_type<typename From::rep, typename To::rep,
-                                decltype(Factor::num)>::type;
-
-  // force conversion of From::rep -> IntermediateRep to be safe,
-  // even if it will never happen be narrowing in this context.
-  IntermediateRep count =
-      safe_float_conversion<IntermediateRep>(from.count(), ec);
-  if (ec) {
-    return {};
-  }
-
-  // multiply with Factor::num without overflow or underflow
-  if (detail::const_check(Factor::num != 1)) {
-    constexpr auto max1 = detail::max_value<IntermediateRep>() /
-                          static_cast<IntermediateRep>(Factor::num);
-    if (count > max1) {
-      ec = 1;
-      return {};
-    }
-    constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
-                          static_cast<IntermediateRep>(Factor::num);
-    if (count < min1) {
-      ec = 1;
-      return {};
-    }
-    count *= static_cast<IntermediateRep>(Factor::num);
-  }
-
-  // this can't go wrong, right? den>0 is checked earlier.
-  if (detail::const_check(Factor::den != 1)) {
-    using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
-    count /= static_cast<common_t>(Factor::den);
-  }
-
-  // convert to the to type, safely
-  using ToRep = typename To::rep;
-
-  const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
-  if (ec) {
-    return {};
-  }
-  return To{tocount};
+template <
+    typename To,
+    typename FromRep,
+    typename FromPeriod,
+    FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
+    FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
+To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from, int &ec)
+{
+    using From = std::chrono::duration<FromRep, FromPeriod>;
+    ec         = 0;
+    if(std::isnan(from.count()))
+    {
+        // nan in, gives nan out. easy.
+        return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
+    }
+    // maybe we should also check if from is denormal, and decide what to do about
+    // it.
+
+    // +-inf should be preserved.
+    if(std::isinf(from.count()))
+    {
+        return To{from.count()};
+    }
+
+    // the basic idea is that we need to convert from count() in the from type
+    // to count() in the To type, by multiplying it with this:
+    struct Factor : std::ratio_divide<typename From::period, typename To::period>
+    {
+    };
+
+    static_assert(Factor::num > 0, "num must be positive");
+    static_assert(Factor::den > 0, "den must be positive");
+
+    // the conversion is like this: multiply from.count() with Factor::num
+    // /Factor::den and convert it to To::rep, all this without
+    // overflow/underflow. let's start by finding a suitable type that can hold
+    // both To, From and Factor::num
+    using IntermediateRep =
+        typename std::common_type<typename From::rep, typename To::rep, decltype(Factor::num)>::type;
+
+    // force conversion of From::rep -> IntermediateRep to be safe,
+    // even if it will never happen be narrowing in this context.
+    IntermediateRep count = safe_float_conversion<IntermediateRep>(from.count(), ec);
+    if(ec)
+    {
+        return {};
+    }
+
+    // multiply with Factor::num without overflow or underflow
+    if(detail::const_check(Factor::num != 1))
+    {
+        constexpr auto max1 = detail::max_value<IntermediateRep>() / static_cast<IntermediateRep>(Factor::num);
+        if(count > max1)
+        {
+            ec = 1;
+            return {};
+        }
+        constexpr auto min1 =
+            std::numeric_limits<IntermediateRep>::lowest() / static_cast<IntermediateRep>(Factor::num);
+        if(count < min1)
+        {
+            ec = 1;
+            return {};
+        }
+        count *= static_cast<IntermediateRep>(Factor::num);
+    }
+
+    // this can't go wrong, right? den>0 is checked earlier.
+    if(detail::const_check(Factor::den != 1))
+    {
+        using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
+        count /= static_cast<common_t>(Factor::den);
+    }
+
+    // convert to the to type, safely
+    using ToRep = typename To::rep;
+
+    const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
+    if(ec)
+    {
+        return {};
+    }
+    return To{tocount};
 }
-}  // namespace safe_duration_cast
+} // namespace safe_duration_cast
 #endif
 
 // Prevents expansion of a preceding token as a function-style macro.
 // Usage: f FMT_NOMACRO()
 #define FMT_NOMACRO
 
-namespace detail {
-template <typename T = void> struct null {};
-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 detail
+{
+template <typename T = void>
+struct null
+{
+};
+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<>();
+}
 
-inline const std::locale& get_classic_locale() {
-  static const auto& locale = std::locale::classic();
-  return locale;
+inline const std::locale &get_classic_locale()
+{
+    static const auto &locale = std::locale::classic();
+    return locale;
 }
 
-template <typename CodeUnit> struct codecvt_result {
-  static constexpr const size_t max_size = 32;
-  CodeUnit buf[max_size];
-  CodeUnit* end;
+template <typename CodeUnit>
+struct codecvt_result
+{
+    static constexpr const size_t max_size = 32;
+    CodeUnit                      buf[max_size];
+    CodeUnit                     *end;
 };
 template <typename CodeUnit>
 constexpr const size_t codecvt_result<CodeUnit>::max_size;
 
 template <typename CodeUnit>
-void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
-                   const std::locale& loc) {
+void write_codecvt(codecvt_result<CodeUnit> &out, string_view in_buf, const std::locale &loc)
+{
 #if FMT_CLANG_VERSION
-#  pragma clang diagnostic push
-#  pragma clang diagnostic ignored "-Wdeprecated"
-  auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
-#  pragma clang diagnostic pop
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated"
+    auto &f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
+#pragma clang diagnostic pop
 #else
-  auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
+    auto &f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
 #endif
-  auto mb = std::mbstate_t();
-  const char* from_next = nullptr;
-  auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next,
-                     std::begin(out.buf), std::end(out.buf), out.end);
-  if (result != std::codecvt_base::ok)
-    FMT_THROW(format_error("failed to format time"));
+    auto        mb        = std::mbstate_t();
+    const char *from_next = nullptr;
+    auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next, std::begin(out.buf), std::end(out.buf), out.end);
+    if(result != std::codecvt_base::ok)
+        FMT_THROW(format_error("failed to format time"));
 }
 
 template <typename OutputIt>
-auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
-    -> OutputIt {
-  if (detail::is_utf8() && loc != get_classic_locale()) {
-    // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
-    // gcc-4.
-#if FMT_MSC_VERSION != 0 || \
-    (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
-    // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
-    // and newer.
-    using code_unit = wchar_t;
+auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale &loc) -> OutputIt
+{
+    if(detail::is_utf8() && loc != get_classic_locale())
+    {
+        // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
+        // gcc-4.
+#if FMT_MSC_VERSION != 0 || (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
+        // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
+        // and newer.
+        using code_unit = wchar_t;
 #else
-    using code_unit = char32_t;
+        using code_unit = char32_t;
 #endif
 
-    using unit_t = codecvt_result<code_unit>;
-    unit_t unit;
-    write_codecvt(unit, in, loc);
-    // In UTF-8 is used one to four one-byte code units.
-    auto&& buf = basic_memory_buffer<char, unit_t::max_size * 4>();
-    for (code_unit* p = unit.buf; p != unit.end; ++p) {
-      uint32_t c = static_cast<uint32_t>(*p);
-      if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) {
-        // surrogate pair
-        ++p;
-        if (p == unit.end || (c & 0xfc00) != 0xd800 ||
-            (*p & 0xfc00) != 0xdc00) {
-          FMT_THROW(format_error("failed to format time"));
+        using unit_t = codecvt_result<code_unit>;
+        unit_t unit;
+        write_codecvt(unit, in, loc);
+        // In UTF-8 is used one to four one-byte code units.
+        auto &&buf = basic_memory_buffer<char, unit_t::max_size * 4>();
+        for(code_unit *p = unit.buf; p != unit.end; ++p)
+        {
+            uint32_t c = static_cast<uint32_t>(*p);
+            if(sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff)
+            {
+                // surrogate pair
+                ++p;
+                if(p == unit.end || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00)
+                {
+                    FMT_THROW(format_error("failed to format time"));
+                }
+                c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
+            }
+            if(c < 0x80)
+            {
+                buf.push_back(static_cast<char>(c));
+            }
+            else if(c < 0x800)
+            {
+                buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
+                buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
+            }
+            else if((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff))
+            {
+                buf.push_back(static_cast<char>(0xe0 | (c >> 12)));
+                buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
+                buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
+            }
+            else if(c >= 0x10000 && c <= 0x10ffff)
+            {
+                buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
+                buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
+                buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
+                buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
+            }
+            else
+            {
+                FMT_THROW(format_error("failed to format time"));
+            }
         }
-        c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
-      }
-      if (c < 0x80) {
-        buf.push_back(static_cast<char>(c));
-      } else if (c < 0x800) {
-        buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
-        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
-      } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
-        buf.push_back(static_cast<char>(0xe0 | (c >> 12)));
-        buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
-        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
-      } else if (c >= 0x10000 && c <= 0x10ffff) {
-        buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
-        buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
-        buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
-        buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
-      } else {
-        FMT_THROW(format_error("failed to format time"));
-      }
+        return copy_str<char>(buf.data(), buf.data() + buf.size(), out);
     }
-    return copy_str<char>(buf.data(), buf.data() + buf.size(), out);
-  }
-  return copy_str<char>(in.data(), in.data() + in.size(), out);
+    return copy_str<char>(in.data(), in.data() + in.size(), out);
 }
 
-template <typename Char, typename OutputIt,
-          FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
-auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
-    -> OutputIt {
-  codecvt_result<Char> unit;
-  write_codecvt(unit, sv, loc);
-  return copy_str<Char>(unit.buf, unit.end, out);
+template <typename Char, typename OutputIt, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+auto write_tm_str(OutputIt out, string_view sv, const std::locale &loc) -> OutputIt
+{
+    codecvt_result<Char> unit;
+    write_codecvt(unit, sv, loc);
+    return copy_str<Char>(unit.buf, unit.end, out);
 }
 
-template <typename Char, typename OutputIt,
-          FMT_ENABLE_IF(std::is_same<Char, char>::value)>
-auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
-    -> OutputIt {
-  return write_encoded_tm_str(out, sv, loc);
+template <typename Char, typename OutputIt, FMT_ENABLE_IF(std::is_same<Char, char>::value)>
+auto write_tm_str(OutputIt out, string_view sv, const std::locale &loc) -> OutputIt
+{
+    return write_encoded_tm_str(out, sv, loc);
 }
 
 template <typename Char>
-inline void do_write(buffer<Char>& buf, const std::tm& time,
-                     const std::locale& loc, char format, char modifier) {
-  auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
-  auto&& os = std::basic_ostream<Char>(&format_buf);
-  os.imbue(loc);
-  using iterator = std::ostreambuf_iterator<Char>;
-  const auto& facet = std::use_facet<std::time_put<Char, iterator>>(loc);
-  auto end = facet.put(os, os, Char(' '), &time, format, modifier);
-  if (end.failed()) FMT_THROW(format_error("failed to format time"));
+inline void do_write(buffer<Char> &buf, const std::tm &time, const std::locale &loc, char format, char modifier)
+{
+    auto &&format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
+    auto &&os         = std::basic_ostream<Char>(&format_buf);
+    os.imbue(loc);
+    using iterator    = std::ostreambuf_iterator<Char>;
+    const auto &facet = std::use_facet<std::time_put<Char, iterator>>(loc);
+    auto        end   = facet.put(os, os, Char(' '), &time, format, modifier);
+    if(end.failed())
+        FMT_THROW(format_error("failed to format time"));
 }
 
-template <typename Char, typename OutputIt,
-          FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
-auto write(OutputIt out, const std::tm& time, const std::locale& loc,
-           char format, char modifier = 0) -> OutputIt {
-  auto&& buf = get_buffer<Char>(out);
-  do_write<Char>(buf, time, loc, format, modifier);
-  return buf.out();
+template <typename Char, typename OutputIt, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
+auto write(OutputIt out, const std::tm &time, const std::locale &loc, char format, char modifier = 0) -> OutputIt
+{
+    auto &&buf = get_buffer<Char>(out);
+    do_write<Char>(buf, time, loc, format, modifier);
+    return buf.out();
 }
 
-template <typename Char, typename OutputIt,
-          FMT_ENABLE_IF(std::is_same<Char, char>::value)>
-auto write(OutputIt out, const std::tm& time, const std::locale& loc,
-           char format, char modifier = 0) -> OutputIt {
-  auto&& buf = basic_memory_buffer<Char>();
-  do_write<char>(buf, time, loc, format, modifier);
-  return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
+template <typename Char, typename OutputIt, FMT_ENABLE_IF(std::is_same<Char, char>::value)>
+auto write(OutputIt out, const std::tm &time, const std::locale &loc, char format, char modifier = 0) -> OutputIt
+{
+    auto &&buf = basic_memory_buffer<Char>();
+    do_write<char>(buf, time, loc, format, modifier);
+    return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
 }
 
-}  // namespace detail
+} // namespace detail
 
 FMT_MODULE_EXPORT_BEGIN
 
@@ -448,45 +501,52 @@ FMT_MODULE_EXPORT_BEGIN
   expressed in local time. Unlike ``std::localtime``, this function is
   thread-safe on most platforms.
  */
-inline std::tm localtime(std::time_t time) {
-  struct dispatcher {
-    std::time_t time_;
-    std::tm tm_;
-
-    dispatcher(std::time_t t) : time_(t) {}
-
-    bool run() {
-      using namespace fmt::detail;
-      return handle(localtime_r(&time_, &tm_));
-    }
+inline std::tm localtime(std::time_t time)
+{
+    struct dispatcher
+    {
+        std::time_t time_;
+        std::tm     tm_;
+
+        dispatcher(std::time_t t) : time_(t) {}
+
+        bool run()
+        {
+            using namespace fmt::detail;
+            return handle(localtime_r(&time_, &tm_));
+        }
 
-    bool handle(std::tm* tm) { return tm != nullptr; }
+        bool handle(std::tm *tm) { return tm != nullptr; }
 
-    bool handle(detail::null<>) {
-      using namespace fmt::detail;
-      return fallback(localtime_s(&tm_, &time_));
-    }
+        bool handle(detail::null<>)
+        {
+            using namespace fmt::detail;
+            return fallback(localtime_s(&tm_, &time_));
+        }
 
-    bool fallback(int res) { return res == 0; }
+        bool fallback(int res) { return res == 0; }
 
 #if !FMT_MSC_VERSION
-    bool fallback(detail::null<>) {
-      using namespace fmt::detail;
-      std::tm* tm = std::localtime(&time_);
-      if (tm) tm_ = *tm;
-      return tm != nullptr;
-    }
+        bool fallback(detail::null<>)
+        {
+            using namespace fmt::detail;
+            std::tm *tm = std::localtime(&time_);
+            if(tm)
+                tm_ = *tm;
+            return tm != nullptr;
+        }
 #endif
-  };
-  dispatcher lt(time);
-  // Too big time values may be unsupported.
-  if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
-  return lt.tm_;
+    };
+    dispatcher lt(time);
+    // Too big time values may be unsupported.
+    if(!lt.run())
+        FMT_THROW(format_error("time_t value out of range"));
+    return lt.tm_;
 }
 
-inline std::tm localtime(
-    std::chrono::time_point<std::chrono::system_clock> time_point) {
-  return localtime(std::chrono::system_clock::to_time_t(time_point));
+inline std::tm localtime(std::chrono::time_point<std::chrono::system_clock> time_point)
+{
+    return localtime(std::chrono::system_clock::to_time_t(time_point));
 }
 
 /**
@@ -494,44 +554,51 @@ inline std::tm localtime(
   expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
   function is thread-safe on most platforms.
  */
-inline std::tm gmtime(std::time_t time) {
-  struct dispatcher {
-    std::time_t time_;
-    std::tm tm_;
-
-    dispatcher(std::time_t t) : time_(t) {}
-
-    bool run() {
-      using namespace fmt::detail;
-      return handle(gmtime_r(&time_, &tm_));
-    }
+inline std::tm gmtime(std::time_t time)
+{
+    struct dispatcher
+    {
+        std::time_t time_;
+        std::tm     tm_;
+
+        dispatcher(std::time_t t) : time_(t) {}
+
+        bool run()
+        {
+            using namespace fmt::detail;
+            return handle(gmtime_r(&time_, &tm_));
+        }
 
-    bool handle(std::tm* tm) { return tm != nullptr; }
+        bool handle(std::tm *tm) { return tm != nullptr; }
 
-    bool handle(detail::null<>) {
-      using namespace fmt::detail;
-      return fallback(gmtime_s(&tm_, &time_));
-    }
+        bool handle(detail::null<>)
+        {
+            using namespace fmt::detail;
+            return fallback(gmtime_s(&tm_, &time_));
+        }
 
-    bool fallback(int res) { return res == 0; }
+        bool fallback(int res) { return res == 0; }
 
 #if !FMT_MSC_VERSION
-    bool fallback(detail::null<>) {
-      std::tm* tm = std::gmtime(&time_);
-      if (tm) tm_ = *tm;
-      return tm != nullptr;
-    }
+        bool fallback(detail::null<>)
+        {
+            std::tm *tm = std::gmtime(&time_);
+            if(tm)
+                tm_ = *tm;
+            return tm != nullptr;
+        }
 #endif
-  };
-  dispatcher gt(time);
-  // Too big time values may be unsupported.
-  if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
-  return gt.tm_;
+    };
+    dispatcher gt(time);
+    // Too big time values may be unsupported.
+    if(!gt.run())
+        FMT_THROW(format_error("time_t value out of range"));
+    return gt.tm_;
 }
 
-inline std::tm gmtime(
-    std::chrono::time_point<std::chrono::system_clock> time_point) {
-  return gmtime(std::chrono::system_clock::to_time_t(time_point));
+inline std::tm gmtime(std::chrono::time_point<std::chrono::system_clock> time_point)
+{
+    return gmtime(std::chrono::system_clock::to_time_t(time_point));
 }
 
 FMT_BEGIN_DETAIL_NAMESPACE
@@ -539,1298 +606,1537 @@ FMT_BEGIN_DETAIL_NAMESPACE
 // Writes two-digit numbers a, b and c separated by sep to buf.
 // The method by Pavel Novikov based on
 // https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
-inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
-                                   unsigned c, char sep) {
-  unsigned long long digits =
-      a | (b << 24) | (static_cast<unsigned long long>(c) << 48);
-  // Convert each value to BCD.
-  // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
-  // The difference is
-  //   y - x = a * 6
-  // a can be found from x:
-  //   a = floor(x / 10)
-  // then
-  //   y = x + a * 6 = x + floor(x / 10) * 6
-  // floor(x / 10) is (x * 205) >> 11 (needs 16 bits).
-  digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6;
-  // Put low nibbles to high bytes and high nibbles to low bytes.
-  digits = ((digits & 0x00f00000f00000f0) >> 4) |
-           ((digits & 0x000f00000f00000f) << 8);
-  auto usep = static_cast<unsigned long long>(sep);
-  // Add ASCII '0' to each digit byte and insert separators.
-  digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
-
-  constexpr const size_t len = 8;
-  if (const_check(is_big_endian())) {
-    char tmp[len];
-    std::memcpy(tmp, &digits, len);
-    std::reverse_copy(tmp, tmp + len, buf);
-  } else {
-    std::memcpy(buf, &digits, len);
-  }
+inline void write_digit2_separated(char *buf, unsigned a, unsigned b, unsigned c, char sep)
+{
+    unsigned long long digits = a | (b << 24) | (static_cast<unsigned long long>(c) << 48);
+    // Convert each value to BCD.
+    // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
+    // The difference is
+    //   y - x = a * 6
+    // a can be found from x:
+    //   a = floor(x / 10)
+    // then
+    //   y = x + a * 6 = x + floor(x / 10) * 6
+    // floor(x / 10) is (x * 205) >> 11 (needs 16 bits).
+    digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6;
+    // Put low nibbles to high bytes and high nibbles to low bytes.
+    digits    = ((digits & 0x00f00000f00000f0) >> 4) | ((digits & 0x000f00000f00000f) << 8);
+    auto usep = static_cast<unsigned long long>(sep);
+    // Add ASCII '0' to each digit byte and insert separators.
+    digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
+
+    constexpr const size_t len = 8;
+    if(const_check(is_big_endian()))
+    {
+        char tmp[len];
+        std::memcpy(tmp, &digits, len);
+        std::reverse_copy(tmp, tmp + len, buf);
+    }
+    else
+    {
+        std::memcpy(buf, &digits, len);
+    }
 }
 
-template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
-  if (std::is_same<Period, std::atto>::value) return "as";
-  if (std::is_same<Period, std::femto>::value) return "fs";
-  if (std::is_same<Period, std::pico>::value) return "ps";
-  if (std::is_same<Period, std::nano>::value) return "ns";
-  if (std::is_same<Period, std::micro>::value) return "µs";
-  if (std::is_same<Period, std::milli>::value) return "ms";
-  if (std::is_same<Period, std::centi>::value) return "cs";
-  if (std::is_same<Period, std::deci>::value) return "ds";
-  if (std::is_same<Period, std::ratio<1>>::value) return "s";
-  if (std::is_same<Period, std::deca>::value) return "das";
-  if (std::is_same<Period, std::hecto>::value) return "hs";
-  if (std::is_same<Period, std::kilo>::value) return "ks";
-  if (std::is_same<Period, std::mega>::value) return "Ms";
-  if (std::is_same<Period, std::giga>::value) return "Gs";
-  if (std::is_same<Period, std::tera>::value) return "Ts";
-  if (std::is_same<Period, std::peta>::value) return "Ps";
-  if (std::is_same<Period, std::exa>::value) return "Es";
-  if (std::is_same<Period, std::ratio<60>>::value) return "m";
-  if (std::is_same<Period, std::ratio<3600>>::value) return "h";
-  return nullptr;
+template <typename Period>
+FMT_CONSTEXPR inline const char *get_units()
+{
+    if(std::is_same<Period, std::atto>::value)
+        return "as";
+    if(std::is_same<Period, std::femto>::value)
+        return "fs";
+    if(std::is_same<Period, std::pico>::value)
+        return "ps";
+    if(std::is_same<Period, std::nano>::value)
+        return "ns";
+    if(std::is_same<Period, std::micro>::value)
+        return "µs";
+    if(std::is_same<Period, std::milli>::value)
+        return "ms";
+    if(std::is_same<Period, std::centi>::value)
+        return "cs";
+    if(std::is_same<Period, std::deci>::value)
+        return "ds";
+    if(std::is_same<Period, std::ratio<1>>::value)
+        return "s";
+    if(std::is_same<Period, std::deca>::value)
+        return "das";
+    if(std::is_same<Period, std::hecto>::value)
+        return "hs";
+    if(std::is_same<Period, std::kilo>::value)
+        return "ks";
+    if(std::is_same<Period, std::mega>::value)
+        return "Ms";
+    if(std::is_same<Period, std::giga>::value)
+        return "Gs";
+    if(std::is_same<Period, std::tera>::value)
+        return "Ts";
+    if(std::is_same<Period, std::peta>::value)
+        return "Ps";
+    if(std::is_same<Period, std::exa>::value)
+        return "Es";
+    if(std::is_same<Period, std::ratio<60>>::value)
+        return "m";
+    if(std::is_same<Period, std::ratio<3600>>::value)
+        return "h";
+    return nullptr;
 }
 
-enum class numeric_system {
-  standard,
-  // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
-  alternative
+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) FMT_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;
-    }
-    // Year:
-    case 'Y':
-      handler.on_year(numeric_system::standard);
-      break;
-    case 'y':
-      handler.on_short_year(numeric_system::standard);
-      break;
-    case 'C':
-      handler.on_century(numeric_system::standard);
-      break;
-    case 'G':
-      handler.on_iso_week_based_year();
-      break;
-    case 'g':
-      handler.on_iso_week_based_short_year();
-      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':
-    case 'h':
-      handler.on_abbr_month();
-      break;
-    case 'B':
-      handler.on_full_month();
-      break;
-    case 'm':
-      handler.on_dec_month(numeric_system::standard);
-      break;
-    // Day of the year/month:
-    case 'U':
-      handler.on_dec0_week_of_year(numeric_system::standard);
-      break;
-    case 'W':
-      handler.on_dec1_week_of_year(numeric_system::standard);
-      break;
-    case 'V':
-      handler.on_iso_week_of_year(numeric_system::standard);
-      break;
-    case 'j':
-      handler.on_day_of_year();
-      break;
-    case 'd':
-      handler.on_day_of_month(numeric_system::standard);
-      break;
-    case 'e':
-      handler.on_day_of_month_space(numeric_system::standard);
-      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 'Q':
-      handler.on_duration_value();
-      break;
-    case 'q':
-      handler.on_duration_unit();
-      break;
-    case 'z':
-      handler.on_utc_offset();
-      break;
-    case 'Z':
-      handler.on_tz_name();
-      break;
-    // Alternative representation:
-    case 'E': {
-      if (ptr == end) FMT_THROW(format_error("invalid format"));
-      c = *ptr++;
-      switch (c) {
-      case 'Y':
-        handler.on_year(numeric_system::alternative);
-        break;
-      case 'y':
-        handler.on_offset_year();
-        break;
-      case 'C':
-        handler.on_century(numeric_system::alternative);
-        break;
-      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:
-        FMT_THROW(format_error("invalid format"));
-      }
-      break;
-    }
-    case 'O':
-      if (ptr == end) FMT_THROW(format_error("invalid format"));
-      c = *ptr++;
-      switch (c) {
-      case 'y':
-        handler.on_short_year(numeric_system::alternative);
-        break;
-      case 'm':
-        handler.on_dec_month(numeric_system::alternative);
-        break;
-      case 'U':
-        handler.on_dec0_week_of_year(numeric_system::alternative);
-        break;
-      case 'W':
-        handler.on_dec1_week_of_year(numeric_system::alternative);
-        break;
-      case 'V':
-        handler.on_iso_week_of_year(numeric_system::alternative);
-        break;
-      case 'd':
-        handler.on_day_of_month(numeric_system::alternative);
-        break;
-      case 'e':
-        handler.on_day_of_month_space(numeric_system::alternative);
-        break;
-      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:
-        FMT_THROW(format_error("invalid format"));
-      }
-      break;
-    default:
-      FMT_THROW(format_error("invalid format"));
-    }
-    begin = ptr;
-  }
-  if (begin != ptr) handler.on_text(begin, ptr);
-  return ptr;
+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)
+            FMT_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;
+            }
+            // Year:
+            case 'Y':
+                handler.on_year(numeric_system::standard);
+                break;
+            case 'y':
+                handler.on_short_year(numeric_system::standard);
+                break;
+            case 'C':
+                handler.on_century(numeric_system::standard);
+                break;
+            case 'G':
+                handler.on_iso_week_based_year();
+                break;
+            case 'g':
+                handler.on_iso_week_based_short_year();
+                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':
+            case 'h':
+                handler.on_abbr_month();
+                break;
+            case 'B':
+                handler.on_full_month();
+                break;
+            case 'm':
+                handler.on_dec_month(numeric_system::standard);
+                break;
+            // Day of the year/month:
+            case 'U':
+                handler.on_dec0_week_of_year(numeric_system::standard);
+                break;
+            case 'W':
+                handler.on_dec1_week_of_year(numeric_system::standard);
+                break;
+            case 'V':
+                handler.on_iso_week_of_year(numeric_system::standard);
+                break;
+            case 'j':
+                handler.on_day_of_year();
+                break;
+            case 'd':
+                handler.on_day_of_month(numeric_system::standard);
+                break;
+            case 'e':
+                handler.on_day_of_month_space(numeric_system::standard);
+                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 'Q':
+                handler.on_duration_value();
+                break;
+            case 'q':
+                handler.on_duration_unit();
+                break;
+            case 'z':
+                handler.on_utc_offset();
+                break;
+            case 'Z':
+                handler.on_tz_name();
+                break;
+            // Alternative representation:
+            case 'E':
+            {
+                if(ptr == end)
+                    FMT_THROW(format_error("invalid format"));
+                c = *ptr++;
+                switch(c)
+                {
+                    case 'Y':
+                        handler.on_year(numeric_system::alternative);
+                        break;
+                    case 'y':
+                        handler.on_offset_year();
+                        break;
+                    case 'C':
+                        handler.on_century(numeric_system::alternative);
+                        break;
+                    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:
+                        FMT_THROW(format_error("invalid format"));
+                }
+                break;
+            }
+            case 'O':
+                if(ptr == end)
+                    FMT_THROW(format_error("invalid format"));
+                c = *ptr++;
+                switch(c)
+                {
+                    case 'y':
+                        handler.on_short_year(numeric_system::alternative);
+                        break;
+                    case 'm':
+                        handler.on_dec_month(numeric_system::alternative);
+                        break;
+                    case 'U':
+                        handler.on_dec0_week_of_year(numeric_system::alternative);
+                        break;
+                    case 'W':
+                        handler.on_dec1_week_of_year(numeric_system::alternative);
+                        break;
+                    case 'V':
+                        handler.on_iso_week_of_year(numeric_system::alternative);
+                        break;
+                    case 'd':
+                        handler.on_day_of_month(numeric_system::alternative);
+                        break;
+                    case 'e':
+                        handler.on_day_of_month_space(numeric_system::alternative);
+                        break;
+                    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:
+                        FMT_THROW(format_error("invalid format"));
+                }
+                break;
+            default:
+                FMT_THROW(format_error("invalid format"));
+        }
+        begin = ptr;
+    }
+    if(begin != ptr)
+        handler.on_text(begin, ptr);
+    return ptr;
 }
 
-template <typename Derived> struct null_chrono_spec_handler {
-  FMT_CONSTEXPR void unsupported() {
-    static_cast<Derived*>(this)->unsupported();
-  }
-  FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_offset_year() { unsupported(); }
-  FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); }
-  FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); }
-  FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
-  FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
-  FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
-  FMT_CONSTEXPR void on_full_month() { unsupported(); }
-  FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_day_of_year() { unsupported(); }
-  FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); }
-  FMT_CONSTEXPR void on_us_date() { unsupported(); }
-  FMT_CONSTEXPR void on_iso_date() { unsupported(); }
-  FMT_CONSTEXPR void on_12_hour_time() { unsupported(); }
-  FMT_CONSTEXPR void on_24_hour_time() { unsupported(); }
-  FMT_CONSTEXPR void on_iso_time() { unsupported(); }
-  FMT_CONSTEXPR void on_am_pm() { unsupported(); }
-  FMT_CONSTEXPR void on_duration_value() { unsupported(); }
-  FMT_CONSTEXPR void on_duration_unit() { unsupported(); }
-  FMT_CONSTEXPR void on_utc_offset() { unsupported(); }
-  FMT_CONSTEXPR void on_tz_name() { unsupported(); }
+template <typename Derived>
+struct null_chrono_spec_handler
+{
+    FMT_CONSTEXPR void unsupported() { static_cast<Derived *>(this)->unsupported(); }
+    FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_offset_year() { unsupported(); }
+    FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); }
+    FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); }
+    FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
+    FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
+    FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
+    FMT_CONSTEXPR void on_full_month() { unsupported(); }
+    FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_day_of_year() { unsupported(); }
+    FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); }
+    FMT_CONSTEXPR void on_us_date() { unsupported(); }
+    FMT_CONSTEXPR void on_iso_date() { unsupported(); }
+    FMT_CONSTEXPR void on_12_hour_time() { unsupported(); }
+    FMT_CONSTEXPR void on_24_hour_time() { unsupported(); }
+    FMT_CONSTEXPR void on_iso_time() { unsupported(); }
+    FMT_CONSTEXPR void on_am_pm() { unsupported(); }
+    FMT_CONSTEXPR void on_duration_value() { unsupported(); }
+    FMT_CONSTEXPR void on_duration_unit() { unsupported(); }
+    FMT_CONSTEXPR void on_utc_offset() { unsupported(); }
+    FMT_CONSTEXPR void on_tz_name() { unsupported(); }
 };
 
-struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
-  FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
-
-  template <typename Char>
-  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
-  FMT_CONSTEXPR void on_year(numeric_system) {}
-  FMT_CONSTEXPR void on_short_year(numeric_system) {}
-  FMT_CONSTEXPR void on_offset_year() {}
-  FMT_CONSTEXPR void on_century(numeric_system) {}
-  FMT_CONSTEXPR void on_iso_week_based_year() {}
-  FMT_CONSTEXPR void on_iso_week_based_short_year() {}
-  FMT_CONSTEXPR void on_abbr_weekday() {}
-  FMT_CONSTEXPR void on_full_weekday() {}
-  FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {}
-  FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {}
-  FMT_CONSTEXPR void on_abbr_month() {}
-  FMT_CONSTEXPR void on_full_month() {}
-  FMT_CONSTEXPR void on_dec_month(numeric_system) {}
-  FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {}
-  FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {}
-  FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {}
-  FMT_CONSTEXPR void on_day_of_year() {}
-  FMT_CONSTEXPR void on_day_of_month(numeric_system) {}
-  FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {}
-  FMT_CONSTEXPR void on_24_hour(numeric_system) {}
-  FMT_CONSTEXPR void on_12_hour(numeric_system) {}
-  FMT_CONSTEXPR void on_minute(numeric_system) {}
-  FMT_CONSTEXPR void on_second(numeric_system) {}
-  FMT_CONSTEXPR void on_datetime(numeric_system) {}
-  FMT_CONSTEXPR void on_loc_date(numeric_system) {}
-  FMT_CONSTEXPR void on_loc_time(numeric_system) {}
-  FMT_CONSTEXPR void on_us_date() {}
-  FMT_CONSTEXPR void on_iso_date() {}
-  FMT_CONSTEXPR void on_12_hour_time() {}
-  FMT_CONSTEXPR void on_24_hour_time() {}
-  FMT_CONSTEXPR void on_iso_time() {}
-  FMT_CONSTEXPR void on_am_pm() {}
-  FMT_CONSTEXPR void on_utc_offset() {}
-  FMT_CONSTEXPR void on_tz_name() {}
+struct tm_format_checker : null_chrono_spec_handler<tm_format_checker>
+{
+    FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
+
+    template <typename Char>
+    FMT_CONSTEXPR void on_text(const Char *, const Char *)
+    {
+    }
+    FMT_CONSTEXPR void on_year(numeric_system) {}
+    FMT_CONSTEXPR void on_short_year(numeric_system) {}
+    FMT_CONSTEXPR void on_offset_year() {}
+    FMT_CONSTEXPR void on_century(numeric_system) {}
+    FMT_CONSTEXPR void on_iso_week_based_year() {}
+    FMT_CONSTEXPR void on_iso_week_based_short_year() {}
+    FMT_CONSTEXPR void on_abbr_weekday() {}
+    FMT_CONSTEXPR void on_full_weekday() {}
+    FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {}
+    FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {}
+    FMT_CONSTEXPR void on_abbr_month() {}
+    FMT_CONSTEXPR void on_full_month() {}
+    FMT_CONSTEXPR void on_dec_month(numeric_system) {}
+    FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {}
+    FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {}
+    FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {}
+    FMT_CONSTEXPR void on_day_of_year() {}
+    FMT_CONSTEXPR void on_day_of_month(numeric_system) {}
+    FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {}
+    FMT_CONSTEXPR void on_24_hour(numeric_system) {}
+    FMT_CONSTEXPR void on_12_hour(numeric_system) {}
+    FMT_CONSTEXPR void on_minute(numeric_system) {}
+    FMT_CONSTEXPR void on_second(numeric_system) {}
+    FMT_CONSTEXPR void on_datetime(numeric_system) {}
+    FMT_CONSTEXPR void on_loc_date(numeric_system) {}
+    FMT_CONSTEXPR void on_loc_time(numeric_system) {}
+    FMT_CONSTEXPR void on_us_date() {}
+    FMT_CONSTEXPR void on_iso_date() {}
+    FMT_CONSTEXPR void on_12_hour_time() {}
+    FMT_CONSTEXPR void on_24_hour_time() {}
+    FMT_CONSTEXPR void on_iso_time() {}
+    FMT_CONSTEXPR void on_am_pm() {}
+    FMT_CONSTEXPR void on_utc_offset() {}
+    FMT_CONSTEXPR void on_tz_name() {}
 };
 
-inline const char* tm_wday_full_name(int wday) {
-  static constexpr const char* full_name_list[] = {
-      "Sunday",   "Monday", "Tuesday", "Wednesday",
-      "Thursday", "Friday", "Saturday"};
-  return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?";
+inline const char *tm_wday_full_name(int wday)
+{
+    static constexpr const char *full_name_list[] = {
+        "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
+    return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?";
 }
-inline const char* tm_wday_short_name(int wday) {
-  static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed",
-                                                    "Thu", "Fri", "Sat"};
-  return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???";
+inline const char *tm_wday_short_name(int wday)
+{
+    static constexpr const char *short_name_list[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+    return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???";
 }
 
-inline const char* tm_mon_full_name(int mon) {
-  static constexpr const char* full_name_list[] = {
-      "January", "February", "March",     "April",   "May",      "June",
-      "July",    "August",   "September", "October", "November", "December"};
-  return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?";
+inline const char *tm_mon_full_name(int mon)
+{
+    static constexpr const char *full_name_list[] = {
+        "January",
+        "February",
+        "March",
+        "April",
+        "May",
+        "June",
+        "July",
+        "August",
+        "September",
+        "October",
+        "November",
+        "December"};
+    return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?";
 }
-inline const char* tm_mon_short_name(int mon) {
-  static constexpr const char* short_name_list[] = {
-      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
-  };
-  return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???";
+inline const char *tm_mon_short_name(int mon)
+{
+    static constexpr const char *short_name_list[] = {
+        "Jan",
+        "Feb",
+        "Mar",
+        "Apr",
+        "May",
+        "Jun",
+        "Jul",
+        "Aug",
+        "Sep",
+        "Oct",
+        "Nov",
+        "Dec",
+    };
+    return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???";
 }
 
 template <typename T, typename = void>
-struct has_member_data_tm_gmtoff : std::false_type {};
+struct has_member_data_tm_gmtoff : std::false_type
+{
+};
 template <typename T>
-struct has_member_data_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>>
-    : std::true_type {};
+struct has_member_data_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>> : std::true_type
+{
+};
 
 template <typename T, typename = void>
-struct has_member_data_tm_zone : std::false_type {};
+struct has_member_data_tm_zone : std::false_type
+{
+};
 template <typename T>
-struct has_member_data_tm_zone<T, void_t<decltype(T::tm_zone)>>
-    : std::true_type {};
+struct has_member_data_tm_zone<T, void_t<decltype(T::tm_zone)>> : std::true_type
+{
+};
 
 #if FMT_USE_TZSET
-inline void tzset_once() {
-  static bool init = []() -> bool {
-    _tzset();
-    return true;
-  }();
-  ignore_unused(init);
+inline void tzset_once()
+{
+    static bool init = []() -> bool
+    {
+        _tzset();
+        return true;
+    }();
+    ignore_unused(init);
 }
 #endif
 
-template <typename OutputIt, typename Char> class tm_writer {
- private:
-  static constexpr int days_per_week = 7;
-
-  const std::locale& loc_;
-  const bool is_classic_;
-  OutputIt out_;
-  const std::tm& tm_;
-
-  auto tm_sec() const noexcept -> int {
-    FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, "");
-    return tm_.tm_sec;
-  }
-  auto tm_min() const noexcept -> int {
-    FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, "");
-    return tm_.tm_min;
-  }
-  auto tm_hour() const noexcept -> int {
-    FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, "");
-    return tm_.tm_hour;
-  }
-  auto tm_mday() const noexcept -> int {
-    FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, "");
-    return tm_.tm_mday;
-  }
-  auto tm_mon() const noexcept -> int {
-    FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, "");
-    return tm_.tm_mon;
-  }
-  auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; }
-  auto tm_wday() const noexcept -> int {
-    FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, "");
-    return tm_.tm_wday;
-  }
-  auto tm_yday() const noexcept -> int {
-    FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, "");
-    return tm_.tm_yday;
-  }
-
-  auto tm_hour12() const noexcept -> int {
-    const auto h = tm_hour();
-    const auto z = h < 12 ? h : h - 12;
-    return z == 0 ? 12 : z;
-  }
-
-  // POSIX and the C Standard are unclear or inconsistent about what %C and %y
-  // do if the year is negative or exceeds 9999. Use the convention that %C
-  // concatenated with %y yields the same output as %Y, and that %Y contains at
-  // least 4 characters, with more only if necessary.
-  auto split_year_lower(long long year) const noexcept -> int {
-    auto l = year % 100;
-    if (l < 0) l = -l;  // l in [0, 99]
-    return static_cast<int>(l);
-  }
-
-  // Algorithm:
-  // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
-  auto iso_year_weeks(long long curr_year) const noexcept -> int {
-    const auto prev_year = curr_year - 1;
-    const auto curr_p =
-        (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
-        days_per_week;
-    const auto prev_p =
-        (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
-        days_per_week;
-    return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
-  }
-  auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int {
-    return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) /
-           days_per_week;
-  }
-  auto tm_iso_week_year() const noexcept -> long long {
-    const auto year = tm_year();
-    const auto w = iso_week_num(tm_yday(), tm_wday());
-    if (w < 1) return year - 1;
-    if (w > iso_year_weeks(year)) return year + 1;
-    return year;
-  }
-  auto tm_iso_week_of_year() const noexcept -> int {
-    const auto year = tm_year();
-    const auto w = iso_week_num(tm_yday(), tm_wday());
-    if (w < 1) return iso_year_weeks(year - 1);
-    if (w > iso_year_weeks(year)) return 1;
-    return w;
-  }
-
-  void write1(int value) {
-    *out_++ = static_cast<char>('0' + to_unsigned(value) % 10);
-  }
-  void write2(int value) {
-    const char* d = digits2(to_unsigned(value) % 100);
-    *out_++ = *d++;
-    *out_++ = *d;
-  }
-
-  void write_year_extended(long long year) {
-    // At least 4 characters.
-    int width = 4;
-    if (year < 0) {
-      *out_++ = '-';
-      year = 0 - year;
-      --width;
-    }
-    uint32_or_64_or_128_t<long long> n = to_unsigned(year);
-    const int num_digits = count_digits(n);
-    if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0');
-    out_ = format_decimal<Char>(out_, n, num_digits).end;
-  }
-  void write_year(long long year) {
-    if (year >= 0 && year < 10000) {
-      write2(static_cast<int>(year / 100));
-      write2(static_cast<int>(year % 100));
-    } else {
-      write_year_extended(year);
-    }
-  }
-
-  void write_utc_offset(long offset) {
-    if (offset < 0) {
-      *out_++ = '-';
-      offset = -offset;
-    } else {
-      *out_++ = '+';
-    }
-    offset /= 60;
-    write2(static_cast<int>(offset / 60));
-    write2(static_cast<int>(offset % 60));
-  }
-  template <typename T, FMT_ENABLE_IF(has_member_data_tm_gmtoff<T>::value)>
-  void format_utc_offset_impl(const T& tm) {
-    write_utc_offset(tm.tm_gmtoff);
-  }
-  template <typename T, FMT_ENABLE_IF(!has_member_data_tm_gmtoff<T>::value)>
-  void format_utc_offset_impl(const T& tm) {
+template <typename OutputIt, typename Char>
+class tm_writer
+{
+private:
+    static constexpr int days_per_week = 7;
+
+    const std::locale &loc_;
+    const bool         is_classic_;
+    OutputIt           out_;
+    const std::tm     &tm_;
+
+    auto tm_sec() const noexcept -> int
+    {
+        FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, "");
+        return tm_.tm_sec;
+    }
+    auto tm_min() const noexcept -> int
+    {
+        FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, "");
+        return tm_.tm_min;
+    }
+    auto tm_hour() const noexcept -> int
+    {
+        FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, "");
+        return tm_.tm_hour;
+    }
+    auto tm_mday() const noexcept -> int
+    {
+        FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, "");
+        return tm_.tm_mday;
+    }
+    auto tm_mon() const noexcept -> int
+    {
+        FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, "");
+        return tm_.tm_mon;
+    }
+    auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; }
+    auto tm_wday() const noexcept -> int
+    {
+        FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, "");
+        return tm_.tm_wday;
+    }
+    auto tm_yday() const noexcept -> int
+    {
+        FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, "");
+        return tm_.tm_yday;
+    }
+
+    auto tm_hour12() const noexcept -> int
+    {
+        const auto h = tm_hour();
+        const auto z = h < 12 ? h : h - 12;
+        return z == 0 ? 12 : z;
+    }
+
+    // POSIX and the C Standard are unclear or inconsistent about what %C and %y
+    // do if the year is negative or exceeds 9999. Use the convention that %C
+    // concatenated with %y yields the same output as %Y, and that %Y contains at
+    // least 4 characters, with more only if necessary.
+    auto split_year_lower(long long year) const noexcept -> int
+    {
+        auto l = year % 100;
+        if(l < 0)
+            l = -l; // l in [0, 99]
+        return static_cast<int>(l);
+    }
+
+    // Algorithm:
+    // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
+    auto iso_year_weeks(long long curr_year) const noexcept -> int
+    {
+        const auto prev_year = curr_year - 1;
+        const auto curr_p    = (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % days_per_week;
+        const auto prev_p    = (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % days_per_week;
+        return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
+    }
+    auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int
+    {
+        return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) / days_per_week;
+    }
+    auto tm_iso_week_year() const noexcept -> long long
+    {
+        const auto year = tm_year();
+        const auto w    = iso_week_num(tm_yday(), tm_wday());
+        if(w < 1)
+            return year - 1;
+        if(w > iso_year_weeks(year))
+            return year + 1;
+        return year;
+    }
+    auto tm_iso_week_of_year() const noexcept -> int
+    {
+        const auto year = tm_year();
+        const auto w    = iso_week_num(tm_yday(), tm_wday());
+        if(w < 1)
+            return iso_year_weeks(year - 1);
+        if(w > iso_year_weeks(year))
+            return 1;
+        return w;
+    }
+
+    void write1(int value) { *out_++ = static_cast<char>('0' + to_unsigned(value) % 10); }
+    void write2(int value)
+    {
+        const char *d = digits2(to_unsigned(value) % 100);
+        *out_++       = *d++;
+        *out_++       = *d;
+    }
+
+    void write_year_extended(long long year)
+    {
+        // At least 4 characters.
+        int width = 4;
+        if(year < 0)
+        {
+            *out_++ = '-';
+            year    = 0 - year;
+            --width;
+        }
+        uint32_or_64_or_128_t<long long> n          = to_unsigned(year);
+        const int                        num_digits = count_digits(n);
+        if(width > num_digits)
+            out_ = std::fill_n(out_, width - num_digits, '0');
+        out_ = format_decimal<Char>(out_, n, num_digits).end;
+    }
+    void write_year(long long year)
+    {
+        if(year >= 0 && year < 10000)
+        {
+            write2(static_cast<int>(year / 100));
+            write2(static_cast<int>(year % 100));
+        }
+        else
+        {
+            write_year_extended(year);
+        }
+    }
+
+    void write_utc_offset(long offset)
+    {
+        if(offset < 0)
+        {
+            *out_++ = '-';
+            offset  = -offset;
+        }
+        else
+        {
+            *out_++ = '+';
+        }
+        offset /= 60;
+        write2(static_cast<int>(offset / 60));
+        write2(static_cast<int>(offset % 60));
+    }
+    template <typename T, FMT_ENABLE_IF(has_member_data_tm_gmtoff<T>::value)>
+    void format_utc_offset_impl(const T &tm)
+    {
+        write_utc_offset(tm.tm_gmtoff);
+    }
+    template <typename T, FMT_ENABLE_IF(!has_member_data_tm_gmtoff<T>::value)>
+    void format_utc_offset_impl(const T &tm)
+    {
 #if defined(_WIN32) && defined(_UCRT)
-#  if FMT_USE_TZSET
-    tzset_once();
-#  endif
-    long offset = 0;
-    _get_timezone(&offset);
-    if (tm.tm_isdst) {
-      long dstbias = 0;
-      _get_dstbias(&dstbias);
-      offset += dstbias;
-    }
-    write_utc_offset(-offset);
+#if FMT_USE_TZSET
+        tzset_once();
+#endif
+        long offset = 0;
+        _get_timezone(&offset);
+        if(tm.tm_isdst)
+        {
+            long dstbias = 0;
+            _get_dstbias(&dstbias);
+            offset += dstbias;
+        }
+        write_utc_offset(-offset);
 #else
-    ignore_unused(tm);
-    format_localized('z');
+        ignore_unused(tm);
+        format_localized('z');
 #endif
-  }
+    }
 
-  template <typename T, FMT_ENABLE_IF(has_member_data_tm_zone<T>::value)>
-  void format_tz_name_impl(const T& tm) {
-    if (is_classic_)
-      out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
-    else
-      format_localized('Z');
-  }
-  template <typename T, FMT_ENABLE_IF(!has_member_data_tm_zone<T>::value)>
-  void format_tz_name_impl(const T&) {
-    format_localized('Z');
-  }
-
-  void format_localized(char format, char modifier = 0) {
-    out_ = write<Char>(out_, tm_, loc_, format, modifier);
-  }
-
- public:
-  tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm)
-      : loc_(loc),
-        is_classic_(loc_ == get_classic_locale()),
-        out_(out),
-        tm_(tm) {}
-
-  OutputIt out() const { return out_; }
-
-  FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
-    out_ = copy_str<Char>(begin, end, out_);
-  }
-
-  void on_abbr_weekday() {
-    if (is_classic_)
-      out_ = write(out_, tm_wday_short_name(tm_wday()));
-    else
-      format_localized('a');
-  }
-  void on_full_weekday() {
-    if (is_classic_)
-      out_ = write(out_, tm_wday_full_name(tm_wday()));
-    else
-      format_localized('A');
-  }
-  void on_dec0_weekday(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday());
-    format_localized('w', 'O');
-  }
-  void on_dec1_weekday(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard) {
-      auto wday = tm_wday();
-      write1(wday == 0 ? days_per_week : wday);
-    } else {
-      format_localized('u', 'O');
-    }
-  }
-
-  void on_abbr_month() {
-    if (is_classic_)
-      out_ = write(out_, tm_mon_short_name(tm_mon()));
-    else
-      format_localized('b');
-  }
-  void on_full_month() {
-    if (is_classic_)
-      out_ = write(out_, tm_mon_full_name(tm_mon()));
-    else
-      format_localized('B');
-  }
-
-  void on_datetime(numeric_system ns) {
-    if (is_classic_) {
-      on_abbr_weekday();
-      *out_++ = ' ';
-      on_abbr_month();
-      *out_++ = ' ';
-      on_day_of_month_space(numeric_system::standard);
-      *out_++ = ' ';
-      on_iso_time();
-      *out_++ = ' ';
-      on_year(numeric_system::standard);
-    } else {
-      format_localized('c', ns == numeric_system::standard ? '\0' : 'E');
-    }
-  }
-  void on_loc_date(numeric_system ns) {
-    if (is_classic_)
-      on_us_date();
-    else
-      format_localized('x', ns == numeric_system::standard ? '\0' : 'E');
-  }
-  void on_loc_time(numeric_system ns) {
-    if (is_classic_)
-      on_iso_time();
-    else
-      format_localized('X', ns == numeric_system::standard ? '\0' : 'E');
-  }
-  void on_us_date() {
-    char buf[8];
-    write_digit2_separated(buf, to_unsigned(tm_mon() + 1),
-                           to_unsigned(tm_mday()),
-                           to_unsigned(split_year_lower(tm_year())), '/');
-    out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
-  }
-  void on_iso_date() {
-    auto year = tm_year();
-    char buf[10];
-    size_t offset = 0;
-    if (year >= 0 && year < 10000) {
-      copy2(buf, digits2(static_cast<size_t>(year / 100)));
-    } else {
-      offset = 4;
-      write_year_extended(year);
-      year = 0;
-    }
-    write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100),
-                           to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
-                           '-');
-    out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_);
-  }
-
-  void on_utc_offset() { format_utc_offset_impl(tm_); }
-  void on_tz_name() { format_tz_name_impl(tm_); }
-
-  void on_year(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard)
-      return write_year(tm_year());
-    format_localized('Y', 'E');
-  }
-  void on_short_year(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard)
-      return write2(split_year_lower(tm_year()));
-    format_localized('y', 'O');
-  }
-  void on_offset_year() {
-    if (is_classic_) return write2(split_year_lower(tm_year()));
-    format_localized('y', 'E');
-  }
-
-  void on_century(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard) {
-      auto year = tm_year();
-      auto upper = year / 100;
-      if (year >= -99 && year < 0) {
-        // Zero upper on negative year.
-        *out_++ = '-';
-        *out_++ = '0';
-      } else if (upper >= 0 && upper < 100) {
-        write2(static_cast<int>(upper));
-      } else {
-        out_ = write<Char>(out_, upper);
-      }
-    } else {
-      format_localized('C', 'E');
-    }
-  }
-
-  void on_dec_month(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard)
-      return write2(tm_mon() + 1);
-    format_localized('m', 'O');
-  }
-
-  void on_dec0_week_of_year(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard)
-      return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week);
-    format_localized('U', 'O');
-  }
-  void on_dec1_week_of_year(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard) {
-      auto wday = tm_wday();
-      write2((tm_yday() + days_per_week -
-              (wday == 0 ? (days_per_week - 1) : (wday - 1))) /
-             days_per_week);
-    } else {
-      format_localized('W', 'O');
-    }
-  }
-  void on_iso_week_of_year(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard)
-      return write2(tm_iso_week_of_year());
-    format_localized('V', 'O');
-  }
-
-  void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
-  void on_iso_week_based_short_year() {
-    write2(split_year_lower(tm_iso_week_year()));
-  }
-
-  void on_day_of_year() {
-    auto yday = tm_yday() + 1;
-    write1(yday / 100);
-    write2(yday % 100);
-  }
-  void on_day_of_month(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday());
-    format_localized('d', 'O');
-  }
-  void on_day_of_month_space(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard) {
-      auto mday = to_unsigned(tm_mday()) % 100;
-      const char* d2 = digits2(mday);
-      *out_++ = mday < 10 ? ' ' : d2[0];
-      *out_++ = d2[1];
-    } else {
-      format_localized('e', 'O');
-    }
-  }
-
-  void on_24_hour(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour());
-    format_localized('H', 'O');
-  }
-  void on_12_hour(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard)
-      return write2(tm_hour12());
-    format_localized('I', 'O');
-  }
-  void on_minute(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard) return write2(tm_min());
-    format_localized('M', 'O');
-  }
-  void on_second(numeric_system ns) {
-    if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec());
-    format_localized('S', 'O');
-  }
-
-  void on_12_hour_time() {
-    if (is_classic_) {
-      char buf[8];
-      write_digit2_separated(buf, to_unsigned(tm_hour12()),
-                             to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
-      out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
-      *out_++ = ' ';
-      on_am_pm();
-    } else {
-      format_localized('r');
-    }
-  }
-  void on_24_hour_time() {
-    write2(tm_hour());
-    *out_++ = ':';
-    write2(tm_min());
-  }
-  void on_iso_time() {
-    char buf[8];
-    write_digit2_separated(buf, to_unsigned(tm_hour()), to_unsigned(tm_min()),
-                           to_unsigned(tm_sec()), ':');
-    out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
-  }
-
-  void on_am_pm() {
-    if (is_classic_) {
-      *out_++ = tm_hour() < 12 ? 'A' : 'P';
-      *out_++ = 'M';
-    } else {
-      format_localized('p');
-    }
-  }
-
-  // These apply to chrono durations but not tm.
-  void on_duration_value() {}
-  void on_duration_unit() {}
+    template <typename T, FMT_ENABLE_IF(has_member_data_tm_zone<T>::value)>
+    void format_tz_name_impl(const T &tm)
+    {
+        if(is_classic_)
+            out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
+        else
+            format_localized('Z');
+    }
+    template <typename T, FMT_ENABLE_IF(!has_member_data_tm_zone<T>::value)>
+    void format_tz_name_impl(const T &)
+    {
+        format_localized('Z');
+    }
+
+    void format_localized(char format, char modifier = 0)
+    {
+        out_ = write<Char>(out_, tm_, loc_, format, modifier);
+    }
+
+public:
+    tm_writer(const std::locale &loc, OutputIt out, const std::tm &tm) :
+        loc_(loc), is_classic_(loc_ == get_classic_locale()), out_(out), tm_(tm)
+    {
+    }
+
+    OutputIt out() const
+    {
+        return out_;
+    }
+
+    FMT_CONSTEXPR void on_text(const Char *begin, const Char *end)
+    {
+        out_ = copy_str<Char>(begin, end, out_);
+    }
+
+    void on_abbr_weekday()
+    {
+        if(is_classic_)
+            out_ = write(out_, tm_wday_short_name(tm_wday()));
+        else
+            format_localized('a');
+    }
+    void on_full_weekday()
+    {
+        if(is_classic_)
+            out_ = write(out_, tm_wday_full_name(tm_wday()));
+        else
+            format_localized('A');
+    }
+    void on_dec0_weekday(numeric_system ns)
+    {
+        if(is_classic_ || ns == numeric_system::standard)
+            return write1(tm_wday());
+        format_localized('w', 'O');
+    }
+    void on_dec1_weekday(numeric_system ns)
+    {
+        if(is_classic_ || ns == numeric_system::standard)
+        {
+            auto wday = tm_wday();
+            write1(wday == 0 ? days_per_week : wday);
+        }
+        else
+        {
+            format_localized('u', 'O');
+        }
+    }
+
+    void on_abbr_month()
+    {
+        if(is_classic_)
+            out_ = write(out_, tm_mon_short_name(tm_mon()));
+        else
+            format_localized('b');
+    }
+    void on_full_month()
+    {
+        if(is_classic_)
+            out_ = write(out_, tm_mon_full_name(tm_mon()));
+        else
+            format_localized('B');
+    }
+
+    void on_datetime(numeric_system ns)
+    {
+        if(is_classic_)
+        {
+            on_abbr_weekday();
+            *out_++ = ' ';
+            on_abbr_month();
+            *out_++ = ' ';
+            on_day_of_month_space(numeric_system::standard);
+            *out_++ = ' ';
+            on_iso_time();
+            *out_++ = ' ';
+            on_year(numeric_system::standard);
+        }
+        else
+        {
+            format_localized('c', ns == numeric_system::standard ? '\0' : 'E');
+        }
+    }
+    void on_loc_date(numeric_system ns)
+    {
+        if(is_classic_)
+            on_us_date();
+        else
+            format_localized('x', ns == numeric_system::standard ? '\0' : 'E');
+    }
+    void on_loc_time(numeric_system ns)
+    {
+        if(is_classic_)
+            on_iso_time();
+        else
+            format_localized('X', ns == numeric_system::standard ? '\0' : 'E');
+    }
+    void on_us_date()
+    {
+        char buf[8];
+        write_digit2_separated(
+            buf, to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), to_unsigned(split_year_lower(tm_year())), '/');
+        out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
+    }
+    void on_iso_date()
+    {
+        auto   year = tm_year();
+        char   buf[10];
+        size_t offset = 0;
+        if(year >= 0 && year < 10000)
+        {
+            copy2(buf, digits2(static_cast<size_t>(year / 100)));
+        }
+        else
+        {
+            offset = 4;
+            write_year_extended(year);
+            year = 0;
+        }
+        write_digit2_separated(
+            buf + 2, static_cast<unsigned>(year % 100), to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), '-');
+        out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_);
+    }
+
+    void on_utc_offset()
+    {
+        format_utc_offset_impl(tm_);
+    }
+    void on_tz_name()
+    {
+        format_tz_name_impl(tm_);
+    }
+
+    void on_year(numeric_system ns)
+    {
+        if(is_classic_ || ns == numeric_system::standard)
+            return write_year(tm_year());
+        format_localized('Y', 'E');
+    }
+    void on_short_year(numeric_system ns)
+    {
+        if(is_classic_ || ns == numeric_system::standard)
+            return write2(split_year_lower(tm_year()));
+        format_localized('y', 'O');
+    }
+    void on_offset_year()
+    {
+        if(is_classic_)
+            return write2(split_year_lower(tm_year()));
+        format_localized('y', 'E');
+    }
+
+    void on_century(numeric_system ns)
+    {
+        if(is_classic_ || ns == numeric_system::standard)
+        {
+            auto year  = tm_year();
+            auto upper = year / 100;
+            if(year >= -99 && year < 0)
+            {
+                // Zero upper on negative year.
+                *out_++ = '-';
+                *out_++ = '0';
+            }
+            else if(upper >= 0 && upper < 100)
+            {
+                write2(static_cast<int>(upper));
+            }
+            else
+            {
+                out_ = write<Char>(out_, upper);
+            }
+        }
+        else
+        {
+            format_localized('C', 'E');
+        }
+    }
+
+    void on_dec_month(numeric_system ns)
+    {
+        if(is_classic_ || ns == numeric_system::standard)
+            return write2(tm_mon() + 1);
+        format_localized('m', 'O');
+    }
+
+    void on_dec0_week_of_year(numeric_system ns)
+    {
+        if(is_classic_ || ns == numeric_system::standard)
+            return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week);
+        format_localized('U', 'O');
+    }
+    void on_dec1_week_of_year(numeric_system ns)
+    {
+        if(is_classic_ || ns == numeric_system::standard)
+        {
+            auto wday = tm_wday();
+            write2((tm_yday() + days_per_week - (wday == 0 ? (days_per_week - 1) : (wday - 1))) / days_per_week);
+        }
+        else
+        {
+            format_localized('W', 'O');
+        }
+    }
+    void on_iso_week_of_year(numeric_system ns)
+    {
+        if(is_classic_ || ns == numeric_system::standard)
+            return write2(tm_iso_week_of_year());
+        format_localized('V', 'O');
+    }
+
+    void on_iso_week_based_year()
+    {
+        write_year(tm_iso_week_year());
+    }
+    void on_iso_week_based_short_year()
+    {
+        write2(split_year_lower(tm_iso_week_year()));
+    }
+
+    void on_day_of_year()
+    {
+        auto yday = tm_yday() + 1;
+        write1(yday / 100);
+        write2(yday % 100);
+    }
+    void on_day_of_month(numeric_system ns)
+    {
+        if(is_classic_ || ns == numeric_system::standard)
+            return write2(tm_mday());
+        format_localized('d', 'O');
+    }
+    void on_day_of_month_space(numeric_system ns)
+    {
+        if(is_classic_ || ns == numeric_system::standard)
+        {
+            auto        mday = to_unsigned(tm_mday()) % 100;
+            const char *d2   = digits2(mday);
+            *out_++          = mday < 10 ? ' ' : d2[0];
+            *out_++          = d2[1];
+        }
+        else
+        {
+            format_localized('e', 'O');
+        }
+    }
+
+    void on_24_hour(numeric_system ns)
+    {
+        if(is_classic_ || ns == numeric_system::standard)
+            return write2(tm_hour());
+        format_localized('H', 'O');
+    }
+    void on_12_hour(numeric_system ns)
+    {
+        if(is_classic_ || ns == numeric_system::standard)
+            return write2(tm_hour12());
+        format_localized('I', 'O');
+    }
+    void on_minute(numeric_system ns)
+    {
+        if(is_classic_ || ns == numeric_system::standard)
+            return write2(tm_min());
+        format_localized('M', 'O');
+    }
+    void on_second(numeric_system ns)
+    {
+        if(is_classic_ || ns == numeric_system::standard)
+            return write2(tm_sec());
+        format_localized('S', 'O');
+    }
+
+    void on_12_hour_time()
+    {
+        if(is_classic_)
+        {
+            char buf[8];
+            write_digit2_separated(buf, to_unsigned(tm_hour12()), to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
+            out_    = copy_str<Char>(std::begin(buf), std::end(buf), out_);
+            *out_++ = ' ';
+            on_am_pm();
+        }
+        else
+        {
+            format_localized('r');
+        }
+    }
+    void on_24_hour_time()
+    {
+        write2(tm_hour());
+        *out_++ = ':';
+        write2(tm_min());
+    }
+    void on_iso_time()
+    {
+        char buf[8];
+        write_digit2_separated(buf, to_unsigned(tm_hour()), to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
+        out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
+    }
+
+    void on_am_pm()
+    {
+        if(is_classic_)
+        {
+            *out_++ = tm_hour() < 12 ? 'A' : 'P';
+            *out_++ = 'M';
+        }
+        else
+        {
+            format_localized('p');
+        }
+    }
+
+    // These apply to chrono durations but not tm.
+    void on_duration_value() {}
+    void on_duration_unit() {}
 };
 
-struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
-  FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
-
-  template <typename Char>
-  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
-  FMT_CONSTEXPR void on_24_hour(numeric_system) {}
-  FMT_CONSTEXPR void on_12_hour(numeric_system) {}
-  FMT_CONSTEXPR void on_minute(numeric_system) {}
-  FMT_CONSTEXPR void on_second(numeric_system) {}
-  FMT_CONSTEXPR void on_12_hour_time() {}
-  FMT_CONSTEXPR void on_24_hour_time() {}
-  FMT_CONSTEXPR void on_iso_time() {}
-  FMT_CONSTEXPR void on_am_pm() {}
-  FMT_CONSTEXPR void on_duration_value() {}
-  FMT_CONSTEXPR void on_duration_unit() {}
+struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker>
+{
+    FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
+
+    template <typename Char>
+    FMT_CONSTEXPR void on_text(const Char *, const Char *)
+    {
+    }
+    FMT_CONSTEXPR void on_24_hour(numeric_system) {}
+    FMT_CONSTEXPR void on_12_hour(numeric_system) {}
+    FMT_CONSTEXPR void on_minute(numeric_system) {}
+    FMT_CONSTEXPR void on_second(numeric_system) {}
+    FMT_CONSTEXPR void on_12_hour_time() {}
+    FMT_CONSTEXPR void on_24_hour_time() {}
+    FMT_CONSTEXPR void on_iso_time() {}
+    FMT_CONSTEXPR void on_am_pm() {}
+    FMT_CONSTEXPR void on_duration_value() {}
+    FMT_CONSTEXPR void on_duration_unit() {}
 };
 
 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-inline bool isfinite(T) {
-  return true;
+inline bool isfinite(T)
+{
+    return true;
 }
 
 // Converts value to Int and checks that it's in the range [0, upper).
 template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
-inline Int to_nonnegative_int(T value, Int upper) {
-  FMT_ASSERT(std::is_unsigned<Int>::value ||
-             (value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
-             "invalid value");
-  (void)upper;
-  return static_cast<Int>(value);
+inline Int to_nonnegative_int(T value, Int upper)
+{
+    FMT_ASSERT(
+        std::is_unsigned<Int>::value || (value >= 0 && to_unsigned(value) <= to_unsigned(upper)), "invalid value");
+    (void) upper;
+    return static_cast<Int>(value);
 }
 template <typename T, typename Int, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-inline Int to_nonnegative_int(T value, Int upper) {
-  if (value < 0 || value > static_cast<T>(upper))
-    FMT_THROW(format_error("invalid value"));
-  return static_cast<Int>(value);
+inline Int to_nonnegative_int(T value, Int upper)
+{
+    if(value < 0 || value > static_cast<T>(upper))
+        FMT_THROW(format_error("invalid value"));
+    return static_cast<Int>(value);
 }
 
 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-inline T mod(T x, int y) {
-  return x % static_cast<T>(y);
+inline T mod(T x, int y)
+{
+    return x % static_cast<T>(y);
 }
 template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
-inline T mod(T x, int y) {
-  return std::fmod(x, static_cast<T>(y));
+inline T mod(T x, int y)
+{
+    return std::fmod(x, static_cast<T>(y));
 }
 
 // If T is an integral type, maps T to its unsigned counterpart, otherwise
 // leaves it unchanged (unlike std::make_unsigned).
 template <typename T, bool INTEGRAL = std::is_integral<T>::value>
-struct make_unsigned_or_unchanged {
-  using type = T;
+struct make_unsigned_or_unchanged
+{
+    using type = T;
 };
 
-template <typename T> struct make_unsigned_or_unchanged<T, true> {
-  using type = typename std::make_unsigned<T>::type;
+template <typename T>
+struct make_unsigned_or_unchanged<T, true>
+{
+    using type = typename std::make_unsigned<T>::type;
 };
 
 #if FMT_SAFE_DURATION_CAST
 // throwing version of safe_duration_cast
 template <typename To, typename FromRep, typename FromPeriod>
-To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
-  int ec;
-  To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
-  if (ec) FMT_THROW(format_error("cannot format duration"));
-  return to;
+To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from)
+{
+    int ec;
+    To  to = safe_duration_cast::safe_duration_cast<To>(from, ec);
+    if(ec)
+        FMT_THROW(format_error("cannot format duration"));
+    return to;
 }
 #endif
 
-template <typename Rep, typename Period,
-          FMT_ENABLE_IF(std::is_integral<Rep>::value)>
-inline std::chrono::duration<Rep, std::milli> get_milliseconds(
-    std::chrono::duration<Rep, Period> d) {
-  // this may overflow and/or the result may not fit in the
-  // target type.
+template <typename Rep, typename Period, FMT_ENABLE_IF(std::is_integral<Rep>::value)>
+inline std::chrono::duration<Rep, std::milli> get_milliseconds(std::chrono::duration<Rep, Period> d)
+{
+    // this may overflow and/or the result may not fit in the
+    // target type.
 #if FMT_SAFE_DURATION_CAST
-  using CommonSecondsType =
-      typename std::common_type<decltype(d), std::chrono::seconds>::type;
-  const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
-  const auto d_as_whole_seconds =
-      fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
-  // this conversion should be nonproblematic
-  const auto diff = d_as_common - d_as_whole_seconds;
-  const auto ms =
-      fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
-  return ms;
+    using CommonSecondsType       = typename std::common_type<decltype(d), std::chrono::seconds>::type;
+    const auto d_as_common        = fmt_safe_duration_cast<CommonSecondsType>(d);
+    const auto d_as_whole_seconds = fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
+    // this conversion should be nonproblematic
+    const auto diff = d_as_common - d_as_whole_seconds;
+    const auto ms   = fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
+    return ms;
 #else
-  auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
-  return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
+    auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
+    return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
 #endif
 }
 
 // Counts the number of fractional digits in the range [0, 18] according to the
 // C++20 spec. If more than 18 fractional digits are required then returns 6 for
 // microseconds precision.
-template <long long Num, long long Den, int N = 0,
-          bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
-struct count_fractional_digits {
-  static constexpr int value =
-      Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
+template <long long Num, long long Den, int N = 0, bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
+struct count_fractional_digits
+{
+    static constexpr int value = Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
 };
 
 // Base case that doesn't instantiate any more templates
 // in order to avoid overflow.
 template <long long Num, long long Den, int N>
-struct count_fractional_digits<Num, Den, N, false> {
-  static constexpr int value = (Num % Den == 0) ? N : 6;
+struct count_fractional_digits<Num, Den, N, false>
+{
+    static constexpr int value = (Num % Den == 0) ? N : 6;
 };
 
-constexpr long long pow10(std::uint32_t n) {
-  return n == 0 ? 1 : 10 * pow10(n - 1);
+constexpr long long pow10(std::uint32_t n)
+{
+    return n == 0 ? 1 : 10 * pow10(n - 1);
 }
 
-template <class Rep, class Period,
-          FMT_ENABLE_IF(std::numeric_limits<Rep>::is_signed)>
-constexpr std::chrono::duration<Rep, Period> abs(
-    std::chrono::duration<Rep, Period> d) {
-  // We need to compare the duration using the count() method directly
-  // due to a compiler bug in clang-11 regarding the spaceship operator,
-  // when -Wzero-as-null-pointer-constant is enabled.
-  // In clang-12 the bug has been fixed. See
-  // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example:
-  // https://www.godbolt.org/z/Knbb5joYx.
-  return d.count() >= d.zero().count() ? d : -d;
+template <class Rep, class Period, FMT_ENABLE_IF(std::numeric_limits<Rep>::is_signed)>
+constexpr std::chrono::duration<Rep, Period> abs(std::chrono::duration<Rep, Period> d)
+{
+    // We need to compare the duration using the count() method directly
+    // due to a compiler bug in clang-11 regarding the spaceship operator,
+    // when -Wzero-as-null-pointer-constant is enabled.
+    // In clang-12 the bug has been fixed. See
+    // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example:
+    // https://www.godbolt.org/z/Knbb5joYx.
+    return d.count() >= d.zero().count() ? d : -d;
 }
 
-template <class Rep, class Period,
-          FMT_ENABLE_IF(!std::numeric_limits<Rep>::is_signed)>
-constexpr std::chrono::duration<Rep, Period> abs(
-    std::chrono::duration<Rep, Period> d) {
-  return d;
+template <class Rep, class Period, FMT_ENABLE_IF(!std::numeric_limits<Rep>::is_signed)>
+constexpr std::chrono::duration<Rep, Period> abs(std::chrono::duration<Rep, Period> d)
+{
+    return d;
 }
 
-template <typename Char, typename Rep, typename OutputIt,
-          FMT_ENABLE_IF(std::is_integral<Rep>::value)>
-OutputIt format_duration_value(OutputIt out, Rep val, int) {
-  return write<Char>(out, val);
+template <typename Char, typename Rep, typename OutputIt, FMT_ENABLE_IF(std::is_integral<Rep>::value)>
+OutputIt format_duration_value(OutputIt out, Rep val, int)
+{
+    return write<Char>(out, val);
 }
 
-template <typename Char, typename Rep, typename OutputIt,
-          FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
-OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
-  auto specs = basic_format_specs<Char>();
-  specs.precision = precision;
-  specs.type = precision >= 0 ? presentation_type::fixed_lower
-                              : presentation_type::general_lower;
-  return write<Char>(out, val, specs);
+template <typename Char, typename Rep, typename OutputIt, FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
+OutputIt format_duration_value(OutputIt out, Rep val, int precision)
+{
+    auto specs      = basic_format_specs<Char>();
+    specs.precision = precision;
+    specs.type      = precision >= 0 ? presentation_type::fixed_lower : presentation_type::general_lower;
+    return write<Char>(out, val, specs);
 }
 
 template <typename Char, typename OutputIt>
-OutputIt copy_unit(string_view unit, OutputIt out, Char) {
-  return std::copy(unit.begin(), unit.end(), out);
+OutputIt copy_unit(string_view unit, OutputIt out, Char)
+{
+    return std::copy(unit.begin(), unit.end(), out);
 }
 
 template <typename OutputIt>
-OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
-  // This works when wchar_t is UTF-32 because units only contain characters
-  // that have the same representation in UTF-16 and UTF-32.
-  utf8_to_utf16 u(unit);
-  return std::copy(u.c_str(), u.c_str() + u.size(), out);
+OutputIt copy_unit(string_view unit, OutputIt out, wchar_t)
+{
+    // This works when wchar_t is UTF-32 because units only contain characters
+    // that have the same representation in UTF-16 and UTF-32.
+    utf8_to_utf16 u(unit);
+    return std::copy(u.c_str(), u.c_str() + u.size(), out);
 }
 
 template <typename Char, typename Period, typename OutputIt>
-OutputIt format_duration_unit(OutputIt out) {
-  if (const char* unit = get_units<Period>())
-    return copy_unit(string_view(unit), out, Char());
-  *out++ = '[';
-  out = write<Char>(out, Period::num);
-  if (const_check(Period::den != 1)) {
-    *out++ = '/';
-    out = write<Char>(out, Period::den);
-  }
-  *out++ = ']';
-  *out++ = 's';
-  return out;
+OutputIt format_duration_unit(OutputIt out)
+{
+    if(const char *unit = get_units<Period>())
+        return copy_unit(string_view(unit), out, Char());
+    *out++ = '[';
+    out    = write<Char>(out, Period::num);
+    if(const_check(Period::den != 1))
+    {
+        *out++ = '/';
+        out    = write<Char>(out, Period::den);
+    }
+    *out++ = ']';
+    *out++ = 's';
+    return out;
 }
 
-class get_locale {
- private:
-  union {
-    std::locale locale_;
-  };
-  bool has_locale_ = false;
-
- public:
-  get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
-    if (localized)
-      ::new (&locale_) std::locale(loc.template get<std::locale>());
-  }
-  ~get_locale() {
-    if (has_locale_) locale_.~locale();
-  }
-  operator const std::locale&() const {
-    return has_locale_ ? locale_ : get_classic_locale();
-  }
+class get_locale
+{
+private:
+    union
+    {
+        std::locale locale_;
+    };
+    bool has_locale_ = false;
+
+public:
+    get_locale(bool localized, locale_ref loc) : has_locale_(localized)
+    {
+        if(localized)
+            ::new(&locale_) std::locale(loc.template get<std::locale>());
+    }
+    ~get_locale()
+    {
+        if(has_locale_)
+            locale_.~locale();
+    }
+    operator const std::locale &() const { return has_locale_ ? locale_ : get_classic_locale(); }
 };
 
-template <typename FormatContext, typename OutputIt, typename Rep,
-          typename Period>
-struct chrono_formatter {
-  FormatContext& context;
-  OutputIt out;
-  int precision;
-  bool localized = false;
-  // rep is unsigned to avoid overflow.
-  using rep =
-      conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
-                    unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
-  rep val;
-  using seconds = std::chrono::duration<rep>;
-  seconds s;
-  using milliseconds = std::chrono::duration<rep, std::milli>;
-  bool negative;
-
-  using char_type = typename FormatContext::char_type;
-  using tm_writer_type = tm_writer<OutputIt, char_type>;
-
-  chrono_formatter(FormatContext& ctx, OutputIt o,
-                   std::chrono::duration<Rep, Period> d)
-      : context(ctx),
-        out(o),
-        val(static_cast<rep>(d.count())),
-        negative(false) {
-    if (d.count() < 0) {
-      val = 0 - val;
-      negative = true;
-    }
+template <typename FormatContext, typename OutputIt, typename Rep, typename Period>
+struct chrono_formatter
+{
+    FormatContext &context;
+    OutputIt       out;
+    int            precision;
+    bool           localized = false;
+    // rep is unsigned to avoid overflow.
+    using rep = conditional_t<
+        std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
+        unsigned,
+        typename make_unsigned_or_unchanged<Rep>::type>;
+    rep val;
+    using seconds = std::chrono::duration<rep>;
+    seconds s;
+    using milliseconds = std::chrono::duration<rep, std::milli>;
+    bool negative;
+
+    using char_type      = typename FormatContext::char_type;
+    using tm_writer_type = tm_writer<OutputIt, char_type>;
+
+    chrono_formatter(FormatContext &ctx, OutputIt o, std::chrono::duration<Rep, Period> d) :
+        context(ctx), out(o), val(static_cast<rep>(d.count())), negative(false)
+    {
+        if(d.count() < 0)
+        {
+            val      = 0 - val;
+            negative = true;
+        }
 
-    // this may overflow and/or the result may not fit in the
-    // target type.
+        // this may overflow and/or the result may not fit in the
+        // target type.
 #if FMT_SAFE_DURATION_CAST
-    // might need checked conversion (rep!=Rep)
-    auto tmpval = std::chrono::duration<rep, Period>(val);
-    s = fmt_safe_duration_cast<seconds>(tmpval);
+        // might need checked conversion (rep!=Rep)
+        auto tmpval = std::chrono::duration<rep, Period>(val);
+        s           = fmt_safe_duration_cast<seconds>(tmpval);
 #else
-    s = std::chrono::duration_cast<seconds>(
-        std::chrono::duration<rep, Period>(val));
+        s = std::chrono::duration_cast<seconds>(std::chrono::duration<rep, Period>(val));
 #endif
-  }
+    }
+
+    // returns true if nan or inf, writes to out.
+    bool handle_nan_inf()
+    {
+        if(isfinite(val))
+        {
+            return false;
+        }
+        if(isnan(val))
+        {
+            write_nan();
+            return true;
+        }
+        // must be +-inf
+        if(val > 0)
+        {
+            write_pinf();
+        }
+        else
+        {
+            write_ninf();
+        }
+        return true;
+    }
 
-  // returns true if nan or inf, writes to out.
-  bool handle_nan_inf() {
-    if (isfinite(val)) {
-      return false;
+    Rep hour() const
+    {
+        return static_cast<Rep>(mod((s.count() / 3600), 24));
     }
-    if (isnan(val)) {
-      write_nan();
-      return true;
+
+    Rep hour12() const
+    {
+        Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
+        return hour <= 0 ? 12 : hour;
     }
-    // must be +-inf
-    if (val > 0) {
-      write_pinf();
-    } else {
-      write_ninf();
+
+    Rep minute() const
+    {
+        return static_cast<Rep>(mod((s.count() / 60), 60));
     }
-    return true;
-  }
-
-  Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
-
-  Rep hour12() const {
-    Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
-    return hour <= 0 ? 12 : hour;
-  }
-
-  Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); }
-  Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); }
-
-  std::tm time() const {
-    auto time = std::tm();
-    time.tm_hour = to_nonnegative_int(hour(), 24);
-    time.tm_min = to_nonnegative_int(minute(), 60);
-    time.tm_sec = to_nonnegative_int(second(), 60);
-    return time;
-  }
-
-  void write_sign() {
-    if (negative) {
-      *out++ = '-';
-      negative = false;
-    }
-  }
-
-  void write(Rep value, int width) {
-    write_sign();
-    if (isnan(value)) return write_nan();
-    uint32_or_64_or_128_t<int> n =
-        to_unsigned(to_nonnegative_int(value, max_value<int>()));
-    int num_digits = detail::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).end;
-  }
-
-  template <typename Duration> void write_fractional_seconds(Duration d) {
-    FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
-    constexpr auto num_fractional_digits =
-        count_fractional_digits<Duration::period::num,
-                                Duration::period::den>::value;
-
-    using subsecond_precision = std::chrono::duration<
-        typename std::common_type<typename Duration::rep,
-                                  std::chrono::seconds::rep>::type,
-        std::ratio<1, detail::pow10(num_fractional_digits)>>;
-    if (std::ratio_less<typename subsecond_precision::period,
-                        std::chrono::seconds::period>::value) {
-      *out++ = '.';
-      auto fractional =
-          detail::abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d);
-      auto subseconds =
-          std::chrono::treat_as_floating_point<
-              typename subsecond_precision::rep>::value
-              ? fractional.count()
-              : std::chrono::duration_cast<subsecond_precision>(fractional)
-                    .count();
-      uint32_or_64_or_128_t<long long> n =
-          to_unsigned(to_nonnegative_int(subseconds, max_value<long long>()));
-      int num_digits = detail::count_digits(n);
-      if (num_fractional_digits > num_digits)
-        out = std::fill_n(out, num_fractional_digits - num_digits, '0');
-      out = format_decimal<char_type>(out, n, num_digits).end;
-    }
-  }
-
-  void write_nan() { std::copy_n("nan", 3, out); }
-  void write_pinf() { std::copy_n("inf", 3, out); }
-  void write_ninf() { std::copy_n("-inf", 4, out); }
-
-  template <typename Callback, typename... Args>
-  void format_tm(const tm& time, Callback cb, Args... args) {
-    if (isnan(val)) return write_nan();
-    get_locale loc(localized, context.locale());
-    auto w = tm_writer_type(loc, out, time);
-    (w.*cb)(args...);
-    out = w.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_year(numeric_system) {}
-  void on_short_year(numeric_system) {}
-  void on_offset_year() {}
-  void on_century(numeric_system) {}
-  void on_iso_week_based_year() {}
-  void on_iso_week_based_short_year() {}
-  void on_dec_month(numeric_system) {}
-  void on_dec0_week_of_year(numeric_system) {}
-  void on_dec1_week_of_year(numeric_system) {}
-  void on_iso_week_of_year(numeric_system) {}
-  void on_day_of_year() {}
-  void on_day_of_month(numeric_system) {}
-  void on_day_of_month_space(numeric_system) {}
-
-  void on_24_hour(numeric_system ns) {
-    if (handle_nan_inf()) return;
-
-    if (ns == numeric_system::standard) return write(hour(), 2);
-    auto time = tm();
-    time.tm_hour = to_nonnegative_int(hour(), 24);
-    format_tm(time, &tm_writer_type::on_24_hour, ns);
-  }
-
-  void on_12_hour(numeric_system ns) {
-    if (handle_nan_inf()) return;
-
-    if (ns == numeric_system::standard) return write(hour12(), 2);
-    auto time = tm();
-    time.tm_hour = to_nonnegative_int(hour12(), 12);
-    format_tm(time, &tm_writer_type::on_12_hour, ns);
-  }
-
-  void on_minute(numeric_system ns) {
-    if (handle_nan_inf()) return;
-
-    if (ns == numeric_system::standard) return write(minute(), 2);
-    auto time = tm();
-    time.tm_min = to_nonnegative_int(minute(), 60);
-    format_tm(time, &tm_writer_type::on_minute, ns);
-  }
-
-  void on_second(numeric_system ns) {
-    if (handle_nan_inf()) return;
-
-    if (ns == numeric_system::standard) {
-      if (std::is_floating_point<rep>::value) {
+    Rep second() const
+    {
+        return static_cast<Rep>(mod(s.count(), 60));
+    }
+
+    std::tm time() const
+    {
+        auto time    = std::tm();
+        time.tm_hour = to_nonnegative_int(hour(), 24);
+        time.tm_min  = to_nonnegative_int(minute(), 60);
+        time.tm_sec  = to_nonnegative_int(second(), 60);
+        return time;
+    }
+
+    void write_sign()
+    {
+        if(negative)
+        {
+            *out++   = '-';
+            negative = false;
+        }
+    }
+
+    void write(Rep value, int width)
+    {
+        write_sign();
+        if(isnan(value))
+            return write_nan();
+        uint32_or_64_or_128_t<int> n          = to_unsigned(to_nonnegative_int(value, max_value<int>()));
+        int                        num_digits = detail::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).end;
+    }
+
+    template <typename Duration>
+    void write_fractional_seconds(Duration d)
+    {
+        FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
         constexpr auto num_fractional_digits =
-            count_fractional_digits<Period::num, Period::den>::value;
-        auto buf = memory_buffer();
-        format_to(std::back_inserter(buf), runtime("{:.{}f}"),
-                  std::fmod(val * static_cast<rep>(Period::num) /
-                                static_cast<rep>(Period::den),
-                            static_cast<rep>(60)),
-                  num_fractional_digits);
-        if (negative) *out++ = '-';
-        if (buf.size() < 2 || buf[1] == '.') *out++ = '0';
-        out = std::copy(buf.begin(), buf.end(), out);
-      } else {
-        write(second(), 2);
-        write_fractional_seconds(std::chrono::duration<rep, Period>(val));
-      }
-      return;
-    }
-    auto time = tm();
-    time.tm_sec = to_nonnegative_int(second(), 60);
-    format_tm(time, &tm_writer_type::on_second, ns);
-  }
-
-  void on_12_hour_time() {
-    if (handle_nan_inf()) return;
-    format_tm(time(), &tm_writer_type::on_12_hour_time);
-  }
-
-  void on_24_hour_time() {
-    if (handle_nan_inf()) {
-      *out++ = ':';
-      handle_nan_inf();
-      return;
-    }
-
-    write(hour(), 2);
-    *out++ = ':';
-    write(minute(), 2);
-  }
-
-  void on_iso_time() {
-    on_24_hour_time();
-    *out++ = ':';
-    if (handle_nan_inf()) return;
-    on_second(numeric_system::standard);
-  }
-
-  void on_am_pm() {
-    if (handle_nan_inf()) return;
-    format_tm(time(), &tm_writer_type::on_am_pm);
-  }
-
-  void on_duration_value() {
-    if (handle_nan_inf()) return;
-    write_sign();
-    out = format_duration_value<char_type>(out, val, precision);
-  }
-
-  void on_duration_unit() {
-    out = format_duration_unit<char_type, Period>(out);
-  }
+            count_fractional_digits<Duration::period::num, Duration::period::den>::value;
+
+        using subsecond_precision = std::chrono::duration<
+            typename std::common_type<typename Duration::rep, std::chrono::seconds::rep>::type,
+            std::ratio<1, detail::pow10(num_fractional_digits)>>;
+        if(std::ratio_less<typename subsecond_precision::period, std::chrono::seconds::period>::value)
+        {
+            *out++          = '.';
+            auto fractional = detail::abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d);
+            auto subseconds = std::chrono::treat_as_floating_point<typename subsecond_precision::rep>::value ?
+                                  fractional.count() :
+                                  std::chrono::duration_cast<subsecond_precision>(fractional).count();
+            uint32_or_64_or_128_t<long long> n = to_unsigned(to_nonnegative_int(subseconds, max_value<long long>()));
+            int                              num_digits = detail::count_digits(n);
+            if(num_fractional_digits > num_digits)
+                out = std::fill_n(out, num_fractional_digits - num_digits, '0');
+            out = format_decimal<char_type>(out, n, num_digits).end;
+        }
+    }
+
+    void write_nan()
+    {
+        std::copy_n("nan", 3, out);
+    }
+    void write_pinf()
+    {
+        std::copy_n("inf", 3, out);
+    }
+    void write_ninf()
+    {
+        std::copy_n("-inf", 4, out);
+    }
+
+    template <typename Callback, typename... Args>
+    void format_tm(const tm &time, Callback cb, Args... args)
+    {
+        if(isnan(val))
+            return write_nan();
+        get_locale loc(localized, context.locale());
+        auto       w = tm_writer_type(loc, out, time);
+        (w.*cb)(args...);
+        out = w.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_year(numeric_system) {}
+    void on_short_year(numeric_system) {}
+    void on_offset_year() {}
+    void on_century(numeric_system) {}
+    void on_iso_week_based_year() {}
+    void on_iso_week_based_short_year() {}
+    void on_dec_month(numeric_system) {}
+    void on_dec0_week_of_year(numeric_system) {}
+    void on_dec1_week_of_year(numeric_system) {}
+    void on_iso_week_of_year(numeric_system) {}
+    void on_day_of_year() {}
+    void on_day_of_month(numeric_system) {}
+    void on_day_of_month_space(numeric_system) {}
+
+    void on_24_hour(numeric_system ns)
+    {
+        if(handle_nan_inf())
+            return;
+
+        if(ns == numeric_system::standard)
+            return write(hour(), 2);
+        auto time    = tm();
+        time.tm_hour = to_nonnegative_int(hour(), 24);
+        format_tm(time, &tm_writer_type::on_24_hour, ns);
+    }
+
+    void on_12_hour(numeric_system ns)
+    {
+        if(handle_nan_inf())
+            return;
+
+        if(ns == numeric_system::standard)
+            return write(hour12(), 2);
+        auto time    = tm();
+        time.tm_hour = to_nonnegative_int(hour12(), 12);
+        format_tm(time, &tm_writer_type::on_12_hour, ns);
+    }
+
+    void on_minute(numeric_system ns)
+    {
+        if(handle_nan_inf())
+            return;
+
+        if(ns == numeric_system::standard)
+            return write(minute(), 2);
+        auto time   = tm();
+        time.tm_min = to_nonnegative_int(minute(), 60);
+        format_tm(time, &tm_writer_type::on_minute, ns);
+    }
+
+    void on_second(numeric_system ns)
+    {
+        if(handle_nan_inf())
+            return;
+
+        if(ns == numeric_system::standard)
+        {
+            if(std::is_floating_point<rep>::value)
+            {
+                constexpr auto num_fractional_digits = count_fractional_digits<Period::num, Period::den>::value;
+                auto           buf                   = memory_buffer();
+                format_to(
+                    std::back_inserter(buf),
+                    runtime("{:.{}f}"),
+                    std::fmod(
+                        val * static_cast<rep>(Period::num) / static_cast<rep>(Period::den), static_cast<rep>(60)),
+                    num_fractional_digits);
+                if(negative)
+                    *out++ = '-';
+                if(buf.size() < 2 || buf[1] == '.')
+                    *out++ = '0';
+                out = std::copy(buf.begin(), buf.end(), out);
+            }
+            else
+            {
+                write(second(), 2);
+                write_fractional_seconds(std::chrono::duration<rep, Period>(val));
+            }
+            return;
+        }
+        auto time   = tm();
+        time.tm_sec = to_nonnegative_int(second(), 60);
+        format_tm(time, &tm_writer_type::on_second, ns);
+    }
+
+    void on_12_hour_time()
+    {
+        if(handle_nan_inf())
+            return;
+        format_tm(time(), &tm_writer_type::on_12_hour_time);
+    }
+
+    void on_24_hour_time()
+    {
+        if(handle_nan_inf())
+        {
+            *out++ = ':';
+            handle_nan_inf();
+            return;
+        }
+
+        write(hour(), 2);
+        *out++ = ':';
+        write(minute(), 2);
+    }
+
+    void on_iso_time()
+    {
+        on_24_hour_time();
+        *out++ = ':';
+        if(handle_nan_inf())
+            return;
+        on_second(numeric_system::standard);
+    }
+
+    void on_am_pm()
+    {
+        if(handle_nan_inf())
+            return;
+        format_tm(time(), &tm_writer_type::on_am_pm);
+    }
+
+    void on_duration_value()
+    {
+        if(handle_nan_inf())
+            return;
+        write_sign();
+        out = format_duration_value<char_type>(out, val, precision);
+    }
+
+    void on_duration_unit()
+    {
+        out = format_duration_unit<char_type, Period>(out);
+    }
 };
 
 FMT_END_DETAIL_NAMESPACE
@@ -1839,231 +2145,252 @@ FMT_END_DETAIL_NAMESPACE
 using weekday = std::chrono::weekday;
 #else
 // A fallback version of weekday.
-class weekday {
- private:
-  unsigned char value;
-
- public:
-  weekday() = default;
-  explicit constexpr weekday(unsigned wd) noexcept
-      : value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
-  constexpr unsigned c_encoding() const noexcept { return value; }
+class weekday
+{
+private:
+    unsigned char value;
+
+public:
+    weekday() = default;
+    explicit constexpr weekday(unsigned wd) noexcept : value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
+    constexpr unsigned c_encoding() const noexcept { return value; }
 };
 
-class year_month_day {};
+class year_month_day
+{
+};
 #endif
 
 // A rudimentary weekday formatter.
-template <typename Char> struct formatter<weekday, Char> {
- private:
-  bool localized = false;
-
- public:
-  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-      -> decltype(ctx.begin()) {
-    auto begin = ctx.begin(), end = ctx.end();
-    if (begin != end && *begin == 'L') {
-      ++begin;
-      localized = true;
-    }
-    return begin;
-  }
-
-  template <typename FormatContext>
-  auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) {
-    auto time = std::tm();
-    time.tm_wday = static_cast<int>(wd.c_encoding());
-    detail::get_locale loc(localized, ctx.locale());
-    auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
-    w.on_abbr_weekday();
-    return w.out();
-  }
+template <typename Char>
+struct formatter<weekday, Char>
+{
+private:
+    bool localized = false;
+
+public:
+    FMT_CONSTEXPR auto parse(basic_format_parse_context<Char> &ctx) -> decltype(ctx.begin())
+    {
+        auto begin = ctx.begin(), end = ctx.end();
+        if(begin != end && *begin == 'L')
+        {
+            ++begin;
+            localized = true;
+        }
+        return begin;
+    }
+
+    template <typename FormatContext>
+    auto format(weekday wd, FormatContext &ctx) const -> decltype(ctx.out())
+    {
+        auto time    = std::tm();
+        time.tm_wday = static_cast<int>(wd.c_encoding());
+        detail::get_locale loc(localized, ctx.locale());
+        auto               w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
+        w.on_abbr_weekday();
+        return w.out();
+    }
 };
 
 template <typename Rep, typename Period, typename Char>
-struct formatter<std::chrono::duration<Rep, Period>, Char> {
- private:
-  basic_format_specs<Char> specs;
-  int precision = -1;
-  using arg_ref_type = detail::arg_ref<Char>;
-  arg_ref_type width_ref;
-  arg_ref_type precision_ref;
-  bool localized = false;
-  basic_string_view<Char> format_str;
-  using duration = std::chrono::duration<Rep, Period>;
-
-  struct spec_handler {
-    formatter& f;
-    basic_format_parse_context<Char>& context;
+struct formatter<std::chrono::duration<Rep, Period>, Char>
+{
+private:
+    basic_format_specs<Char> specs;
+    int                      precision = -1;
+    using arg_ref_type                 = detail::arg_ref<Char>;
+    arg_ref_type            width_ref;
+    arg_ref_type            precision_ref;
+    bool                    localized = false;
     basic_string_view<Char> format_str;
+    using duration = std::chrono::duration<Rep, Period>;
+
+    struct spec_handler
+    {
+        formatter                        &f;
+        basic_format_parse_context<Char> &context;
+        basic_string_view<Char>           format_str;
+
+        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(basic_string_view<Char> arg_id)
+        {
+            context.check_arg_id(arg_id);
+            return arg_ref_type(arg_id);
+        }
+
+        FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) { return arg_ref_type(context.next_arg_id()); }
+
+        void               on_error(const char *msg) { FMT_THROW(format_error(msg)); }
+        FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) { f.specs.fill = fill; }
+        FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; }
+        FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; }
+        FMT_CONSTEXPR void on_precision(int _precision) { f.precision = _precision; }
+        FMT_CONSTEXPR void end_precision() {}
+
+        template <typename Id>
+        FMT_CONSTEXPR void on_dynamic_width(Id arg_id)
+        {
+            f.width_ref = make_arg_ref(arg_id);
+        }
+
+        template <typename Id>
+        FMT_CONSTEXPR void on_dynamic_precision(Id arg_id)
+        {
+            f.precision_ref = make_arg_ref(arg_id);
+        }
+    };
+
+    using iterator = typename basic_format_parse_context<Char>::iterator;
+    struct parse_range
+    {
+        iterator begin;
+        iterator end;
+    };
+
+    FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char> &ctx)
+    {
+        auto begin = ctx.begin(), end = ctx.end();
+        if(begin == end || *begin == '}')
+            return {begin, begin};
+        spec_handler handler{*this, ctx, format_str};
+        begin = detail::parse_align(begin, end, handler);
+        if(begin == end)
+            return {begin, begin};
+        begin = detail::parse_width(begin, end, handler);
+        if(begin == end)
+            return {begin, begin};
+        if(*begin == '.')
+        {
+            if(std::is_floating_point<Rep>::value)
+                begin = detail::parse_precision(begin, end, handler);
+            else
+                handler.on_error("precision not allowed for this argument type");
+        }
+        if(begin != end && *begin == 'L')
+        {
+            ++begin;
+            localized = true;
+        }
+        end = detail::parse_chrono_format(begin, end, detail::chrono_format_checker());
+        return {begin, end};
+    }
 
-    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(basic_string_view<Char> arg_id) {
-      context.check_arg_id(arg_id);
-      return arg_ref_type(arg_id);
-    }
-
-    FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) {
-      return arg_ref_type(context.next_arg_id());
-    }
-
-    void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
-    FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
-      f.specs.fill = fill;
-    }
-    FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; }
-    FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; }
-    FMT_CONSTEXPR void on_precision(int _precision) {
-      f.precision = _precision;
-    }
-    FMT_CONSTEXPR void end_precision() {}
-
-    template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
-      f.width_ref = make_arg_ref(arg_id);
-    }
-
-    template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
-      f.precision_ref = make_arg_ref(arg_id);
-    }
-  };
-
-  using iterator = typename basic_format_parse_context<Char>::iterator;
-  struct parse_range {
-    iterator begin;
-    iterator end;
-  };
-
-  FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
-    auto begin = ctx.begin(), end = ctx.end();
-    if (begin == end || *begin == '}') return {begin, begin};
-    spec_handler handler{*this, ctx, format_str};
-    begin = detail::parse_align(begin, end, handler);
-    if (begin == end) return {begin, begin};
-    begin = detail::parse_width(begin, end, handler);
-    if (begin == end) return {begin, begin};
-    if (*begin == '.') {
-      if (std::is_floating_point<Rep>::value)
-        begin = detail::parse_precision(begin, end, handler);
-      else
-        handler.on_error("precision not allowed for this argument type");
-    }
-    if (begin != end && *begin == 'L') {
-      ++begin;
-      localized = true;
-    }
-    end = detail::parse_chrono_format(begin, end,
-                                      detail::chrono_format_checker());
-    return {begin, end};
-  }
-
- public:
-  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-      -> decltype(ctx.begin()) {
-    auto range = do_parse(ctx);
-    format_str = basic_string_view<Char>(
-        &*range.begin, detail::to_unsigned(range.end - range.begin));
-    return range.end;
-  }
-
-  template <typename FormatContext>
-  auto format(const duration& d, FormatContext& ctx) const
-      -> decltype(ctx.out()) {
-    auto specs_copy = specs;
-    auto precision_copy = precision;
-    auto begin = format_str.begin(), end = format_str.end();
-    // As a possible future optimization, we could avoid extra copying if width
-    // is not specified.
-    basic_memory_buffer<Char> buf;
-    auto out = std::back_inserter(buf);
-    detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
-                                                       width_ref, ctx);
-    detail::handle_dynamic_spec<detail::precision_checker>(precision_copy,
-                                                           precision_ref, ctx);
-    if (begin == end || *begin == '}') {
-      out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
-      detail::format_duration_unit<Char, Period>(out);
-    } else {
-      detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
-          ctx, out, d);
-      f.precision = precision_copy;
-      f.localized = localized;
-      detail::parse_chrono_format(begin, end, f);
-    }
-    return detail::write(
-        ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
-  }
+public:
+    FMT_CONSTEXPR auto parse(basic_format_parse_context<Char> &ctx) -> decltype(ctx.begin())
+    {
+        auto range = do_parse(ctx);
+        format_str = basic_string_view<Char>(&*range.begin, detail::to_unsigned(range.end - range.begin));
+        return range.end;
+    }
+
+    template <typename FormatContext>
+    auto format(const duration &d, FormatContext &ctx) const -> decltype(ctx.out())
+    {
+        auto specs_copy     = specs;
+        auto precision_copy = precision;
+        auto begin = format_str.begin(), end = format_str.end();
+        // As a possible future optimization, we could avoid extra copying if width
+        // is not specified.
+        basic_memory_buffer<Char> buf;
+        auto                      out = std::back_inserter(buf);
+        detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width, width_ref, ctx);
+        detail::handle_dynamic_spec<detail::precision_checker>(precision_copy, precision_ref, ctx);
+        if(begin == end || *begin == '}')
+        {
+            out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
+            detail::format_duration_unit<Char, Period>(out);
+        }
+        else
+        {
+            detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(ctx, out, d);
+            f.precision = precision_copy;
+            f.localized = localized;
+            detail::parse_chrono_format(begin, end, f);
+        }
+        return detail::write(ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
+    }
 };
 
 template <typename Char, typename Duration>
-struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
-                 Char> : formatter<std::tm, Char> {
-  FMT_CONSTEXPR formatter() {
-    basic_string_view<Char> default_specs =
-        detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
-    this->do_parse(default_specs.begin(), default_specs.end());
-  }
-
-  template <typename FormatContext>
-  auto format(std::chrono::time_point<std::chrono::system_clock> val,
-              FormatContext& ctx) const -> decltype(ctx.out()) {
-    return formatter<std::tm, Char>::format(localtime(val), ctx);
-  }
+struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>, Char> : formatter<std::tm, Char>
+{
+    FMT_CONSTEXPR formatter()
+    {
+        basic_string_view<Char> default_specs = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
+        this->do_parse(default_specs.begin(), default_specs.end());
+    }
+
+    template <typename FormatContext>
+    auto format(std::chrono::time_point<std::chrono::system_clock> val, FormatContext &ctx) const -> decltype(ctx.out())
+    {
+        return formatter<std::tm, Char>::format(localtime(val), ctx);
+    }
 };
 
-template <typename Char> struct formatter<std::tm, Char> {
- private:
-  enum class spec {
-    unknown,
-    year_month_day,
-    hh_mm_ss,
-  };
-  spec spec_ = spec::unknown;
-  basic_string_view<Char> specs;
-
- protected:
-  template <typename It> FMT_CONSTEXPR auto do_parse(It begin, It end) -> It {
-    if (begin != end && *begin == ':') ++begin;
-    end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
-    // Replace default spec only if the new spec is not empty.
-    if (end != begin) specs = {begin, detail::to_unsigned(end - begin)};
-    return end;
-  }
-
- public:
-  FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-      -> decltype(ctx.begin()) {
-    auto end = this->do_parse(ctx.begin(), ctx.end());
-    // basic_string_view<>::compare isn't constexpr before C++17.
-    if (specs.size() == 2 && specs[0] == Char('%')) {
-      if (specs[1] == Char('F'))
-        spec_ = spec::year_month_day;
-      else if (specs[1] == Char('T'))
-        spec_ = spec::hh_mm_ss;
-    }
-    return end;
-  }
-
-  template <typename FormatContext>
-  auto format(const std::tm& tm, FormatContext& ctx) const
-      -> decltype(ctx.out()) {
-    const auto loc_ref = ctx.locale();
-    detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
-    auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), tm);
-    if (spec_ == spec::year_month_day)
-      w.on_iso_date();
-    else if (spec_ == spec::hh_mm_ss)
-      w.on_iso_time();
-    else
-      detail::parse_chrono_format(specs.begin(), specs.end(), w);
-    return w.out();
-  }
+template <typename Char>
+struct formatter<std::tm, Char>
+{
+private:
+    enum class spec
+    {
+        unknown,
+        year_month_day,
+        hh_mm_ss,
+    };
+    spec                    spec_ = spec::unknown;
+    basic_string_view<Char> specs;
+
+protected:
+    template <typename It>
+    FMT_CONSTEXPR auto do_parse(It begin, It end) -> It
+    {
+        if(begin != end && *begin == ':')
+            ++begin;
+        end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
+        // Replace default spec only if the new spec is not empty.
+        if(end != begin)
+            specs = {begin, detail::to_unsigned(end - begin)};
+        return end;
+    }
+
+public:
+    FMT_CONSTEXPR auto parse(basic_format_parse_context<Char> &ctx) -> decltype(ctx.begin())
+    {
+        auto end = this->do_parse(ctx.begin(), ctx.end());
+        // basic_string_view<>::compare isn't constexpr before C++17.
+        if(specs.size() == 2 && specs[0] == Char('%'))
+        {
+            if(specs[1] == Char('F'))
+                spec_ = spec::year_month_day;
+            else if(specs[1] == Char('T'))
+                spec_ = spec::hh_mm_ss;
+        }
+        return end;
+    }
+
+    template <typename FormatContext>
+    auto format(const std::tm &tm, FormatContext &ctx) const -> decltype(ctx.out())
+    {
+        const auto         loc_ref = ctx.locale();
+        detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
+        auto               w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), tm);
+        if(spec_ == spec::year_month_day)
+            w.on_iso_date();
+        else if(spec_ == spec::hh_mm_ss)
+            w.on_iso_time();
+        else
+            detail::parse_chrono_format(specs.begin(), specs.end(), w);
+        return w.out();
+    }
 };
 
 FMT_MODULE_EXPORT_END
 FMT_END_NAMESPACE
 
-#endif  // FMT_CHRONO_H_
+#endif // FMT_CHRONO_H_
diff --git a/include/spdlog/fmt/bundled/color.h b/include/spdlog/fmt/bundled/color.h
index 4c163277e..d087bb849 100644
--- a/include/spdlog/fmt/bundled/color.h
+++ b/include/spdlog/fmt/bundled/color.h
@@ -13,483 +13,545 @@
 FMT_BEGIN_NAMESPACE
 FMT_MODULE_EXPORT_BEGIN
 
-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 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 emphasis : uint8_t {
-  bold = 1,
-  faint = 1 << 1,
-  italic = 1 << 2,
-  underline = 1 << 3,
-  blink = 1 << 4,
-  reverse = 1 << 5,
-  conceal = 1 << 6,
-  strikethrough = 1 << 7,
+enum class emphasis : uint8_t
+{
+    bold          = 1,
+    faint         = 1 << 1,
+    italic        = 1 << 2,
+    underline     = 1 << 3,
+    blink         = 1 << 4,
+    reverse       = 1 << 5,
+    conceal       = 1 << 6,
+    strikethrough = 1 << 7,
 };
 
 // rgb is a struct for red, green and blue colors.
 // Using the name "rgb" makes some editors show the color in a tooltip.
-struct rgb {
-  FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
-  FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
-  FMT_CONSTEXPR rgb(uint32_t hex)
-      : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
-  FMT_CONSTEXPR 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;
+struct rgb
+{
+    FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
+    FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
+    FMT_CONSTEXPR rgb(uint32_t hex) : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
+    FMT_CONSTEXPR 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;
 };
 
 FMT_BEGIN_DETAIL_NAMESPACE
 
 // color is a struct of either a rgb color or a terminal color.
-struct color_type {
-  FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
-  FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} {
-    value.rgb_color = static_cast<uint32_t>(rgb_color);
-  }
-  FMT_CONSTEXPR color_type(rgb rgb_color) 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) 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;
+struct color_type
+{
+    FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
+    FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{}
+    {
+        value.rgb_color = static_cast<uint32_t>(rgb_color);
+    }
+    FMT_CONSTEXPR color_type(rgb rgb_color) 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) 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;
 };
 
 FMT_END_DETAIL_NAMESPACE
 
 /** A text style consisting of foreground and background colors and emphasis. */
-class text_style {
- public:
-  FMT_CONSTEXPR text_style(emphasis em = emphasis()) 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)
-        FMT_THROW(format_error("can't OR a terminal color"));
-      foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
+class text_style
+{
+public:
+    FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept :
+        set_foreground_color(), set_background_color(), ems(em)
+    {
     }
 
-    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)
-        FMT_THROW(format_error("can't OR a terminal color"));
-      background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
+    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)
+                FMT_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)
+                FMT_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;
     }
 
-    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 noexcept {
-    return set_foreground_color;
-  }
-  FMT_CONSTEXPR bool has_background() const noexcept {
-    return set_background_color;
-  }
-  FMT_CONSTEXPR bool has_emphasis() const noexcept {
-    return static_cast<uint8_t>(ems) != 0;
-  }
-  FMT_CONSTEXPR detail::color_type get_foreground() const noexcept {
-    FMT_ASSERT(has_foreground(), "no foreground specified for this style");
-    return foreground_color;
-  }
-  FMT_CONSTEXPR detail::color_type get_background() const noexcept {
-    FMT_ASSERT(has_background(), "no background specified for this style");
-    return background_color;
-  }
-  FMT_CONSTEXPR emphasis get_emphasis() const noexcept {
-    FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
-    return ems;
-  }
-
- private:
-  FMT_CONSTEXPR text_style(bool is_foreground,
-                           detail::color_type text_color) 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 text_style operator|(text_style lhs, const text_style &rhs) { return lhs |= rhs; }
+
+    FMT_CONSTEXPR bool has_foreground() const noexcept { return set_foreground_color; }
+    FMT_CONSTEXPR bool has_background() const noexcept { return set_background_color; }
+    FMT_CONSTEXPR bool has_emphasis() const noexcept { return static_cast<uint8_t>(ems) != 0; }
+    FMT_CONSTEXPR detail::color_type get_foreground() const noexcept
+    {
+        FMT_ASSERT(has_foreground(), "no foreground specified for this style");
+        return foreground_color;
+    }
+    FMT_CONSTEXPR detail::color_type get_background() const noexcept
+    {
+        FMT_ASSERT(has_background(), "no background specified for this style");
+        return background_color;
+    }
+    FMT_CONSTEXPR emphasis get_emphasis() const noexcept
+    {
+        FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
+        return ems;
     }
-  }
 
-  friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept;
+private:
+    FMT_CONSTEXPR text_style(bool is_foreground, detail::color_type text_color) 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 text_style bg(detail::color_type background) noexcept;
+    friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept;
 
-  detail::color_type foreground_color;
-  detail::color_type background_color;
-  bool set_foreground_color;
-  bool set_background_color;
-  emphasis ems;
+    friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept;
+
+    detail::color_type foreground_color;
+    detail::color_type background_color;
+    bool               set_foreground_color;
+    bool               set_background_color;
+    emphasis           ems;
 };
 
 /** Creates a text style from the foreground (text) color. */
-FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept {
-  return text_style(true, foreground);
+FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept
+{
+    return text_style(true, foreground);
 }
 
 /** Creates a text style from the background color. */
-FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept {
-  return text_style(false, background);
+FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept
+{
+    return text_style(false, background);
 }
 
-FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
-  return text_style(lhs) | rhs;
+FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept
+{
+    return text_style(lhs) | rhs;
 }
 
 FMT_BEGIN_DETAIL_NAMESPACE
 
-template <typename Char> struct ansi_color_escape {
-  FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
-                                  const char* esc) noexcept {
-    // If we have a terminal color, we need to output another escape code
-    // sequence.
-    if (!text_color.is_rgb) {
-      bool is_background = esc == string_view("\x1b[48;2;");
-      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;
-
-      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;
+template <typename Char>
+struct ansi_color_escape
+{
+    FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, const char *esc) noexcept
+    {
+        // If we have a terminal color, we need to output another escape code
+        // sequence.
+        if(!text_color.is_rgb)
+        {
+            bool     is_background = esc == string_view("\x1b[48;2;");
+            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;
+
+            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) noexcept
+    {
+        uint8_t em_codes[num_emphases] = {};
+        if(has_emphasis(em, emphasis::bold))
+            em_codes[0] = 1;
+        if(has_emphasis(em, emphasis::faint))
+            em_codes[1] = 2;
+        if(has_emphasis(em, emphasis::italic))
+            em_codes[2] = 3;
+        if(has_emphasis(em, emphasis::underline))
+            em_codes[3] = 4;
+        if(has_emphasis(em, emphasis::blink))
+            em_codes[4] = 5;
+        if(has_emphasis(em, emphasis::reverse))
+            em_codes[5] = 7;
+        if(has_emphasis(em, emphasis::conceal))
+            em_codes[6] = 8;
+        if(has_emphasis(em, emphasis::strikethrough))
+            em_codes[7] = 9;
+
+        size_t index = 0;
+        for(size_t i = 0; i < num_emphases; ++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 noexcept { return buffer; }
 
-    for (int i = 0; i < 7; i++) {
-      buffer[i] = static_cast<Char>(esc[i]);
+    FMT_CONSTEXPR const Char             *begin() const noexcept { return buffer; }
+    FMT_CONSTEXPR_CHAR_TRAITS const Char *end() const noexcept
+    {
+        return buffer + std::char_traits<Char>::length(buffer);
     }
-    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) noexcept {
-    uint8_t em_codes[num_emphases] = {};
-    if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
-    if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
-    if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
-    if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
-    if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
-    if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
-    if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
-    if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
-
-    size_t index = 0;
-    for (size_t i = 0; i < num_emphases; ++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');
+
+private:
+    static constexpr size_t num_emphases = 8;
+    Char                    buffer[7u + 3u * num_emphases + 1u];
+
+    static FMT_CONSTEXPR void to_esc(uint8_t c, Char *out, char delimiter) 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);
+    }
+    static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept
+    {
+        return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
     }
-    buffer[index++] = static_cast<Char>(0);
-  }
-  FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
-
-  FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; }
-  FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept {
-    return buffer + std::char_traits<Char>::length(buffer);
-  }
-
- private:
-  static constexpr size_t num_emphases = 8;
-  Char buffer[7u + 3u * num_emphases + 1u];
-
-  static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
-                                   char delimiter) 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);
-  }
-  static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept {
-    return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
-  }
 };
 
 template <typename Char>
-FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
-    detail::color_type foreground) noexcept {
-  return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
+FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(detail::color_type foreground) noexcept
+{
+    return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
 }
 
 template <typename Char>
-FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
-    detail::color_type background) noexcept {
-  return ansi_color_escape<Char>(background, "\x1b[48;2;");
+FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(detail::color_type background) noexcept
+{
+    return ansi_color_escape<Char>(background, "\x1b[48;2;");
 }
 
 template <typename Char>
-FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
-  return ansi_color_escape<Char>(em);
+FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept
+{
+    return ansi_color_escape<Char>(em);
 }
 
-template <typename Char> inline void fputs(const Char* chars, FILE* stream) {
-  int result = std::fputs(chars, stream);
-  if (result < 0)
-    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
+template <typename Char>
+inline void fputs(const Char *chars, FILE *stream)
+{
+    int result = std::fputs(chars, stream);
+    if(result < 0)
+        FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
 }
 
-template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) {
-  int result = std::fputws(chars, stream);
-  if (result < 0)
-    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
+template <>
+inline void fputs<wchar_t>(const wchar_t *chars, FILE *stream)
+{
+    int result = std::fputws(chars, stream);
+    if(result < 0)
+        FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
 }
 
-template <typename Char> inline void reset_color(FILE* stream) {
-  fputs("\x1b[0m", stream);
+template <typename Char>
+inline void reset_color(FILE *stream)
+{
+    fputs("\x1b[0m", stream);
 }
 
-template <> inline void reset_color<wchar_t>(FILE* stream) {
-  fputs(L"\x1b[0m", stream);
+template <>
+inline void reset_color<wchar_t>(FILE *stream)
+{
+    fputs(L"\x1b[0m", stream);
 }
 
-template <typename Char> inline void reset_color(buffer<Char>& buffer) {
-  auto reset_color = string_view("\x1b[0m");
-  buffer.append(reset_color.begin(), reset_color.end());
+template <typename Char>
+inline void reset_color(buffer<Char> &buffer)
+{
+    auto reset_color = string_view("\x1b[0m");
+    buffer.append(reset_color.begin(), reset_color.end());
 }
 
-template <typename T> struct styled_arg {
-  const T& value;
-  text_style style;
+template <typename T>
+struct styled_arg
+{
+    const T   &value;
+    text_style style;
 };
 
 template <typename Char>
-void vformat_to(buffer<Char>& buf, const text_style& ts,
-                basic_string_view<Char> format_str,
-                basic_format_args<buffer_context<type_identity_t<Char>>> args) {
-  bool has_style = false;
-  if (ts.has_emphasis()) {
-    has_style = true;
-    auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
-    buf.append(emphasis.begin(), emphasis.end());
-  }
-  if (ts.has_foreground()) {
-    has_style = true;
-    auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
-    buf.append(foreground.begin(), foreground.end());
-  }
-  if (ts.has_background()) {
-    has_style = true;
-    auto background = detail::make_background_color<Char>(ts.get_background());
-    buf.append(background.begin(), background.end());
-  }
-  detail::vformat_to(buf, format_str, args, {});
-  if (has_style) detail::reset_color<Char>(buf);
+void vformat_to(
+    buffer<Char>                                            &buf,
+    const text_style                                        &ts,
+    basic_string_view<Char>                                  format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args)
+{
+    bool has_style = false;
+    if(ts.has_emphasis())
+    {
+        has_style     = true;
+        auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
+        buf.append(emphasis.begin(), emphasis.end());
+    }
+    if(ts.has_foreground())
+    {
+        has_style       = true;
+        auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
+        buf.append(foreground.begin(), foreground.end());
+    }
+    if(ts.has_background())
+    {
+        has_style       = true;
+        auto background = detail::make_background_color<Char>(ts.get_background());
+        buf.append(background.begin(), background.end());
+    }
+    detail::vformat_to(buf, format_str, args, {});
+    if(has_style)
+        detail::reset_color<Char>(buf);
 }
 
 FMT_END_DETAIL_NAMESPACE
 
 template <typename S, typename Char = char_t<S>>
-void vprint(std::FILE* f, const text_style& ts, const S& format,
-            basic_format_args<buffer_context<type_identity_t<Char>>> args) {
-  basic_memory_buffer<Char> buf;
-  detail::vformat_to(buf, ts, detail::to_string_view(format), args);
-  if (detail::is_utf8()) {
-    detail::print(f, basic_string_view<Char>(buf.begin(), buf.size()));
-  } else {
-    buf.push_back(Char(0));
-    detail::fputs(buf.data(), f);
-  }
+void vprint(
+    std::FILE                                               *f,
+    const text_style                                        &ts,
+    const S                                                 &format,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args)
+{
+    basic_memory_buffer<Char> buf;
+    detail::vformat_to(buf, ts, detail::to_string_view(format), args);
+    if(detail::is_utf8())
+    {
+        detail::print(f, basic_string_view<Char>(buf.begin(), buf.size()));
+    }
+    else
+    {
+        buf.push_back(Char(0));
+        detail::fputs(buf.data(), f);
+    }
 }
 
 /**
@@ -503,12 +565,10 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
                "Elapsed time: {0:.2f} seconds", 1.23);
   \endrst
  */
-template <typename S, typename... Args,
-          FMT_ENABLE_IF(detail::is_string<S>::value)>
-void print(std::FILE* f, const text_style& ts, const S& format_str,
-           const Args&... args) {
-  vprint(f, ts, format_str,
-         fmt::make_format_args<buffer_context<char_t<S>>>(args...));
+template <typename S, typename... Args, FMT_ENABLE_IF(detail::is_string<S>::value)>
+void print(std::FILE *f, const text_style &ts, const S &format_str, const Args &...args)
+{
+    vprint(f, ts, format_str, fmt::make_format_args<buffer_context<char_t<S>>>(args...));
 }
 
 /**
@@ -522,19 +582,19 @@ void print(std::FILE* f, const text_style& ts, const S& format_str,
                "Elapsed time: {0:.2f} seconds", 1.23);
   \endrst
  */
-template <typename S, typename... Args,
-          FMT_ENABLE_IF(detail::is_string<S>::value)>
-void print(const text_style& ts, const S& format_str, const Args&... args) {
-  return print(stdout, ts, format_str, args...);
+template <typename S, typename... Args, FMT_ENABLE_IF(detail::is_string<S>::value)>
+void print(const text_style &ts, const S &format_str, const Args &...args)
+{
+    return print(stdout, ts, format_str, args...);
 }
 
 template <typename S, typename Char = char_t<S>>
-inline std::basic_string<Char> vformat(
-    const text_style& ts, const S& format_str,
-    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
-  basic_memory_buffer<Char> buf;
-  detail::vformat_to(buf, ts, detail::to_string_view(format_str), args);
-  return fmt::to_string(buf);
+inline std::basic_string<Char>
+vformat(const text_style &ts, const S &format_str, basic_format_args<buffer_context<type_identity_t<Char>>> args)
+{
+    basic_memory_buffer<Char> buf;
+    detail::vformat_to(buf, ts, detail::to_string_view(format_str), args);
+    return fmt::to_string(buf);
 }
 
 /**
@@ -550,23 +610,24 @@ inline std::basic_string<Char> vformat(
   \endrst
 */
 template <typename S, typename... Args, typename Char = char_t<S>>
-inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
-                                      const Args&... args) {
-  return fmt::vformat(ts, detail::to_string_view(format_str),
-                      fmt::make_format_args<buffer_context<Char>>(args...));
+inline std::basic_string<Char> format(const text_style &ts, const S &format_str, const Args &...args)
+{
+    return fmt::vformat(ts, detail::to_string_view(format_str), fmt::make_format_args<buffer_context<Char>>(args...));
 }
 
 /**
   Formats a string with the given text_style and writes the output to ``out``.
  */
-template <typename OutputIt, typename Char,
-          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
+template <typename OutputIt, typename Char, FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
 OutputIt vformat_to(
-    OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
-    basic_format_args<buffer_context<type_identity_t<Char>>> args) {
-  auto&& buf = detail::get_buffer<Char>(out);
-  detail::vformat_to(buf, ts, format_str, args);
-  return detail::get_iterator(buf);
+    OutputIt                                                 out,
+    const text_style                                        &ts,
+    basic_string_view<Char>                                  format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args)
+{
+    auto &&buf = detail::get_buffer<Char>(out);
+    detail::vformat_to(buf, ts, format_str, args);
+    return detail::get_iterator(buf);
 }
 
 /**
@@ -581,50 +642,55 @@ OutputIt vformat_to(
                    fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
   \endrst
 */
-template <typename OutputIt, typename S, typename... Args,
-          bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
-              detail::is_string<S>::value>
-inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
-                      Args&&... args) ->
-    typename std::enable_if<enable, OutputIt>::type {
-  return vformat_to(out, ts, detail::to_string_view(format_str),
-                    fmt::make_format_args<buffer_context<char_t<S>>>(args...));
+template <
+    typename OutputIt,
+    typename S,
+    typename... Args,
+    bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value &&detail::is_string<S>::value>
+inline auto format_to(OutputIt out, const text_style &ts, const S &format_str, Args &&...args) ->
+    typename std::enable_if<enable, OutputIt>::type
+{
+    return vformat_to(
+        out, ts, detail::to_string_view(format_str), fmt::make_format_args<buffer_context<char_t<S>>>(args...));
 }
 
 template <typename T, typename Char>
-struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
-  template <typename FormatContext>
-  auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
-      -> decltype(ctx.out()) {
-    const auto& ts = arg.style;
-    const auto& value = arg.value;
-    auto out = ctx.out();
-
-    bool has_style = false;
-    if (ts.has_emphasis()) {
-      has_style = true;
-      auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
-      out = std::copy(emphasis.begin(), emphasis.end(), out);
-    }
-    if (ts.has_foreground()) {
-      has_style = true;
-      auto foreground =
-          detail::make_foreground_color<Char>(ts.get_foreground());
-      out = std::copy(foreground.begin(), foreground.end(), out);
-    }
-    if (ts.has_background()) {
-      has_style = true;
-      auto background =
-          detail::make_background_color<Char>(ts.get_background());
-      out = std::copy(background.begin(), background.end(), out);
-    }
-    out = formatter<T, Char>::format(value, ctx);
-    if (has_style) {
-      auto reset_color = string_view("\x1b[0m");
-      out = std::copy(reset_color.begin(), reset_color.end(), out);
+struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char>
+{
+    template <typename FormatContext>
+    auto format(const detail::styled_arg<T> &arg, FormatContext &ctx) const -> decltype(ctx.out())
+    {
+        const auto &ts    = arg.style;
+        const auto &value = arg.value;
+        auto        out   = ctx.out();
+
+        bool has_style = false;
+        if(ts.has_emphasis())
+        {
+            has_style     = true;
+            auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
+            out           = std::copy(emphasis.begin(), emphasis.end(), out);
+        }
+        if(ts.has_foreground())
+        {
+            has_style       = true;
+            auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
+            out             = std::copy(foreground.begin(), foreground.end(), out);
+        }
+        if(ts.has_background())
+        {
+            has_style       = true;
+            auto background = detail::make_background_color<Char>(ts.get_background());
+            out             = std::copy(background.begin(), background.end(), out);
+        }
+        out = formatter<T, Char>::format(value, ctx);
+        if(has_style)
+        {
+            auto reset_color = string_view("\x1b[0m");
+            out              = std::copy(reset_color.begin(), reset_color.end(), out);
+        }
+        return out;
     }
-    return out;
-  }
 };
 
 /**
@@ -640,12 +706,12 @@ struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
   \endrst
  */
 template <typename T>
-FMT_CONSTEXPR auto styled(const T& value, text_style ts)
-    -> detail::styled_arg<remove_cvref_t<T>> {
-  return detail::styled_arg<remove_cvref_t<T>>{value, ts};
+FMT_CONSTEXPR auto styled(const T &value, text_style ts) -> detail::styled_arg<remove_cvref_t<T>>
+{
+    return detail::styled_arg<remove_cvref_t<T>>{value, ts};
 }
 
 FMT_MODULE_EXPORT_END
 FMT_END_NAMESPACE
 
-#endif  // FMT_COLOR_H_
+#endif // FMT_COLOR_H_
diff --git a/include/spdlog/fmt/bundled/compile.h b/include/spdlog/fmt/bundled/compile.h
index 933668c41..bcb81b6ea 100644
--- a/include/spdlog/fmt/bundled/compile.h
+++ b/include/spdlog/fmt/bundled/compile.h
@@ -11,97 +11,105 @@
 #include "format.h"
 
 FMT_BEGIN_NAMESPACE
-namespace detail {
+namespace detail
+{
 
 template <typename Char, typename InputIt>
-FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end,
-                                                counting_iterator it) {
-  return it + (end - begin);
+FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end, counting_iterator it)
+{
+    return it + (end - begin);
 }
 
-template <typename OutputIt> class truncating_iterator_base {
- protected:
-  OutputIt out_;
-  size_t limit_;
-  size_t count_ = 0;
-
-  truncating_iterator_base() : out_(), limit_(0) {}
-
-  truncating_iterator_base(OutputIt out, size_t limit)
-      : out_(out), limit_(limit) {}
-
- public:
-  using iterator_category = std::output_iterator_tag;
-  using value_type = typename std::iterator_traits<OutputIt>::value_type;
-  using difference_type = std::ptrdiff_t;
-  using pointer = void;
-  using reference = void;
-  FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
-
-  OutputIt base() const { return out_; }
-  size_t count() const { return count_; }
+template <typename OutputIt>
+class truncating_iterator_base
+{
+protected:
+    OutputIt out_;
+    size_t   limit_;
+    size_t   count_ = 0;
+
+    truncating_iterator_base() : out_(), limit_(0) {}
+
+    truncating_iterator_base(OutputIt out, size_t limit) : out_(out), limit_(limit) {}
+
+public:
+    using iterator_category = std::output_iterator_tag;
+    using value_type        = typename std::iterator_traits<OutputIt>::value_type;
+    using difference_type   = std::ptrdiff_t;
+    using pointer           = void;
+    using reference         = void;
+    FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
+
+    OutputIt base() const { return out_; }
+    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>
+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<OutputIt, std::false_type>
-    : public truncating_iterator_base<OutputIt> {
-  mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
+class truncating_iterator<OutputIt, std::false_type> : public truncating_iterator_base<OutputIt>
+{
+    mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
 
- public:
-  using value_type = typename truncating_iterator_base<OutputIt>::value_type;
+public:
+    using value_type = typename truncating_iterator_base<OutputIt>::value_type;
 
-  truncating_iterator() = default;
+    truncating_iterator() = default;
 
-  truncating_iterator(OutputIt out, size_t limit)
-      : truncating_iterator_base<OutputIt>(out, limit) {}
+    truncating_iterator(OutputIt out, size_t limit) : truncating_iterator_base<OutputIt>(out, limit) {}
 
-  truncating_iterator& operator++() {
-    if (this->count_++ < this->limit_) ++this->out_;
-    return *this;
-  }
+    truncating_iterator &operator++()
+    {
+        if(this->count_++ < this->limit_)
+            ++this->out_;
+        return *this;
+    }
 
-  truncating_iterator operator++(int) {
-    auto it = *this;
-    ++*this;
-    return it;
-  }
+    truncating_iterator operator++(int)
+    {
+        auto it = *this;
+        ++*this;
+        return it;
+    }
 
-  value_type& operator*() const {
-    return this->count_ < this->limit_ ? *this->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:
-  truncating_iterator() = default;
-
-  truncating_iterator(OutputIt out, size_t limit)
-      : truncating_iterator_base<OutputIt>(out, limit) {}
-
-  template <typename T> truncating_iterator& operator=(T 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; }
+class truncating_iterator<OutputIt, std::true_type> : public truncating_iterator_base<OutputIt>
+{
+public:
+    truncating_iterator() = default;
+
+    truncating_iterator(OutputIt out, size_t limit) : truncating_iterator_base<OutputIt>(out, limit) {}
+
+    template <typename T>
+    truncating_iterator &operator=(T 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; }
 };
 
 // A compile-time string which is compiled into fast formatting code.
-class compiled_string {};
+class compiled_string
+{
+};
 
 template <typename S>
-struct is_compiled_string : std::is_base_of<compiled_string, S> {};
+struct is_compiled_string : std::is_base_of<compiled_string, S>
+{
+};
 
 /**
   \rst
@@ -117,495 +125,585 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
   \endrst
  */
 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
-#  define FMT_COMPILE(s) \
-    FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
+#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
 #else
-#  define FMT_COMPILE(s) FMT_STRING(s)
+#define FMT_COMPILE(s) FMT_STRING(s)
 #endif
 
 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
-template <typename Char, size_t N,
-          fmt::detail_exported::fixed_string<Char, N> Str>
-struct udl_compiled_string : compiled_string {
-  using char_type = Char;
-  explicit constexpr operator basic_string_view<char_type>() const {
-    return {Str.data, N - 1};
-  }
+template <typename Char, size_t N, fmt::detail_exported::fixed_string<Char, N> Str>
+struct udl_compiled_string : compiled_string
+{
+    using char_type = Char;
+    explicit constexpr operator basic_string_view<char_type>() const { return {Str.data, N - 1}; }
 };
 #endif
 
 template <typename T, typename... Tail>
-const T& first(const T& value, const Tail&...) {
-  return value;
+const T &first(const T &value, const Tail &...)
+{
+    return value;
 }
 
 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
-template <typename... Args> struct type_list {};
+template <typename... Args>
+struct type_list
+{
+};
 
 // Returns a reference to the argument at index N from [first, rest...].
 template <int N, typename T, typename... Args>
-constexpr const auto& get([[maybe_unused]] const T& first,
-                          [[maybe_unused]] const Args&... rest) {
-  static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
-  if constexpr (N == 0)
-    return first;
-  else
-    return detail::get<N - 1>(rest...);
+constexpr const auto &get([[maybe_unused]] const T &first, [[maybe_unused]] const Args &...rest)
+{
+    static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
+    if constexpr(N == 0)
+        return first;
+    else
+        return detail::get<N - 1>(rest...);
 }
 
 template <typename Char, typename... Args>
-constexpr int get_arg_index_by_name(basic_string_view<Char> name,
-                                    type_list<Args...>) {
-  return get_arg_index_by_name<Args...>(name);
+constexpr int get_arg_index_by_name(basic_string_view<Char> name, type_list<Args...>)
+{
+    return get_arg_index_by_name<Args...>(name);
 }
 
-template <int N, typename> struct get_type_impl;
+template <int N, typename>
+struct get_type_impl;
 
-template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
-  using type =
-      remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
+template <int N, typename... Args>
+struct get_type_impl<N, type_list<Args...>>
+{
+    using type = remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
 };
 
 template <int N, typename T>
 using get_type = typename get_type_impl<N, T>::type;
 
-template <typename T> struct is_compiled_format : std::false_type {};
-
-template <typename Char> struct text {
-  basic_string_view<Char> data;
-  using char_type = Char;
+template <typename T>
+struct is_compiled_format : std::false_type
+{
+};
 
-  template <typename OutputIt, typename... Args>
-  constexpr OutputIt format(OutputIt out, const Args&...) const {
-    return write<Char>(out, data);
-  }
+template <typename Char>
+struct text
+{
+    basic_string_view<Char> data;
+    using char_type = Char;
+
+    template <typename OutputIt, typename... Args>
+    constexpr OutputIt format(OutputIt out, const Args &...) const
+    {
+        return write<Char>(out, data);
+    }
 };
 
 template <typename Char>
-struct is_compiled_format<text<Char>> : std::true_type {};
+struct is_compiled_format<text<Char>> : std::true_type
+{
+};
 
 template <typename Char>
-constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
-                               size_t size) {
-  return {{&s[pos], size}};
+constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos, size_t size)
+{
+    return {{&s[pos], size}};
 }
 
-template <typename Char> struct code_unit {
-  Char value;
-  using char_type = Char;
-
-  template <typename OutputIt, typename... Args>
-  constexpr OutputIt format(OutputIt out, const Args&...) const {
-    return write<Char>(out, value);
-  }
+template <typename Char>
+struct code_unit
+{
+    Char value;
+    using char_type = Char;
+
+    template <typename OutputIt, typename... Args>
+    constexpr OutputIt format(OutputIt out, const Args &...) const
+    {
+        return write<Char>(out, value);
+    }
 };
 
 // This ensures that the argument type is convertible to `const T&`.
 template <typename T, int N, typename... Args>
-constexpr const T& get_arg_checked(const Args&... args) {
-  const auto& arg = detail::get<N>(args...);
-  if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
-    return arg.value;
-  } else {
-    return arg;
-  }
+constexpr const T &get_arg_checked(const Args &...args)
+{
+    const auto &arg = detail::get<N>(args...);
+    if constexpr(detail::is_named_arg<remove_cvref_t<decltype(arg)>>())
+    {
+        return arg.value;
+    }
+    else
+    {
+        return arg;
+    }
 }
 
 template <typename Char>
-struct is_compiled_format<code_unit<Char>> : std::true_type {};
+struct is_compiled_format<code_unit<Char>> : std::true_type
+{
+};
 
 // A replacement field that refers to argument N.
-template <typename Char, typename T, int N> struct field {
-  using char_type = Char;
-
-  template <typename OutputIt, typename... Args>
-  constexpr OutputIt format(OutputIt out, const Args&... args) const {
-    return write<Char>(out, get_arg_checked<T, N>(args...));
-  }
+template <typename Char, typename T, int N>
+struct field
+{
+    using char_type = Char;
+
+    template <typename OutputIt, typename... Args>
+    constexpr OutputIt format(OutputIt out, const Args &...args) const
+    {
+        return write<Char>(out, get_arg_checked<T, N>(args...));
+    }
 };
 
 template <typename Char, typename T, int N>
-struct is_compiled_format<field<Char, T, N>> : std::true_type {};
+struct is_compiled_format<field<Char, T, N>> : std::true_type
+{
+};
 
 // A replacement field that refers to argument with name.
-template <typename Char> struct runtime_named_field {
-  using char_type = Char;
-  basic_string_view<Char> name;
-
-  template <typename OutputIt, typename T>
-  constexpr static bool try_format_argument(
-      OutputIt& out,
-      // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
-      [[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
-    if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
-      if (arg_name == arg.name) {
-        out = write<Char>(out, arg.value);
-        return true;
-      }
+template <typename Char>
+struct runtime_named_field
+{
+    using char_type = Char;
+    basic_string_view<Char> name;
+
+    template <typename OutputIt, typename T>
+    constexpr static bool try_format_argument(
+        OutputIt &out,
+        // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
+        [[maybe_unused]] basic_string_view<Char> arg_name,
+        const T                                 &arg)
+    {
+        if constexpr(is_named_arg<typename std::remove_cv<T>::type>::value)
+        {
+            if(arg_name == arg.name)
+            {
+                out = write<Char>(out, arg.value);
+                return true;
+            }
+        }
+        return false;
     }
-    return false;
-  }
-
-  template <typename OutputIt, typename... Args>
-  constexpr OutputIt format(OutputIt out, const Args&... args) const {
-    bool found = (try_format_argument(out, name, args) || ...);
-    if (!found) {
-      FMT_THROW(format_error("argument with specified name is not found"));
+
+    template <typename OutputIt, typename... Args>
+    constexpr OutputIt format(OutputIt out, const Args &...args) const
+    {
+        bool found = (try_format_argument(out, name, args) || ...);
+        if(!found)
+        {
+            FMT_THROW(format_error("argument with specified name is not found"));
+        }
+        return out;
     }
-    return out;
-  }
 };
 
 template <typename Char>
-struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
+struct is_compiled_format<runtime_named_field<Char>> : std::true_type
+{
+};
 
 // A replacement field that refers to argument N and has format specifiers.
-template <typename Char, typename T, int N> struct spec_field {
-  using char_type = Char;
-  formatter<T, Char> fmt;
-
-  template <typename OutputIt, typename... Args>
-  constexpr FMT_INLINE OutputIt format(OutputIt out,
-                                       const Args&... args) const {
-    const auto& vargs =
-        fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
-    basic_format_context<OutputIt, Char> ctx(out, vargs);
-    return fmt.format(get_arg_checked<T, N>(args...), ctx);
-  }
+template <typename Char, typename T, int N>
+struct spec_field
+{
+    using char_type = Char;
+    formatter<T, Char> fmt;
+
+    template <typename OutputIt, typename... Args>
+    constexpr FMT_INLINE OutputIt format(OutputIt out, const Args &...args) const
+    {
+        const auto &vargs = fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
+        basic_format_context<OutputIt, Char> ctx(out, vargs);
+        return fmt.format(get_arg_checked<T, N>(args...), ctx);
+    }
 };
 
 template <typename Char, typename T, int N>
-struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
-
-template <typename L, typename R> struct concat {
-  L lhs;
-  R rhs;
-  using char_type = typename L::char_type;
-
-  template <typename OutputIt, typename... Args>
-  constexpr OutputIt format(OutputIt out, const Args&... args) const {
-    out = lhs.format(out, args...);
-    return rhs.format(out, args...);
-  }
+struct is_compiled_format<spec_field<Char, T, N>> : std::true_type
+{
 };
 
 template <typename L, typename R>
-struct is_compiled_format<concat<L, R>> : std::true_type {};
+struct concat
+{
+    L lhs;
+    R rhs;
+    using char_type = typename L::char_type;
+
+    template <typename OutputIt, typename... Args>
+    constexpr OutputIt format(OutputIt out, const Args &...args) const
+    {
+        out = lhs.format(out, args...);
+        return rhs.format(out, args...);
+    }
+};
+
+template <typename L, typename R>
+struct is_compiled_format<concat<L, R>> : std::true_type
+{
+};
 
 template <typename L, typename R>
-constexpr concat<L, R> make_concat(L lhs, R rhs) {
-  return {lhs, rhs};
+constexpr concat<L, R> make_concat(L lhs, R rhs)
+{
+    return {lhs, rhs};
 }
 
-struct unknown_format {};
+struct unknown_format
+{
+};
 
 template <typename Char>
-constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
-  for (size_t size = str.size(); pos != size; ++pos) {
-    if (str[pos] == '{' || str[pos] == '}') break;
-  }
-  return pos;
+constexpr size_t parse_text(basic_string_view<Char> str, size_t pos)
+{
+    for(size_t size = str.size(); pos != size; ++pos)
+    {
+        if(str[pos] == '{' || str[pos] == '}')
+            break;
+    }
+    return pos;
 }
 
 template <typename Args, size_t POS, int ID, typename S>
 constexpr auto compile_format_string(S format_str);
 
 template <typename Args, size_t POS, int ID, typename T, typename S>
-constexpr auto parse_tail(T head, S format_str) {
-  if constexpr (POS !=
-                basic_string_view<typename S::char_type>(format_str).size()) {
-    constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
-    if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
-                               unknown_format>())
-      return tail;
+constexpr auto parse_tail(T head, S format_str)
+{
+    if constexpr(POS != basic_string_view<typename S::char_type>(format_str).size())
+    {
+        constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
+        if constexpr(std::is_same<remove_cvref_t<decltype(tail)>, unknown_format>())
+            return tail;
+        else
+            return make_concat(head, tail);
+    }
     else
-      return make_concat(head, tail);
-  } else {
-    return head;
-  }
+    {
+        return head;
+    }
 }
 
-template <typename T, typename Char> struct parse_specs_result {
-  formatter<T, Char> fmt;
-  size_t end;
-  int next_arg_id;
+template <typename T, typename Char>
+struct parse_specs_result
+{
+    formatter<T, Char> fmt;
+    size_t             end;
+    int                next_arg_id;
 };
 
 constexpr int manual_indexing_id = -1;
 
 template <typename T, typename Char>
-constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
-                                                  size_t pos, int next_arg_id) {
-  str.remove_prefix(pos);
-  auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, {},
-                                         next_arg_id);
-  auto f = formatter<T, Char>();
-  auto end = f.parse(ctx);
-  return {f, pos + fmt::detail::to_unsigned(end - str.data()),
-          next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
+constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, size_t pos, int next_arg_id)
+{
+    str.remove_prefix(pos);
+    auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, {}, next_arg_id);
+    auto f   = formatter<T, Char>();
+    auto end = f.parse(ctx);
+    return {
+        f, pos + fmt::detail::to_unsigned(end - str.data()), next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
 }
 
-template <typename Char> struct arg_id_handler {
-  arg_ref<Char> arg_id;
-
-  constexpr int operator()() {
-    FMT_ASSERT(false, "handler cannot be used with automatic indexing");
-    return 0;
-  }
-  constexpr int operator()(int id) {
-    arg_id = arg_ref<Char>(id);
-    return 0;
-  }
-  constexpr int operator()(basic_string_view<Char> id) {
-    arg_id = arg_ref<Char>(id);
-    return 0;
-  }
-
-  constexpr void on_error(const char* message) {
-    FMT_THROW(format_error(message));
-  }
+template <typename Char>
+struct arg_id_handler
+{
+    arg_ref<Char> arg_id;
+
+    constexpr int operator()()
+    {
+        FMT_ASSERT(false, "handler cannot be used with automatic indexing");
+        return 0;
+    }
+    constexpr int operator()(int id)
+    {
+        arg_id = arg_ref<Char>(id);
+        return 0;
+    }
+    constexpr int operator()(basic_string_view<Char> id)
+    {
+        arg_id = arg_ref<Char>(id);
+        return 0;
+    }
+
+    constexpr void on_error(const char *message) { FMT_THROW(format_error(message)); }
 };
 
-template <typename Char> struct parse_arg_id_result {
-  arg_ref<Char> arg_id;
-  const Char* arg_id_end;
+template <typename Char>
+struct parse_arg_id_result
+{
+    arg_ref<Char> arg_id;
+    const Char   *arg_id_end;
 };
 
 template <int ID, typename Char>
-constexpr auto parse_arg_id(const Char* begin, const Char* end) {
-  auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
-  auto arg_id_end = parse_arg_id(begin, end, handler);
-  return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
+constexpr auto parse_arg_id(const Char *begin, const Char *end)
+{
+    auto handler    = arg_id_handler<Char>{arg_ref<Char>{}};
+    auto arg_id_end = parse_arg_id(begin, end, handler);
+    return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
 }
 
-template <typename T, typename Enable = void> struct field_type {
-  using type = remove_cvref_t<T>;
+template <typename T, typename Enable = void>
+struct field_type
+{
+    using type = remove_cvref_t<T>;
 };
 
 template <typename T>
-struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
-  using type = remove_cvref_t<decltype(T::value)>;
+struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>>
+{
+    using type = remove_cvref_t<decltype(T::value)>;
 };
 
-template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
-          typename S>
-constexpr auto parse_replacement_field_then_tail(S format_str) {
-  using char_type = typename S::char_type;
-  constexpr auto str = basic_string_view<char_type>(format_str);
-  constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
-  if constexpr (c == '}') {
-    return parse_tail<Args, END_POS + 1, NEXT_ID>(
-        field<char_type, typename field_type<T>::type, ARG_INDEX>(),
-        format_str);
-  } else if constexpr (c != ':') {
-    FMT_THROW(format_error("expected ':'"));
-  } else {
-    constexpr auto result = parse_specs<typename field_type<T>::type>(
-        str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
-    if constexpr (result.end >= str.size() || str[result.end] != '}') {
-      FMT_THROW(format_error("expected '}'"));
-      return 0;
-    } else {
-      return parse_tail<Args, result.end + 1, result.next_arg_id>(
-          spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
-              result.fmt},
-          format_str);
+template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID, typename S>
+constexpr auto parse_replacement_field_then_tail(S format_str)
+{
+    using char_type         = typename S::char_type;
+    constexpr auto      str = basic_string_view<char_type>(format_str);
+    constexpr char_type c   = END_POS != str.size() ? str[END_POS] : char_type();
+    if constexpr(c == '}')
+    {
+        return parse_tail<Args, END_POS + 1, NEXT_ID>(
+            field<char_type, typename field_type<T>::type, ARG_INDEX>(), format_str);
+    }
+    else if constexpr(c != ':')
+    {
+        FMT_THROW(format_error("expected ':'"));
+    }
+    else
+    {
+        constexpr auto result =
+            parse_specs<typename field_type<T>::type>(str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
+        if constexpr(result.end >= str.size() || str[result.end] != '}')
+        {
+            FMT_THROW(format_error("expected '}'"));
+            return 0;
+        }
+        else
+        {
+            return parse_tail<Args, result.end + 1, result.next_arg_id>(
+                spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{result.fmt}, format_str);
+        }
     }
-  }
 }
 
 // Compiles a non-empty format string and returns the compiled representation
 // or unknown_format() on unrecognized input.
 template <typename Args, size_t POS, int ID, typename S>
-constexpr auto compile_format_string(S format_str) {
-  using char_type = typename S::char_type;
-  constexpr auto str = basic_string_view<char_type>(format_str);
-  if constexpr (str[POS] == '{') {
-    if constexpr (POS + 1 == str.size())
-      FMT_THROW(format_error("unmatched '{' in format string"));
-    if constexpr (str[POS + 1] == '{') {
-      return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
-    } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
-      static_assert(ID != manual_indexing_id,
-                    "cannot switch from manual to automatic argument indexing");
-      constexpr auto next_id =
-          ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
-      return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
-                                               POS + 1, ID, next_id>(
-          format_str);
-    } else {
-      constexpr auto arg_id_result =
-          parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
-      constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
-      constexpr char_type c =
-          arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
-      static_assert(c == '}' || c == ':', "missing '}' in format string");
-      if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
-        static_assert(
-            ID == manual_indexing_id || ID == 0,
-            "cannot switch from automatic to manual argument indexing");
-        constexpr auto arg_index = arg_id_result.arg_id.val.index;
-        return parse_replacement_field_then_tail<get_type<arg_index, Args>,
-                                                 Args, arg_id_end_pos,
-                                                 arg_index, manual_indexing_id>(
-            format_str);
-      } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
-        constexpr auto arg_index =
-            get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
-        if constexpr (arg_index != invalid_arg_index) {
-          constexpr auto next_id =
-              ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
-          return parse_replacement_field_then_tail<
-              decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
-              arg_index, next_id>(format_str);
-        } else {
-          if constexpr (c == '}') {
-            return parse_tail<Args, arg_id_end_pos + 1, ID>(
-                runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
-                format_str);
-          } else if constexpr (c == ':') {
-            return unknown_format();  // no type info for specs parsing
-          }
+constexpr auto compile_format_string(S format_str)
+{
+    using char_type    = typename S::char_type;
+    constexpr auto str = basic_string_view<char_type>(format_str);
+    if constexpr(str[POS] == '{')
+    {
+        if constexpr(POS + 1 == str.size())
+            FMT_THROW(format_error("unmatched '{' in format string"));
+        if constexpr(str[POS + 1] == '{')
+        {
+            return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
+        }
+        else if constexpr(str[POS + 1] == '}' || str[POS + 1] == ':')
+        {
+            static_assert(ID != manual_indexing_id, "cannot switch from manual to automatic argument indexing");
+            constexpr auto next_id = ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
+            return parse_replacement_field_then_tail<get_type<ID, Args>, Args, POS + 1, ID, next_id>(format_str);
+        }
+        else
+        {
+            constexpr auto      arg_id_result  = parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
+            constexpr auto      arg_id_end_pos = arg_id_result.arg_id_end - str.data();
+            constexpr char_type c              = arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
+            static_assert(c == '}' || c == ':', "missing '}' in format string");
+            if constexpr(arg_id_result.arg_id.kind == arg_id_kind::index)
+            {
+                static_assert(
+                    ID == manual_indexing_id || ID == 0, "cannot switch from automatic to manual argument indexing");
+                constexpr auto arg_index = arg_id_result.arg_id.val.index;
+                return parse_replacement_field_then_tail<
+                    get_type<arg_index, Args>,
+                    Args,
+                    arg_id_end_pos,
+                    arg_index,
+                    manual_indexing_id>(format_str);
+            }
+            else if constexpr(arg_id_result.arg_id.kind == arg_id_kind::name)
+            {
+                constexpr auto arg_index = get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
+                if constexpr(arg_index != invalid_arg_index)
+                {
+                    constexpr auto next_id = ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
+                    return parse_replacement_field_then_tail<
+                        decltype(get_type<arg_index, Args>::value),
+                        Args,
+                        arg_id_end_pos,
+                        arg_index,
+                        next_id>(format_str);
+                }
+                else
+                {
+                    if constexpr(c == '}')
+                    {
+                        return parse_tail<Args, arg_id_end_pos + 1, ID>(
+                            runtime_named_field<char_type>{arg_id_result.arg_id.val.name}, format_str);
+                    }
+                    else if constexpr(c == ':')
+                    {
+                        return unknown_format(); // no type info for specs parsing
+                    }
+                }
+            }
         }
-      }
     }
-  } else if constexpr (str[POS] == '}') {
-    if constexpr (POS + 1 == str.size())
-      FMT_THROW(format_error("unmatched '}' in format string"));
-    return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
-  } else {
-    constexpr auto end = parse_text(str, POS + 1);
-    if constexpr (end - POS > 1) {
-      return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
-                                       format_str);
-    } else {
-      return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
-                                       format_str);
+    else if constexpr(str[POS] == '}')
+    {
+        if constexpr(POS + 1 == str.size())
+            FMT_THROW(format_error("unmatched '}' in format string"));
+        return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
+    }
+    else
+    {
+        constexpr auto end = parse_text(str, POS + 1);
+        if constexpr(end - POS > 1)
+        {
+            return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), format_str);
+        }
+        else
+        {
+            return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, format_str);
+        }
     }
-  }
 }
 
-template <typename... Args, typename S,
-          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-constexpr auto compile(S format_str) {
-  constexpr auto str = basic_string_view<typename S::char_type>(format_str);
-  if constexpr (str.size() == 0) {
-    return detail::make_text(str, 0, 0);
-  } else {
-    constexpr auto result =
-        detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
-            format_str);
-    return result;
-  }
+template <typename... Args, typename S, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+constexpr auto compile(S format_str)
+{
+    constexpr auto str = basic_string_view<typename S::char_type>(format_str);
+    if constexpr(str.size() == 0)
+    {
+        return detail::make_text(str, 0, 0);
+    }
+    else
+    {
+        constexpr auto result = detail::compile_format_string<detail::type_list<Args...>, 0, 0>(format_str);
+        return result;
+    }
 }
-#endif  // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
-}  // namespace detail
+#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
+} // namespace detail
 
 FMT_MODULE_EXPORT_BEGIN
 
 #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
 
-template <typename CompiledFormat, typename... Args,
-          typename Char = typename CompiledFormat::char_type,
-          FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
-FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
-                                          const Args&... args) {
-  auto s = std::basic_string<Char>();
-  cf.format(std::back_inserter(s), args...);
-  return s;
+template <
+    typename CompiledFormat,
+    typename... Args,
+    typename Char = typename CompiledFormat::char_type,
+    FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
+FMT_INLINE std::basic_string<Char> format(const CompiledFormat &cf, const Args &...args)
+{
+    auto s = std::basic_string<Char>();
+    cf.format(std::back_inserter(s), args...);
+    return s;
 }
 
-template <typename OutputIt, typename CompiledFormat, typename... Args,
-          FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
-constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
-                                        const Args&... args) {
-  return cf.format(out, args...);
+template <
+    typename OutputIt,
+    typename CompiledFormat,
+    typename... Args,
+    FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
+constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat &cf, const Args &...args)
+{
+    return cf.format(out, args...);
 }
 
-template <typename S, typename... Args,
-          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
-                                                           Args&&... args) {
-  if constexpr (std::is_same<typename S::char_type, char>::value) {
-    constexpr auto str = basic_string_view<typename S::char_type>(S());
-    if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
-      const auto& first = detail::first(args...);
-      if constexpr (detail::is_named_arg<
-                        remove_cvref_t<decltype(first)>>::value) {
-        return fmt::to_string(first.value);
-      } else {
-        return fmt::to_string(first);
-      }
+template <typename S, typename... Args, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+FMT_INLINE std::basic_string<typename S::char_type> format(const S &, Args &&...args)
+{
+    if constexpr(std::is_same<typename S::char_type, char>::value)
+    {
+        constexpr auto str = basic_string_view<typename S::char_type>(S());
+        if constexpr(str.size() == 2 && str[0] == '{' && str[1] == '}')
+        {
+            const auto &first = detail::first(args...);
+            if constexpr(detail::is_named_arg<remove_cvref_t<decltype(first)>>::value)
+            {
+                return fmt::to_string(first.value);
+            }
+            else
+            {
+                return fmt::to_string(first);
+            }
+        }
+    }
+    constexpr auto compiled = detail::compile<Args...>(S());
+    if constexpr(std::is_same<remove_cvref_t<decltype(compiled)>, detail::unknown_format>())
+    {
+        return fmt::format(static_cast<basic_string_view<typename S::char_type>>(S()), std::forward<Args>(args)...);
+    }
+    else
+    {
+        return fmt::format(compiled, std::forward<Args>(args)...);
     }
-  }
-  constexpr auto compiled = detail::compile<Args...>(S());
-  if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
-                             detail::unknown_format>()) {
-    return fmt::format(
-        static_cast<basic_string_view<typename S::char_type>>(S()),
-        std::forward<Args>(args)...);
-  } else {
-    return fmt::format(compiled, std::forward<Args>(args)...);
-  }
 }
 
-template <typename OutputIt, typename S, typename... Args,
-          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
-  constexpr auto compiled = detail::compile<Args...>(S());
-  if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
-                             detail::unknown_format>()) {
-    return fmt::format_to(
-        out, static_cast<basic_string_view<typename S::char_type>>(S()),
-        std::forward<Args>(args)...);
-  } else {
-    return fmt::format_to(out, compiled, std::forward<Args>(args)...);
-  }
+template <typename OutputIt, typename S, typename... Args, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S &, Args &&...args)
+{
+    constexpr auto compiled = detail::compile<Args...>(S());
+    if constexpr(std::is_same<remove_cvref_t<decltype(compiled)>, detail::unknown_format>())
+    {
+        return fmt::format_to(
+            out, static_cast<basic_string_view<typename S::char_type>>(S()), std::forward<Args>(args)...);
+    }
+    else
+    {
+        return fmt::format_to(out, compiled, std::forward<Args>(args)...);
+    }
 }
 #endif
 
-template <typename OutputIt, typename S, typename... Args,
-          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
-                                         const S& format_str, Args&&... args) {
-  auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
-                           format_str, std::forward<Args>(args)...);
-  return {it.base(), it.count()};
+template <typename OutputIt, typename S, typename... Args, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S &format_str, Args &&...args)
+{
+    auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n), format_str, std::forward<Args>(args)...);
+    return {it.base(), it.count()};
 }
 
-template <typename S, typename... Args,
-          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-FMT_CONSTEXPR20 size_t formatted_size(const S& format_str,
-                                      const Args&... args) {
-  return fmt::format_to(detail::counting_iterator(), format_str, args...)
-      .count();
+template <typename S, typename... Args, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+FMT_CONSTEXPR20 size_t formatted_size(const S &format_str, const Args &...args)
+{
+    return fmt::format_to(detail::counting_iterator(), format_str, args...).count();
 }
 
-template <typename S, typename... Args,
-          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-void print(std::FILE* f, const S& format_str, const Args&... args) {
-  memory_buffer buffer;
-  fmt::format_to(std::back_inserter(buffer), format_str, args...);
-  detail::print(f, {buffer.data(), buffer.size()});
+template <typename S, typename... Args, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+void print(std::FILE *f, const S &format_str, const Args &...args)
+{
+    memory_buffer buffer;
+    fmt::format_to(std::back_inserter(buffer), format_str, args...);
+    detail::print(f, {buffer.data(), buffer.size()});
 }
 
-template <typename S, typename... Args,
-          FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-void print(const S& format_str, const Args&... args) {
-  print(stdout, format_str, args...);
+template <typename S, typename... Args, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
+void print(const S &format_str, const Args &...args)
+{
+    print(stdout, format_str, args...);
 }
 
 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
-inline namespace literals {
-template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
-  using char_t = remove_cvref_t<decltype(Str.data[0])>;
-  return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
-                                     Str>();
+inline namespace literals
+{
+template <detail_exported::fixed_string Str>
+constexpr auto operator""_cf()
+{
+    using char_t = remove_cvref_t<decltype(Str.data[0])>;
+    return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t), Str>();
 }
-}  // namespace literals
+} // namespace literals
 #endif
 
 FMT_MODULE_EXPORT_END
 FMT_END_NAMESPACE
 
-#endif  // FMT_COMPILE_H_
+#endif // FMT_COMPILE_H_
diff --git a/include/spdlog/fmt/bundled/core.h b/include/spdlog/fmt/bundled/core.h
index f6a37af9e..c4a806c98 100644
--- a/include/spdlog/fmt/bundled/core.h
+++ b/include/spdlog/fmt/bundled/core.h
@@ -8,9 +8,9 @@
 #ifndef FMT_CORE_H_
 #define FMT_CORE_H_
 
-#include <cstddef>  // std::byte
-#include <cstdio>   // std::FILE
-#include <cstring>  // std::strlen
+#include <cstddef> // std::byte
+#include <cstdio>  // std::FILE
+#include <cstring> // std::strlen
 #include <iterator>
 #include <limits>
 #include <string>
@@ -20,268 +20,258 @@
 #define FMT_VERSION 90100
 
 #if defined(__clang__) && !defined(__ibmxl__)
-#  define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
+#define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
 #else
-#  define FMT_CLANG_VERSION 0
+#define FMT_CLANG_VERSION 0
 #endif
 
-#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \
-    !defined(__NVCOMPILER)
-#  define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__NVCOMPILER)
+#define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
 #else
-#  define FMT_GCC_VERSION 0
+#define FMT_GCC_VERSION 0
 #endif
 
 #ifndef FMT_GCC_PRAGMA
 // Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884.
-#  if FMT_GCC_VERSION >= 504
-#    define FMT_GCC_PRAGMA(arg) _Pragma(arg)
-#  else
-#    define FMT_GCC_PRAGMA(arg)
-#  endif
+#if FMT_GCC_VERSION >= 504
+#define FMT_GCC_PRAGMA(arg) _Pragma(arg)
+#else
+#define FMT_GCC_PRAGMA(arg)
+#endif
 #endif
 
 #ifdef __ICL
-#  define FMT_ICC_VERSION __ICL
+#define FMT_ICC_VERSION __ICL
 #elif defined(__INTEL_COMPILER)
-#  define FMT_ICC_VERSION __INTEL_COMPILER
+#define FMT_ICC_VERSION __INTEL_COMPILER
 #else
-#  define FMT_ICC_VERSION 0
+#define FMT_ICC_VERSION 0
 #endif
 
 #ifdef _MSC_VER
-#  define FMT_MSC_VERSION _MSC_VER
-#  define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__))
+#define FMT_MSC_VERSION      _MSC_VER
+#define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__))
 #else
-#  define FMT_MSC_VERSION 0
-#  define FMT_MSC_WARNING(...)
+#define FMT_MSC_VERSION 0
+#define FMT_MSC_WARNING(...)
 #endif
 
 #ifdef _MSVC_LANG
-#  define FMT_CPLUSPLUS _MSVC_LANG
+#define FMT_CPLUSPLUS _MSVC_LANG
 #else
-#  define FMT_CPLUSPLUS __cplusplus
+#define FMT_CPLUSPLUS __cplusplus
 #endif
 
 #ifdef __has_feature
-#  define FMT_HAS_FEATURE(x) __has_feature(x)
+#define FMT_HAS_FEATURE(x) __has_feature(x)
 #else
-#  define FMT_HAS_FEATURE(x) 0
+#define FMT_HAS_FEATURE(x) 0
 #endif
 
-#if (defined(__has_include) || FMT_ICC_VERSION >= 1600 || \
-     FMT_MSC_VERSION > 1900) &&                           \
-    !defined(__INTELLISENSE__)
-#  define FMT_HAS_INCLUDE(x) __has_include(x)
+#if(defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900) && !defined(__INTELLISENSE__)
+#define FMT_HAS_INCLUDE(x) __has_include(x)
 #else
-#  define FMT_HAS_INCLUDE(x) 0
+#define FMT_HAS_INCLUDE(x) 0
 #endif
 
 #ifdef __has_cpp_attribute
-#  define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
+#define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
 #else
-#  define FMT_HAS_CPP_ATTRIBUTE(x) 0
+#define FMT_HAS_CPP_ATTRIBUTE(x) 0
 #endif
 
-#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
-  (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+#define FMT_HAS_CPP14_ATTRIBUTE(attribute) (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
 
-#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
-  (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
+#define FMT_HAS_CPP17_ATTRIBUTE(attribute) (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
 
 // Check if relaxed C++14 constexpr is supported.
 // GCC doesn't allow throw in constexpr until version 6 (bug 67371).
 #ifndef FMT_USE_CONSTEXPR
-#  if (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 || \
-       (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) &&             \
-      !FMT_ICC_VERSION && !defined(__NVCC__)
-#    define FMT_USE_CONSTEXPR 1
-#  else
-#    define FMT_USE_CONSTEXPR 0
-#  endif
+#if(FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 ||                                               \
+    (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) &&                                                           \
+    !FMT_ICC_VERSION && !defined(__NVCC__)
+#define FMT_USE_CONSTEXPR 1
+#else
+#define FMT_USE_CONSTEXPR 0
+#endif
 #endif
 #if FMT_USE_CONSTEXPR
-#  define FMT_CONSTEXPR constexpr
+#define FMT_CONSTEXPR constexpr
 #else
-#  define FMT_CONSTEXPR
+#define FMT_CONSTEXPR
 #endif
 
-#if ((FMT_CPLUSPLUS >= 202002L) &&                            \
-     (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \
+#if((FMT_CPLUSPLUS >= 202002L) && (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) ||                             \
     (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
-#  define FMT_CONSTEXPR20 constexpr
+#define FMT_CONSTEXPR20 constexpr
 #else
-#  define FMT_CONSTEXPR20
+#define FMT_CONSTEXPR20
 #endif
 
 // Check if constexpr std::char_traits<>::{compare,length} are supported.
 #if defined(__GLIBCXX__)
-#  if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) && \
-      _GLIBCXX_RELEASE >= 7  // GCC 7+ libstdc++ has _GLIBCXX_RELEASE.
-#    define FMT_CONSTEXPR_CHAR_TRAITS constexpr
-#  endif
-#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && \
-    _LIBCPP_VERSION >= 4000
-#  define FMT_CONSTEXPR_CHAR_TRAITS constexpr
+#if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) &&                                                           \
+    _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE.
+#define FMT_CONSTEXPR_CHAR_TRAITS constexpr
+#endif
+#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && _LIBCPP_VERSION >= 4000
+#define FMT_CONSTEXPR_CHAR_TRAITS constexpr
 #elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L
-#  define FMT_CONSTEXPR_CHAR_TRAITS constexpr
+#define FMT_CONSTEXPR_CHAR_TRAITS constexpr
 #endif
 #ifndef FMT_CONSTEXPR_CHAR_TRAITS
-#  define FMT_CONSTEXPR_CHAR_TRAITS
+#define FMT_CONSTEXPR_CHAR_TRAITS
 #endif
 
 // Check if exceptions are disabled.
 #ifndef FMT_EXCEPTIONS
-#  if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \
-      (FMT_MSC_VERSION && !_HAS_EXCEPTIONS)
-#    define FMT_EXCEPTIONS 0
-#  else
-#    define FMT_EXCEPTIONS 1
-#  endif
+#if(defined(__GNUC__) && !defined(__EXCEPTIONS)) || (FMT_MSC_VERSION && !_HAS_EXCEPTIONS)
+#define FMT_EXCEPTIONS 0
+#else
+#define FMT_EXCEPTIONS 1
+#endif
 #endif
 
 #ifndef FMT_DEPRECATED
-#  if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900
-#    define FMT_DEPRECATED [[deprecated]]
-#  else
-#    if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
-#      define FMT_DEPRECATED __attribute__((deprecated))
-#    elif FMT_MSC_VERSION
-#      define FMT_DEPRECATED __declspec(deprecated)
-#    else
-#      define FMT_DEPRECATED /* deprecated */
-#    endif
-#  endif
+#if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900
+#define FMT_DEPRECATED [[deprecated]]
+#else
+#if(defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
+#define FMT_DEPRECATED __attribute__((deprecated))
+#elif FMT_MSC_VERSION
+#define FMT_DEPRECATED __declspec(deprecated)
+#else
+#define FMT_DEPRECATED /* deprecated */
+#endif
+#endif
 #endif
 
 // [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code
 // warnings.
-#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && \
-    !defined(__NVCC__)
-#  define FMT_NORETURN [[noreturn]]
+#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && !defined(__NVCC__)
+#define FMT_NORETURN [[noreturn]]
 #else
-#  define FMT_NORETURN
+#define FMT_NORETURN
 #endif
 
 #if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
-#  define FMT_FALLTHROUGH [[fallthrough]]
+#define FMT_FALLTHROUGH [[fallthrough]]
 #elif defined(__clang__)
-#  define FMT_FALLTHROUGH [[clang::fallthrough]]
-#elif FMT_GCC_VERSION >= 700 && \
-    (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
-#  define FMT_FALLTHROUGH [[gnu::fallthrough]]
+#define FMT_FALLTHROUGH [[clang::fallthrough]]
+#elif FMT_GCC_VERSION >= 700 && (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
+#define FMT_FALLTHROUGH [[gnu::fallthrough]]
 #else
-#  define FMT_FALLTHROUGH
+#define FMT_FALLTHROUGH
 #endif
 
 #ifndef FMT_NODISCARD
-#  if FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
-#    define FMT_NODISCARD [[nodiscard]]
-#  else
-#    define FMT_NODISCARD
-#  endif
+#if FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
+#define FMT_NODISCARD [[nodiscard]]
+#else
+#define FMT_NODISCARD
+#endif
 #endif
 
 #ifndef FMT_USE_FLOAT
-#  define FMT_USE_FLOAT 1
+#define FMT_USE_FLOAT 1
 #endif
 #ifndef FMT_USE_DOUBLE
-#  define FMT_USE_DOUBLE 1
+#define FMT_USE_DOUBLE 1
 #endif
 #ifndef FMT_USE_LONG_DOUBLE
-#  define FMT_USE_LONG_DOUBLE 1
+#define FMT_USE_LONG_DOUBLE 1
 #endif
 
 #ifndef FMT_INLINE
-#  if FMT_GCC_VERSION || FMT_CLANG_VERSION
-#    define FMT_INLINE inline __attribute__((always_inline))
-#  else
-#    define FMT_INLINE inline
-#  endif
+#if FMT_GCC_VERSION || FMT_CLANG_VERSION
+#define FMT_INLINE inline __attribute__((always_inline))
+#else
+#define FMT_INLINE inline
+#endif
 #endif
 
 // An inline std::forward replacement.
-#define FMT_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
+#define FMT_FORWARD(...) static_cast<decltype(__VA_ARGS__) &&>(__VA_ARGS__)
 
 #ifdef _MSC_VER
-#  define FMT_UNCHECKED_ITERATOR(It) \
-    using _Unchecked_type = It  // Mark iterator as checked.
+#define FMT_UNCHECKED_ITERATOR(It) using _Unchecked_type = It // Mark iterator as checked.
 #else
-#  define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It
+#define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It
 #endif
 
 #ifndef FMT_BEGIN_NAMESPACE
-#  define FMT_BEGIN_NAMESPACE \
-    namespace fmt {           \
-    inline namespace v9 {
-#  define FMT_END_NAMESPACE \
-    }                       \
+#define FMT_BEGIN_NAMESPACE                                                                                            \
+    namespace fmt                                                                                                      \
+    {                                                                                                                  \
+    inline namespace v9                                                                                                \
+    {
+#define FMT_END_NAMESPACE                                                                                              \
+    }                                                                                                                  \
     }
 #endif
 
 #ifndef FMT_MODULE_EXPORT
-#  define FMT_MODULE_EXPORT
-#  define FMT_MODULE_EXPORT_BEGIN
-#  define FMT_MODULE_EXPORT_END
-#  define FMT_BEGIN_DETAIL_NAMESPACE namespace detail {
-#  define FMT_END_DETAIL_NAMESPACE }
+#define FMT_MODULE_EXPORT
+#define FMT_MODULE_EXPORT_BEGIN
+#define FMT_MODULE_EXPORT_END
+#define FMT_BEGIN_DETAIL_NAMESPACE                                                                                     \
+    namespace detail                                                                                                   \
+    {
+#define FMT_END_DETAIL_NAMESPACE }
 #endif
 
 #if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
-#  define FMT_CLASS_API FMT_MSC_WARNING(suppress : 4275)
-#  ifdef FMT_EXPORT
-#    define FMT_API __declspec(dllexport)
-#  elif defined(FMT_SHARED)
-#    define FMT_API __declspec(dllimport)
-#  endif
+#define FMT_CLASS_API FMT_MSC_WARNING(suppress : 4275)
+#ifdef FMT_EXPORT
+#define FMT_API __declspec(dllexport)
+#elif defined(FMT_SHARED)
+#define FMT_API __declspec(dllimport)
+#endif
 #else
-#  define FMT_CLASS_API
-#  if defined(FMT_EXPORT) || defined(FMT_SHARED)
-#    if defined(__GNUC__) || defined(__clang__)
-#      define FMT_API __attribute__((visibility("default")))
-#    endif
-#  endif
+#define FMT_CLASS_API
+#if defined(FMT_EXPORT) || defined(FMT_SHARED)
+#if defined(__GNUC__) || defined(__clang__)
+#define FMT_API __attribute__((visibility("default")))
+#endif
+#endif
 #endif
 #ifndef FMT_API
-#  define FMT_API
+#define FMT_API
 #endif
 
 // libc++ supports string_view in pre-c++17.
-#if FMT_HAS_INCLUDE(<string_view>) && \
-    (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION))
-#  include <string_view>
-#  define FMT_USE_STRING_VIEW
+#if FMT_HAS_INCLUDE(<string_view>) && (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION))
+#include <string_view>
+#define FMT_USE_STRING_VIEW
 #elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L
-#  include <experimental/string_view>
-#  define FMT_USE_EXPERIMENTAL_STRING_VIEW
+#include <experimental/string_view>
+#define FMT_USE_EXPERIMENTAL_STRING_VIEW
 #endif
 
 #ifndef FMT_UNICODE
-#  define FMT_UNICODE !FMT_MSC_VERSION
+#define FMT_UNICODE !FMT_MSC_VERSION
 #endif
 
 #ifndef FMT_CONSTEVAL
-#  if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) &&         \
-       FMT_CPLUSPLUS >= 202002L && !defined(__apple_build_version__)) || \
-      (defined(__cpp_consteval) &&                                       \
-       (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704))
+#if((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && FMT_CPLUSPLUS >= 202002L &&                              \
+    !defined(__apple_build_version__)) ||                                                                              \
+    (defined(__cpp_consteval) && (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704))
 // consteval is broken in MSVC before VS2022 and Apple clang 13.
-#    define FMT_CONSTEVAL consteval
-#    define FMT_HAS_CONSTEVAL
-#  else
-#    define FMT_CONSTEVAL
-#  endif
+#define FMT_CONSTEVAL consteval
+#define FMT_HAS_CONSTEVAL
+#else
+#define FMT_CONSTEVAL
+#endif
 #endif
 
 #ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS
-#  if defined(__cpp_nontype_template_args) &&                  \
-      ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \
-       __cpp_nontype_template_args >= 201911L) &&              \
-      !defined(__NVCOMPILER)
-#    define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
-#  else
-#    define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
-#  endif
+#if defined(__cpp_nontype_template_args) &&                                                                            \
+    ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || __cpp_nontype_template_args >= 201911L) &&                \
+    !defined(__NVCOMPILER)
+#define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
+#else
+#define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
+#endif
 #endif
 
 // Enable minimal optimizations for more compact code in debug mode.
@@ -298,41 +288,62 @@ template <bool B, typename T = void>
 using enable_if_t = typename std::enable_if<B, T>::type;
 template <bool B, typename T, typename F>
 using conditional_t = typename std::conditional<B, T, F>::type;
-template <bool B> using bool_constant = std::integral_constant<bool, B>;
+template <bool B>
+using bool_constant = std::integral_constant<bool, B>;
 template <typename T>
 using remove_reference_t = typename std::remove_reference<T>::type;
 template <typename T>
 using remove_const_t = typename std::remove_const<T>::type;
 template <typename T>
 using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
-template <typename T> struct type_identity { using type = T; };
-template <typename T> using type_identity_t = typename type_identity<T>::type;
+template <typename T>
+struct type_identity
+{
+    using type = T;
+};
+template <typename T>
+using type_identity_t = typename type_identity<T>::type;
 template <typename T>
 using underlying_t = typename std::underlying_type<T>::type;
 
-template <typename...> struct disjunction : std::false_type {};
-template <typename P> struct disjunction<P> : P {};
+template <typename...>
+struct disjunction : std::false_type
+{
+};
+template <typename P>
+struct disjunction<P> : P
+{
+};
 template <typename P1, typename... Pn>
-struct disjunction<P1, Pn...>
-    : conditional_t<bool(P1::value), P1, disjunction<Pn...>> {};
+struct disjunction<P1, Pn...> : conditional_t<bool(P1::value), P1, disjunction<Pn...>>
+{
+};
 
-template <typename...> struct conjunction : std::true_type {};
-template <typename P> struct conjunction<P> : P {};
+template <typename...>
+struct conjunction : std::true_type
+{
+};
+template <typename P>
+struct conjunction<P> : P
+{
+};
 template <typename P1, typename... Pn>
-struct conjunction<P1, Pn...>
-    : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
+struct conjunction<P1, Pn...> : conditional_t<bool(P1::value), conjunction<Pn...>, P1>
+{
+};
 
-struct monostate {
-  constexpr monostate() {}
+struct monostate
+{
+    constexpr monostate() {}
 };
 
 // An enable_if helper to be used in template parameters which results in much
 // shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed
 // to workaround a bug in MSVC 2019 (see #1140 and #1186).
 #ifdef FMT_DOC
-#  define FMT_ENABLE_IF(...)
+#define FMT_ENABLE_IF(...)
 #else
-#  define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0
+#define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0
 #endif
 
 FMT_BEGIN_DETAIL_NAMESPACE
@@ -340,83 +351,100 @@ FMT_BEGIN_DETAIL_NAMESPACE
 // Suppresses "unused variable" warnings with the method described in
 // https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
 // (void)var does not work on many Intel compilers.
-template <typename... T> FMT_CONSTEXPR void ignore_unused(const T&...) {}
+template <typename... T>
+FMT_CONSTEXPR void ignore_unused(const T &...)
+{
+}
 
-constexpr FMT_INLINE auto is_constant_evaluated(
-    bool default_value = false) noexcept -> bool {
+constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false) noexcept -> bool
+{
 #ifdef __cpp_lib_is_constant_evaluated
-  ignore_unused(default_value);
-  return std::is_constant_evaluated();
+    ignore_unused(default_value);
+    return std::is_constant_evaluated();
 #else
-  return default_value;
+    return default_value;
 #endif
 }
 
 // Suppresses "conditional expression is constant" warnings.
-template <typename T> constexpr FMT_INLINE auto const_check(T value) -> T {
-  return value;
+template <typename T>
+constexpr FMT_INLINE auto const_check(T value) -> T
+{
+    return value;
 }
 
-FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
-                                      const char* message);
+FMT_NORETURN FMT_API void assert_fail(const char *file, int line, const char *message);
 
 #ifndef FMT_ASSERT
-#  ifdef NDEBUG
+#ifdef NDEBUG
 // FMT_ASSERT is not empty to avoid -Wempty-body.
-#    define FMT_ASSERT(condition, message) \
-      ::fmt::detail::ignore_unused((condition), (message))
-#  else
-#    define FMT_ASSERT(condition, message)                                    \
-      ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
-           ? (void)0                                                          \
-           : ::fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
-#  endif
+#define FMT_ASSERT(condition, message) ::fmt::detail::ignore_unused((condition), (message))
+#else
+#define FMT_ASSERT(condition, message)                                                                                 \
+    ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */                                            \
+         ?                                                                                                             \
+         (void) 0 :                                                                                                    \
+         ::fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
+#endif
 #endif
 
 #if defined(FMT_USE_STRING_VIEW)
-template <typename Char> using std_string_view = std::basic_string_view<Char>;
+template <typename Char>
+using std_string_view = std::basic_string_view<Char>;
 #elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
 template <typename Char>
 using std_string_view = std::experimental::basic_string_view<Char>;
 #else
-template <typename T> struct std_string_view {};
+template <typename T>
+struct std_string_view
+{
+};
 #endif
 
 #ifdef FMT_USE_INT128
 // Do nothing.
-#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && \
-    !(FMT_CLANG_VERSION && FMT_MSC_VERSION)
-#  define FMT_USE_INT128 1
-using int128_opt = __int128_t;  // An optional native 128-bit integer.
+#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && !(FMT_CLANG_VERSION && FMT_MSC_VERSION)
+#define FMT_USE_INT128 1
+using int128_opt = __int128_t; // An optional native 128-bit integer.
 using uint128_opt = __uint128_t;
-template <typename T> inline auto convert_for_visit(T value) -> T {
-  return value;
+template <typename T>
+inline auto convert_for_visit(T value) -> T
+{
+    return value;
 }
 #else
-#  define FMT_USE_INT128 0
+#define FMT_USE_INT128 0
 #endif
 #if !FMT_USE_INT128
-enum class int128_opt {};
-enum class uint128_opt {};
+enum class int128_opt
+{
+};
+enum class uint128_opt
+{
+};
 // Reduce template instantiations.
-template <typename T> auto convert_for_visit(T) -> monostate { return {}; }
+template <typename T>
+auto convert_for_visit(T) -> monostate
+{
+    return {};
+}
 #endif
 
 // Casts a nonnegative integer to unsigned.
 template <typename Int>
-FMT_CONSTEXPR auto to_unsigned(Int value) ->
-    typename std::make_unsigned<Int>::type {
-  FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value");
-  return static_cast<typename std::make_unsigned<Int>::type>(value);
+FMT_CONSTEXPR auto to_unsigned(Int value) -> typename std::make_unsigned<Int>::type
+{
+    FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value");
+    return static_cast<typename std::make_unsigned<Int>::type>(value);
 }
 
 FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5";
 
-constexpr auto is_utf8() -> bool {
-  // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297).
-  using uchar = unsigned char;
-  return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 &&
-                         uchar(micro[1]) == 0xB5);
+constexpr auto is_utf8() -> bool
+{
+    // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297).
+    using uchar = unsigned char;
+    return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 && uchar(micro[1]) == 0xB5);
 }
 FMT_END_DETAIL_NAMESPACE
 
@@ -427,134 +455,138 @@ FMT_END_DETAIL_NAMESPACE
   compiled with a different ``-std`` option than the client code (which is not
   recommended).
  */
-template <typename Char> class basic_string_view {
- private:
-  const Char* data_;
-  size_t size_;
-
- public:
-  using value_type = Char;
-  using iterator = const Char*;
-
-  constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
-
-  /** Constructs a string reference object from a C string and a size. */
-  constexpr basic_string_view(const Char* s, size_t count) noexcept
-      : data_(s), size_(count) {}
-
-  /**
-    \rst
-    Constructs a string reference object from a C string computing
-    the size with ``std::char_traits<Char>::length``.
-    \endrst
-   */
-  FMT_CONSTEXPR_CHAR_TRAITS
-  FMT_INLINE
-  basic_string_view(const Char* s)
-      : data_(s),
-        size_(detail::const_check(std::is_same<Char, char>::value &&
-                                  !detail::is_constant_evaluated(true))
-                  ? std::strlen(reinterpret_cast<const char*>(s))
-                  : std::char_traits<Char>::length(s)) {}
-
-  /** Constructs a string reference from a ``std::basic_string`` object. */
-  template <typename Traits, typename Alloc>
-  FMT_CONSTEXPR basic_string_view(
-      const std::basic_string<Char, Traits, Alloc>& s) noexcept
-      : data_(s.data()), size_(s.size()) {}
-
-  template <typename S, FMT_ENABLE_IF(std::is_same<
-                                      S, detail::std_string_view<Char>>::value)>
-  FMT_CONSTEXPR basic_string_view(S s) noexcept
-      : data_(s.data()), size_(s.size()) {}
-
-  /** Returns a pointer to the string data. */
-  constexpr auto data() const noexcept -> const Char* { return data_; }
-
-  /** Returns the string size. */
-  constexpr auto size() const noexcept -> size_t { return size_; }
-
-  constexpr auto begin() const noexcept -> iterator { return data_; }
-  constexpr auto end() const noexcept -> iterator { return data_ + size_; }
-
-  constexpr auto operator[](size_t pos) const noexcept -> const Char& {
-    return data_[pos];
-  }
-
-  FMT_CONSTEXPR void remove_prefix(size_t n) noexcept {
-    data_ += n;
-    size_ -= n;
-  }
-
-  // Lexicographically compare this string reference to other.
-  FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int {
-    size_t str_size = size_ < other.size_ ? size_ : other.size_;
-    int result = std::char_traits<Char>::compare(data_, other.data_, str_size);
-    if (result == 0)
-      result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
-    return result;
-  }
-
-  FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs,
-                                                   basic_string_view rhs)
-      -> bool {
-    return lhs.compare(rhs) == 0;
-  }
-  friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool {
-    return lhs.compare(rhs) != 0;
-  }
-  friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool {
-    return lhs.compare(rhs) < 0;
-  }
-  friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool {
-    return lhs.compare(rhs) <= 0;
-  }
-  friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool {
-    return lhs.compare(rhs) > 0;
-  }
-  friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool {
-    return lhs.compare(rhs) >= 0;
-  }
+template <typename Char>
+class basic_string_view
+{
+private:
+    const Char *data_;
+    size_t      size_;
+
+public:
+    using value_type = Char;
+    using iterator   = const Char *;
+
+    constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
+
+    /** Constructs a string reference object from a C string and a size. */
+    constexpr basic_string_view(const Char *s, size_t count) noexcept : data_(s), size_(count) {}
+
+    /**
+      \rst
+      Constructs a string reference object from a C string computing
+      the size with ``std::char_traits<Char>::length``.
+      \endrst
+     */
+    FMT_CONSTEXPR_CHAR_TRAITS
+    FMT_INLINE
+    basic_string_view(const Char *s) :
+        data_(s),
+        size_(
+            detail::const_check(std::is_same<Char, char>::value && !detail::is_constant_evaluated(true)) ?
+                std::strlen(reinterpret_cast<const char *>(s)) :
+                std::char_traits<Char>::length(s))
+    {
+    }
+
+    /** Constructs a string reference from a ``std::basic_string`` object. */
+    template <typename Traits, typename Alloc>
+    FMT_CONSTEXPR basic_string_view(const std::basic_string<Char, Traits, Alloc> &s) noexcept :
+        data_(s.data()), size_(s.size())
+    {
+    }
+
+    template <typename S, FMT_ENABLE_IF(std::is_same<S, detail::std_string_view<Char>>::value)>
+    FMT_CONSTEXPR basic_string_view(S s) noexcept : data_(s.data()), size_(s.size())
+    {
+    }
+
+    /** Returns a pointer to the string data. */
+    constexpr auto data() const noexcept -> const Char * { return data_; }
+
+    /** Returns the string size. */
+    constexpr auto size() const noexcept -> size_t { return size_; }
+
+    constexpr auto begin() const noexcept -> iterator { return data_; }
+    constexpr auto end() const noexcept -> iterator { return data_ + size_; }
+
+    constexpr auto operator[](size_t pos) const noexcept -> const Char & { return data_[pos]; }
+
+    FMT_CONSTEXPR void remove_prefix(size_t n) noexcept
+    {
+        data_ += n;
+        size_ -= n;
+    }
+
+    // Lexicographically compare this string reference to other.
+    FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int
+    {
+        size_t str_size = size_ < other.size_ ? size_ : other.size_;
+        int    result   = std::char_traits<Char>::compare(data_, other.data_, str_size);
+        if(result == 0)
+            result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
+        return result;
+    }
+
+    FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs, basic_string_view rhs) -> bool
+    {
+        return lhs.compare(rhs) == 0;
+    }
+    friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) != 0; }
+    friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) < 0; }
+    friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) <= 0; }
+    friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) > 0; }
+    friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) >= 0; }
 };
 
 using string_view = basic_string_view<char>;
 
 /** Specifies if ``T`` is a character type. Can be specialized by users. */
-template <typename T> struct is_char : std::false_type {};
-template <> struct is_char<char> : std::true_type {};
+template <typename T>
+struct is_char : std::false_type
+{
+};
+template <>
+struct is_char<char> : std::true_type
+{
+};
 
 FMT_BEGIN_DETAIL_NAMESPACE
 
 // A base class for compile-time strings.
-struct compile_string {};
+struct compile_string
+{
+};
 
 template <typename S>
-struct is_compile_string : std::is_base_of<compile_string, S> {};
+struct is_compile_string : std::is_base_of<compile_string, S>
+{
+};
 
 // Returns a string view of `s`.
 template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
-FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view<Char> {
-  return s;
+FMT_INLINE auto to_string_view(const Char *s) -> basic_string_view<Char>
+{
+    return s;
 }
 template <typename Char, typename Traits, typename Alloc>
-inline auto to_string_view(const std::basic_string<Char, Traits, Alloc>& s)
-    -> basic_string_view<Char> {
-  return s;
+inline auto to_string_view(const std::basic_string<Char, Traits, Alloc> &s) -> basic_string_view<Char>
+{
+    return s;
 }
 template <typename Char>
-constexpr auto to_string_view(basic_string_view<Char> s)
-    -> basic_string_view<Char> {
-  return s;
+constexpr auto to_string_view(basic_string_view<Char> s) -> basic_string_view<Char>
+{
+    return s;
 }
-template <typename Char,
-          FMT_ENABLE_IF(!std::is_empty<std_string_view<Char>>::value)>
-inline auto to_string_view(std_string_view<Char> s) -> basic_string_view<Char> {
-  return s;
+template <typename Char, FMT_ENABLE_IF(!std::is_empty<std_string_view<Char>>::value)>
+inline auto to_string_view(std_string_view<Char> s) -> basic_string_view<Char>
+{
+    return s;
 }
 template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
-constexpr auto to_string_view(const S& s)
-    -> basic_string_view<typename S::char_type> {
-  return basic_string_view<typename S::char_type>(s);
+constexpr auto to_string_view(const S &s) -> basic_string_view<typename S::char_type>
+{
+    return basic_string_view<typename S::char_type>(s);
 }
 void to_string_view(...);
 
@@ -563,46 +595,56 @@ void to_string_view(...);
 // enable_if and MSVC 2015 fails to compile it as an alias template.
 // ADL invocation of to_string_view is DEPRECATED!
 template <typename S>
-struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))> {
-};
-
-template <typename S, typename = void> struct char_t_impl {};
-template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
-  using result = decltype(to_string_view(std::declval<S>()));
-  using type = typename result::value_type;
-};
-
-enum class type {
-  none_type,
-  // Integer types should go first,
-  int_type,
-  uint_type,
-  long_long_type,
-  ulong_long_type,
-  int128_type,
-  uint128_type,
-  bool_type,
-  char_type,
-  last_integer_type = char_type,
-  // followed by floating-point types.
-  float_type,
-  double_type,
-  long_double_type,
-  last_numeric_type = long_double_type,
-  cstring_type,
-  string_type,
-  pointer_type,
-  custom_type
+struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))>
+{
+};
+
+template <typename S, typename = void>
+struct char_t_impl
+{
+};
+template <typename S>
+struct char_t_impl<S, enable_if_t<is_string<S>::value>>
+{
+    using result = decltype(to_string_view(std::declval<S>()));
+    using type   = typename result::value_type;
+};
+
+enum class type
+{
+    none_type,
+    // Integer types should go first,
+    int_type,
+    uint_type,
+    long_long_type,
+    ulong_long_type,
+    int128_type,
+    uint128_type,
+    bool_type,
+    char_type,
+    last_integer_type = char_type,
+    // followed by floating-point types.
+    float_type,
+    double_type,
+    long_double_type,
+    last_numeric_type = long_double_type,
+    cstring_type,
+    string_type,
+    pointer_type,
+    custom_type
 };
 
 // Maps core type T to the corresponding type enum constant.
 template <typename T, typename Char>
-struct type_constant : std::integral_constant<type, type::custom_type> {};
+struct type_constant : std::integral_constant<type, type::custom_type>
+{
+};
 
-#define FMT_TYPE_CONSTANT(Type, constant) \
-  template <typename Char>                \
-  struct type_constant<Type, Char>        \
-      : std::integral_constant<type, type::constant> {}
+#define FMT_TYPE_CONSTANT(Type, constant)                                                                              \
+    template <typename Char>                                                                                           \
+    struct type_constant<Type, Char> : std::integral_constant<type, type::constant>                                    \
+    {                                                                                                                  \
+    }
 
 FMT_TYPE_CONSTANT(int, int_type);
 FMT_TYPE_CONSTANT(unsigned, uint_type);
@@ -615,33 +657,35 @@ FMT_TYPE_CONSTANT(Char, char_type);
 FMT_TYPE_CONSTANT(float, float_type);
 FMT_TYPE_CONSTANT(double, double_type);
 FMT_TYPE_CONSTANT(long double, long_double_type);
-FMT_TYPE_CONSTANT(const Char*, cstring_type);
+FMT_TYPE_CONSTANT(const Char *, cstring_type);
 FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
-FMT_TYPE_CONSTANT(const void*, pointer_type);
+FMT_TYPE_CONSTANT(const void *, pointer_type);
 
-constexpr bool is_integral_type(type t) {
-  return t > type::none_type && t <= type::last_integer_type;
+constexpr bool is_integral_type(type t)
+{
+    return t > type::none_type && t <= type::last_integer_type;
 }
 
-constexpr bool is_arithmetic_type(type t) {
-  return t > type::none_type && t <= type::last_numeric_type;
+constexpr bool is_arithmetic_type(type t)
+{
+    return t > type::none_type && t <= type::last_numeric_type;
 }
 
-FMT_NORETURN FMT_API void throw_format_error(const char* message);
+FMT_NORETURN FMT_API void throw_format_error(const char *message);
 
-struct error_handler {
-  constexpr error_handler() = default;
-  constexpr error_handler(const error_handler&) = default;
+struct error_handler
+{
+    constexpr error_handler()                      = default;
+    constexpr error_handler(const error_handler &) = default;
 
-  // This function is intentionally not constexpr to give a compile-time error.
-  FMT_NORETURN void on_error(const char* message) {
-    throw_format_error(message);
-  }
+    // This function is intentionally not constexpr to give a compile-time error.
+    FMT_NORETURN void on_error(const char *message) { throw_format_error(message); }
 };
 FMT_END_DETAIL_NAMESPACE
 
 /** String's character type. */
-template <typename S> using char_t = typename detail::char_t_impl<S>::type;
+template <typename S>
+using char_t = typename detail::char_t_impl<S>::type;
 
 /**
   \rst
@@ -651,74 +695,76 @@ template <typename S> using char_t = typename detail::char_t_impl<S>::type;
   \endrst
  */
 template <typename Char, typename ErrorHandler = detail::error_handler>
-class basic_format_parse_context : private ErrorHandler {
- private:
-  basic_string_view<Char> format_str_;
-  int next_arg_id_;
-
-  FMT_CONSTEXPR void do_check_arg_id(int id);
-
- public:
-  using char_type = Char;
-  using iterator = typename basic_string_view<Char>::iterator;
-
-  explicit constexpr basic_format_parse_context(
-      basic_string_view<Char> format_str, ErrorHandler eh = {},
-      int next_arg_id = 0)
-      : ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {}
-
-  /**
-    Returns an iterator to the beginning of the format string range being
-    parsed.
-   */
-  constexpr auto begin() const noexcept -> iterator {
-    return format_str_.begin();
-  }
-
-  /**
-    Returns an iterator past the end of the format string range being parsed.
-   */
-  constexpr auto end() const noexcept -> iterator { return format_str_.end(); }
-
-  /** Advances the begin iterator to ``it``. */
-  FMT_CONSTEXPR void advance_to(iterator it) {
-    format_str_.remove_prefix(detail::to_unsigned(it - begin()));
-  }
-
-  /**
-    Reports an error if using the manual argument indexing; otherwise returns
-    the next argument index and switches to the automatic indexing.
-   */
-  FMT_CONSTEXPR auto next_arg_id() -> int {
-    if (next_arg_id_ < 0) {
-      on_error("cannot switch from manual to automatic argument indexing");
-      return 0;
-    }
-    int id = next_arg_id_++;
-    do_check_arg_id(id);
-    return id;
-  }
-
-  /**
-    Reports an error if using the automatic argument indexing; otherwise
-    switches to the manual indexing.
-   */
-  FMT_CONSTEXPR void check_arg_id(int id) {
-    if (next_arg_id_ > 0) {
-      on_error("cannot switch from automatic to manual argument indexing");
-      return;
-    }
-    next_arg_id_ = -1;
-    do_check_arg_id(id);
-  }
-  FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
-  FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
-
-  FMT_CONSTEXPR void on_error(const char* message) {
-    ErrorHandler::on_error(message);
-  }
-
-  constexpr auto error_handler() const -> ErrorHandler { return *this; }
+class basic_format_parse_context : private ErrorHandler
+{
+private:
+    basic_string_view<Char> format_str_;
+    int                     next_arg_id_;
+
+    FMT_CONSTEXPR void do_check_arg_id(int id);
+
+public:
+    using char_type = Char;
+    using iterator  = typename basic_string_view<Char>::iterator;
+
+    explicit constexpr basic_format_parse_context(
+        basic_string_view<Char> format_str,
+        ErrorHandler            eh          = {},
+        int                     next_arg_id = 0) :
+        ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id)
+    {
+    }
+
+    /**
+      Returns an iterator to the beginning of the format string range being
+      parsed.
+     */
+    constexpr auto begin() const noexcept -> iterator { return format_str_.begin(); }
+
+    /**
+      Returns an iterator past the end of the format string range being parsed.
+     */
+    constexpr auto end() const noexcept -> iterator { return format_str_.end(); }
+
+    /** Advances the begin iterator to ``it``. */
+    FMT_CONSTEXPR void advance_to(iterator it) { format_str_.remove_prefix(detail::to_unsigned(it - begin())); }
+
+    /**
+      Reports an error if using the manual argument indexing; otherwise returns
+      the next argument index and switches to the automatic indexing.
+     */
+    FMT_CONSTEXPR auto next_arg_id() -> int
+    {
+        if(next_arg_id_ < 0)
+        {
+            on_error("cannot switch from manual to automatic argument indexing");
+            return 0;
+        }
+        int id = next_arg_id_++;
+        do_check_arg_id(id);
+        return id;
+    }
+
+    /**
+      Reports an error if using the automatic argument indexing; otherwise
+      switches to the manual indexing.
+     */
+    FMT_CONSTEXPR void check_arg_id(int id)
+    {
+        if(next_arg_id_ > 0)
+        {
+            on_error("cannot switch from automatic to manual argument indexing");
+            return;
+        }
+        next_arg_id_ = -1;
+        do_check_arg_id(id);
+    }
+    FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
+    FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
+
+    FMT_CONSTEXPR void on_error(const char *message) { ErrorHandler::on_error(message); }
+
+    constexpr auto error_handler() const -> ErrorHandler { return *this; }
 };
 
 using format_parse_context = basic_format_parse_context<char>;
@@ -726,131 +772,158 @@ using format_parse_context = basic_format_parse_context<char>;
 FMT_BEGIN_DETAIL_NAMESPACE
 // A parse context with extra data used only in compile-time checks.
 template <typename Char, typename ErrorHandler = detail::error_handler>
-class compile_parse_context
-    : public basic_format_parse_context<Char, ErrorHandler> {
- private:
-  int num_args_;
-  const type* types_;
-  using base = basic_format_parse_context<Char, ErrorHandler>;
-
- public:
-  explicit FMT_CONSTEXPR compile_parse_context(
-      basic_string_view<Char> format_str, int num_args, const type* types,
-      ErrorHandler eh = {}, int next_arg_id = 0)
-      : base(format_str, eh, next_arg_id), num_args_(num_args), types_(types) {}
-
-  constexpr auto num_args() const -> int { return num_args_; }
-  constexpr auto arg_type(int id) const -> type { return types_[id]; }
-
-  FMT_CONSTEXPR auto next_arg_id() -> int {
-    int id = base::next_arg_id();
-    if (id >= num_args_) this->on_error("argument not found");
-    return id;
-  }
-
-  FMT_CONSTEXPR void check_arg_id(int id) {
-    base::check_arg_id(id);
-    if (id >= num_args_) this->on_error("argument not found");
-  }
-  using base::check_arg_id;
-
-  FMT_CONSTEXPR void check_dynamic_spec(int arg_id) {
-    if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id]))
-      this->on_error("width/precision is not integer");
-  }
+class compile_parse_context : public basic_format_parse_context<Char, ErrorHandler>
+{
+private:
+    int         num_args_;
+    const type *types_;
+    using base = basic_format_parse_context<Char, ErrorHandler>;
+
+public:
+    explicit FMT_CONSTEXPR compile_parse_context(
+        basic_string_view<Char> format_str,
+        int                     num_args,
+        const type             *types,
+        ErrorHandler            eh          = {},
+        int                     next_arg_id = 0) :
+        base(format_str, eh, next_arg_id), num_args_(num_args), types_(types)
+    {
+    }
+
+    constexpr auto num_args() const -> int { return num_args_; }
+    constexpr auto arg_type(int id) const -> type { return types_[id]; }
+
+    FMT_CONSTEXPR auto next_arg_id() -> int
+    {
+        int id = base::next_arg_id();
+        if(id >= num_args_)
+            this->on_error("argument not found");
+        return id;
+    }
+
+    FMT_CONSTEXPR void check_arg_id(int id)
+    {
+        base::check_arg_id(id);
+        if(id >= num_args_)
+            this->on_error("argument not found");
+    }
+    using base::check_arg_id;
+
+    FMT_CONSTEXPR void check_dynamic_spec(int arg_id)
+    {
+        if(arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id]))
+            this->on_error("width/precision is not integer");
+    }
 };
 FMT_END_DETAIL_NAMESPACE
 
 template <typename Char, typename ErrorHandler>
-FMT_CONSTEXPR void
-basic_format_parse_context<Char, ErrorHandler>::do_check_arg_id(int id) {
-  // Argument id is only checked at compile-time during parsing because
-  // formatting has its own validation.
-  if (detail::is_constant_evaluated() && FMT_GCC_VERSION >= 1200) {
-    using context = detail::compile_parse_context<Char, ErrorHandler>;
-    if (id >= static_cast<context*>(this)->num_args())
-      on_error("argument not found");
-  }
+FMT_CONSTEXPR void basic_format_parse_context<Char, ErrorHandler>::do_check_arg_id(int id)
+{
+    // Argument id is only checked at compile-time during parsing because
+    // formatting has its own validation.
+    if(detail::is_constant_evaluated() && FMT_GCC_VERSION >= 1200)
+    {
+        using context = detail::compile_parse_context<Char, ErrorHandler>;
+        if(id >= static_cast<context *>(this)->num_args())
+            on_error("argument not found");
+    }
 }
 
 template <typename Char, typename ErrorHandler>
-FMT_CONSTEXPR void
-basic_format_parse_context<Char, ErrorHandler>::check_dynamic_spec(int arg_id) {
-  if (detail::is_constant_evaluated()) {
-    using context = detail::compile_parse_context<Char, ErrorHandler>;
-    static_cast<context*>(this)->check_dynamic_spec(arg_id);
-  }
+FMT_CONSTEXPR void basic_format_parse_context<Char, ErrorHandler>::check_dynamic_spec(int arg_id)
+{
+    if(detail::is_constant_evaluated())
+    {
+        using context = detail::compile_parse_context<Char, ErrorHandler>;
+        static_cast<context *>(this)->check_dynamic_spec(arg_id);
+    }
 }
 
-template <typename Context> class basic_format_arg;
-template <typename Context> class basic_format_args;
-template <typename Context> class dynamic_format_arg_store;
+template <typename Context>
+class basic_format_arg;
+template <typename Context>
+class basic_format_args;
+template <typename Context>
+class dynamic_format_arg_store;
 
 // A formatter for objects of type T.
 template <typename T, typename Char = char, typename Enable = void>
-struct formatter {
-  // A deleted default constructor indicates a disabled formatter.
-  formatter() = delete;
+struct formatter
+{
+    // A deleted default constructor indicates a disabled formatter.
+    formatter() = delete;
 };
 
 // Specifies if T has an enabled formatter specialization. A type can be
 // formattable even if it doesn't have a formatter e.g. via a conversion.
 template <typename T, typename Context>
-using has_formatter =
-    std::is_constructible<typename Context::template formatter_type<T>>;
+using has_formatter = std::is_constructible<typename Context::template formatter_type<T>>;
 
 // Checks whether T is a container with contiguous storage.
-template <typename T> struct is_contiguous : std::false_type {};
+template <typename T>
+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
+{
+};
 
 class appender;
 
 FMT_BEGIN_DETAIL_NAMESPACE
 
 template <typename Context, typename T>
-constexpr auto has_const_formatter_impl(T*)
-    -> decltype(typename Context::template formatter_type<T>().format(
-                    std::declval<const T&>(), std::declval<Context&>()),
-                true) {
-  return true;
+constexpr auto has_const_formatter_impl(T *)
+    -> decltype(typename Context::template formatter_type<T>().format(std::declval<const T &>(), std::declval<Context &>()), true)
+{
+    return true;
 }
 template <typename Context>
-constexpr auto has_const_formatter_impl(...) -> bool {
-  return false;
+constexpr auto has_const_formatter_impl(...) -> bool
+{
+    return false;
 }
 template <typename T, typename Context>
-constexpr auto has_const_formatter() -> bool {
-  return has_const_formatter_impl<Context>(static_cast<T*>(nullptr));
+constexpr auto has_const_formatter() -> bool
+{
+    return has_const_formatter_impl<Context>(static_cast<T *>(nullptr));
 }
 
 // Extracts a reference to the container from back_insert_iterator.
 template <typename Container>
-inline auto get_container(std::back_insert_iterator<Container> it)
-    -> Container& {
-  using base = std::back_insert_iterator<Container>;
-  struct accessor : base {
-    accessor(base b) : base(b) {}
-    using base::container;
-  };
-  return *accessor(it).container;
+inline auto get_container(std::back_insert_iterator<Container> it) -> Container &
+{
+    using base = std::back_insert_iterator<Container>;
+    struct accessor : base
+    {
+        accessor(base b) : base(b) {}
+        using base::container;
+    };
+    return *accessor(it).container;
 }
 
 template <typename Char, typename InputIt, typename OutputIt>
-FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out)
-    -> OutputIt {
-  while (begin != end) *out++ = static_cast<Char>(*begin++);
-  return out;
+FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) -> OutputIt
+{
+    while(begin != end)
+        *out++ = static_cast<Char>(*begin++);
+    return out;
 }
 
-template <typename Char, typename T, typename U,
-          FMT_ENABLE_IF(
-              std::is_same<remove_const_t<T>, U>::value&& is_char<U>::value)>
-FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* {
-  if (is_constant_evaluated()) return copy_str<Char, T*, U*>(begin, end, out);
-  auto size = to_unsigned(end - begin);
-  memcpy(out, begin, size * sizeof(U));
-  return out + size;
+template <
+    typename Char,
+    typename T,
+    typename U,
+    FMT_ENABLE_IF(std::is_same<remove_const_t<T>, U>::value &&is_char<U>::value)>
+FMT_CONSTEXPR auto copy_str(T *begin, T *end, U *out) -> U *
+{
+    if(is_constant_evaluated())
+        return copy_str<Char, T *, U *>(begin, end, out);
+    auto size = to_unsigned(end - begin);
+    memcpy(out, begin, size * sizeof(U));
+    return out + size;
 }
 
 /**
@@ -859,269 +932,311 @@ FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* {
   class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`.
   \endrst
  */
-template <typename T> class buffer {
- private:
-  T* ptr_;
-  size_t size_;
-  size_t capacity_;
-
- protected:
-  // Don't initialize ptr_ since it is not accessed to save a few cycles.
-  FMT_MSC_WARNING(suppress : 26495)
-  buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {}
-
-  FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept
-      : ptr_(p), size_(sz), capacity_(cap) {}
+template <typename T>
+class buffer
+{
+private:
+    T     *ptr_;
+    size_t size_;
+    size_t capacity_;
+
+protected:
+    // Don't initialize ptr_ since it is not accessed to save a few cycles.
+    FMT_MSC_WARNING(suppress : 26495)
+    buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {}
+
+    FMT_CONSTEXPR20 buffer(T *p = nullptr, size_t sz = 0, size_t cap = 0) noexcept : ptr_(p), size_(sz), capacity_(cap)
+    {
+    }
 
-  FMT_CONSTEXPR20 ~buffer() = default;
-  buffer(buffer&&) = default;
+    FMT_CONSTEXPR20 ~buffer() = default;
+    buffer(buffer &&)         = default;
 
-  /** Sets the buffer data and capacity. */
-  FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept {
-    ptr_ = buf_data;
-    capacity_ = buf_capacity;
-  }
+    /** Sets the buffer data and capacity. */
+    FMT_CONSTEXPR void set(T *buf_data, size_t buf_capacity) noexcept
+    {
+        ptr_      = buf_data;
+        capacity_ = buf_capacity;
+    }
 
-  /** Increases the buffer capacity to hold at least *capacity* elements. */
-  virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0;
+    /** Increases the buffer capacity to hold at least *capacity* elements. */
+    virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0;
 
- public:
-  using value_type = T;
-  using const_reference = const T&;
+public:
+    using value_type      = T;
+    using const_reference = const T &;
 
-  buffer(const buffer&) = delete;
-  void operator=(const buffer&) = delete;
+    buffer(const buffer &)         = delete;
+    void operator=(const buffer &) = delete;
 
-  auto begin() noexcept -> T* { return ptr_; }
-  auto end() noexcept -> T* { return ptr_ + size_; }
+    auto begin() noexcept -> T * { return ptr_; }
+    auto end() noexcept -> T * { return ptr_ + size_; }
 
-  auto begin() const noexcept -> const T* { return ptr_; }
-  auto end() const noexcept -> const T* { return ptr_ + size_; }
+    auto begin() const noexcept -> const T * { return ptr_; }
+    auto end() const noexcept -> const T * { return ptr_ + size_; }
 
-  /** Returns the size of this buffer. */
-  constexpr auto size() const noexcept -> size_t { return size_; }
+    /** Returns the size of this buffer. */
+    constexpr auto size() const noexcept -> size_t { return size_; }
 
-  /** Returns the capacity of this buffer. */
-  constexpr auto capacity() const noexcept -> size_t { return capacity_; }
+    /** Returns the capacity of this buffer. */
+    constexpr auto capacity() const noexcept -> size_t { return capacity_; }
 
-  /** Returns a pointer to the buffer data. */
-  FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; }
+    /** Returns a pointer to the buffer data. */
+    FMT_CONSTEXPR auto data() noexcept -> T * { return ptr_; }
 
-  /** Returns a pointer to the buffer data. */
-  FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; }
+    /** Returns a pointer to the buffer data. */
+    FMT_CONSTEXPR auto data() const noexcept -> const T * { return ptr_; }
 
-  /** Clears this buffer. */
-  void clear() { size_ = 0; }
+    /** Clears this buffer. */
+    void clear() { size_ = 0; }
 
-  // Tries resizing the buffer to contain *count* elements. If T is a POD type
-  // the new elements may not be initialized.
-  FMT_CONSTEXPR20 void try_resize(size_t count) {
-    try_reserve(count);
-    size_ = count <= capacity_ ? count : capacity_;
-  }
+    // Tries resizing the buffer to contain *count* elements. If T is a POD type
+    // the new elements may not be initialized.
+    FMT_CONSTEXPR20 void try_resize(size_t count)
+    {
+        try_reserve(count);
+        size_ = count <= capacity_ ? count : capacity_;
+    }
 
-  // Tries increasing the buffer capacity to *new_capacity*. It can increase the
-  // capacity by a smaller amount than requested but guarantees there is space
-  // for at least one additional element either by increasing the capacity or by
-  // flushing the buffer if it is full.
-  FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) {
-    if (new_capacity > capacity_) grow(new_capacity);
-  }
+    // Tries increasing the buffer capacity to *new_capacity*. It can increase the
+    // capacity by a smaller amount than requested but guarantees there is space
+    // for at least one additional element either by increasing the capacity or by
+    // flushing the buffer if it is full.
+    FMT_CONSTEXPR20 void try_reserve(size_t new_capacity)
+    {
+        if(new_capacity > capacity_)
+            grow(new_capacity);
+    }
 
-  FMT_CONSTEXPR20 void push_back(const T& value) {
-    try_reserve(size_ + 1);
-    ptr_[size_++] = value;
-  }
+    FMT_CONSTEXPR20 void push_back(const T &value)
+    {
+        try_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);
+    /** Appends data to the end of the buffer. */
+    template <typename U>
+    void append(const U *begin, const U *end);
 
-  template <typename Idx> FMT_CONSTEXPR auto operator[](Idx index) -> T& {
-    return ptr_[index];
-  }
-  template <typename Idx>
-  FMT_CONSTEXPR auto operator[](Idx index) const -> const T& {
-    return ptr_[index];
-  }
+    template <typename Idx>
+    FMT_CONSTEXPR auto operator[](Idx index) -> T &
+    {
+        return ptr_[index];
+    }
+    template <typename Idx>
+    FMT_CONSTEXPR auto operator[](Idx index) const -> const T &
+    {
+        return ptr_[index];
+    }
 };
 
-struct buffer_traits {
-  explicit buffer_traits(size_t) {}
-  auto count() const -> size_t { return 0; }
-  auto limit(size_t size) -> size_t { return size; }
+struct buffer_traits
+{
+    explicit buffer_traits(size_t) {}
+    auto count() const -> size_t { return 0; }
+    auto limit(size_t size) -> size_t { return size; }
 };
 
-class fixed_buffer_traits {
- private:
-  size_t count_ = 0;
-  size_t limit_;
-
- public:
-  explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
-  auto count() const -> size_t { return count_; }
-  auto limit(size_t size) -> size_t {
-    size_t n = limit_ > count_ ? limit_ - count_ : 0;
-    count_ += size;
-    return size < n ? size : n;
-  }
+class fixed_buffer_traits
+{
+private:
+    size_t count_ = 0;
+    size_t limit_;
+
+public:
+    explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
+    auto count() const -> size_t { return count_; }
+    auto limit(size_t size) -> size_t
+    {
+        size_t n = limit_ > count_ ? limit_ - count_ : 0;
+        count_ += size;
+        return size < n ? size : n;
+    }
 };
 
 // A buffer that writes to an output iterator when flushed.
 template <typename OutputIt, typename T, typename Traits = buffer_traits>
-class iterator_buffer final : public Traits, public buffer<T> {
- private:
-  OutputIt out_;
-  enum { buffer_size = 256 };
-  T data_[buffer_size];
-
- protected:
-  FMT_CONSTEXPR20 void grow(size_t) override {
-    if (this->size() == buffer_size) flush();
-  }
-
-  void flush() {
-    auto size = this->size();
-    this->clear();
-    out_ = copy_str<T>(data_, data_ + this->limit(size), out_);
-  }
-
- public:
-  explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
-      : Traits(n), buffer<T>(data_, 0, buffer_size), out_(out) {}
-  iterator_buffer(iterator_buffer&& other)
-      : Traits(other), buffer<T>(data_, 0, buffer_size), out_(other.out_) {}
-  ~iterator_buffer() { flush(); }
-
-  auto out() -> OutputIt {
-    flush();
-    return out_;
-  }
-  auto count() const -> size_t { return Traits::count() + this->size(); }
+class iterator_buffer final : public Traits, public buffer<T>
+{
+private:
+    OutputIt out_;
+    enum
+    {
+        buffer_size = 256
+    };
+    T data_[buffer_size];
+
+protected:
+    FMT_CONSTEXPR20 void grow(size_t) override
+    {
+        if(this->size() == buffer_size)
+            flush();
+    }
+
+    void flush()
+    {
+        auto size = this->size();
+        this->clear();
+        out_ = copy_str<T>(data_, data_ + this->limit(size), out_);
+    }
+
+public:
+    explicit iterator_buffer(OutputIt out, size_t n = buffer_size) :
+        Traits(n), buffer<T>(data_, 0, buffer_size), out_(out)
+    {
+    }
+    iterator_buffer(iterator_buffer &&other) : Traits(other), buffer<T>(data_, 0, buffer_size), out_(other.out_) {}
+    ~iterator_buffer() { flush(); }
+
+    auto out() -> OutputIt
+    {
+        flush();
+        return out_;
+    }
+    auto count() const -> size_t { return Traits::count() + this->size(); }
 };
 
 template <typename T>
-class iterator_buffer<T*, T, fixed_buffer_traits> final
-    : public fixed_buffer_traits,
-      public buffer<T> {
- private:
-  T* out_;
-  enum { buffer_size = 256 };
-  T data_[buffer_size];
-
- protected:
-  FMT_CONSTEXPR20 void grow(size_t) override {
-    if (this->size() == this->capacity()) flush();
-  }
-
-  void flush() {
-    size_t n = this->limit(this->size());
-    if (this->data() == out_) {
-      out_ += n;
-      this->set(data_, buffer_size);
-    }
-    this->clear();
-  }
-
- public:
-  explicit iterator_buffer(T* out, size_t n = buffer_size)
-      : fixed_buffer_traits(n), buffer<T>(out, 0, n), out_(out) {}
-  iterator_buffer(iterator_buffer&& other)
-      : fixed_buffer_traits(other),
-        buffer<T>(std::move(other)),
-        out_(other.out_) {
-    if (this->data() != out_) {
-      this->set(data_, buffer_size);
-      this->clear();
-    }
-  }
-  ~iterator_buffer() { flush(); }
-
-  auto out() -> T* {
-    flush();
-    return out_;
-  }
-  auto count() const -> size_t {
-    return fixed_buffer_traits::count() + this->size();
-  }
-};
-
-template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
- protected:
-  FMT_CONSTEXPR20 void grow(size_t) override {}
-
- public:
-  explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
-
-  auto out() -> T* { return &*this->end(); }
+class iterator_buffer<T *, T, fixed_buffer_traits> final : public fixed_buffer_traits, public buffer<T>
+{
+private:
+    T *out_;
+    enum
+    {
+        buffer_size = 256
+    };
+    T data_[buffer_size];
+
+protected:
+    FMT_CONSTEXPR20 void grow(size_t) override
+    {
+        if(this->size() == this->capacity())
+            flush();
+    }
+
+    void flush()
+    {
+        size_t n = this->limit(this->size());
+        if(this->data() == out_)
+        {
+            out_ += n;
+            this->set(data_, buffer_size);
+        }
+        this->clear();
+    }
+
+public:
+    explicit iterator_buffer(T *out, size_t n = buffer_size) : fixed_buffer_traits(n), buffer<T>(out, 0, n), out_(out)
+    {
+    }
+    iterator_buffer(iterator_buffer &&other) : fixed_buffer_traits(other), buffer<T>(std::move(other)), out_(other.out_)
+    {
+        if(this->data() != out_)
+        {
+            this->set(data_, buffer_size);
+            this->clear();
+        }
+    }
+    ~iterator_buffer() { flush(); }
+
+    auto out() -> T *
+    {
+        flush();
+        return out_;
+    }
+    auto count() const -> size_t { return fixed_buffer_traits::count() + this->size(); }
+};
+
+template <typename T>
+class iterator_buffer<T *, T> final : public buffer<T>
+{
+protected:
+    FMT_CONSTEXPR20 void grow(size_t) override {}
+
+public:
+    explicit iterator_buffer(T *out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
+
+    auto out() -> T * { return &*this->end(); }
 };
 
 // A buffer that writes to a container with the contiguous storage.
 template <typename Container>
-class iterator_buffer<std::back_insert_iterator<Container>,
-                      enable_if_t<is_contiguous<Container>::value,
-                                  typename Container::value_type>>
-    final : public buffer<typename Container::value_type> {
- private:
-  Container& container_;
-
- protected:
-  FMT_CONSTEXPR20 void grow(size_t capacity) override {
-    container_.resize(capacity);
-    this->set(&container_[0], capacity);
-  }
-
- public:
-  explicit iterator_buffer(Container& c)
-      : buffer<typename Container::value_type>(c.size()), container_(c) {}
-  explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
-      : iterator_buffer(get_container(out)) {}
-
-  auto out() -> std::back_insert_iterator<Container> {
-    return std::back_inserter(container_);
-  }
+class iterator_buffer<
+    std::back_insert_iterator<Container>,
+    enable_if_t<is_contiguous<Container>::value, typename Container::value_type>>
+    final : public buffer<typename Container::value_type>
+{
+private:
+    Container &container_;
+
+protected:
+    FMT_CONSTEXPR20 void grow(size_t capacity) override
+    {
+        container_.resize(capacity);
+        this->set(&container_[0], capacity);
+    }
+
+public:
+    explicit iterator_buffer(Container &c) : buffer<typename Container::value_type>(c.size()), container_(c) {}
+    explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0) : iterator_buffer(get_container(out))
+    {
+    }
+
+    auto out() -> std::back_insert_iterator<Container> { return std::back_inserter(container_); }
 };
 
 // A buffer that counts the number of code units written discarding the output.
-template <typename T = char> class counting_buffer final : public buffer<T> {
- private:
-  enum { buffer_size = 256 };
-  T data_[buffer_size];
-  size_t count_ = 0;
-
- protected:
-  FMT_CONSTEXPR20 void grow(size_t) override {
-    if (this->size() != buffer_size) return;
-    count_ += this->size();
-    this->clear();
-  }
+template <typename T = char>
+class counting_buffer final : public buffer<T>
+{
+private:
+    enum
+    {
+        buffer_size = 256
+    };
+    T      data_[buffer_size];
+    size_t count_ = 0;
+
+protected:
+    FMT_CONSTEXPR20 void grow(size_t) override
+    {
+        if(this->size() != buffer_size)
+            return;
+        count_ += this->size();
+        this->clear();
+    }
 
- public:
-  counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
+public:
+    counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
 
-  auto count() -> size_t { return count_ + this->size(); }
+    auto count() -> size_t { return count_ + this->size(); }
 };
 
 template <typename T>
-using buffer_appender = conditional_t<std::is_same<T, char>::value, appender,
-                                      std::back_insert_iterator<buffer<T>>>;
+using buffer_appender = conditional_t<std::is_same<T, char>::value, appender, std::back_insert_iterator<buffer<T>>>;
 
 // Maps an output iterator to a buffer.
 template <typename T, typename OutputIt>
-auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T> {
-  return iterator_buffer<OutputIt, T>(out);
+auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T>
+{
+    return iterator_buffer<OutputIt, T>(out);
 }
 
 template <typename Buffer>
-auto get_iterator(Buffer& buf) -> decltype(buf.out()) {
-  return buf.out();
+auto get_iterator(Buffer &buf) -> decltype(buf.out())
+{
+    return buf.out();
 }
-template <typename T> auto get_iterator(buffer<T>& buf) -> buffer_appender<T> {
-  return buffer_appender<T>(buf);
+template <typename T>
+auto get_iterator(buffer<T> &buf) -> buffer_appender<T>
+{
+    return buffer_appender<T>(buf);
 }
 
 template <typename T, typename Char = char, typename Enable = void>
-struct fallback_formatter {
-  fallback_formatter() = delete;
+struct fallback_formatter
+{
+    fallback_formatter() = delete;
 };
 
 // Specifies if T has an enabled fallback_formatter specialization.
@@ -1133,483 +1248,573 @@ using has_fallback_formatter =
     std::false_type;
 #endif
 
-struct view {};
+struct view
+{
+};
 
-template <typename Char, typename T> struct named_arg : view {
-  const Char* name;
-  const T& value;
-  named_arg(const Char* n, const T& v) : name(n), value(v) {}
+template <typename Char, typename T>
+struct named_arg : view
+{
+    const Char *name;
+    const T    &value;
+    named_arg(const Char *n, const T &v) : name(n), value(v) {}
 };
 
-template <typename Char> struct named_arg_info {
-  const Char* name;
-  int id;
+template <typename Char>
+struct named_arg_info
+{
+    const Char *name;
+    int         id;
 };
 
 template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
-struct arg_data {
-  // args_[0].named_args points to named_args_ to avoid bloating format_args.
-  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
-  T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
-  named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
-
-  template <typename... U>
-  arg_data(const U&... init) : args_{T(named_args_, NUM_NAMED_ARGS), init...} {}
-  arg_data(const arg_data& other) = delete;
-  auto args() const -> const T* { return args_ + 1; }
-  auto named_args() -> named_arg_info<Char>* { return named_args_; }
+struct arg_data
+{
+    // args_[0].named_args points to named_args_ to avoid bloating format_args.
+    // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
+    T                    args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
+    named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
+
+    template <typename... U>
+    arg_data(const U &...init) : args_{T(named_args_, NUM_NAMED_ARGS), init...}
+    {
+    }
+    arg_data(const arg_data &other) = delete;
+    auto args() const -> const T * { return args_ + 1; }
+    auto named_args() -> named_arg_info<Char> * { return named_args_; }
 };
 
 template <typename T, typename Char, size_t NUM_ARGS>
-struct arg_data<T, Char, NUM_ARGS, 0> {
-  // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
-  T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
+struct arg_data<T, Char, NUM_ARGS, 0>
+{
+    // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
+    T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
 
-  template <typename... U>
-  FMT_CONSTEXPR FMT_INLINE arg_data(const U&... init) : args_{init...} {}
-  FMT_CONSTEXPR FMT_INLINE auto args() const -> const T* { return args_; }
-  FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t {
-    return nullptr;
-  }
+    template <typename... U>
+    FMT_CONSTEXPR FMT_INLINE arg_data(const U &...init) : args_{init...}
+    {
+    }
+    FMT_CONSTEXPR FMT_INLINE auto args() const -> const T * { return args_; }
+    FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t { return nullptr; }
 };
 
 template <typename Char>
-inline void init_named_args(named_arg_info<Char>*, int, int) {}
+inline void init_named_args(named_arg_info<Char> *, int, int)
+{
+}
 
-template <typename T> struct is_named_arg : std::false_type {};
-template <typename T> struct is_statically_named_arg : std::false_type {};
+template <typename T>
+struct is_named_arg : std::false_type
+{
+};
+template <typename T>
+struct is_statically_named_arg : std::false_type
+{
+};
 
 template <typename T, typename Char>
-struct is_named_arg<named_arg<Char, T>> : std::true_type {};
+struct is_named_arg<named_arg<Char, T>> : std::true_type
+{
+};
 
-template <typename Char, typename T, typename... Tail,
-          FMT_ENABLE_IF(!is_named_arg<T>::value)>
-void init_named_args(named_arg_info<Char>* named_args, int arg_count,
-                     int named_arg_count, const T&, const Tail&... args) {
-  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
+template <typename Char, typename T, typename... Tail, FMT_ENABLE_IF(!is_named_arg<T>::value)>
+void init_named_args(
+    named_arg_info<Char> *named_args,
+    int                   arg_count,
+    int                   named_arg_count,
+    const T &,
+    const Tail &...args)
+{
+    init_named_args(named_args, arg_count + 1, named_arg_count, args...);
 }
 
-template <typename Char, typename T, typename... Tail,
-          FMT_ENABLE_IF(is_named_arg<T>::value)>
-void init_named_args(named_arg_info<Char>* named_args, int arg_count,
-                     int named_arg_count, const T& arg, const Tail&... args) {
-  named_args[named_arg_count++] = {arg.name, arg_count};
-  init_named_args(named_args, arg_count + 1, named_arg_count, args...);
+template <typename Char, typename T, typename... Tail, FMT_ENABLE_IF(is_named_arg<T>::value)>
+void init_named_args(
+    named_arg_info<Char> *named_args,
+    int                   arg_count,
+    int                   named_arg_count,
+    const T              &arg,
+    const Tail &...args)
+{
+    named_args[named_arg_count++] = {arg.name, arg_count};
+    init_named_args(named_args, arg_count + 1, named_arg_count, args...);
 }
 
 template <typename... Args>
-FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int,
-                                              const Args&...) {}
+FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args &...)
+{
+}
 
-template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; }
-template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t {
-  return (B1 ? 1 : 0) + count<B2, Tail...>();
+template <bool B = false>
+constexpr auto count() -> size_t
+{
+    return B ? 1 : 0;
+}
+template <bool B1, bool B2, bool... Tail>
+constexpr auto count() -> size_t
+{
+    return (B1 ? 1 : 0) + count<B2, Tail...>();
 }
 
-template <typename... Args> constexpr auto count_named_args() -> size_t {
-  return count<is_named_arg<Args>::value...>();
+template <typename... Args>
+constexpr auto count_named_args() -> size_t
+{
+    return count<is_named_arg<Args>::value...>();
 }
 
 template <typename... Args>
-constexpr auto count_statically_named_args() -> size_t {
-  return count<is_statically_named_arg<Args>::value...>();
+constexpr auto count_statically_named_args() -> size_t
+{
+    return count<is_statically_named_arg<Args>::value...>();
 }
 
-struct unformattable {};
-struct unformattable_char : unformattable {};
-struct unformattable_const : unformattable {};
-struct unformattable_pointer : unformattable {};
+struct unformattable
+{
+};
+struct unformattable_char : unformattable
+{
+};
+struct unformattable_const : unformattable
+{
+};
+struct unformattable_pointer : unformattable
+{
+};
 
-template <typename Char> struct string_value {
-  const Char* data;
-  size_t size;
+template <typename Char>
+struct string_value
+{
+    const Char *data;
+    size_t      size;
 };
 
-template <typename Char> struct named_arg_value {
-  const named_arg_info<Char>* data;
-  size_t size;
+template <typename Char>
+struct named_arg_value
+{
+    const named_arg_info<Char> *data;
+    size_t                      size;
 };
 
-template <typename Context> struct custom_value {
-  using parse_context = typename Context::parse_context_type;
-  void* value;
-  void (*format)(void* arg, parse_context& parse_ctx, Context& ctx);
+template <typename Context>
+struct custom_value
+{
+    using parse_context = typename Context::parse_context_type;
+    void *value;
+    void (*format)(void *arg, parse_context &parse_ctx, Context &ctx);
 };
 
 // A formatting argument value.
-template <typename Context> class value {
- public:
-  using char_type = typename Context::char_type;
-
-  union {
-    monostate no_value;
-    int int_value;
-    unsigned uint_value;
-    long long long_long_value;
-    unsigned long long ulong_long_value;
-    int128_opt int128_value;
-    uint128_opt uint128_value;
-    bool bool_value;
-    char_type char_value;
-    float float_value;
-    double double_value;
-    long double long_double_value;
-    const void* pointer;
-    string_value<char_type> string;
-    custom_value<Context> custom;
-    named_arg_value<char_type> named_args;
-  };
-
-  constexpr FMT_INLINE value() : no_value() {}
-  constexpr FMT_INLINE value(int val) : int_value(val) {}
-  constexpr FMT_INLINE value(unsigned val) : uint_value(val) {}
-  constexpr FMT_INLINE value(long long val) : long_long_value(val) {}
-  constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {}
-  FMT_INLINE value(int128_opt val) : int128_value(val) {}
-  FMT_INLINE value(uint128_opt val) : uint128_value(val) {}
-  constexpr FMT_INLINE value(float val) : float_value(val) {}
-  constexpr FMT_INLINE value(double val) : double_value(val) {}
-  FMT_INLINE value(long double val) : long_double_value(val) {}
-  constexpr FMT_INLINE value(bool val) : bool_value(val) {}
-  constexpr FMT_INLINE value(char_type val) : char_value(val) {}
-  FMT_CONSTEXPR FMT_INLINE value(const char_type* val) {
-    string.data = val;
-    if (is_constant_evaluated()) string.size = {};
-  }
-  FMT_CONSTEXPR FMT_INLINE value(basic_string_view<char_type> val) {
-    string.data = val.data();
-    string.size = val.size();
-  }
-  FMT_INLINE value(const void* val) : pointer(val) {}
-  FMT_INLINE value(const named_arg_info<char_type>* args, size_t size)
-      : named_args{args, size} {}
-
-  template <typename T> FMT_CONSTEXPR FMT_INLINE value(T& val) {
-    using value_type = remove_cvref_t<T>;
-    custom.value = const_cast<value_type*>(&val);
-    // Get the formatter type through the context to allow different contexts
-    // have different extension points, e.g. `formatter<T>` for `format` and
-    // `printf_formatter<T>` for `printf`.
-    custom.format = format_custom_arg<
-        value_type,
-        conditional_t<has_formatter<value_type, Context>::value,
-                      typename Context::template formatter_type<value_type>,
-                      fallback_formatter<value_type, char_type>>>;
-  }
-  value(unformattable);
-  value(unformattable_char);
-  value(unformattable_const);
-  value(unformattable_pointer);
-
- private:
-  // Formats an argument of a custom type, such as a user-defined class.
-  template <typename T, typename Formatter>
-  static void format_custom_arg(void* arg,
-                                typename Context::parse_context_type& parse_ctx,
-                                Context& ctx) {
-    auto f = Formatter();
-    parse_ctx.advance_to(f.parse(parse_ctx));
-    using qualified_type =
-        conditional_t<has_const_formatter<T, Context>(), const T, T>;
-    ctx.advance_to(f.format(*static_cast<qualified_type*>(arg), ctx));
-  }
+template <typename Context>
+class value
+{
+public:
+    using char_type = typename Context::char_type;
+
+    union
+    {
+        monostate                  no_value;
+        int                        int_value;
+        unsigned                   uint_value;
+        long long                  long_long_value;
+        unsigned long long         ulong_long_value;
+        int128_opt                 int128_value;
+        uint128_opt                uint128_value;
+        bool                       bool_value;
+        char_type                  char_value;
+        float                      float_value;
+        double                     double_value;
+        long double                long_double_value;
+        const void                *pointer;
+        string_value<char_type>    string;
+        custom_value<Context>      custom;
+        named_arg_value<char_type> named_args;
+    };
+
+    constexpr FMT_INLINE     value() : no_value() {}
+    constexpr FMT_INLINE     value(int val) : int_value(val) {}
+    constexpr FMT_INLINE     value(unsigned val) : uint_value(val) {}
+    constexpr FMT_INLINE     value(long long val) : long_long_value(val) {}
+    constexpr FMT_INLINE     value(unsigned long long val) : ulong_long_value(val) {}
+    FMT_INLINE               value(int128_opt val) : int128_value(val) {}
+    FMT_INLINE               value(uint128_opt val) : uint128_value(val) {}
+    constexpr FMT_INLINE     value(float val) : float_value(val) {}
+    constexpr FMT_INLINE     value(double val) : double_value(val) {}
+    FMT_INLINE               value(long double val) : long_double_value(val) {}
+    constexpr FMT_INLINE     value(bool val) : bool_value(val) {}
+    constexpr FMT_INLINE     value(char_type val) : char_value(val) {}
+    FMT_CONSTEXPR FMT_INLINE value(const char_type *val)
+    {
+        string.data = val;
+        if(is_constant_evaluated())
+            string.size = {};
+    }
+    FMT_CONSTEXPR FMT_INLINE value(basic_string_view<char_type> val)
+    {
+        string.data = val.data();
+        string.size = val.size();
+    }
+    FMT_INLINE value(const void *val) : pointer(val) {}
+    FMT_INLINE value(const named_arg_info<char_type> *args, size_t size) : named_args{args, size} {}
+
+    template <typename T>
+    FMT_CONSTEXPR FMT_INLINE value(T &val)
+    {
+        using value_type = remove_cvref_t<T>;
+        custom.value     = const_cast<value_type *>(&val);
+        // Get the formatter type through the context to allow different contexts
+        // have different extension points, e.g. `formatter<T>` for `format` and
+        // `printf_formatter<T>` for `printf`.
+        custom.format = format_custom_arg<
+            value_type,
+            conditional_t<
+                has_formatter<value_type, Context>::value,
+                typename Context::template formatter_type<value_type>,
+                fallback_formatter<value_type, char_type>>>;
+    }
+    value(unformattable);
+    value(unformattable_char);
+    value(unformattable_const);
+    value(unformattable_pointer);
+
+private:
+    // Formats an argument of a custom type, such as a user-defined class.
+    template <typename T, typename Formatter>
+    static void format_custom_arg(void *arg, typename Context::parse_context_type &parse_ctx, Context &ctx)
+    {
+        auto f = Formatter();
+        parse_ctx.advance_to(f.parse(parse_ctx));
+        using qualified_type = conditional_t<has_const_formatter<T, Context>(), const T, T>;
+        ctx.advance_to(f.format(*static_cast<qualified_type *>(arg), ctx));
+    }
 };
 
 template <typename Context, typename T>
-FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context>;
+FMT_CONSTEXPR auto make_arg(T &&value) -> basic_format_arg<Context>;
 
 // To minimize the number of types we need to deal with, long is translated
 // either to int or to long long depending on its size.
-enum { long_short = sizeof(long) == sizeof(int) };
-using long_type = conditional_t<long_short, int, long long>;
+enum
+{
+    long_short = sizeof(long) == sizeof(int)
+};
+using long_type  = conditional_t<long_short, int, long long>;
 using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
 
 #ifdef __cpp_lib_byte
-inline auto format_as(std::byte b) -> unsigned char {
-  return static_cast<unsigned char>(b);
+inline auto format_as(std::byte b) -> unsigned char
+{
+    return static_cast<unsigned char>(b);
 }
 #endif
 
-template <typename T> struct has_format_as {
-  template <typename U, typename V = decltype(format_as(U())),
-            FMT_ENABLE_IF(std::is_enum<U>::value&& std::is_integral<V>::value)>
-  static auto check(U*) -> std::true_type;
-  static auto check(...) -> std::false_type;
-
-  enum { value = decltype(check(static_cast<T*>(nullptr)))::value };
+template <typename T>
+struct has_format_as
+{
+    template <
+        typename U,
+        typename V = decltype(format_as(U())),
+        FMT_ENABLE_IF(std::is_enum<U>::value &&std::is_integral<V>::value)>
+    static auto check(U *) -> std::true_type;
+    static auto check(...) -> std::false_type;
+
+    enum
+    {
+        value = decltype(check(static_cast<T *>(nullptr)))::value
+    };
 };
 
 // Maps formatting arguments to core types.
 // arg_mapper reports errors by returning unformattable instead of using
 // static_assert because it's used in the is_formattable trait.
-template <typename Context> struct arg_mapper {
-  using char_type = typename Context::char_type;
-
-  FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; }
-  FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned {
-    return val;
-  }
-  FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; }
-  FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned {
-    return val;
-  }
-  FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; }
-  FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; }
-  FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; }
-  FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type {
-    return val;
-  }
-  FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; }
-  FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val)
-      -> unsigned long long {
-    return val;
-  }
-  FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt {
-    return val;
-  }
-  FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt {
-    return val;
-  }
-  FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; }
-
-  template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value ||
-                                      std::is_same<T, char_type>::value)>
-  FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type {
-    return val;
-  }
-  template <typename T, enable_if_t<(std::is_same<T, wchar_t>::value ||
+template <typename Context>
+struct arg_mapper
+{
+    using char_type = typename Context::char_type;
+
+    FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; }
+    FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned { return val; }
+    FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; }
+    FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned { return val; }
+    FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; }
+    FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; }
+    FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; }
+    FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type { return val; }
+    FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; }
+    FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val) -> unsigned long long { return val; }
+    FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt { return val; }
+    FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt { return val; }
+    FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; }
+
+    template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value || std::is_same<T, char_type>::value)>
+    FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type
+    {
+        return val;
+    }
+    template <
+        typename T,
+        enable_if_t<
+            (std::is_same<T, wchar_t>::value ||
 #ifdef __cpp_char8_t
-                                     std::is_same<T, char8_t>::value ||
+             std::is_same<T, char8_t>::value ||
 #endif
-                                     std::is_same<T, char16_t>::value ||
-                                     std::is_same<T, char32_t>::value) &&
-                                        !std::is_same<T, char_type>::value,
-                                    int> = 0>
-  FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char {
-    return {};
-  }
-
-  FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; }
-  FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; }
-  FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double {
-    return val;
-  }
-
-  FMT_CONSTEXPR FMT_INLINE auto map(char_type* val) -> const char_type* {
-    return val;
-  }
-  FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* {
-    return val;
-  }
-  template <typename T,
-            FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
-                          std::is_same<char_type, char_t<T>>::value)>
-  FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-      -> basic_string_view<char_type> {
-    return to_string_view(val);
-  }
-  template <typename T,
-            FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value &&
-                          !std::is_same<char_type, char_t<T>>::value)>
-  FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char {
-    return {};
-  }
-  template <typename T,
-            FMT_ENABLE_IF(
-                std::is_convertible<T, basic_string_view<char_type>>::value &&
-                !is_string<T>::value && !has_formatter<T, Context>::value &&
-                !has_fallback_formatter<T, char_type>::value)>
-  FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-      -> basic_string_view<char_type> {
-    return basic_string_view<char_type>(val);
-  }
-  template <typename T,
-            FMT_ENABLE_IF(
-                std::is_convertible<T, std_string_view<char_type>>::value &&
-                !std::is_convertible<T, basic_string_view<char_type>>::value &&
-                !is_string<T>::value && !has_formatter<T, Context>::value &&
-                !has_fallback_formatter<T, char_type>::value)>
-  FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-      -> basic_string_view<char_type> {
-    return std_string_view<char_type>(val);
-  }
-
-  FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; }
-  FMT_CONSTEXPR FMT_INLINE auto map(const void* val) -> const void* {
-    return val;
-  }
-  FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void* {
-    return val;
-  }
-
-  // We use SFINAE instead of a const T* parameter to avoid conflicting with
-  // the C array overload.
-  template <
-      typename T,
-      FMT_ENABLE_IF(
-          std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
-          std::is_function<typename std::remove_pointer<T>::type>::value ||
-          (std::is_convertible<const T&, const void*>::value &&
-           !std::is_convertible<const T&, const char_type*>::value &&
-           !has_formatter<T, Context>::value))>
-  FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer {
-    return {};
-  }
-
-  template <typename T, std::size_t N,
-            FMT_ENABLE_IF(!std::is_same<T, wchar_t>::value)>
-  FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] {
-    return values;
-  }
-
-  template <typename T,
-            FMT_ENABLE_IF(
-                std::is_enum<T>::value&& std::is_convertible<T, int>::value &&
-                !has_format_as<T>::value && !has_formatter<T, Context>::value &&
-                !has_fallback_formatter<T, char_type>::value)>
-  FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-      -> decltype(std::declval<arg_mapper>().map(
-          static_cast<underlying_t<T>>(val))) {
-    return map(static_cast<underlying_t<T>>(val));
-  }
-
-  template <typename T, FMT_ENABLE_IF(has_format_as<T>::value &&
-                                      !has_formatter<T, Context>::value)>
-  FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
-      -> decltype(std::declval<arg_mapper>().map(format_as(T()))) {
-    return map(format_as(val));
-  }
-
-  template <typename T, typename U = remove_cvref_t<T>>
-  struct formattable
-      : bool_constant<has_const_formatter<U, Context>() ||
-                      !std::is_const<remove_reference_t<T>>::value ||
-                      has_fallback_formatter<U, char_type>::value> {};
-
-#if (FMT_MSC_VERSION != 0 && FMT_MSC_VERSION < 1910) || \
-    FMT_ICC_VERSION != 0 || defined(__NVCC__)
-  // Workaround a bug in MSVC and Intel (Issue 2746).
-  template <typename T> FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
-    return val;
-  }
+             std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value) &&
+                !std::is_same<T, char_type>::value,
+            int> = 0>
+    FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char
+    {
+        return {};
+    }
+
+    FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float
+    {
+        return val;
+    }
+    FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double
+    {
+        return val;
+    }
+    FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double
+    {
+        return val;
+    }
+
+    FMT_CONSTEXPR FMT_INLINE auto map(char_type *val) -> const char_type *
+    {
+        return val;
+    }
+    FMT_CONSTEXPR FMT_INLINE auto map(const char_type *val) -> const char_type *
+    {
+        return val;
+    }
+    template <
+        typename T,
+        FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value && std::is_same<char_type, char_t<T>>::value)>
+    FMT_CONSTEXPR FMT_INLINE auto map(const T &val) -> basic_string_view<char_type>
+    {
+        return to_string_view(val);
+    }
+    template <
+        typename T,
+        FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value && !std::is_same<char_type, char_t<T>>::value)>
+    FMT_CONSTEXPR FMT_INLINE auto map(const T &) -> unformattable_char
+    {
+        return {};
+    }
+    template <
+        typename T,
+        FMT_ENABLE_IF(
+            std::is_convertible<T, basic_string_view<char_type>>::value && !is_string<T>::value &&
+            !has_formatter<T, Context>::value && !has_fallback_formatter<T, char_type>::value)>
+    FMT_CONSTEXPR FMT_INLINE auto map(const T &val) -> basic_string_view<char_type>
+    {
+        return basic_string_view<char_type>(val);
+    }
+    template <
+        typename T,
+        FMT_ENABLE_IF(
+            std::is_convertible<T, std_string_view<char_type>>::value &&
+            !std::is_convertible<T, basic_string_view<char_type>>::value && !is_string<T>::value &&
+            !has_formatter<T, Context>::value && !has_fallback_formatter<T, char_type>::value)>
+    FMT_CONSTEXPR FMT_INLINE auto map(const T &val) -> basic_string_view<char_type>
+    {
+        return std_string_view<char_type>(val);
+    }
+
+    FMT_CONSTEXPR FMT_INLINE auto map(void *val) -> const void *
+    {
+        return val;
+    }
+    FMT_CONSTEXPR FMT_INLINE auto map(const void *val) -> const void *
+    {
+        return val;
+    }
+    FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void *
+    {
+        return val;
+    }
+
+    // We use SFINAE instead of a const T* parameter to avoid conflicting with
+    // the C array overload.
+    template <
+        typename T,
+        FMT_ENABLE_IF(
+            std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
+            std::is_function<typename std::remove_pointer<T>::type>::value ||
+            (std::is_convertible<const T &, const void *>::value &&
+             !std::is_convertible<const T &, const char_type *>::value && !has_formatter<T, Context>::value))>
+    FMT_CONSTEXPR auto map(const T &) -> unformattable_pointer
+    {
+        return {};
+    }
+
+    template <typename T, std::size_t N, FMT_ENABLE_IF(!std::is_same<T, wchar_t>::value)>
+    FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N]
+    {
+        return values;
+    }
+
+    template <
+        typename T,
+        FMT_ENABLE_IF(
+            std::is_enum<T>::value &&std::is_convertible<T, int>::value && !has_format_as<T>::value &&
+            !has_formatter<T, Context>::value && !has_fallback_formatter<T, char_type>::value)>
+    FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const T &val)
+        -> decltype(std::declval<arg_mapper>().map(static_cast<underlying_t<T>>(val)))
+    {
+        return map(static_cast<underlying_t<T>>(val));
+    }
+
+    template <typename T, FMT_ENABLE_IF(has_format_as<T>::value && !has_formatter<T, Context>::value)>
+    FMT_CONSTEXPR FMT_INLINE auto map(const T &val) -> decltype(std::declval<arg_mapper>().map(format_as(T())))
+    {
+        return map(format_as(val));
+    }
+
+    template <typename T, typename U = remove_cvref_t<T>>
+    struct formattable : bool_constant<
+                             has_const_formatter<U, Context>() || !std::is_const<remove_reference_t<T>>::value ||
+                             has_fallback_formatter<U, char_type>::value>
+    {
+    };
+
+#if(FMT_MSC_VERSION != 0 && FMT_MSC_VERSION < 1910) || FMT_ICC_VERSION != 0 || defined(__NVCC__)
+    // Workaround a bug in MSVC and Intel (Issue 2746).
+    template <typename T>
+    FMT_CONSTEXPR FMT_INLINE auto do_map(T &&val) -> T &
+    {
+        return val;
+    }
 #else
-  template <typename T, FMT_ENABLE_IF(formattable<T>::value)>
-  FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& {
-    return val;
-  }
-  template <typename T, FMT_ENABLE_IF(!formattable<T>::value)>
-  FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable_const {
-    return {};
-  }
+    template <typename T, FMT_ENABLE_IF(formattable<T>::value)>
+    FMT_CONSTEXPR FMT_INLINE auto do_map(T &&val) -> T &
+    {
+        return val;
+    }
+    template <typename T, FMT_ENABLE_IF(!formattable<T>::value)>
+    FMT_CONSTEXPR FMT_INLINE auto do_map(T &&) -> unformattable_const
+    {
+        return {};
+    }
 #endif
 
-  template <typename T, typename U = remove_cvref_t<T>,
-            FMT_ENABLE_IF(!is_string<U>::value && !is_char<U>::value &&
-                          !std::is_array<U>::value &&
-                          !std::is_pointer<U>::value &&
-                          !has_format_as<U>::value &&
-                          (has_formatter<U, Context>::value ||
-                           has_fallback_formatter<U, char_type>::value))>
-  FMT_CONSTEXPR FMT_INLINE auto map(T&& val)
-      -> decltype(this->do_map(std::forward<T>(val))) {
-    return do_map(std::forward<T>(val));
-  }
+    template <
+        typename T,
+        typename U = remove_cvref_t<T>,
+        FMT_ENABLE_IF(
+            !is_string<U>::value && !is_char<U>::value && !std::is_array<U>::value && !std::is_pointer<U>::value &&
+            !has_format_as<U>::value &&
+            (has_formatter<U, Context>::value || has_fallback_formatter<U, char_type>::value))>
+    FMT_CONSTEXPR FMT_INLINE auto map(T &&val) -> decltype(this->do_map(std::forward<T>(val)))
+    {
+        return do_map(std::forward<T>(val));
+    }
 
-  template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
-  FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg)
-      -> decltype(std::declval<arg_mapper>().map(named_arg.value)) {
-    return map(named_arg.value);
-  }
+    template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
+    FMT_CONSTEXPR FMT_INLINE auto map(const T &named_arg) -> decltype(std::declval<arg_mapper>().map(named_arg.value))
+    {
+        return map(named_arg.value);
+    }
 
-  auto map(...) -> unformattable { return {}; }
+    auto map(...) -> unformattable
+    {
+        return {};
+    }
 };
 
 // A type constant after applying arg_mapper<Context>.
 template <typename T, typename Context>
 using mapped_type_constant =
-    type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
-                  typename Context::char_type>;
+    type_constant<decltype(arg_mapper<Context>().map(std::declval<const T &>())), typename Context::char_type>;
 
-enum { packed_arg_bits = 4 };
+enum
+{
+    packed_arg_bits = 4
+};
 // Maximum number of arguments with packed types.
-enum { max_packed_args = 62 / packed_arg_bits };
-enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
-enum : unsigned long long { has_named_args_bit = 1ULL << 62 };
+enum
+{
+    max_packed_args = 62 / packed_arg_bits
+};
+enum : unsigned long long
+{
+    is_unpacked_bit = 1ULL << 63
+};
+enum : unsigned long long
+{
+    has_named_args_bit = 1ULL << 62
+};
 
 FMT_END_DETAIL_NAMESPACE
 
 // An output iterator that appends to a buffer.
 // It is used to reduce symbol sizes for the common case.
-class appender : public std::back_insert_iterator<detail::buffer<char>> {
-  using base = std::back_insert_iterator<detail::buffer<char>>;
+class appender : public std::back_insert_iterator<detail::buffer<char>>
+{
+    using base = std::back_insert_iterator<detail::buffer<char>>;
 
-  template <typename T>
-  friend auto get_buffer(appender out) -> detail::buffer<char>& {
-    return detail::get_container(out);
-  }
+    template <typename T>
+    friend auto get_buffer(appender out) -> detail::buffer<char> &
+    {
+        return detail::get_container(out);
+    }
 
- public:
-  using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
-  appender(base it) noexcept : base(it) {}
-  FMT_UNCHECKED_ITERATOR(appender);
+public:
+    using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
+    appender(base it) noexcept : base(it) {}
+    FMT_UNCHECKED_ITERATOR(appender);
 
-  auto operator++() noexcept -> appender& { return *this; }
-  auto operator++(int) noexcept -> appender { return *this; }
+    auto operator++() noexcept -> appender & { return *this; }
+    auto operator++(int) noexcept -> appender { return *this; }
 };
 
 // A formatting argument. It is a trivially copyable/constructible type to
 // allow storage in basic_memory_buffer.
-template <typename Context> class basic_format_arg {
- private:
-  detail::value<Context> value_;
-  detail::type type_;
+template <typename Context>
+class basic_format_arg
+{
+private:
+    detail::value<Context> value_;
+    detail::type           type_;
 
-  template <typename ContextType, typename T>
-  friend FMT_CONSTEXPR auto detail::make_arg(T&& value)
-      -> basic_format_arg<ContextType>;
+    template <typename ContextType, typename T>
+    friend FMT_CONSTEXPR auto detail::make_arg(T &&value) -> basic_format_arg<ContextType>;
 
-  template <typename Visitor, typename Ctx>
-  friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
-                                             const basic_format_arg<Ctx>& arg)
-      -> decltype(vis(0));
+    template <typename Visitor, typename Ctx>
+    friend FMT_CONSTEXPR auto visit_format_arg(Visitor &&vis, const basic_format_arg<Ctx> &arg) -> decltype(vis(0));
 
-  friend class basic_format_args<Context>;
-  friend class dynamic_format_arg_store<Context>;
+    friend class basic_format_args<Context>;
+    friend class dynamic_format_arg_store<Context>;
 
-  using char_type = typename Context::char_type;
+    using char_type = typename Context::char_type;
 
-  template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
-  friend struct detail::arg_data;
+    template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
+    friend struct detail::arg_data;
 
-  basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
-      : value_(args, size) {}
+    basic_format_arg(const detail::named_arg_info<char_type> *args, size_t size) : value_(args, size) {}
 
- public:
-  class handle {
-   public:
-    explicit handle(detail::custom_value<Context> custom) : custom_(custom) {}
+public:
+    class handle
+    {
+    public:
+        explicit handle(detail::custom_value<Context> custom) : custom_(custom) {}
 
-    void format(typename Context::parse_context_type& parse_ctx,
-                Context& ctx) const {
-      custom_.format(custom_.value, parse_ctx, ctx);
-    }
+        void format(typename Context::parse_context_type &parse_ctx, Context &ctx) const
+        {
+            custom_.format(custom_.value, parse_ctx, ctx);
+        }
 
-   private:
-    detail::custom_value<Context> custom_;
-  };
+    private:
+        detail::custom_value<Context> custom_;
+    };
 
-  constexpr basic_format_arg() : type_(detail::type::none_type) {}
+    constexpr basic_format_arg() : type_(detail::type::none_type) {}
 
-  constexpr explicit operator bool() const noexcept {
-    return type_ != detail::type::none_type;
-  }
+    constexpr explicit operator bool() const noexcept { return type_ != detail::type::none_type; }
 
-  auto type() const -> detail::type { return type_; }
+    auto type() const -> detail::type { return type_; }
 
-  auto is_integral() const -> bool { return detail::is_integral_type(type_); }
-  auto is_arithmetic() const -> bool {
-    return detail::is_arithmetic_type(type_);
-  }
+    auto is_integral() const -> bool { return detail::is_integral_type(type_); }
+    auto is_arithmetic() const -> bool { return detail::is_arithmetic_type(type_); }
 };
 
 /**
@@ -1620,238 +1825,261 @@ template <typename Context> class basic_format_arg {
   \endrst
  */
 template <typename Visitor, typename Context>
-FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
-    Visitor&& vis, const basic_format_arg<Context>& arg) -> decltype(vis(0)) {
-  switch (arg.type_) {
-  case detail::type::none_type:
-    break;
-  case detail::type::int_type:
-    return vis(arg.value_.int_value);
-  case detail::type::uint_type:
-    return vis(arg.value_.uint_value);
-  case detail::type::long_long_type:
-    return vis(arg.value_.long_long_value);
-  case detail::type::ulong_long_type:
-    return vis(arg.value_.ulong_long_value);
-  case detail::type::int128_type:
-    return vis(detail::convert_for_visit(arg.value_.int128_value));
-  case detail::type::uint128_type:
-    return vis(detail::convert_for_visit(arg.value_.uint128_value));
-  case detail::type::bool_type:
-    return vis(arg.value_.bool_value);
-  case detail::type::char_type:
-    return vis(arg.value_.char_value);
-  case detail::type::float_type:
-    return vis(arg.value_.float_value);
-  case detail::type::double_type:
-    return vis(arg.value_.double_value);
-  case detail::type::long_double_type:
-    return vis(arg.value_.long_double_value);
-  case detail::type::cstring_type:
-    return vis(arg.value_.string.data);
-  case detail::type::string_type:
-    using sv = basic_string_view<typename Context::char_type>;
-    return vis(sv(arg.value_.string.data, arg.value_.string.size));
-  case detail::type::pointer_type:
-    return vis(arg.value_.pointer);
-  case detail::type::custom_type:
-    return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
-  }
-  return vis(monostate());
+FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(Visitor &&vis, const basic_format_arg<Context> &arg) -> decltype(vis(0))
+{
+    switch(arg.type_)
+    {
+        case detail::type::none_type:
+            break;
+        case detail::type::int_type:
+            return vis(arg.value_.int_value);
+        case detail::type::uint_type:
+            return vis(arg.value_.uint_value);
+        case detail::type::long_long_type:
+            return vis(arg.value_.long_long_value);
+        case detail::type::ulong_long_type:
+            return vis(arg.value_.ulong_long_value);
+        case detail::type::int128_type:
+            return vis(detail::convert_for_visit(arg.value_.int128_value));
+        case detail::type::uint128_type:
+            return vis(detail::convert_for_visit(arg.value_.uint128_value));
+        case detail::type::bool_type:
+            return vis(arg.value_.bool_value);
+        case detail::type::char_type:
+            return vis(arg.value_.char_value);
+        case detail::type::float_type:
+            return vis(arg.value_.float_value);
+        case detail::type::double_type:
+            return vis(arg.value_.double_value);
+        case detail::type::long_double_type:
+            return vis(arg.value_.long_double_value);
+        case detail::type::cstring_type:
+            return vis(arg.value_.string.data);
+        case detail::type::string_type:
+            using sv = basic_string_view<typename Context::char_type>;
+            return vis(sv(arg.value_.string.data, arg.value_.string.size));
+        case detail::type::pointer_type:
+            return vis(arg.value_.pointer);
+        case detail::type::custom_type:
+            return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
+    }
+    return vis(monostate());
 }
 
 FMT_BEGIN_DETAIL_NAMESPACE
 
 template <typename Char, typename InputIt>
-auto copy_str(InputIt begin, InputIt end, appender out) -> appender {
-  get_container(out).append(begin, end);
-  return out;
+auto copy_str(InputIt begin, InputIt end, appender out) -> appender
+{
+    get_container(out).append(begin, end);
+    return out;
 }
 
 template <typename Char, typename R, typename OutputIt>
-FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt {
-  return detail::copy_str<Char>(rng.begin(), rng.end(), out);
+FMT_CONSTEXPR auto copy_str(R &&rng, OutputIt out) -> OutputIt
+{
+    return detail::copy_str<Char>(rng.begin(), rng.end(), out);
 }
 
 #if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
 // A workaround for gcc 4.8 to make void_t work in a SFINAE context.
-template <typename... Ts> struct void_t_impl { using type = void; };
+template <typename... Ts>
+struct void_t_impl
+{
+    using type = void;
+};
 template <typename... Ts>
 using void_t = typename detail::void_t_impl<Ts...>::type;
 #else
-template <typename...> using void_t = void;
+template <typename...>
+using void_t = void;
 #endif
 
 template <typename It, typename T, typename Enable = void>
-struct is_output_iterator : std::false_type {};
+struct is_output_iterator : std::false_type
+{
+};
 
 template <typename It, typename T>
 struct is_output_iterator<
-    It, T,
-    void_t<typename std::iterator_traits<It>::iterator_category,
-           decltype(*std::declval<It>() = std::declval<T>())>>
-    : std::true_type {};
+    It,
+    T,
+    void_t<typename std::iterator_traits<It>::iterator_category, decltype(*std::declval<It>() = std::declval<T>())>>
+    : std::true_type
+{
+};
 
 template <typename OutputIt>
-struct is_back_insert_iterator : std::false_type {};
+struct is_back_insert_iterator : std::false_type
+{
+};
 template <typename Container>
-struct is_back_insert_iterator<std::back_insert_iterator<Container>>
-    : std::true_type {};
+struct is_back_insert_iterator<std::back_insert_iterator<Container>> : std::true_type
+{
+};
 
 template <typename OutputIt>
-struct is_contiguous_back_insert_iterator : std::false_type {};
+struct is_contiguous_back_insert_iterator : std::false_type
+{
+};
 template <typename Container>
-struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
-    : is_contiguous<Container> {};
+struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>> : is_contiguous<Container>
+{
+};
 template <>
-struct is_contiguous_back_insert_iterator<appender> : std::true_type {};
+struct is_contiguous_back_insert_iterator<appender> : std::true_type
+{
+};
 
 // A type-erased reference to an std::locale to avoid a heavy <locale> include.
-class locale_ref {
- private:
-  const void* locale_;  // A type-erased pointer to std::locale.
+class locale_ref
+{
+private:
+    const void *locale_; // A type-erased pointer to std::locale.
 
- public:
-  constexpr locale_ref() : locale_(nullptr) {}
-  template <typename Locale> explicit locale_ref(const Locale& loc);
+public:
+    constexpr locale_ref() : locale_(nullptr) {}
+    template <typename Locale>
+    explicit locale_ref(const Locale &loc);
 
-  explicit operator bool() const noexcept { return locale_ != nullptr; }
+    explicit operator bool() const noexcept { return locale_ != nullptr; }
 
-  template <typename Locale> auto get() const -> Locale;
+    template <typename Locale>
+    auto get() const -> Locale;
 };
 
-template <typename> constexpr auto encode_types() -> unsigned long long {
-  return 0;
+template <typename>
+constexpr auto encode_types() -> unsigned long long
+{
+    return 0;
 }
 
 template <typename Context, typename Arg, typename... Args>
-constexpr auto encode_types() -> unsigned long long {
-  return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
-         (encode_types<Context, Args...>() << packed_arg_bits);
+constexpr auto encode_types() -> unsigned long long
+{
+    return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
+           (encode_types<Context, Args...>() << packed_arg_bits);
 }
 
 template <typename Context, typename T>
-FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value<Context> {
-  const auto& arg = arg_mapper<Context>().map(FMT_FORWARD(val));
-
-  constexpr bool formattable_char =
-      !std::is_same<decltype(arg), const unformattable_char&>::value;
-  static_assert(formattable_char, "Mixing character types is disallowed.");
-
-  constexpr bool formattable_const =
-      !std::is_same<decltype(arg), const unformattable_const&>::value;
-  static_assert(formattable_const, "Cannot format a const argument.");
-
-  // Formatting of arbitrary pointers is disallowed. If you want to output
-  // a pointer cast it to "void *" or "const void *". In particular, this
-  // forbids formatting of "[const] volatile char *" which is printed as bool
-  // by iostreams.
-  constexpr bool formattable_pointer =
-      !std::is_same<decltype(arg), const unformattable_pointer&>::value;
-  static_assert(formattable_pointer,
-                "Formatting of non-void pointers is disallowed.");
-
-  constexpr bool formattable =
-      !std::is_same<decltype(arg), const unformattable&>::value;
-  static_assert(
-      formattable,
-      "Cannot format an argument. To make type T formattable provide a "
-      "formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
-  return {arg};
+FMT_CONSTEXPR FMT_INLINE auto make_value(T &&val) -> value<Context>
+{
+    const auto &arg = arg_mapper<Context>().map(FMT_FORWARD(val));
+
+    constexpr bool formattable_char = !std::is_same<decltype(arg), const unformattable_char &>::value;
+    static_assert(formattable_char, "Mixing character types is disallowed.");
+
+    constexpr bool formattable_const = !std::is_same<decltype(arg), const unformattable_const &>::value;
+    static_assert(formattable_const, "Cannot format a const argument.");
+
+    // Formatting of arbitrary pointers is disallowed. If you want to output
+    // a pointer cast it to "void *" or "const void *". In particular, this
+    // forbids formatting of "[const] volatile char *" which is printed as bool
+    // by iostreams.
+    constexpr bool formattable_pointer = !std::is_same<decltype(arg), const unformattable_pointer &>::value;
+    static_assert(formattable_pointer, "Formatting of non-void pointers is disallowed.");
+
+    constexpr bool formattable = !std::is_same<decltype(arg), const unformattable &>::value;
+    static_assert(
+        formattable,
+        "Cannot format an argument. To make type T formattable provide a "
+        "formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
+    return {arg};
 }
 
 template <typename Context, typename T>
-FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context> {
-  basic_format_arg<Context> arg;
-  arg.type_ = mapped_type_constant<T, Context>::value;
-  arg.value_ = make_value<Context>(value);
-  return arg;
+FMT_CONSTEXPR auto make_arg(T &&value) -> basic_format_arg<Context>
+{
+    basic_format_arg<Context> arg;
+    arg.type_  = mapped_type_constant<T, Context>::value;
+    arg.value_ = make_value<Context>(value);
+    return arg;
 }
 
 // The type template parameter is there to avoid an ODR violation when using
 // a fallback formatter in one translation unit and an implicit conversion in
 // another (not recommended).
-template <bool IS_PACKED, typename Context, type, typename T,
-          FMT_ENABLE_IF(IS_PACKED)>
-FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
-  return make_value<Context>(val);
+template <bool IS_PACKED, typename Context, type, typename T, FMT_ENABLE_IF(IS_PACKED)>
+FMT_CONSTEXPR FMT_INLINE auto make_arg(T &&val) -> value<Context>
+{
+    return make_value<Context>(val);
 }
 
-template <bool IS_PACKED, typename Context, type, typename T,
-          FMT_ENABLE_IF(!IS_PACKED)>
-FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg<Context> {
-  return make_arg<Context>(value);
+template <bool IS_PACKED, typename Context, type, typename T, FMT_ENABLE_IF(!IS_PACKED)>
+FMT_CONSTEXPR inline auto make_arg(T &&value) -> basic_format_arg<Context>
+{
+    return make_arg<Context>(value);
 }
 FMT_END_DETAIL_NAMESPACE
 
 // Formatting context.
-template <typename OutputIt, typename Char> class basic_format_context {
- public:
-  /** The character type for the output. */
-  using char_type = Char;
-
- private:
-  OutputIt out_;
-  basic_format_args<basic_format_context> args_;
-  detail::locale_ref loc_;
-
- public:
-  using iterator = OutputIt;
-  using format_arg = basic_format_arg<basic_format_context>;
-  using parse_context_type = basic_format_parse_context<Char>;
-  template <typename T> using formatter_type = formatter<T, char_type>;
-
-  basic_format_context(basic_format_context&&) = default;
-  basic_format_context(const basic_format_context&) = delete;
-  void operator=(const basic_format_context&) = delete;
-  /**
-   Constructs a ``basic_format_context`` object. References to the arguments are
-   stored in the object so make sure they have appropriate lifetimes.
-   */
-  constexpr basic_format_context(
-      OutputIt out, basic_format_args<basic_format_context> ctx_args,
-      detail::locale_ref loc = detail::locale_ref())
-      : out_(out), args_(ctx_args), loc_(loc) {}
-
-  constexpr auto arg(int id) const -> format_arg { return args_.get(id); }
-  FMT_CONSTEXPR auto arg(basic_string_view<char_type> name) -> format_arg {
-    return args_.get(name);
-  }
-  FMT_CONSTEXPR auto arg_id(basic_string_view<char_type> name) -> int {
-    return args_.get_id(name);
-  }
-  auto args() const -> const basic_format_args<basic_format_context>& {
-    return args_;
-  }
-
-  FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; }
-  void on_error(const char* message) { error_handler().on_error(message); }
-
-  // Returns an iterator to the beginning of the output range.
-  FMT_CONSTEXPR auto out() -> iterator { return out_; }
-
-  // Advances the begin iterator to ``it``.
-  void advance_to(iterator it) {
-    if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
-  }
-
-  FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; }
+template <typename OutputIt, typename Char>
+class basic_format_context
+{
+public:
+    /** The character type for the output. */
+    using char_type = Char;
+
+private:
+    OutputIt                                out_;
+    basic_format_args<basic_format_context> args_;
+    detail::locale_ref                      loc_;
+
+public:
+    using iterator           = OutputIt;
+    using format_arg         = basic_format_arg<basic_format_context>;
+    using parse_context_type = basic_format_parse_context<Char>;
+    template <typename T>
+    using formatter_type = formatter<T, char_type>;
+
+    basic_format_context(basic_format_context &&)      = default;
+    basic_format_context(const basic_format_context &) = delete;
+    void operator=(const basic_format_context &)       = delete;
+    /**
+     Constructs a ``basic_format_context`` object. References to the arguments are
+     stored in the object so make sure they have appropriate lifetimes.
+     */
+    constexpr basic_format_context(
+        OutputIt                                out,
+        basic_format_args<basic_format_context> ctx_args,
+        detail::locale_ref                      loc = detail::locale_ref()) :
+        out_(out), args_(ctx_args), loc_(loc)
+    {
+    }
+
+    constexpr auto     arg(int id) const -> format_arg { return args_.get(id); }
+    FMT_CONSTEXPR auto arg(basic_string_view<char_type> name) -> format_arg { return args_.get(name); }
+    FMT_CONSTEXPR auto arg_id(basic_string_view<char_type> name) -> int { return args_.get_id(name); }
+    auto               args() const -> const basic_format_args<basic_format_context>               &{ return args_; }
+
+    FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; }
+    void               on_error(const char *message) { error_handler().on_error(message); }
+
+    // Returns an iterator to the beginning of the output range.
+    FMT_CONSTEXPR auto out() -> iterator { return out_; }
+
+    // Advances the begin iterator to ``it``.
+    void advance_to(iterator it)
+    {
+        if(!detail::is_back_insert_iterator<iterator>())
+            out_ = it;
+    }
+
+    FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; }
 };
 
 template <typename Char>
-using buffer_context =
-    basic_format_context<detail::buffer_appender<Char>, Char>;
+using buffer_context = basic_format_context<detail::buffer_appender<Char>, Char>;
 using format_context = buffer_context<char>;
 
 // Workaround an alias issue: https://stackoverflow.com/q/62767544/471164.
-#define FMT_BUFFER_CONTEXT(Char) \
-  basic_format_context<detail::buffer_appender<Char>, Char>
+#define FMT_BUFFER_CONTEXT(Char) basic_format_context<detail::buffer_appender<Char>, Char>
 
 template <typename T, typename Char = char>
 using is_formattable = bool_constant<
-    !std::is_base_of<detail::unformattable,
-                     decltype(detail::arg_mapper<buffer_context<Char>>().map(
-                         std::declval<T>()))>::value &&
+    !std::is_base_of<
+        detail::unformattable,
+        decltype(detail::arg_mapper<buffer_context<Char>>().map(std::declval<T>()))>::value &&
     !detail::has_fallback_formatter<T, Char>::value>;
 
 /**
@@ -1868,40 +2096,32 @@ class format_arg_store
     : public basic_format_args<Context>
 #endif
 {
- private:
-  static const size_t num_args = sizeof...(Args);
-  static const size_t num_named_args = detail::count_named_args<Args...>();
-  static const bool is_packed = num_args <= detail::max_packed_args;
+private:
+    static const size_t num_args       = sizeof...(Args);
+    static const size_t num_named_args = detail::count_named_args<Args...>();
+    static const bool   is_packed      = num_args <= detail::max_packed_args;
 
-  using value_type = conditional_t<is_packed, detail::value<Context>,
-                                   basic_format_arg<Context>>;
+    using value_type = conditional_t<is_packed, detail::value<Context>, basic_format_arg<Context>>;
 
-  detail::arg_data<value_type, typename Context::char_type, num_args,
-                   num_named_args>
-      data_;
+    detail::arg_data<value_type, typename Context::char_type, num_args, num_named_args> data_;
 
-  friend class basic_format_args<Context>;
+    friend class basic_format_args<Context>;
 
-  static constexpr unsigned long long desc =
-      (is_packed ? detail::encode_types<Context, Args...>()
-                 : detail::is_unpacked_bit | num_args) |
-      (num_named_args != 0
-           ? static_cast<unsigned long long>(detail::has_named_args_bit)
-           : 0);
+    static constexpr unsigned long long desc =
+        (is_packed ? detail::encode_types<Context, Args...>() : detail::is_unpacked_bit | num_args) |
+        (num_named_args != 0 ? static_cast<unsigned long long>(detail::has_named_args_bit) : 0);
 
- public:
-  template <typename... T>
-  FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args)
-      :
+public:
+    template <typename... T>
+    FMT_CONSTEXPR FMT_INLINE format_arg_store(T &&...args) :
 #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
         basic_format_args<Context>(*this),
 #endif
-        data_{detail::make_arg<
-            is_packed, Context,
-            detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>(
-            FMT_FORWARD(args))...} {
-    detail::init_named_args(data_.named_args(), 0, 0, args...);
-  }
+        data_{detail::make_arg<is_packed, Context, detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>(
+            FMT_FORWARD(args))...}
+    {
+        detail::init_named_args(data_.named_args(), 0, 0, args...);
+    }
 };
 
 /**
@@ -1913,9 +2133,9 @@ class format_arg_store
   \endrst
  */
 template <typename Context = format_context, typename... Args>
-constexpr auto make_format_args(Args&&... args)
-    -> format_arg_store<Context, remove_cvref_t<Args>...> {
-  return {FMT_FORWARD(args)...};
+constexpr auto make_format_args(Args &&...args) -> format_arg_store<Context, remove_cvref_t<Args>...>
+{
+    return {FMT_FORWARD(args)...};
 }
 
 /**
@@ -1930,9 +2150,10 @@ constexpr auto make_format_args(Args&&... args)
   \endrst
  */
 template <typename Char, typename T>
-inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
-  static_assert(!detail::is_named_arg<T>(), "nested named arguments");
-  return {name, arg};
+inline auto arg(const Char *name, const T &arg) -> detail::named_arg<Char, T>
+{
+    static_assert(!detail::is_named_arg<T>(), "nested named arguments");
+    return {name, arg};
 }
 
 /**
@@ -1945,115 +2166,126 @@ inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
     format_args args = make_format_args(42);  // Error: dangling reference
   \endrst
  */
-template <typename Context> class basic_format_args {
- public:
-  using size_type = int;
-  using format_arg = basic_format_arg<Context>;
-
- private:
-  // A descriptor that contains information about formatting arguments.
-  // If the number of arguments is less or equal to max_packed_args then
-  // argument types are passed in the descriptor. This reduces binary code size
-  // per formatting function call.
-  unsigned long long desc_;
-  union {
-    // If is_packed() returns true then argument values are stored in values_;
-    // otherwise they are stored in args_. This is done to improve cache
-    // locality and reduce compiled code size since storing larger objects
-    // may require more code (at least on x86-64) even if the same amount of
-    // data is actually copied to stack. It saves ~10% on the bloat test.
-    const detail::value<Context>* values_;
-    const format_arg* args_;
-  };
-
-  constexpr auto is_packed() const -> bool {
-    return (desc_ & detail::is_unpacked_bit) == 0;
-  }
-  auto has_named_args() const -> bool {
-    return (desc_ & detail::has_named_args_bit) != 0;
-  }
-
-  FMT_CONSTEXPR auto type(int index) const -> detail::type {
-    int shift = index * detail::packed_arg_bits;
-    unsigned int mask = (1 << detail::packed_arg_bits) - 1;
-    return static_cast<detail::type>((desc_ >> shift) & mask);
-  }
-
-  constexpr FMT_INLINE basic_format_args(unsigned long long desc,
-                                         const detail::value<Context>* values)
-      : desc_(desc), values_(values) {}
-  constexpr basic_format_args(unsigned long long desc, const format_arg* args)
-      : desc_(desc), args_(args) {}
-
- public:
-  constexpr basic_format_args() : desc_(0), args_(nullptr) {}
-
-  /**
-   \rst
-   Constructs a `basic_format_args` object from `~fmt::format_arg_store`.
-   \endrst
-   */
-  template <typename... Args>
-  constexpr FMT_INLINE basic_format_args(
-      const format_arg_store<Context, Args...>& store)
-      : basic_format_args(format_arg_store<Context, Args...>::desc,
-                          store.data_.args()) {}
-
-  /**
-   \rst
-   Constructs a `basic_format_args` object from
-   `~fmt::dynamic_format_arg_store`.
-   \endrst
-   */
-  constexpr FMT_INLINE basic_format_args(
-      const dynamic_format_arg_store<Context>& store)
-      : basic_format_args(store.get_types(), store.data()) {}
-
-  /**
-   \rst
-   Constructs a `basic_format_args` object from a dynamic set of arguments.
-   \endrst
-   */
-  constexpr basic_format_args(const format_arg* args, int count)
-      : basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count),
-                          args) {}
-
-  /** Returns the argument with the specified id. */
-  FMT_CONSTEXPR auto get(int id) const -> format_arg {
-    format_arg arg;
-    if (!is_packed()) {
-      if (id < max_size()) arg = args_[id];
-      return arg;
-    }
-    if (id >= detail::max_packed_args) return arg;
-    arg.type_ = type(id);
-    if (arg.type_ == detail::type::none_type) return arg;
-    arg.value_ = values_[id];
-    return arg;
-  }
+template <typename Context>
+class basic_format_args
+{
+public:
+    using size_type  = int;
+    using format_arg = basic_format_arg<Context>;
+
+private:
+    // A descriptor that contains information about formatting arguments.
+    // If the number of arguments is less or equal to max_packed_args then
+    // argument types are passed in the descriptor. This reduces binary code size
+    // per formatting function call.
+    unsigned long long desc_;
+    union
+    {
+        // If is_packed() returns true then argument values are stored in values_;
+        // otherwise they are stored in args_. This is done to improve cache
+        // locality and reduce compiled code size since storing larger objects
+        // may require more code (at least on x86-64) even if the same amount of
+        // data is actually copied to stack. It saves ~10% on the bloat test.
+        const detail::value<Context> *values_;
+        const format_arg             *args_;
+    };
+
+    constexpr auto is_packed() const -> bool { return (desc_ & detail::is_unpacked_bit) == 0; }
+    auto           has_named_args() const -> bool { return (desc_ & detail::has_named_args_bit) != 0; }
+
+    FMT_CONSTEXPR auto type(int index) const -> detail::type
+    {
+        int          shift = index * detail::packed_arg_bits;
+        unsigned int mask  = (1 << detail::packed_arg_bits) - 1;
+        return static_cast<detail::type>((desc_ >> shift) & mask);
+    }
+
+    constexpr FMT_INLINE basic_format_args(unsigned long long desc, const detail::value<Context> *values) :
+        desc_(desc), values_(values)
+    {
+    }
+    constexpr basic_format_args(unsigned long long desc, const format_arg *args) : desc_(desc), args_(args) {}
+
+public:
+    constexpr basic_format_args() : desc_(0), args_(nullptr) {}
+
+    /**
+     \rst
+     Constructs a `basic_format_args` object from `~fmt::format_arg_store`.
+     \endrst
+     */
+    template <typename... Args>
+    constexpr FMT_INLINE basic_format_args(const format_arg_store<Context, Args...> &store) :
+        basic_format_args(format_arg_store<Context, Args...>::desc, store.data_.args())
+    {
+    }
 
-  template <typename Char>
-  auto get(basic_string_view<Char> name) const -> format_arg {
-    int id = get_id(name);
-    return id >= 0 ? get(id) : format_arg();
-  }
+    /**
+     \rst
+     Constructs a `basic_format_args` object from
+     `~fmt::dynamic_format_arg_store`.
+     \endrst
+     */
+    constexpr FMT_INLINE basic_format_args(const dynamic_format_arg_store<Context> &store) :
+        basic_format_args(store.get_types(), store.data())
+    {
+    }
+
+    /**
+     \rst
+     Constructs a `basic_format_args` object from a dynamic set of arguments.
+     \endrst
+     */
+    constexpr basic_format_args(const format_arg *args, int count) :
+        basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count), args)
+    {
+    }
+
+    /** Returns the argument with the specified id. */
+    FMT_CONSTEXPR auto get(int id) const -> format_arg
+    {
+        format_arg arg;
+        if(!is_packed())
+        {
+            if(id < max_size())
+                arg = args_[id];
+            return arg;
+        }
+        if(id >= detail::max_packed_args)
+            return arg;
+        arg.type_ = type(id);
+        if(arg.type_ == detail::type::none_type)
+            return arg;
+        arg.value_ = values_[id];
+        return arg;
+    }
+
+    template <typename Char>
+    auto get(basic_string_view<Char> name) const -> format_arg
+    {
+        int id = get_id(name);
+        return id >= 0 ? get(id) : format_arg();
+    }
 
-  template <typename Char>
-  auto get_id(basic_string_view<Char> name) const -> int {
-    if (!has_named_args()) return -1;
-    const auto& named_args =
-        (is_packed() ? values_[-1] : args_[-1].value_).named_args;
-    for (size_t i = 0; i < named_args.size; ++i) {
-      if (named_args.data[i].name == name) return named_args.data[i].id;
+    template <typename Char>
+    auto get_id(basic_string_view<Char> name) const -> int
+    {
+        if(!has_named_args())
+            return -1;
+        const auto &named_args = (is_packed() ? values_[-1] : args_[-1].value_).named_args;
+        for(size_t i = 0; i < named_args.size; ++i)
+        {
+            if(named_args.data[i].name == name)
+                return named_args.data[i].id;
+        }
+        return -1;
     }
-    return -1;
-  }
 
-  auto max_size() const -> int {
-    unsigned long long max_packed = detail::max_packed_args;
-    return static_cast<int>(is_packed() ? max_packed
-                                        : desc_ & ~detail::is_unpacked_bit);
-  }
+    auto max_size() const -> int
+    {
+        unsigned long long max_packed = detail::max_packed_args;
+        return static_cast<int>(is_packed() ? max_packed : desc_ & ~detail::is_unpacked_bit);
+    }
 };
 
 /** An alias to ``basic_format_args<format_context>``. */
@@ -2066,16 +2298,17 @@ using format_args = basic_format_args<format_context>;
 // Additionally, if an underlying type is specified, older gcc incorrectly warns
 // that the type is too small. Both bugs are fixed in gcc 9.3.
 #if FMT_GCC_VERSION && FMT_GCC_VERSION < 903
-#  define FMT_ENUM_UNDERLYING_TYPE(type)
+#define FMT_ENUM_UNDERLYING_TYPE(type)
 #else
-#  define FMT_ENUM_UNDERLYING_TYPE(type) : type
+#define FMT_ENUM_UNDERLYING_TYPE(type) : type
 #endif
-namespace align {
-enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center,
-                                                  numeric};
+namespace align
+{
+enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center, numeric};
 }
 using align_t = align::type;
-namespace sign {
+namespace sign
+{
 enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space};
 }
 using sign_t = sign::type;
@@ -2083,1045 +2316,1160 @@ using sign_t = sign::type;
 FMT_BEGIN_DETAIL_NAMESPACE
 
 // Workaround an array initialization issue in gcc 4.8.
-template <typename Char> struct fill_t {
- private:
-  enum { max_size = 4 };
-  Char data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)};
-  unsigned char size_ = 1;
-
- public:
-  FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
-    auto size = s.size();
-    if (size > max_size) return throw_format_error("invalid fill");
-    for (size_t i = 0; i < size; ++i) data_[i] = s[i];
-    size_ = static_cast<unsigned char>(size);
-  }
-
-  constexpr auto size() const -> size_t { return size_; }
-  constexpr auto data() const -> const Char* { return data_; }
-
-  FMT_CONSTEXPR auto operator[](size_t index) -> Char& { return data_[index]; }
-  FMT_CONSTEXPR auto operator[](size_t index) const -> const Char& {
-    return data_[index];
-  }
+template <typename Char>
+struct fill_t
+{
+private:
+    enum
+    {
+        max_size = 4
+    };
+    Char          data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)};
+    unsigned char size_           = 1;
+
+public:
+    FMT_CONSTEXPR void operator=(basic_string_view<Char> s)
+    {
+        auto size = s.size();
+        if(size > max_size)
+            return throw_format_error("invalid fill");
+        for(size_t i = 0; i < size; ++i)
+            data_[i] = s[i];
+        size_ = static_cast<unsigned char>(size);
+    }
+
+    constexpr auto size() const -> size_t { return size_; }
+    constexpr auto data() const -> const Char * { return data_; }
+
+    FMT_CONSTEXPR auto operator[](size_t index) -> Char & { return data_[index]; }
+    FMT_CONSTEXPR auto operator[](size_t index) const -> const Char & { return data_[index]; }
 };
 FMT_END_DETAIL_NAMESPACE
 
-enum class presentation_type : unsigned char {
-  none,
-  // Integer types should go first,
-  dec,             // 'd'
-  oct,             // 'o'
-  hex_lower,       // 'x'
-  hex_upper,       // 'X'
-  bin_lower,       // 'b'
-  bin_upper,       // 'B'
-  hexfloat_lower,  // 'a'
-  hexfloat_upper,  // 'A'
-  exp_lower,       // 'e'
-  exp_upper,       // 'E'
-  fixed_lower,     // 'f'
-  fixed_upper,     // 'F'
-  general_lower,   // 'g'
-  general_upper,   // 'G'
-  chr,             // 'c'
-  string,          // 's'
-  pointer,         // 'p'
-  debug            // '?'
+enum class presentation_type : unsigned char
+{
+    none,
+    // Integer types should go first,
+    dec,            // 'd'
+    oct,            // 'o'
+    hex_lower,      // 'x'
+    hex_upper,      // 'X'
+    bin_lower,      // 'b'
+    bin_upper,      // 'B'
+    hexfloat_lower, // 'a'
+    hexfloat_upper, // 'A'
+    exp_lower,      // 'e'
+    exp_upper,      // 'E'
+    fixed_lower,    // 'f'
+    fixed_upper,    // 'F'
+    general_lower,  // 'g'
+    general_upper,  // 'G'
+    chr,            // 'c'
+    string,         // 's'
+    pointer,        // 'p'
+    debug           // '?'
 };
 
 // Format specifiers for built-in and string types.
-template <typename Char> struct basic_format_specs {
-  int width;
-  int precision;
-  presentation_type type;
-  align_t align : 4;
-  sign_t sign : 3;
-  bool alt : 1;  // Alternate form ('#').
-  bool localized : 1;
-  detail::fill_t<Char> fill;
-
-  constexpr basic_format_specs()
-      : width(0),
+template <typename Char>
+struct basic_format_specs
+{
+    int                  width;
+    int                  precision;
+    presentation_type    type;
+    align_t              align : 4;
+    sign_t               sign : 3;
+    bool                 alt : 1; // Alternate form ('#').
+    bool                 localized : 1;
+    detail::fill_t<Char> fill;
+
+    constexpr basic_format_specs() :
+        width(0),
         precision(-1),
         type(presentation_type::none),
         align(align::none),
         sign(sign::none),
         alt(false),
-        localized(false) {}
+        localized(false)
+    {
+    }
 };
 
 using format_specs = basic_format_specs<char>;
 
 FMT_BEGIN_DETAIL_NAMESPACE
 
-enum class arg_id_kind { none, index, name };
+enum class arg_id_kind
+{
+    none,
+    index,
+    name
+};
 
 // An argument reference.
-template <typename Char> struct arg_ref {
-  FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
+template <typename Char>
+struct arg_ref
+{
+    FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
 
-  FMT_CONSTEXPR explicit arg_ref(int index)
-      : kind(arg_id_kind::index), val(index) {}
-  FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name)
-      : kind(arg_id_kind::name), val(name) {}
+    FMT_CONSTEXPR explicit arg_ref(int index) : kind(arg_id_kind::index), val(index) {}
+    FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name) : kind(arg_id_kind::name), val(name) {}
 
-  FMT_CONSTEXPR auto operator=(int idx) -> arg_ref& {
-    kind = arg_id_kind::index;
-    val.index = idx;
-    return *this;
-  }
+    FMT_CONSTEXPR auto operator=(int idx) -> arg_ref &
+    {
+        kind      = arg_id_kind::index;
+        val.index = idx;
+        return *this;
+    }
 
-  arg_id_kind kind;
-  union value {
-    FMT_CONSTEXPR value(int id = 0) : index{id} {}
-    FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
+    arg_id_kind kind;
+    union value
+    {
+        FMT_CONSTEXPR value(int id = 0) : index{id} {}
+        FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
 
-    int index;
-    basic_string_view<Char> name;
-  } val;
+        int                     index;
+        basic_string_view<Char> name;
+    } val;
 };
 
 // Format specifiers with width and precision resolved at formatting rather
 // than parsing time to allow re-using the same parsed specifiers with
 // different sets of arguments (precompilation of format strings).
 template <typename Char>
-struct dynamic_format_specs : basic_format_specs<Char> {
-  arg_ref<Char> width_ref;
-  arg_ref<Char> precision_ref;
+struct dynamic_format_specs : basic_format_specs<Char>
+{
+    arg_ref<Char> width_ref;
+    arg_ref<Char> precision_ref;
 };
 
-struct auto_id {};
+struct auto_id
+{
+};
 
 // A format specifier handler that sets fields in basic_format_specs.
-template <typename Char> class specs_setter {
- protected:
-  basic_format_specs<Char>& specs_;
+template <typename Char>
+class specs_setter
+{
+protected:
+    basic_format_specs<Char> &specs_;
 
- public:
-  explicit FMT_CONSTEXPR specs_setter(basic_format_specs<Char>& specs)
-      : specs_(specs) {}
+public:
+    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(align_t align) { specs_.align = align; }
-  FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
-    specs_.fill = fill;
-  }
-  FMT_CONSTEXPR void on_sign(sign_t s) { specs_.sign = s; }
-  FMT_CONSTEXPR void on_hash() { specs_.alt = true; }
-  FMT_CONSTEXPR void on_localized() { specs_.localized = true; }
+    FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; }
+    FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) { specs_.fill = fill; }
+    FMT_CONSTEXPR void on_sign(sign_t s) { specs_.sign = s; }
+    FMT_CONSTEXPR void on_hash() { specs_.alt = true; }
+    FMT_CONSTEXPR void on_localized() { specs_.localized = true; }
 
-  FMT_CONSTEXPR void on_zero() {
-    if (specs_.align == align::none) specs_.align = align::numeric;
-    specs_.fill[0] = Char('0');
-  }
+    FMT_CONSTEXPR void on_zero()
+    {
+        if(specs_.align == align::none)
+            specs_.align = align::numeric;
+        specs_.fill[0] = Char('0');
+    }
 
-  FMT_CONSTEXPR void on_width(int width) { specs_.width = width; }
-  FMT_CONSTEXPR void on_precision(int precision) {
-    specs_.precision = precision;
-  }
-  FMT_CONSTEXPR void end_precision() {}
+    FMT_CONSTEXPR void on_width(int width) { specs_.width = width; }
+    FMT_CONSTEXPR void on_precision(int precision) { specs_.precision = precision; }
+    FMT_CONSTEXPR void end_precision() {}
 
-  FMT_CONSTEXPR void on_type(presentation_type type) { specs_.type = type; }
+    FMT_CONSTEXPR void on_type(presentation_type type) { specs_.type = type; }
 };
 
 // Format spec handler that saves references to arguments representing dynamic
 // width and precision to be resolved at formatting time.
 template <typename ParseContext>
-class dynamic_specs_handler
-    : public specs_setter<typename ParseContext::char_type> {
- public:
-  using char_type = typename ParseContext::char_type;
+class dynamic_specs_handler : public specs_setter<typename ParseContext::char_type>
+{
+public:
+    using char_type = typename ParseContext::char_type;
 
-  FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs<char_type>& specs,
-                                      ParseContext& ctx)
-      : specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
+    FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs<char_type> &specs, ParseContext &ctx) :
+        specs_setter<char_type>(specs), specs_(specs), context_(ctx)
+    {
+    }
 
-  FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler& other)
-      : specs_setter<char_type>(other),
-        specs_(other.specs_),
-        context_(other.context_) {}
+    FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler &other) :
+        specs_setter<char_type>(other), specs_(other.specs_), context_(other.context_)
+    {
+    }
 
-  template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
-    specs_.width_ref = make_arg_ref(arg_id);
-  }
+    template <typename Id>
+    FMT_CONSTEXPR void on_dynamic_width(Id arg_id)
+    {
+        specs_.width_ref = make_arg_ref(arg_id);
+    }
 
-  template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
-    specs_.precision_ref = make_arg_ref(arg_id);
-  }
+    template <typename Id>
+    FMT_CONSTEXPR void on_dynamic_precision(Id arg_id)
+    {
+        specs_.precision_ref = make_arg_ref(arg_id);
+    }
 
-  FMT_CONSTEXPR void on_error(const char* message) {
-    context_.on_error(message);
-  }
+    FMT_CONSTEXPR void on_error(const char *message) { context_.on_error(message); }
 
- private:
-  dynamic_format_specs<char_type>& specs_;
-  ParseContext& context_;
+private:
+    dynamic_format_specs<char_type> &specs_;
+    ParseContext                    &context_;
 
-  using arg_ref_type = arg_ref<char_type>;
+    using arg_ref_type = arg_ref<char_type>;
 
-  FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type {
-    context_.check_arg_id(arg_id);
-    context_.check_dynamic_spec(arg_id);
-    return arg_ref_type(arg_id);
-  }
+    FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type
+    {
+        context_.check_arg_id(arg_id);
+        context_.check_dynamic_spec(arg_id);
+        return arg_ref_type(arg_id);
+    }
 
-  FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type {
-    int arg_id = context_.next_arg_id();
-    context_.check_dynamic_spec(arg_id);
-    return arg_ref_type(arg_id);
-  }
+    FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type
+    {
+        int arg_id = context_.next_arg_id();
+        context_.check_dynamic_spec(arg_id);
+        return arg_ref_type(arg_id);
+    }
 
-  FMT_CONSTEXPR auto make_arg_ref(basic_string_view<char_type> arg_id)
-      -> arg_ref_type {
-    context_.check_arg_id(arg_id);
-    basic_string_view<char_type> format_str(
-        context_.begin(), to_unsigned(context_.end() - context_.begin()));
-    return arg_ref_type(arg_id);
-  }
+    FMT_CONSTEXPR auto make_arg_ref(basic_string_view<char_type> arg_id) -> arg_ref_type
+    {
+        context_.check_arg_id(arg_id);
+        basic_string_view<char_type> format_str(context_.begin(), to_unsigned(context_.end() - context_.begin()));
+        return arg_ref_type(arg_id);
+    }
 };
 
-template <typename Char> constexpr bool is_ascii_letter(Char c) {
-  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+template <typename Char>
+constexpr bool is_ascii_letter(Char c)
+{
+    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
 }
 
 // Converts a character to ASCII. Returns a number > 127 on conversion failure.
 template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
-constexpr auto to_ascii(Char c) -> Char {
-  return c;
+constexpr auto to_ascii(Char c) -> Char
+{
+    return c;
 }
 template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
-constexpr auto to_ascii(Char c) -> underlying_t<Char> {
-  return c;
+constexpr auto to_ascii(Char c) -> underlying_t<Char>
+{
+    return c;
 }
 
-FMT_CONSTEXPR inline auto code_point_length_impl(char c) -> int {
-  return "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"
-      [static_cast<unsigned char>(c) >> 3];
+FMT_CONSTEXPR inline auto code_point_length_impl(char c) -> int
+{
+    return "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"[static_cast<unsigned char>(c) >> 3];
 }
 
 template <typename Char>
-FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
-  if (const_check(sizeof(Char) != 1)) return 1;
-  int len = code_point_length_impl(static_cast<char>(*begin));
+FMT_CONSTEXPR auto code_point_length(const Char *begin) -> int
+{
+    if(const_check(sizeof(Char) != 1))
+        return 1;
+    int len = code_point_length_impl(static_cast<char>(*begin));
 
-  // Compute the pointer to the next character early so that the next
-  // iteration can start working on the next character. Neither Clang
-  // nor GCC figure out this reordering on their own.
-  return len + !len;
+    // Compute the pointer to the next character early so that the next
+    // iteration can start working on the next character. Neither Clang
+    // nor GCC figure out this reordering on their own.
+    return len + !len;
 }
 
 // Return the result via the out param to workaround gcc bug 77539.
-template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
-FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
-  for (out = first; out != last; ++out) {
-    if (*out == value) return true;
-  }
-  return false;
+template <bool IS_CONSTEXPR, typename T, typename Ptr = const T *>
+FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr &out) -> bool
+{
+    for(out = first; out != last; ++out)
+    {
+        if(*out == value)
+            return true;
+    }
+    return false;
 }
 
 template <>
-inline auto find<false, char>(const char* first, const char* last, char value,
-                              const char*& out) -> bool {
-  out = static_cast<const char*>(
-      std::memchr(first, value, to_unsigned(last - first)));
-  return out != nullptr;
+inline auto find<false, char>(const char *first, const char *last, char value, const char *&out) -> bool
+{
+    out = static_cast<const char *>(std::memchr(first, value, to_unsigned(last - first)));
+    return out != nullptr;
 }
 
 // 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>
-FMT_CONSTEXPR auto parse_nonnegative_int(const Char*& begin, const Char* end,
-                                         int error_value) noexcept -> int {
-  FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
-  unsigned value = 0, prev = 0;
-  auto p = begin;
-  do {
-    prev = value;
-    value = value * 10 + unsigned(*p - '0');
-    ++p;
-  } while (p != end && '0' <= *p && *p <= '9');
-  auto num_digits = p - begin;
-  begin = p;
-  if (num_digits <= std::numeric_limits<int>::digits10)
-    return static_cast<int>(value);
-  // Check for overflow.
-  const unsigned max = to_unsigned((std::numeric_limits<int>::max)());
-  return num_digits == std::numeric_limits<int>::digits10 + 1 &&
-                 prev * 10ull + unsigned(p[-1] - '0') <= max
-             ? static_cast<int>(value)
-             : error_value;
+FMT_CONSTEXPR auto parse_nonnegative_int(const Char *&begin, const Char *end, int error_value) noexcept -> int
+{
+    FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
+    unsigned value = 0, prev = 0;
+    auto     p = begin;
+    do
+    {
+        prev  = value;
+        value = value * 10 + unsigned(*p - '0');
+        ++p;
+    } while(p != end && '0' <= *p && *p <= '9');
+    auto num_digits = p - begin;
+    begin           = p;
+    if(num_digits <= std::numeric_limits<int>::digits10)
+        return static_cast<int>(value);
+    // Check for overflow.
+    const unsigned max = to_unsigned((std::numeric_limits<int>::max)());
+    return num_digits == std::numeric_limits<int>::digits10 + 1 && prev * 10ull + unsigned(p[-1] - '0') <= max ?
+               static_cast<int>(value) :
+               error_value;
 }
 
 // Parses fill and alignment.
 template <typename Char, typename Handler>
-FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
-                               Handler&& handler) -> const Char* {
-  FMT_ASSERT(begin != end, "");
-  auto align = align::none;
-  auto p = begin + code_point_length(begin);
-  if (end - p <= 0) p = begin;
-  for (;;) {
-    switch (to_ascii(*p)) {
-    case '<':
-      align = align::left;
-      break;
-    case '>':
-      align = align::right;
-      break;
-    case '^':
-      align = align::center;
-      break;
-    default:
-      break;
-    }
-    if (align != align::none) {
-      if (p != begin) {
-        auto c = *begin;
-        if (c == '{')
-          return handler.on_error("invalid fill character '{'"), begin;
-        handler.on_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
-        begin = p + 1;
-      } else
-        ++begin;
-      handler.on_align(align);
-      break;
-    } else if (p == begin) {
-      break;
+FMT_CONSTEXPR auto parse_align(const Char *begin, const Char *end, Handler &&handler) -> const Char *
+{
+    FMT_ASSERT(begin != end, "");
+    auto align = align::none;
+    auto p     = begin + code_point_length(begin);
+    if(end - p <= 0)
+        p = begin;
+    for(;;)
+    {
+        switch(to_ascii(*p))
+        {
+            case '<':
+                align = align::left;
+                break;
+            case '>':
+                align = align::right;
+                break;
+            case '^':
+                align = align::center;
+                break;
+            default:
+                break;
+        }
+        if(align != align::none)
+        {
+            if(p != begin)
+            {
+                auto c = *begin;
+                if(c == '{')
+                    return handler.on_error("invalid fill character '{'"), begin;
+                handler.on_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
+                begin = p + 1;
+            }
+            else
+                ++begin;
+            handler.on_align(align);
+            break;
+        }
+        else if(p == begin)
+        {
+            break;
+        }
+        p = begin;
     }
-    p = begin;
-  }
-  return begin;
+    return begin;
 }
 
-template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
-  return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
+template <typename Char>
+FMT_CONSTEXPR bool is_name_start(Char c)
+{
+    return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
 }
 
 template <typename Char, typename IDHandler>
-FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end,
-                                   IDHandler&& handler) -> const Char* {
-  FMT_ASSERT(begin != end, "");
-  Char c = *begin;
-  if (c >= '0' && c <= '9') {
-    int index = 0;
-    if (c != '0')
-      index =
-          parse_nonnegative_int(begin, end, (std::numeric_limits<int>::max)());
-    else
-      ++begin;
-    if (begin == end || (*begin != '}' && *begin != ':'))
-      handler.on_error("invalid format string");
-    else
-      handler(index);
-    return begin;
-  }
-  if (!is_name_start(c)) {
-    handler.on_error("invalid format string");
-    return begin;
-  }
-  auto it = begin;
-  do {
-    ++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;
+FMT_CONSTEXPR auto do_parse_arg_id(const Char *begin, const Char *end, IDHandler &&handler) -> const Char *
+{
+    FMT_ASSERT(begin != end, "");
+    Char c = *begin;
+    if(c >= '0' && c <= '9')
+    {
+        int index = 0;
+        if(c != '0')
+            index = parse_nonnegative_int(begin, end, (std::numeric_limits<int>::max)());
+        else
+            ++begin;
+        if(begin == end || (*begin != '}' && *begin != ':'))
+            handler.on_error("invalid format string");
+        else
+            handler(index);
+        return begin;
+    }
+    if(!is_name_start(c))
+    {
+        handler.on_error("invalid format string");
+        return begin;
+    }
+    auto it = begin;
+    do
+    {
+        ++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;
 }
 
 template <typename Char, typename IDHandler>
-FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end,
-                                           IDHandler&& handler) -> const Char* {
-  Char c = *begin;
-  if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler);
-  handler();
-  return begin;
+FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char *begin, const Char *end, IDHandler &&handler) -> const Char *
+{
+    Char c = *begin;
+    if(c != '}' && c != ':')
+        return do_parse_arg_id(begin, end, handler);
+    handler();
+    return begin;
 }
 
 template <typename Char, typename Handler>
-FMT_CONSTEXPR auto parse_width(const Char* begin, const Char* end,
-                               Handler&& handler) -> const Char* {
-  using detail::auto_id;
-  struct width_adapter {
-    Handler& handler;
-
-    FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
-    FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); }
-    FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
-      handler.on_dynamic_width(id);
-    }
-    FMT_CONSTEXPR void on_error(const char* message) {
-      if (message) handler.on_error(message);
-    }
-  };
-
-  FMT_ASSERT(begin != end, "");
-  if ('0' <= *begin && *begin <= '9') {
-    int width = parse_nonnegative_int(begin, end, -1);
-    if (width != -1)
-      handler.on_width(width);
-    else
-      handler.on_error("number is too big");
-  } else if (*begin == '{') {
-    ++begin;
-    if (begin != end) begin = parse_arg_id(begin, end, width_adapter{handler});
-    if (begin == end || *begin != '}')
-      return handler.on_error("invalid format string"), begin;
-    ++begin;
-  }
-  return begin;
+FMT_CONSTEXPR auto parse_width(const Char *begin, const Char *end, Handler &&handler) -> const Char *
+{
+    using detail::auto_id;
+    struct width_adapter
+    {
+        Handler &handler;
+
+        FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
+        FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); }
+        FMT_CONSTEXPR void operator()(basic_string_view<Char> id) { handler.on_dynamic_width(id); }
+        FMT_CONSTEXPR void on_error(const char *message)
+        {
+            if(message)
+                handler.on_error(message);
+        }
+    };
+
+    FMT_ASSERT(begin != end, "");
+    if('0' <= *begin && *begin <= '9')
+    {
+        int width = parse_nonnegative_int(begin, end, -1);
+        if(width != -1)
+            handler.on_width(width);
+        else
+            handler.on_error("number is too big");
+    }
+    else if(*begin == '{')
+    {
+        ++begin;
+        if(begin != end)
+            begin = parse_arg_id(begin, end, width_adapter{handler});
+        if(begin == end || *begin != '}')
+            return handler.on_error("invalid format string"), begin;
+        ++begin;
+    }
+    return begin;
 }
 
 template <typename Char, typename Handler>
-FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end,
-                                   Handler&& handler) -> const Char* {
-  using detail::auto_id;
-  struct precision_adapter {
-    Handler& handler;
-
-    FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
-    FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); }
-    FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
-      handler.on_dynamic_precision(id);
-    }
-    FMT_CONSTEXPR void on_error(const char* message) {
-      if (message) handler.on_error(message);
-    }
-  };
-
-  ++begin;
-  auto c = begin != end ? *begin : Char();
-  if ('0' <= c && c <= '9') {
-    auto precision = parse_nonnegative_int(begin, end, -1);
-    if (precision != -1)
-      handler.on_precision(precision);
-    else
-      handler.on_error("number is too big");
-  } else if (c == '{') {
+FMT_CONSTEXPR auto parse_precision(const Char *begin, const Char *end, Handler &&handler) -> const Char *
+{
+    using detail::auto_id;
+    struct precision_adapter
+    {
+        Handler &handler;
+
+        FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
+        FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); }
+        FMT_CONSTEXPR void operator()(basic_string_view<Char> id) { handler.on_dynamic_precision(id); }
+        FMT_CONSTEXPR void on_error(const char *message)
+        {
+            if(message)
+                handler.on_error(message);
+        }
+    };
+
     ++begin;
-    if (begin != end)
-      begin = parse_arg_id(begin, end, precision_adapter{handler});
-    if (begin == end || *begin++ != '}')
-      return handler.on_error("invalid format string"), begin;
-  } else {
-    return handler.on_error("missing precision specifier"), begin;
-  }
-  handler.end_precision();
-  return begin;
+    auto c = begin != end ? *begin : Char();
+    if('0' <= c && c <= '9')
+    {
+        auto precision = parse_nonnegative_int(begin, end, -1);
+        if(precision != -1)
+            handler.on_precision(precision);
+        else
+            handler.on_error("number is too big");
+    }
+    else if(c == '{')
+    {
+        ++begin;
+        if(begin != end)
+            begin = parse_arg_id(begin, end, precision_adapter{handler});
+        if(begin == end || *begin++ != '}')
+            return handler.on_error("invalid format string"), begin;
+    }
+    else
+    {
+        return handler.on_error("missing precision specifier"), begin;
+    }
+    handler.end_precision();
+    return begin;
 }
 
 template <typename Char>
-FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type {
-  switch (to_ascii(type)) {
-  case 'd':
-    return presentation_type::dec;
-  case 'o':
-    return presentation_type::oct;
-  case 'x':
-    return presentation_type::hex_lower;
-  case 'X':
-    return presentation_type::hex_upper;
-  case 'b':
-    return presentation_type::bin_lower;
-  case 'B':
-    return presentation_type::bin_upper;
-  case 'a':
-    return presentation_type::hexfloat_lower;
-  case 'A':
-    return presentation_type::hexfloat_upper;
-  case 'e':
-    return presentation_type::exp_lower;
-  case 'E':
-    return presentation_type::exp_upper;
-  case 'f':
-    return presentation_type::fixed_lower;
-  case 'F':
-    return presentation_type::fixed_upper;
-  case 'g':
-    return presentation_type::general_lower;
-  case 'G':
-    return presentation_type::general_upper;
-  case 'c':
-    return presentation_type::chr;
-  case 's':
-    return presentation_type::string;
-  case 'p':
-    return presentation_type::pointer;
-  case '?':
-    return presentation_type::debug;
-  default:
-    return presentation_type::none;
-  }
+FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type
+{
+    switch(to_ascii(type))
+    {
+        case 'd':
+            return presentation_type::dec;
+        case 'o':
+            return presentation_type::oct;
+        case 'x':
+            return presentation_type::hex_lower;
+        case 'X':
+            return presentation_type::hex_upper;
+        case 'b':
+            return presentation_type::bin_lower;
+        case 'B':
+            return presentation_type::bin_upper;
+        case 'a':
+            return presentation_type::hexfloat_lower;
+        case 'A':
+            return presentation_type::hexfloat_upper;
+        case 'e':
+            return presentation_type::exp_lower;
+        case 'E':
+            return presentation_type::exp_upper;
+        case 'f':
+            return presentation_type::fixed_lower;
+        case 'F':
+            return presentation_type::fixed_upper;
+        case 'g':
+            return presentation_type::general_lower;
+        case 'G':
+            return presentation_type::general_upper;
+        case 'c':
+            return presentation_type::chr;
+        case 's':
+            return presentation_type::string;
+        case 'p':
+            return presentation_type::pointer;
+        case '?':
+            return presentation_type::debug;
+        default:
+            return presentation_type::none;
+    }
 }
 
 // Parses standard format specifiers and sends notifications about parsed
 // components to handler.
 template <typename Char, typename SpecHandler>
-FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin,
-                                                 const Char* end,
-                                                 SpecHandler&& handler)
-    -> const Char* {
-  if (1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) &&
-      *begin != 'L') {
-    presentation_type type = parse_presentation_type(*begin++);
-    if (type == presentation_type::none)
-      handler.on_error("invalid type specifier");
-    handler.on_type(type);
-    return begin;
-  }
+FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char *begin, const Char *end, SpecHandler &&handler)
+    -> const Char *
+{
+    if(1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) && *begin != 'L')
+    {
+        presentation_type type = parse_presentation_type(*begin++);
+        if(type == presentation_type::none)
+            handler.on_error("invalid type specifier");
+        handler.on_type(type);
+        return begin;
+    }
 
-  if (begin == end) return begin;
+    if(begin == end)
+        return begin;
+
+    begin = parse_align(begin, end, handler);
+    if(begin == end)
+        return begin;
+
+    // Parse sign.
+    switch(to_ascii(*begin))
+    {
+        case '+':
+            handler.on_sign(sign::plus);
+            ++begin;
+            break;
+        case '-':
+            handler.on_sign(sign::minus);
+            ++begin;
+            break;
+        case ' ':
+            handler.on_sign(sign::space);
+            ++begin;
+            break;
+        default:
+            break;
+    }
+    if(begin == end)
+        return begin;
+
+    if(*begin == '#')
+    {
+        handler.on_hash();
+        if(++begin == end)
+            return begin;
+    }
 
-  begin = parse_align(begin, end, handler);
-  if (begin == end) return begin;
+    // Parse zero flag.
+    if(*begin == '0')
+    {
+        handler.on_zero();
+        if(++begin == end)
+            return begin;
+    }
 
-  // Parse sign.
-  switch (to_ascii(*begin)) {
-  case '+':
-    handler.on_sign(sign::plus);
-    ++begin;
-    break;
-  case '-':
-    handler.on_sign(sign::minus);
-    ++begin;
-    break;
-  case ' ':
-    handler.on_sign(sign::space);
-    ++begin;
-    break;
-  default:
-    break;
-  }
-  if (begin == end) return begin;
-
-  if (*begin == '#') {
-    handler.on_hash();
-    if (++begin == end) return begin;
-  }
-
-  // Parse zero flag.
-  if (*begin == '0') {
-    handler.on_zero();
-    if (++begin == end) return begin;
-  }
-
-  begin = parse_width(begin, end, handler);
-  if (begin == end) return begin;
-
-  // Parse precision.
-  if (*begin == '.') {
-    begin = parse_precision(begin, end, handler);
-    if (begin == end) return begin;
-  }
-
-  if (*begin == 'L') {
-    handler.on_localized();
-    ++begin;
-  }
+    begin = parse_width(begin, end, handler);
+    if(begin == end)
+        return begin;
+
+    // Parse precision.
+    if(*begin == '.')
+    {
+        begin = parse_precision(begin, end, handler);
+        if(begin == end)
+            return begin;
+    }
+
+    if(*begin == 'L')
+    {
+        handler.on_localized();
+        ++begin;
+    }
 
-  // Parse type.
-  if (begin != end && *begin != '}') {
-    presentation_type type = parse_presentation_type(*begin++);
-    if (type == presentation_type::none)
-      handler.on_error("invalid type specifier");
-    handler.on_type(type);
-  }
-  return begin;
+    // Parse type.
+    if(begin != end && *begin != '}')
+    {
+        presentation_type type = parse_presentation_type(*begin++);
+        if(type == presentation_type::none)
+            handler.on_error("invalid type specifier");
+        handler.on_type(type);
+    }
+    return begin;
 }
 
 template <typename Char, typename Handler>
-FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end,
-                                           Handler&& handler) -> const Char* {
-  struct id_adapter {
-    Handler& handler;
-    int arg_id;
-
-    FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); }
-    FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); }
-    FMT_CONSTEXPR void operator()(basic_string_view<Char> id) {
-      arg_id = handler.on_arg_id(id);
-    }
-    FMT_CONSTEXPR void on_error(const char* message) {
-      if (message) handler.on_error(message);
-    }
-  };
-
-  ++begin;
-  if (begin == end) return handler.on_error("invalid format string"), end;
-  if (*begin == '}') {
-    handler.on_replacement_field(handler.on_arg_id(), begin);
-  } else if (*begin == '{') {
-    handler.on_text(begin, begin + 1);
-  } else {
-    auto adapter = id_adapter{handler, 0};
-    begin = parse_arg_id(begin, end, adapter);
-    Char c = begin != end ? *begin : Char();
-    if (c == '}') {
-      handler.on_replacement_field(adapter.arg_id, begin);
-    } else if (c == ':') {
-      begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
-      if (begin == end || *begin != '}')
-        return handler.on_error("unknown format specifier"), end;
-    } else {
-      return handler.on_error("missing '}' in format string"), end;
-    }
-  }
-  return begin + 1;
+FMT_CONSTEXPR auto parse_replacement_field(const Char *begin, const Char *end, Handler &&handler) -> const Char *
+{
+    struct id_adapter
+    {
+        Handler &handler;
+        int      arg_id;
+
+        FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); }
+        FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); }
+        FMT_CONSTEXPR void operator()(basic_string_view<Char> id) { arg_id = handler.on_arg_id(id); }
+        FMT_CONSTEXPR void on_error(const char *message)
+        {
+            if(message)
+                handler.on_error(message);
+        }
+    };
+
+    ++begin;
+    if(begin == end)
+        return handler.on_error("invalid format string"), end;
+    if(*begin == '}')
+    {
+        handler.on_replacement_field(handler.on_arg_id(), begin);
+    }
+    else if(*begin == '{')
+    {
+        handler.on_text(begin, begin + 1);
+    }
+    else
+    {
+        auto adapter = id_adapter{handler, 0};
+        begin        = parse_arg_id(begin, end, adapter);
+        Char c       = begin != end ? *begin : Char();
+        if(c == '}')
+        {
+            handler.on_replacement_field(adapter.arg_id, begin);
+        }
+        else if(c == ':')
+        {
+            begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
+            if(begin == end || *begin != '}')
+                return handler.on_error("unknown format specifier"), end;
+        }
+        else
+        {
+            return handler.on_error("missing '}' in format string"), end;
+        }
+    }
+    return begin + 1;
 }
 
 template <bool IS_CONSTEXPR, typename Char, typename Handler>
-FMT_CONSTEXPR FMT_INLINE void parse_format_string(
-    basic_string_view<Char> format_str, Handler&& handler) {
-  // Workaround a name-lookup bug in MSVC's modules implementation.
-  using detail::find;
-
-  auto begin = format_str.data();
-  auto end = begin + format_str.size();
-  if (end - begin < 32) {
-    // Use a simple loop instead of memchr for small strings.
-    const Char* p = begin;
-    while (p != end) {
-      auto c = *p++;
-      if (c == '{') {
-        handler.on_text(begin, p - 1);
-        begin = p = parse_replacement_field(p - 1, end, handler);
-      } else if (c == '}') {
-        if (p == end || *p != '}')
-          return handler.on_error("unmatched '}' in format string");
-        handler.on_text(begin, p);
-        begin = ++p;
-      }
-    }
-    handler.on_text(begin, end);
-    return;
-  }
-  struct writer {
-    FMT_CONSTEXPR void operator()(const Char* from, const Char* to) {
-      if (from == to) return;
-      for (;;) {
-        const Char* p = nullptr;
-        if (!find<IS_CONSTEXPR>(from, to, Char('}'), p))
-          return handler_.on_text(from, to);
-        ++p;
-        if (p == to || *p != '}')
-          return handler_.on_error("unmatched '}' in format string");
-        handler_.on_text(from, p);
-        from = p + 1;
-      }
+FMT_CONSTEXPR FMT_INLINE void parse_format_string(basic_string_view<Char> format_str, Handler &&handler)
+{
+    // Workaround a name-lookup bug in MSVC's modules implementation.
+    using detail::find;
+
+    auto begin = format_str.data();
+    auto end   = begin + format_str.size();
+    if(end - begin < 32)
+    {
+        // Use a simple loop instead of memchr for small strings.
+        const Char *p = begin;
+        while(p != end)
+        {
+            auto c = *p++;
+            if(c == '{')
+            {
+                handler.on_text(begin, p - 1);
+                begin = p = parse_replacement_field(p - 1, end, handler);
+            }
+            else if(c == '}')
+            {
+                if(p == end || *p != '}')
+                    return handler.on_error("unmatched '}' in format string");
+                handler.on_text(begin, p);
+                begin = ++p;
+            }
+        }
+        handler.on_text(begin, end);
+        return;
+    }
+    struct writer
+    {
+        FMT_CONSTEXPR void operator()(const Char *from, const Char *to)
+        {
+            if(from == to)
+                return;
+            for(;;)
+            {
+                const Char *p = nullptr;
+                if(!find<IS_CONSTEXPR>(from, to, Char('}'), p))
+                    return handler_.on_text(from, to);
+                ++p;
+                if(p == to || *p != '}')
+                    return handler_.on_error("unmatched '}' in format string");
+                handler_.on_text(from, p);
+                from = p + 1;
+            }
+        }
+        Handler &handler_;
+    } write = {handler};
+    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.
+        const Char *p = begin;
+        if(*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, Char('{'), p))
+            return write(begin, end);
+        write(begin, p);
+        begin = parse_replacement_field(p, end, handler);
     }
-    Handler& handler_;
-  } write = {handler};
-  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.
-    const Char* p = begin;
-    if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, Char('{'), p))
-      return write(begin, end);
-    write(begin, p);
-    begin = parse_replacement_field(p, end, handler);
-  }
 }
 
-template <typename T, bool = is_named_arg<T>::value> struct strip_named_arg {
-  using type = T;
+template <typename T, bool = is_named_arg<T>::value>
+struct strip_named_arg
+{
+    using type = T;
 };
-template <typename T> struct strip_named_arg<T, true> {
-  using type = remove_cvref_t<decltype(T::value)>;
+template <typename T>
+struct strip_named_arg<T, true>
+{
+    using type = remove_cvref_t<decltype(T::value)>;
 };
 
 template <typename T, typename ParseContext>
-FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
-    -> decltype(ctx.begin()) {
-  using char_type = typename ParseContext::char_type;
-  using context = buffer_context<char_type>;
-  using stripped_type = typename strip_named_arg<T>::type;
-  using mapped_type = conditional_t<
-      mapped_type_constant<T, context>::value != type::custom_type,
-      decltype(arg_mapper<context>().map(std::declval<const T&>())),
-      stripped_type>;
-  auto f = conditional_t<has_formatter<mapped_type, context>::value,
-                         formatter<mapped_type, char_type>,
-                         fallback_formatter<stripped_type, char_type>>();
-  return f.parse(ctx);
+FMT_CONSTEXPR auto parse_format_specs(ParseContext &ctx) -> decltype(ctx.begin())
+{
+    using char_type     = typename ParseContext::char_type;
+    using context       = buffer_context<char_type>;
+    using stripped_type = typename strip_named_arg<T>::type;
+    using mapped_type   = conditional_t<
+        mapped_type_constant<T, context>::value != type::custom_type,
+        decltype(arg_mapper<context>().map(std::declval<const T &>())),
+        stripped_type>;
+    auto f = conditional_t<
+        has_formatter<mapped_type, context>::value,
+        formatter<mapped_type, char_type>,
+        fallback_formatter<stripped_type, char_type>>();
+    return f.parse(ctx);
 }
 
 template <typename ErrorHandler>
-FMT_CONSTEXPR void check_int_type_spec(presentation_type type,
-                                       ErrorHandler&& eh) {
-  if (type > presentation_type::bin_upper && type != presentation_type::chr)
-    eh.on_error("invalid type specifier");
+FMT_CONSTEXPR void check_int_type_spec(presentation_type type, ErrorHandler &&eh)
+{
+    if(type > presentation_type::bin_upper && type != presentation_type::chr)
+        eh.on_error("invalid type specifier");
 }
 
 // Checks char specs and returns true if the type spec is char (and not int).
 template <typename Char, typename ErrorHandler = error_handler>
-FMT_CONSTEXPR auto check_char_specs(const basic_format_specs<Char>& specs,
-                                    ErrorHandler&& eh = {}) -> bool {
-  if (specs.type != presentation_type::none &&
-      specs.type != presentation_type::chr &&
-      specs.type != presentation_type::debug) {
-    check_int_type_spec(specs.type, eh);
-    return false;
-  }
-  if (specs.align == align::numeric || specs.sign != sign::none || specs.alt)
-    eh.on_error("invalid format specifier for char");
-  return true;
+FMT_CONSTEXPR auto check_char_specs(const basic_format_specs<Char> &specs, ErrorHandler &&eh = {}) -> bool
+{
+    if(specs.type != presentation_type::none && specs.type != presentation_type::chr &&
+       specs.type != presentation_type::debug)
+    {
+        check_int_type_spec(specs.type, eh);
+        return false;
+    }
+    if(specs.align == align::numeric || specs.sign != sign::none || specs.alt)
+        eh.on_error("invalid format specifier for char");
+    return true;
 }
 
 // A floating-point presentation format.
-enum class float_format : unsigned char {
-  general,  // General: exponent notation or fixed point based on magnitude.
-  exp,      // Exponent notation with the default precision of 6, e.g. 1.2e-3.
-  fixed,    // Fixed point with the default precision of 6, e.g. 0.0012.
-  hex
+enum class float_format : unsigned char
+{
+    general, // General: exponent notation or fixed point based on magnitude.
+    exp,     // Exponent notation with the default precision of 6, e.g. 1.2e-3.
+    fixed,   // Fixed point with the default precision of 6, e.g. 0.0012.
+    hex
 };
 
-struct float_specs {
-  int precision;
-  float_format format : 8;
-  sign_t sign : 8;
-  bool upper : 1;
-  bool locale : 1;
-  bool binary32 : 1;
-  bool showpoint : 1;
+struct float_specs
+{
+    int          precision;
+    float_format format : 8;
+    sign_t       sign : 8;
+    bool         upper : 1;
+    bool         locale : 1;
+    bool         binary32 : 1;
+    bool         showpoint : 1;
 };
 
 template <typename ErrorHandler = error_handler, typename Char>
-FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs<Char>& specs,
-                                         ErrorHandler&& eh = {})
-    -> float_specs {
-  auto result = float_specs();
-  result.showpoint = specs.alt;
-  result.locale = specs.localized;
-  switch (specs.type) {
-  case presentation_type::none:
-    result.format = float_format::general;
-    break;
-  case presentation_type::general_upper:
-    result.upper = true;
-    FMT_FALLTHROUGH;
-  case presentation_type::general_lower:
-    result.format = float_format::general;
-    break;
-  case presentation_type::exp_upper:
-    result.upper = true;
-    FMT_FALLTHROUGH;
-  case presentation_type::exp_lower:
-    result.format = float_format::exp;
-    result.showpoint |= specs.precision != 0;
-    break;
-  case presentation_type::fixed_upper:
-    result.upper = true;
-    FMT_FALLTHROUGH;
-  case presentation_type::fixed_lower:
-    result.format = float_format::fixed;
-    result.showpoint |= specs.precision != 0;
-    break;
-  case presentation_type::hexfloat_upper:
-    result.upper = true;
-    FMT_FALLTHROUGH;
-  case presentation_type::hexfloat_lower:
-    result.format = float_format::hex;
-    break;
-  default:
-    eh.on_error("invalid type specifier");
-    break;
-  }
-  return result;
+FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs<Char> &specs, ErrorHandler &&eh = {}) -> float_specs
+{
+    auto result      = float_specs();
+    result.showpoint = specs.alt;
+    result.locale    = specs.localized;
+    switch(specs.type)
+    {
+        case presentation_type::none:
+            result.format = float_format::general;
+            break;
+        case presentation_type::general_upper:
+            result.upper = true;
+            FMT_FALLTHROUGH;
+        case presentation_type::general_lower:
+            result.format = float_format::general;
+            break;
+        case presentation_type::exp_upper:
+            result.upper = true;
+            FMT_FALLTHROUGH;
+        case presentation_type::exp_lower:
+            result.format = float_format::exp;
+            result.showpoint |= specs.precision != 0;
+            break;
+        case presentation_type::fixed_upper:
+            result.upper = true;
+            FMT_FALLTHROUGH;
+        case presentation_type::fixed_lower:
+            result.format = float_format::fixed;
+            result.showpoint |= specs.precision != 0;
+            break;
+        case presentation_type::hexfloat_upper:
+            result.upper = true;
+            FMT_FALLTHROUGH;
+        case presentation_type::hexfloat_lower:
+            result.format = float_format::hex;
+            break;
+        default:
+            eh.on_error("invalid type specifier");
+            break;
+    }
+    return result;
 }
 
 template <typename ErrorHandler = error_handler>
-FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type,
-                                           ErrorHandler&& eh = {}) -> bool {
-  if (type == presentation_type::none || type == presentation_type::string ||
-      type == presentation_type::debug)
-    return true;
-  if (type != presentation_type::pointer) eh.on_error("invalid type specifier");
-  return false;
+FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type, ErrorHandler &&eh = {}) -> bool
+{
+    if(type == presentation_type::none || type == presentation_type::string || type == presentation_type::debug)
+        return true;
+    if(type != presentation_type::pointer)
+        eh.on_error("invalid type specifier");
+    return false;
 }
 
 template <typename ErrorHandler = error_handler>
-FMT_CONSTEXPR void check_string_type_spec(presentation_type type,
-                                          ErrorHandler&& eh = {}) {
-  if (type != presentation_type::none && type != presentation_type::string &&
-      type != presentation_type::debug)
-    eh.on_error("invalid type specifier");
+FMT_CONSTEXPR void check_string_type_spec(presentation_type type, ErrorHandler &&eh = {})
+{
+    if(type != presentation_type::none && type != presentation_type::string && type != presentation_type::debug)
+        eh.on_error("invalid type specifier");
 }
 
 template <typename ErrorHandler>
-FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type,
-                                           ErrorHandler&& eh) {
-  if (type != presentation_type::none && type != presentation_type::pointer)
-    eh.on_error("invalid type specifier");
+FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type, ErrorHandler &&eh)
+{
+    if(type != presentation_type::none && type != presentation_type::pointer)
+        eh.on_error("invalid type specifier");
 }
 
 // A parse_format_specs handler that checks if specifiers are consistent with
 // the argument type.
-template <typename Handler> class specs_checker : public Handler {
- private:
-  detail::type arg_type_;
-
-  FMT_CONSTEXPR void require_numeric_argument() {
-    if (!is_arithmetic_type(arg_type_))
-      this->on_error("format specifier requires numeric argument");
-  }
-
- public:
-  FMT_CONSTEXPR specs_checker(const Handler& handler, detail::type arg_type)
-      : Handler(handler), arg_type_(arg_type) {}
-
-  FMT_CONSTEXPR void on_align(align_t align) {
-    if (align == align::numeric) require_numeric_argument();
-    Handler::on_align(align);
-  }
-
-  FMT_CONSTEXPR void on_sign(sign_t s) {
-    require_numeric_argument();
-    if (is_integral_type(arg_type_) && arg_type_ != type::int_type &&
-        arg_type_ != type::long_long_type && arg_type_ != type::int128_type &&
-        arg_type_ != type::char_type) {
-      this->on_error("format specifier requires signed argument");
-    }
-    Handler::on_sign(s);
-  }
-
-  FMT_CONSTEXPR void on_hash() {
-    require_numeric_argument();
-    Handler::on_hash();
-  }
-
-  FMT_CONSTEXPR void on_localized() {
-    require_numeric_argument();
-    Handler::on_localized();
-  }
-
-  FMT_CONSTEXPR void on_zero() {
-    require_numeric_argument();
-    Handler::on_zero();
-  }
-
-  FMT_CONSTEXPR void end_precision() {
-    if (is_integral_type(arg_type_) || arg_type_ == type::pointer_type)
-      this->on_error("precision not allowed for this argument type");
-  }
+template <typename Handler>
+class specs_checker : public Handler
+{
+private:
+    detail::type arg_type_;
+
+    FMT_CONSTEXPR void require_numeric_argument()
+    {
+        if(!is_arithmetic_type(arg_type_))
+            this->on_error("format specifier requires numeric argument");
+    }
+
+public:
+    FMT_CONSTEXPR specs_checker(const Handler &handler, detail::type arg_type) : Handler(handler), arg_type_(arg_type)
+    {
+    }
+
+    FMT_CONSTEXPR void on_align(align_t align)
+    {
+        if(align == align::numeric)
+            require_numeric_argument();
+        Handler::on_align(align);
+    }
+
+    FMT_CONSTEXPR void on_sign(sign_t s)
+    {
+        require_numeric_argument();
+        if(is_integral_type(arg_type_) && arg_type_ != type::int_type && arg_type_ != type::long_long_type &&
+           arg_type_ != type::int128_type && arg_type_ != type::char_type)
+        {
+            this->on_error("format specifier requires signed argument");
+        }
+        Handler::on_sign(s);
+    }
+
+    FMT_CONSTEXPR void on_hash()
+    {
+        require_numeric_argument();
+        Handler::on_hash();
+    }
+
+    FMT_CONSTEXPR void on_localized()
+    {
+        require_numeric_argument();
+        Handler::on_localized();
+    }
+
+    FMT_CONSTEXPR void on_zero()
+    {
+        require_numeric_argument();
+        Handler::on_zero();
+    }
+
+    FMT_CONSTEXPR void end_precision()
+    {
+        if(is_integral_type(arg_type_) || arg_type_ == type::pointer_type)
+            this->on_error("precision not allowed for this argument type");
+    }
 };
 
 constexpr int invalid_arg_index = -1;
 
 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
 template <int N, typename T, typename... Args, typename Char>
-constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
-  if constexpr (detail::is_statically_named_arg<T>()) {
-    if (name == T::name) return N;
-  }
-  if constexpr (sizeof...(Args) > 0)
-    return get_arg_index_by_name<N + 1, Args...>(name);
-  (void)name;  // Workaround an MSVC bug about "unused" parameter.
-  return invalid_arg_index;
+constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int
+{
+    if constexpr(detail::is_statically_named_arg<T>())
+    {
+        if(name == T::name)
+            return N;
+    }
+    if constexpr(sizeof...(Args) > 0)
+        return get_arg_index_by_name<N + 1, Args...>(name);
+    (void) name; // Workaround an MSVC bug about "unused" parameter.
+    return invalid_arg_index;
 }
 #endif
 
 template <typename... Args, typename Char>
-FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
+FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int
+{
 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
-  if constexpr (sizeof...(Args) > 0)
-    return get_arg_index_by_name<0, Args...>(name);
+    if constexpr(sizeof...(Args) > 0)
+        return get_arg_index_by_name<0, Args...>(name);
 #endif
-  (void)name;
-  return invalid_arg_index;
+    (void) name;
+    return invalid_arg_index;
 }
 
 template <typename Char, typename ErrorHandler, typename... Args>
-class format_string_checker {
- private:
-  // In the future basic_format_parse_context will replace compile_parse_context
-  // here and will use is_constant_evaluated and downcasting to access the data
-  // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1.
-  using parse_context_type = compile_parse_context<Char, ErrorHandler>;
-  static constexpr int num_args = sizeof...(Args);
-
-  // Format specifier parsing function.
-  using parse_func = const Char* (*)(parse_context_type&);
-
-  parse_context_type context_;
-  parse_func parse_funcs_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
-  type types_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
-
- public:
-  explicit FMT_CONSTEXPR format_string_checker(
-      basic_string_view<Char> format_str, ErrorHandler eh)
-      : context_(format_str, num_args, types_, eh),
+class format_string_checker
+{
+private:
+    // In the future basic_format_parse_context will replace compile_parse_context
+    // here and will use is_constant_evaluated and downcasting to access the data
+    // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1.
+    using parse_context_type      = compile_parse_context<Char, ErrorHandler>;
+    static constexpr int num_args = sizeof...(Args);
+
+    // Format specifier parsing function.
+    using parse_func = const Char *(*) (parse_context_type &);
+
+    parse_context_type context_;
+    parse_func         parse_funcs_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
+    type               types_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
+
+public:
+    explicit FMT_CONSTEXPR format_string_checker(basic_string_view<Char> format_str, ErrorHandler eh) :
+        context_(format_str, num_args, types_, eh),
         parse_funcs_{&parse_format_specs<Args, parse_context_type>...},
-        types_{
-            mapped_type_constant<Args,
-                                 basic_format_context<Char*, Char>>::value...} {
-  }
-
-  FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
-
-  FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); }
-  FMT_CONSTEXPR auto on_arg_id(int id) -> int {
-    return context_.check_arg_id(id), id;
-  }
-  FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
+        types_{mapped_type_constant<Args, basic_format_context<Char *, Char>>::value...}
+    {
+    }
+
+    FMT_CONSTEXPR void on_text(const Char *, const Char *) {}
+
+    FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); }
+    FMT_CONSTEXPR auto on_arg_id(int id) -> int { return context_.check_arg_id(id), id; }
+    FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int
+    {
 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
-    auto index = get_arg_index_by_name<Args...>(id);
-    if (index == invalid_arg_index) on_error("named argument is not found");
-    return context_.check_arg_id(index), index;
+        auto index = get_arg_index_by_name<Args...>(id);
+        if(index == invalid_arg_index)
+            on_error("named argument is not found");
+        return context_.check_arg_id(index), index;
 #else
-    (void)id;
-    on_error("compile-time checks for named arguments require C++20 support");
-    return 0;
+        (void) id;
+        on_error("compile-time checks for named arguments require C++20 support");
+        return 0;
 #endif
-  }
+    }
 
-  FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
+    FMT_CONSTEXPR void on_replacement_field(int, const Char *) {}
 
-  FMT_CONSTEXPR auto on_format_specs(int id, const Char* begin, const Char*)
-      -> const Char* {
-    context_.advance_to(context_.begin() + (begin - &*context_.begin()));
-    // id >= 0 check is a workaround for gcc 10 bug (#2065).
-    return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin;
-  }
+    FMT_CONSTEXPR auto on_format_specs(int id, const Char *begin, const Char *) -> const Char *
+    {
+        context_.advance_to(context_.begin() + (begin - &*context_.begin()));
+        // id >= 0 check is a workaround for gcc 10 bug (#2065).
+        return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin;
+    }
 
-  FMT_CONSTEXPR void on_error(const char* message) {
-    context_.on_error(message);
-  }
+    FMT_CONSTEXPR void on_error(const char *message)
+    {
+        context_.on_error(message);
+    }
 };
 
 // Reports a compile-time error if S is not a valid format string.
 template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
-FMT_INLINE void check_format_string(const S&) {
+FMT_INLINE void check_format_string(const S &)
+{
 #ifdef FMT_ENFORCE_COMPILE_STRING
-  static_assert(is_compile_string<S>::value,
-                "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
-                "FMT_STRING.");
+    static_assert(
+        is_compile_string<S>::value,
+        "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
+        "FMT_STRING.");
 #endif
 }
-template <typename... Args, typename S,
-          FMT_ENABLE_IF(is_compile_string<S>::value)>
-void check_format_string(S format_str) {
-  FMT_CONSTEXPR auto s = basic_string_view<typename S::char_type>(format_str);
-  using checker = format_string_checker<typename S::char_type, error_handler,
-                                        remove_cvref_t<Args>...>;
-  FMT_CONSTEXPR bool invalid_format =
-      (parse_format_string<true>(s, checker(s, {})), true);
-  ignore_unused(invalid_format);
+template <typename... Args, typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
+void check_format_string(S format_str)
+{
+    FMT_CONSTEXPR auto s = basic_string_view<typename S::char_type>(format_str);
+    using checker        = format_string_checker<typename S::char_type, error_handler, remove_cvref_t<Args>...>;
+    FMT_CONSTEXPR bool invalid_format = (parse_format_string<true>(s, checker(s, {})), true);
+    ignore_unused(invalid_format);
 }
 
 template <typename Char>
 void vformat_to(
-    buffer<Char>& buf, basic_string_view<Char> fmt,
+    buffer<Char>                                                &buf,
+    basic_string_view<Char>                                      fmt,
     basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
-    locale_ref loc = {});
+    locale_ref                                                   loc = {});
 
-FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
+FMT_API void vprint_mojibake(std::FILE *, string_view, format_args);
 #ifndef _WIN32
-inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
+inline void vprint_mojibake(std::FILE *, string_view, format_args) {}
 #endif
 FMT_END_DETAIL_NAMESPACE
 
 // A formatter specialization for the core types corresponding to detail::type
 // constants.
 template <typename T, typename Char>
-struct formatter<T, Char,
-                 enable_if_t<detail::type_constant<T, Char>::value !=
-                             detail::type::custom_type>> {
- private:
-  detail::dynamic_format_specs<Char> specs_;
-
- public:
-  // Parses format specifiers stopping either at the end of the range or at the
-  // terminating '}'.
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    auto begin = ctx.begin(), end = ctx.end();
-    if (begin == end) return begin;
-    using handler_type = detail::dynamic_specs_handler<ParseContext>;
-    auto type = detail::type_constant<T, Char>::value;
-    auto checker =
-        detail::specs_checker<handler_type>(handler_type(specs_, ctx), type);
-    auto it = detail::parse_format_specs(begin, end, checker);
-    auto eh = ctx.error_handler();
-    switch (type) {
-    case detail::type::none_type:
-      FMT_ASSERT(false, "invalid argument type");
-      break;
-    case detail::type::bool_type:
-      if (specs_.type == presentation_type::none ||
-          specs_.type == presentation_type::string) {
-        break;
-      }
-      FMT_FALLTHROUGH;
-    case detail::type::int_type:
-    case detail::type::uint_type:
-    case detail::type::long_long_type:
-    case detail::type::ulong_long_type:
-    case detail::type::int128_type:
-    case detail::type::uint128_type:
-      detail::check_int_type_spec(specs_.type, eh);
-      break;
-    case detail::type::char_type:
-      detail::check_char_specs(specs_, eh);
-      break;
-    case detail::type::float_type:
-      if (detail::const_check(FMT_USE_FLOAT))
-        detail::parse_float_type_spec(specs_, eh);
-      else
-        FMT_ASSERT(false, "float support disabled");
-      break;
-    case detail::type::double_type:
-      if (detail::const_check(FMT_USE_DOUBLE))
-        detail::parse_float_type_spec(specs_, eh);
-      else
-        FMT_ASSERT(false, "double support disabled");
-      break;
-    case detail::type::long_double_type:
-      if (detail::const_check(FMT_USE_LONG_DOUBLE))
-        detail::parse_float_type_spec(specs_, eh);
-      else
-        FMT_ASSERT(false, "long double support disabled");
-      break;
-    case detail::type::cstring_type:
-      detail::check_cstring_type_spec(specs_.type, eh);
-      break;
-    case detail::type::string_type:
-      detail::check_string_type_spec(specs_.type, eh);
-      break;
-    case detail::type::pointer_type:
-      detail::check_pointer_type_spec(specs_.type, eh);
-      break;
-    case detail::type::custom_type:
-      // Custom format specifiers are checked in parse functions of
-      // formatter specializations.
-      break;
+struct formatter<T, Char, enable_if_t<detail::type_constant<T, Char>::value != detail::type::custom_type>>
+{
+private:
+    detail::dynamic_format_specs<Char> specs_;
+
+public:
+    // Parses format specifiers stopping either at the end of the range or at the
+    // terminating '}'.
+    template <typename ParseContext>
+    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
+    {
+        auto begin = ctx.begin(), end = ctx.end();
+        if(begin == end)
+            return begin;
+        using handler_type = detail::dynamic_specs_handler<ParseContext>;
+        auto type          = detail::type_constant<T, Char>::value;
+        auto checker       = detail::specs_checker<handler_type>(handler_type(specs_, ctx), type);
+        auto it            = detail::parse_format_specs(begin, end, checker);
+        auto eh            = ctx.error_handler();
+        switch(type)
+        {
+            case detail::type::none_type:
+                FMT_ASSERT(false, "invalid argument type");
+                break;
+            case detail::type::bool_type:
+                if(specs_.type == presentation_type::none || specs_.type == presentation_type::string)
+                {
+                    break;
+                }
+                FMT_FALLTHROUGH;
+            case detail::type::int_type:
+            case detail::type::uint_type:
+            case detail::type::long_long_type:
+            case detail::type::ulong_long_type:
+            case detail::type::int128_type:
+            case detail::type::uint128_type:
+                detail::check_int_type_spec(specs_.type, eh);
+                break;
+            case detail::type::char_type:
+                detail::check_char_specs(specs_, eh);
+                break;
+            case detail::type::float_type:
+                if(detail::const_check(FMT_USE_FLOAT))
+                    detail::parse_float_type_spec(specs_, eh);
+                else
+                    FMT_ASSERT(false, "float support disabled");
+                break;
+            case detail::type::double_type:
+                if(detail::const_check(FMT_USE_DOUBLE))
+                    detail::parse_float_type_spec(specs_, eh);
+                else
+                    FMT_ASSERT(false, "double support disabled");
+                break;
+            case detail::type::long_double_type:
+                if(detail::const_check(FMT_USE_LONG_DOUBLE))
+                    detail::parse_float_type_spec(specs_, eh);
+                else
+                    FMT_ASSERT(false, "long double support disabled");
+                break;
+            case detail::type::cstring_type:
+                detail::check_cstring_type_spec(specs_.type, eh);
+                break;
+            case detail::type::string_type:
+                detail::check_string_type_spec(specs_.type, eh);
+                break;
+            case detail::type::pointer_type:
+                detail::check_pointer_type_spec(specs_.type, eh);
+                break;
+            case detail::type::custom_type:
+                // Custom format specifiers are checked in parse functions of
+                // formatter specializations.
+                break;
+        }
+        return it;
+    }
+
+    template <
+        detail::type U = detail::type_constant<T, Char>::value,
+        enable_if_t<
+            (U == detail::type::string_type || U == detail::type::cstring_type || U == detail::type::char_type),
+            int> = 0>
+    FMT_CONSTEXPR void set_debug_format()
+    {
+        specs_.type = presentation_type::debug;
+    }
+
+    template <typename FormatContext>
+    FMT_CONSTEXPR auto format(const T &val, FormatContext &ctx) const -> decltype(ctx.out());
+};
+
+#define FMT_FORMAT_AS(Type, Base)                                                                                      \
+    template <typename Char>                                                                                           \
+    struct formatter<Type, Char> : formatter<Base, Char>                                                               \
+    {                                                                                                                  \
+        template <typename FormatContext>                                                                              \
+        auto format(Type const &val, FormatContext &ctx) const -> decltype(ctx.out())                                  \
+        {                                                                                                              \
+            return formatter<Base, Char>::format(static_cast<Base>(val), ctx);                                         \
+        }                                                                                                              \
     }
-    return it;
-  }
-
-  template <detail::type U = detail::type_constant<T, Char>::value,
-            enable_if_t<(U == detail::type::string_type ||
-                         U == detail::type::cstring_type ||
-                         U == detail::type::char_type),
-                        int> = 0>
-  FMT_CONSTEXPR void set_debug_format() {
-    specs_.type = presentation_type::debug;
-  }
-
-  template <typename FormatContext>
-  FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
-      -> decltype(ctx.out());
-};
-
-#define FMT_FORMAT_AS(Type, Base)                                        \
-  template <typename Char>                                               \
-  struct formatter<Type, Char> : formatter<Base, Char> {                 \
-    template <typename FormatContext>                                    \
-    auto format(Type const& val, FormatContext& ctx) const               \
-        -> decltype(ctx.out()) {                                         \
-      return formatter<Base, Char>::format(static_cast<Base>(val), ctx); \
-    }                                                                    \
-  }
 
 FMT_FORMAT_AS(signed char, int);
 FMT_FORMAT_AS(unsigned char, unsigned);
@@ -3129,48 +3477,59 @@ FMT_FORMAT_AS(short, int);
 FMT_FORMAT_AS(unsigned short, unsigned);
 FMT_FORMAT_AS(long, long long);
 FMT_FORMAT_AS(unsigned long, unsigned long long);
-FMT_FORMAT_AS(Char*, const Char*);
+FMT_FORMAT_AS(Char *, const Char *);
 FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
-FMT_FORMAT_AS(std::nullptr_t, const void*);
+FMT_FORMAT_AS(std::nullptr_t, const void *);
 FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
 
-template <typename Char> struct basic_runtime { basic_string_view<Char> str; };
+template <typename Char>
+struct basic_runtime
+{
+    basic_string_view<Char> str;
+};
 
 /** A compile-time format string. */
-template <typename Char, typename... Args> class basic_format_string {
- private:
-  basic_string_view<Char> str_;
-
- public:
-  template <typename S,
-            FMT_ENABLE_IF(
-                std::is_convertible<const S&, basic_string_view<Char>>::value)>
-  FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) {
-    static_assert(
-        detail::count<
-            (std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
-             std::is_reference<Args>::value)...>() == 0,
-        "passing views as lvalues is disallowed");
+template <typename Char, typename... Args>
+class basic_format_string
+{
+private:
+    basic_string_view<Char> str_;
+
+public:
+    template <typename S, FMT_ENABLE_IF(std::is_convertible<const S &, basic_string_view<Char>>::value)>
+    FMT_CONSTEVAL FMT_INLINE basic_format_string(const S &s) : str_(s)
+    {
+        static_assert(
+            detail::count<(
+                    std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
+                    std::is_reference<Args>::value)...>() == 0,
+            "passing views as lvalues is disallowed");
 #ifdef FMT_HAS_CONSTEVAL
-    if constexpr (detail::count_named_args<Args...>() ==
-                  detail::count_statically_named_args<Args...>()) {
-      using checker = detail::format_string_checker<Char, detail::error_handler,
-                                                    remove_cvref_t<Args>...>;
-      detail::parse_format_string<true>(str_, checker(s, {}));
-    }
+        if constexpr(detail::count_named_args<Args...>() == detail::count_statically_named_args<Args...>())
+        {
+            using checker = detail::format_string_checker<Char, detail::error_handler, remove_cvref_t<Args>...>;
+            detail::parse_format_string<true>(str_, checker(s, {}));
+        }
 #else
-    detail::check_format_string<Args...>(s);
+        detail::check_format_string<Args...>(s);
 #endif
-  }
-  basic_format_string(basic_runtime<Char> r) : str_(r.str) {}
+    }
+    basic_format_string(basic_runtime<Char> r) : str_(r.str) {}
 
-  FMT_INLINE operator basic_string_view<Char>() const { return str_; }
+    FMT_INLINE operator basic_string_view<Char>() const
+    {
+        return str_;
+    }
 };
 
 #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
 // Workaround broken conversion on older gcc.
-template <typename...> using format_string = string_view;
-inline auto runtime(string_view s) -> string_view { return s; }
+template <typename...>
+using format_string = string_view;
+inline auto runtime(string_view s) -> string_view
+{
+    return s;
+}
 #else
 template <typename... Args>
 using format_string = basic_format_string<char, type_identity_t<Args>...>;
@@ -3184,7 +3543,10 @@ using format_string = basic_format_string<char, type_identity_t<Args>...>;
     fmt::print(fmt::runtime("{:d}"), "I am not a number");
   \endrst
  */
-inline auto runtime(string_view s) -> basic_runtime<char> { return {{s}}; }
+inline auto runtime(string_view s) -> basic_runtime<char>
+{
+    return {{s}};
+}
 #endif
 
 FMT_API auto vformat(string_view fmt, format_args args) -> std::string;
@@ -3201,19 +3563,19 @@ FMT_API auto vformat(string_view fmt, format_args args) -> std::string;
   \endrst
 */
 template <typename... T>
-FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T&&... args)
-    -> std::string {
-  return vformat(fmt, fmt::make_format_args(args...));
+FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T &&...args) -> std::string
+{
+    return vformat(fmt, fmt::make_format_args(args...));
 }
 
 /** Formats a string and writes the output to ``out``. */
-template <typename OutputIt,
-          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
-auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt {
-  using detail::get_buffer;
-  auto&& buf = get_buffer<char>(out);
-  detail::vformat_to(buf, fmt, args, {});
-  return detail::get_iterator(buf);
+template <typename OutputIt, FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt
+{
+    using detail::get_buffer;
+    auto &&buf = get_buffer<char>(out);
+    detail::vformat_to(buf, fmt, args, {});
+    return detail::get_iterator(buf);
 }
 
 /**
@@ -3228,28 +3590,28 @@ auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt {
    fmt::format_to(std::back_inserter(out), "{}", 42);
  \endrst
  */
-template <typename OutputIt, typename... T,
-          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
-FMT_INLINE auto format_to(OutputIt out, format_string<T...> fmt, T&&... args)
-    -> OutputIt {
-  return vformat_to(out, fmt, fmt::make_format_args(args...));
+template <typename OutputIt, typename... T, FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+FMT_INLINE auto format_to(OutputIt out, format_string<T...> fmt, T &&...args) -> OutputIt
+{
+    return vformat_to(out, fmt, fmt::make_format_args(args...));
 }
 
-template <typename OutputIt> struct format_to_n_result {
-  /** Iterator past the end of the output range. */
-  OutputIt out;
-  /** Total (not truncated) output size. */
-  size_t size;
+template <typename OutputIt>
+struct format_to_n_result
+{
+    /** Iterator past the end of the output range. */
+    OutputIt out;
+    /** Total (not truncated) output size. */
+    size_t size;
 };
 
-template <typename OutputIt, typename... T,
-          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
-auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args)
-    -> format_to_n_result<OutputIt> {
-  using traits = detail::fixed_buffer_traits;
-  auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
-  detail::vformat_to(buf, fmt, args, {});
-  return {buf.out(), buf.count()};
+template <typename OutputIt, typename... T, FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) -> format_to_n_result<OutputIt>
+{
+    using traits = detail::fixed_buffer_traits;
+    auto buf     = detail::iterator_buffer<OutputIt, char, traits>(out, n);
+    detail::vformat_to(buf, fmt, args, {});
+    return {buf.out(), buf.count()};
 }
 
 /**
@@ -3260,24 +3622,24 @@ auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args)
   `format_to_n` does not append a terminating null character.
   \endrst
  */
-template <typename OutputIt, typename... T,
-          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
-FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt,
-                            T&&... args) -> format_to_n_result<OutputIt> {
-  return vformat_to_n(out, n, fmt, fmt::make_format_args(args...));
+template <typename OutputIt, typename... T, FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
+FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt, T &&...args)
+    -> format_to_n_result<OutputIt>
+{
+    return vformat_to_n(out, n, fmt, fmt::make_format_args(args...));
 }
 
 /** Returns the number of chars in the output of ``format(fmt, args...)``. */
 template <typename... T>
-FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt,
-                                             T&&... args) -> size_t {
-  auto buf = detail::counting_buffer<>();
-  detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...), {});
-  return buf.count();
+FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt, T &&...args) -> size_t
+{
+    auto buf = detail::counting_buffer<>();
+    detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...), {});
+    return buf.count();
 }
 
 FMT_API void vprint(string_view fmt, format_args args);
-FMT_API void vprint(std::FILE* f, string_view fmt, format_args args);
+FMT_API void vprint(std::FILE *f, string_view fmt, format_args args);
 
 /**
   \rst
@@ -3290,10 +3652,10 @@ FMT_API void vprint(std::FILE* f, string_view fmt, format_args args);
   \endrst
  */
 template <typename... T>
-FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
-  const auto& vargs = fmt::make_format_args(args...);
-  return detail::is_utf8() ? vprint(fmt, vargs)
-                           : detail::vprint_mojibake(stdout, fmt, vargs);
+FMT_INLINE void print(format_string<T...> fmt, T &&...args)
+{
+    const auto &vargs = fmt::make_format_args(args...);
+    return detail::is_utf8() ? vprint(fmt, vargs) : detail::vprint_mojibake(stdout, fmt, vargs);
 }
 
 /**
@@ -3307,10 +3669,10 @@ FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
   \endrst
  */
 template <typename... T>
-FMT_INLINE void print(std::FILE* f, format_string<T...> fmt, T&&... args) {
-  const auto& vargs = fmt::make_format_args(args...);
-  return detail::is_utf8() ? vprint(f, fmt, vargs)
-                           : detail::vprint_mojibake(f, fmt, vargs);
+FMT_INLINE void print(std::FILE *f, format_string<T...> fmt, T &&...args)
+{
+    const auto &vargs = fmt::make_format_args(args...);
+    return detail::is_utf8() ? vprint(f, fmt, vargs) : detail::vprint_mojibake(f, fmt, vargs);
 }
 
 FMT_MODULE_EXPORT_END
@@ -3318,6 +3680,6 @@ FMT_GCC_PRAGMA("GCC pop_options")
 FMT_END_NAMESPACE
 
 #ifdef FMT_HEADER_ONLY
-#  include "format.h"
+#include "format.h"
 #endif
-#endif  // FMT_CORE_H_
+#endif // FMT_CORE_H_
diff --git a/include/spdlog/fmt/bundled/format-inl.h b/include/spdlog/fmt/bundled/format-inl.h
index 22b1ec8df..7bd314a8b 100644
--- a/include/spdlog/fmt/bundled/format-inl.h
+++ b/include/spdlog/fmt/bundled/format-inl.h
@@ -10,1714 +10,1772 @@
 
 #include <algorithm>
 #include <cctype>
-#include <cerrno>  // errno
+#include <cerrno> // errno
 #include <climits>
 #include <cmath>
 #include <cstdarg>
-#include <cstring>  // std::memmove
+#include <cstring> // std::memmove
 #include <cwchar>
 #include <exception>
 
 #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
-#  include <locale>
+#include <locale>
 #endif
 
 #ifdef _WIN32
-#  include <io.h>  // _isatty
+#include <io.h> // _isatty
 #endif
 
 #include "format.h"
 
 FMT_BEGIN_NAMESPACE
-namespace detail {
-
-FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
-  // Use unchecked std::fprintf to avoid triggering another assertion when
-  // writing to stderr fails
-  std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
-  // Chosen instead of std::abort to satisfy Clang in CUDA mode during device
-  // code pass.
-  std::terminate();
+namespace detail
+{
+
+FMT_FUNC void assert_fail(const char *file, int line, const char *message)
+{
+    // Use unchecked std::fprintf to avoid triggering another assertion when
+    // writing to stderr fails
+    std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
+    // Chosen instead of std::abort to satisfy Clang in CUDA mode during device
+    // code pass.
+    std::terminate();
 }
 
-FMT_FUNC void throw_format_error(const char* message) {
-  FMT_THROW(format_error(message));
+FMT_FUNC void throw_format_error(const char *message)
+{
+    FMT_THROW(format_error(message));
 }
 
-FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
-                                string_view message) noexcept {
-  // Report error code making sure that the output fits into
-  // inline_buffer_size to avoid dynamic memory allocation and potential
-  // bad_alloc.
-  out.try_resize(0);
-  static const char SEP[] = ": ";
-  static const char ERROR_STR[] = "error ";
-  // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
-  size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
-  auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
-  if (detail::is_negative(error_code)) {
-    abs_value = 0 - abs_value;
-    ++error_code_size;
-  }
-  error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
-  auto it = buffer_appender<char>(out);
-  if (message.size() <= inline_buffer_size - error_code_size)
-    format_to(it, FMT_STRING("{}{}"), message, SEP);
-  format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
-  FMT_ASSERT(out.size() <= inline_buffer_size, "");
+FMT_FUNC void format_error_code(detail::buffer<char> &out, int error_code, string_view message) noexcept
+{
+    // Report error code making sure that the output fits into
+    // inline_buffer_size to avoid dynamic memory allocation and potential
+    // bad_alloc.
+    out.try_resize(0);
+    static const char SEP[]       = ": ";
+    static const char ERROR_STR[] = "error ";
+    // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
+    size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
+    auto   abs_value       = static_cast<uint32_or_64_or_128_t<int>>(error_code);
+    if(detail::is_negative(error_code))
+    {
+        abs_value = 0 - abs_value;
+        ++error_code_size;
+    }
+    error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
+    auto it = buffer_appender<char>(out);
+    if(message.size() <= inline_buffer_size - error_code_size)
+        format_to(it, FMT_STRING("{}{}"), message, SEP);
+    format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
+    FMT_ASSERT(out.size() <= inline_buffer_size, "");
 }
 
-FMT_FUNC void report_error(format_func func, int error_code,
-                           const char* message) noexcept {
-  memory_buffer full_message;
-  func(full_message, error_code, message);
-  // Don't use fwrite_fully because the latter may throw.
-  if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0)
-    std::fputc('\n', stderr);
+FMT_FUNC void report_error(format_func func, int error_code, const char *message) noexcept
+{
+    memory_buffer full_message;
+    func(full_message, error_code, message);
+    // Don't use fwrite_fully because the latter may throw.
+    if(std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0)
+        std::fputc('\n', stderr);
 }
 
 // A wrapper around fwrite that throws on error.
-inline void fwrite_fully(const void* ptr, size_t size, size_t count,
-                         FILE* stream) {
-  size_t written = std::fwrite(ptr, size, count, stream);
-  if (written < count)
-    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
+inline void fwrite_fully(const void *ptr, size_t size, size_t count, FILE *stream)
+{
+    size_t written = std::fwrite(ptr, size, count, stream);
+    if(written < count)
+        FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
 }
 
 #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
 template <typename Locale>
-locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
-  static_assert(std::is_same<Locale, std::locale>::value, "");
+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 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 auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
-  auto& facet = std::use_facet<std::numpunct<Char>>(loc.get<std::locale>());
-  auto grouping = facet.grouping();
-  auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
-  return {std::move(grouping), thousands_sep};
+FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char>
+{
+    auto &facet         = std::use_facet<std::numpunct<Char>>(loc.get<std::locale>());
+    auto  grouping      = facet.grouping();
+    auto  thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
+    return {std::move(grouping), thousands_sep};
 }
-template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
-  return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
-      .decimal_point();
+template <typename Char>
+FMT_FUNC Char decimal_point_impl(locale_ref loc)
+{
+    return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()).decimal_point();
 }
 #else
 template <typename Char>
-FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result<Char> {
-  return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR};
+FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result<Char>
+{
+    return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR};
 }
-template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
-  return '.';
+template <typename Char>
+FMT_FUNC Char decimal_point_impl(locale_ref)
+{
+    return '.';
 }
 #endif
-}  // namespace detail
+} // namespace detail
 
 #if !FMT_MSC_VERSION
 FMT_API FMT_FUNC format_error::~format_error() noexcept = default;
 #endif
 
-FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str,
-                                         format_args args) {
-  auto ec = std::error_code(error_code, std::generic_category());
-  return std::system_error(ec, vformat(format_str, args));
+FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str, format_args args)
+{
+    auto ec = std::error_code(error_code, std::generic_category());
+    return std::system_error(ec, vformat(format_str, args));
 }
 
-namespace detail {
+namespace detail
+{
 
-template <typename F> inline bool operator==(basic_fp<F> x, basic_fp<F> y) {
-  return x.f == y.f && x.e == y.e;
+template <typename F>
+inline bool operator==(basic_fp<F> x, basic_fp<F> y)
+{
+    return x.f == y.f && x.e == y.e;
 }
 
 // Compilers should be able to optimize this into the ror instruction.
-FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept {
-  r &= 31;
-  return (n >> r) | (n << (32 - r));
+FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept
+{
+    r &= 31;
+    return (n >> r) | (n << (32 - r));
 }
-FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept {
-  r &= 63;
-  return (n >> r) | (n << (64 - r));
+FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept
+{
+    r &= 63;
+    return (n >> r) | (n << (64 - r));
 }
 
 // Computes 128-bit result of multiplication of two 64-bit unsigned integers.
-inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept {
+inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept
+{
 #if FMT_USE_INT128
-  auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
-  return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
+    auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
+    return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
 #elif defined(_MSC_VER) && defined(_M_X64)
-  auto result = uint128_fallback();
-  result.lo_ = _umul128(x, y, &result.hi_);
-  return result;
+    auto result = uint128_fallback();
+    result.lo_  = _umul128(x, y, &result.hi_);
+    return result;
 #else
-  const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>());
+    const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>());
 
-  uint64_t a = x >> 32;
-  uint64_t b = x & mask;
-  uint64_t c = y >> 32;
-  uint64_t d = y & mask;
+    uint64_t a = x >> 32;
+    uint64_t b = x & mask;
+    uint64_t c = y >> 32;
+    uint64_t d = y & mask;
 
-  uint64_t ac = a * c;
-  uint64_t bc = b * c;
-  uint64_t ad = a * d;
-  uint64_t bd = b * d;
+    uint64_t ac = a * c;
+    uint64_t bc = b * c;
+    uint64_t ad = a * d;
+    uint64_t bd = b * d;
 
-  uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask);
+    uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask);
 
-  return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32),
-          (intermediate << 32) + (bd & mask)};
+    return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), (intermediate << 32) + (bd & mask)};
 #endif
 }
 
 // Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox.
-namespace dragonbox {
-// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
-inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept {
+namespace dragonbox
+{
+    // Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
+    inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept
+    {
 #if FMT_USE_INT128
-  auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
-  return static_cast<uint64_t>(p >> 64);
+        auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
+        return static_cast<uint64_t>(p >> 64);
 #elif defined(_MSC_VER) && defined(_M_X64)
-  return __umulh(x, y);
+        return __umulh(x, y);
 #else
-  return umul128(x, y).high();
+        return umul128(x, y).high();
 #endif
-}
+    }
 
-// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
-// 128-bit unsigned integer.
-inline uint128_fallback umul192_upper128(uint64_t x,
-                                         uint128_fallback y) noexcept {
-  uint128_fallback r = umul128(x, y.high());
-  r += umul128_upper64(x, y.low());
-  return r;
-}
+    // Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
+    // 128-bit unsigned integer.
+    inline uint128_fallback umul192_upper128(uint64_t x, uint128_fallback y) noexcept
+    {
+        uint128_fallback r = umul128(x, y.high());
+        r += umul128_upper64(x, y.low());
+        return r;
+    }
 
-// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a
-// 64-bit unsigned integer.
-inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept {
-  return umul128_upper64(static_cast<uint64_t>(x) << 32, y);
-}
+    // Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a
+    // 64-bit unsigned integer.
+    inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept
+    {
+        return umul128_upper64(static_cast<uint64_t>(x) << 32, y);
+    }
 
-// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a
-// 128-bit unsigned integer.
-inline uint128_fallback umul192_lower128(uint64_t x,
-                                         uint128_fallback y) noexcept {
-  uint64_t high = x * y.high();
-  uint128_fallback high_low = umul128(x, y.low());
-  return {high + high_low.high(), high_low.low()};
-}
+    // Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a
+    // 128-bit unsigned integer.
+    inline uint128_fallback umul192_lower128(uint64_t x, uint128_fallback y) noexcept
+    {
+        uint64_t         high     = x * y.high();
+        uint128_fallback high_low = umul128(x, y.low());
+        return {high + high_low.high(), high_low.low()};
+    }
 
-// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a
-// 64-bit unsigned integer.
-inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept {
-  return x * y;
-}
+    // Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a
+    // 64-bit unsigned integer.
+    inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept
+    {
+        return x * y;
+    }
 
-// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from
-// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1.
-inline int floor_log10_pow2(int e) noexcept {
-  FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent");
-  static_assert((-1 >> 1) == -1, "right shift is not arithmetic");
-  return (e * 315653) >> 20;
-}
+    // Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from
+    // https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1.
+    inline int floor_log10_pow2(int e) noexcept
+    {
+        FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent");
+        static_assert((-1 >> 1) == -1, "right shift is not arithmetic");
+        return (e * 315653) >> 20;
+    }
 
-// Various fast log computations.
-inline int floor_log2_pow10(int e) noexcept {
-  FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
-  return (e * 1741647) >> 19;
-}
-inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept {
-  FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent");
-  return (e * 631305 - 261663) >> 21;
-}
+    // Various fast log computations.
+    inline int floor_log2_pow10(int e) noexcept
+    {
+        FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
+        return (e * 1741647) >> 19;
+    }
+    inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept
+    {
+        FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent");
+        return (e * 631305 - 261663) >> 21;
+    }
 
-static constexpr struct {
-  uint32_t divisor;
-  int shift_amount;
-} div_small_pow10_infos[] = {{10, 16}, {100, 16}};
-
-// Replaces n by floor(n / pow(10, N)) returning true if and only if n is
-// divisible by pow(10, N).
-// Precondition: n <= pow(10, N + 1).
-template <int N>
-bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept {
-  // The numbers below are chosen such that:
-  //   1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100,
-  //   2. nm mod 2^k < m if and only if n is divisible by d,
-  // where m is magic_number, k is shift_amount
-  // and d is divisor.
-  //
-  // Item 1 is a common technique of replacing division by a constant with
-  // multiplication, see e.g. "Division by Invariant Integers Using
-  // Multiplication" by Granlund and Montgomery (1994). magic_number (m) is set
-  // to ceil(2^k/d) for large enough k.
-  // The idea for item 2 originates from Schubfach.
-  constexpr auto info = div_small_pow10_infos[N - 1];
-  FMT_ASSERT(n <= info.divisor * 10, "n is too large");
-  constexpr uint32_t magic_number =
-      (1u << info.shift_amount) / info.divisor + 1;
-  n *= magic_number;
-  const uint32_t comparison_mask = (1u << info.shift_amount) - 1;
-  bool result = (n & comparison_mask) < magic_number;
-  n >>= info.shift_amount;
-  return result;
-}
+    static constexpr struct
+    {
+        uint32_t divisor;
+        int      shift_amount;
+    } div_small_pow10_infos[] = {{10, 16}, {100, 16}};
+
+    // Replaces n by floor(n / pow(10, N)) returning true if and only if n is
+    // divisible by pow(10, N).
+    // Precondition: n <= pow(10, N + 1).
+    template <int N>
+    bool check_divisibility_and_divide_by_pow10(uint32_t &n) noexcept
+    {
+        // The numbers below are chosen such that:
+        //   1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100,
+        //   2. nm mod 2^k < m if and only if n is divisible by d,
+        // where m is magic_number, k is shift_amount
+        // and d is divisor.
+        //
+        // Item 1 is a common technique of replacing division by a constant with
+        // multiplication, see e.g. "Division by Invariant Integers Using
+        // Multiplication" by Granlund and Montgomery (1994). magic_number (m) is set
+        // to ceil(2^k/d) for large enough k.
+        // The idea for item 2 originates from Schubfach.
+        constexpr auto info = div_small_pow10_infos[N - 1];
+        FMT_ASSERT(n <= info.divisor * 10, "n is too large");
+        constexpr uint32_t magic_number = (1u << info.shift_amount) / info.divisor + 1;
+        n *= magic_number;
+        const uint32_t comparison_mask = (1u << info.shift_amount) - 1;
+        bool           result          = (n & comparison_mask) < magic_number;
+        n >>= info.shift_amount;
+        return result;
+    }
 
-// Computes floor(n / pow(10, N)) for small n and N.
-// Precondition: n <= pow(10, N + 1).
-template <int N> uint32_t small_division_by_pow10(uint32_t n) noexcept {
-  constexpr auto info = div_small_pow10_infos[N - 1];
-  FMT_ASSERT(n <= info.divisor * 10, "n is too large");
-  constexpr uint32_t magic_number =
-      (1u << info.shift_amount) / info.divisor + 1;
-  return (n * magic_number) >> info.shift_amount;
-}
+    // Computes floor(n / pow(10, N)) for small n and N.
+    // Precondition: n <= pow(10, N + 1).
+    template <int N>
+    uint32_t small_division_by_pow10(uint32_t n) noexcept
+    {
+        constexpr auto info = div_small_pow10_infos[N - 1];
+        FMT_ASSERT(n <= info.divisor * 10, "n is too large");
+        constexpr uint32_t magic_number = (1u << info.shift_amount) / info.divisor + 1;
+        return (n * magic_number) >> info.shift_amount;
+    }
 
-// Computes floor(n / 10^(kappa + 1)) (float)
-inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept {
-  // 1374389535 = ceil(2^37/100)
-  return static_cast<uint32_t>((static_cast<uint64_t>(n) * 1374389535) >> 37);
-}
-// Computes floor(n / 10^(kappa + 1)) (double)
-inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept {
-  // 2361183241434822607 = ceil(2^(64+7)/1000)
-  return umul128_upper64(n, 2361183241434822607ull) >> 7;
-}
+    // Computes floor(n / 10^(kappa + 1)) (float)
+    inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept
+    {
+        // 1374389535 = ceil(2^37/100)
+        return static_cast<uint32_t>((static_cast<uint64_t>(n) * 1374389535) >> 37);
+    }
+    // Computes floor(n / 10^(kappa + 1)) (double)
+    inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept
+    {
+        // 2361183241434822607 = ceil(2^(64+7)/1000)
+        return umul128_upper64(n, 2361183241434822607ull) >> 7;
+    }
 
-// Various subroutines using pow10 cache
-template <class T> struct cache_accessor;
-
-template <> struct cache_accessor<float> {
-  using carrier_uint = float_info<float>::carrier_uint;
-  using cache_entry_type = uint64_t;
-
-  static uint64_t get_cached_power(int k) noexcept {
-    FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
-               "k is out of range");
-    static constexpr const uint64_t pow10_significands[] = {
-        0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
-        0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
-        0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
-        0xf1c90080baf72cb2, 0x971da05074da7bef, 0xbce5086492111aeb,
-        0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a,
-        0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810,
-        0xe12e13424bb40e14, 0x8cbccc096f5088cc, 0xafebff0bcb24aaff,
-        0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd,
-        0xd6bf94d5e57a42bd, 0x8637bd05af6c69b6, 0xa7c5ac471b478424,
-        0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b,
-        0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000,
-        0xc800000000000000, 0xfa00000000000000, 0x9c40000000000000,
-        0xc350000000000000, 0xf424000000000000, 0x9896800000000000,
-        0xbebc200000000000, 0xee6b280000000000, 0x9502f90000000000,
-        0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000,
-        0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000,
-        0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 0x8ac7230489e80000,
-        0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000,
-        0xa968163f0a57b400, 0xd3c21bcecceda100, 0x84595161401484a0,
-        0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940985,
-        0xa18f07d736b90be6, 0xc9f2c9cd04674edf, 0xfc6f7c4045812297,
-        0x9dc5ada82b70b59e, 0xc5371912364ce306, 0xf684df56c3e01bc7,
-        0x9a130b963a6c115d, 0xc097ce7bc90715b4, 0xf0bdc21abb48db21,
-        0x96769950b50d88f5, 0xbc143fa4e250eb32, 0xeb194f8e1ae525fe,
-        0x92efd1b8d0cf37bf, 0xb7abc627050305ae, 0xe596b7b0c643c71a,
-        0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f};
-    return pow10_significands[k - float_info<float>::min_k];
-  }
-
-  struct compute_mul_result {
-    carrier_uint result;
-    bool is_integer;
-  };
-  struct compute_mul_parity_result {
-    bool parity;
-    bool is_integer;
-  };
-
-  static compute_mul_result compute_mul(
-      carrier_uint u, const cache_entry_type& cache) noexcept {
-    auto r = umul96_upper64(u, cache);
-    return {static_cast<carrier_uint>(r >> 32),
-            static_cast<carrier_uint>(r) == 0};
-  }
-
-  static uint32_t compute_delta(const cache_entry_type& cache,
-                                int beta) noexcept {
-    return static_cast<uint32_t>(cache >> (64 - 1 - beta));
-  }
-
-  static compute_mul_parity_result compute_mul_parity(
-      carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
-    FMT_ASSERT(beta >= 1, "");
-    FMT_ASSERT(beta < 64, "");
-
-    auto r = umul96_lower64(two_f, cache);
-    return {((r >> (64 - beta)) & 1) != 0,
-            static_cast<uint32_t>(r >> (32 - beta)) == 0};
-  }
-
-  static carrier_uint compute_left_endpoint_for_shorter_interval_case(
-      const cache_entry_type& cache, int beta) noexcept {
-    return static_cast<carrier_uint>(
-        (cache - (cache >> (num_significand_bits<float>() + 2))) >>
-        (64 - num_significand_bits<float>() - 1 - beta));
-  }
-
-  static carrier_uint compute_right_endpoint_for_shorter_interval_case(
-      const cache_entry_type& cache, int beta) noexcept {
-    return static_cast<carrier_uint>(
-        (cache + (cache >> (num_significand_bits<float>() + 1))) >>
-        (64 - num_significand_bits<float>() - 1 - beta));
-  }
-
-  static carrier_uint compute_round_up_for_shorter_interval_case(
-      const cache_entry_type& cache, int beta) noexcept {
-    return (static_cast<carrier_uint>(
-                cache >> (64 - num_significand_bits<float>() - 2 - beta)) +
-            1) /
-           2;
-  }
-};
+    // Various subroutines using pow10 cache
+    template <class T>
+    struct cache_accessor;
+
+    template <>
+    struct cache_accessor<float>
+    {
+        using carrier_uint     = float_info<float>::carrier_uint;
+        using cache_entry_type = uint64_t;
+
+        static uint64_t get_cached_power(int k) noexcept
+        {
+            FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k, "k is out of range");
+            static constexpr const uint64_t pow10_significands[] = {
+                0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49,
+                0xc612062576589ddb, 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, 0xf1c90080baf72cb2,
+                0x971da05074da7bef, 0xbce5086492111aeb, 0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a,
+                0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810, 0xe12e13424bb40e14, 0x8cbccc096f5088cc,
+                0xafebff0bcb24aaff, 0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd, 0xd6bf94d5e57a42bd,
+                0x8637bd05af6c69b6, 0xa7c5ac471b478424, 0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b,
+                0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000, 0xc800000000000000, 0xfa00000000000000,
+                0x9c40000000000000, 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, 0xbebc200000000000,
+                0xee6b280000000000, 0x9502f90000000000, 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000,
+                0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, 0xb1a2bc2ec5000000, 0xde0b6b3a76400000,
+                0x8ac7230489e80000, 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, 0xa968163f0a57b400,
+                0xd3c21bcecceda100, 0x84595161401484a0, 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940985,
+                0xa18f07d736b90be6, 0xc9f2c9cd04674edf, 0xfc6f7c4045812297, 0x9dc5ada82b70b59e, 0xc5371912364ce306,
+                0xf684df56c3e01bc7, 0x9a130b963a6c115d, 0xc097ce7bc90715b4, 0xf0bdc21abb48db21, 0x96769950b50d88f5,
+                0xbc143fa4e250eb32, 0xeb194f8e1ae525fe, 0x92efd1b8d0cf37bf, 0xb7abc627050305ae, 0xe596b7b0c643c71a,
+                0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f};
+            return pow10_significands[k - float_info<float>::min_k];
+        }
+
+        struct compute_mul_result
+        {
+            carrier_uint result;
+            bool         is_integer;
+        };
+        struct compute_mul_parity_result
+        {
+            bool parity;
+            bool is_integer;
+        };
+
+        static compute_mul_result compute_mul(carrier_uint u, const cache_entry_type &cache) noexcept
+        {
+            auto r = umul96_upper64(u, cache);
+            return {static_cast<carrier_uint>(r >> 32), static_cast<carrier_uint>(r) == 0};
+        }
+
+        static uint32_t compute_delta(const cache_entry_type &cache, int beta) noexcept
+        {
+            return static_cast<uint32_t>(cache >> (64 - 1 - beta));
+        }
+
+        static compute_mul_parity_result
+        compute_mul_parity(carrier_uint two_f, const cache_entry_type &cache, int beta) noexcept
+        {
+            FMT_ASSERT(beta >= 1, "");
+            FMT_ASSERT(beta < 64, "");
+
+            auto r = umul96_lower64(two_f, cache);
+            return {((r >> (64 - beta)) & 1) != 0, static_cast<uint32_t>(r >> (32 - beta)) == 0};
+        }
+
+        static carrier_uint
+        compute_left_endpoint_for_shorter_interval_case(const cache_entry_type &cache, int beta) noexcept
+        {
+            return static_cast<carrier_uint>(
+                (cache - (cache >> (num_significand_bits<float>() + 2))) >>
+                (64 - num_significand_bits<float>() - 1 - beta));
+        }
+
+        static carrier_uint
+        compute_right_endpoint_for_shorter_interval_case(const cache_entry_type &cache, int beta) noexcept
+        {
+            return static_cast<carrier_uint>(
+                (cache + (cache >> (num_significand_bits<float>() + 1))) >>
+                (64 - num_significand_bits<float>() - 1 - beta));
+        }
+
+        static carrier_uint compute_round_up_for_shorter_interval_case(const cache_entry_type &cache, int beta) noexcept
+        {
+            return (static_cast<carrier_uint>(cache >> (64 - num_significand_bits<float>() - 2 - beta)) + 1) / 2;
+        }
+    };
 
-template <> struct cache_accessor<double> {
-  using carrier_uint = float_info<double>::carrier_uint;
-  using cache_entry_type = uint128_fallback;
+    template <>
+    struct cache_accessor<double>
+    {
+        using carrier_uint     = float_info<double>::carrier_uint;
+        using cache_entry_type = uint128_fallback;
 
-  static uint128_fallback get_cached_power(int k) noexcept {
-    FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
-               "k is out of range");
+        static uint128_fallback get_cached_power(int k) noexcept
+        {
+            FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k, "k is out of range");
 
-    static constexpr const uint128_fallback pow10_significands[] = {
+            static constexpr const uint128_fallback pow10_significands[] = {
 #if FMT_USE_FULL_CACHE_DRAGONBOX
-      {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
-      {0x9faacf3df73609b1, 0x77b191618c54e9ad},
-      {0xc795830d75038c1d, 0xd59df5b9ef6a2418},
-      {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e},
-      {0x9becce62836ac577, 0x4ee367f9430aec33},
-      {0xc2e801fb244576d5, 0x229c41f793cda740},
-      {0xf3a20279ed56d48a, 0x6b43527578c11110},
-      {0x9845418c345644d6, 0x830a13896b78aaaa},
-      {0xbe5691ef416bd60c, 0x23cc986bc656d554},
-      {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9},
-      {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa},
-      {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54},
-      {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69},
-      {0x91376c36d99995be, 0x23100809b9c21fa2},
-      {0xb58547448ffffb2d, 0xabd40a0c2832a78b},
-      {0xe2e69915b3fff9f9, 0x16c90c8f323f516d},
-      {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4},
-      {0xb1442798f49ffb4a, 0x99cd11cfdf41779d},
-      {0xdd95317f31c7fa1d, 0x40405643d711d584},
-      {0x8a7d3eef7f1cfc52, 0x482835ea666b2573},
-      {0xad1c8eab5ee43b66, 0xda3243650005eed0},
-      {0xd863b256369d4a40, 0x90bed43e40076a83},
-      {0x873e4f75e2224e68, 0x5a7744a6e804a292},
-      {0xa90de3535aaae202, 0x711515d0a205cb37},
-      {0xd3515c2831559a83, 0x0d5a5b44ca873e04},
-      {0x8412d9991ed58091, 0xe858790afe9486c3},
-      {0xa5178fff668ae0b6, 0x626e974dbe39a873},
-      {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
-      {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a},
-      {0xa139029f6a239f72, 0x1c1fffc1ebc44e81},
-      {0xc987434744ac874e, 0xa327ffb266b56221},
-      {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9},
-      {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa},
-      {0xc4ce17b399107c22, 0xcb550fb4384d21d4},
-      {0xf6019da07f549b2b, 0x7e2a53a146606a49},
-      {0x99c102844f94e0fb, 0x2eda7444cbfc426e},
-      {0xc0314325637a1939, 0xfa911155fefb5309},
-      {0xf03d93eebc589f88, 0x793555ab7eba27cb},
-      {0x96267c7535b763b5, 0x4bc1558b2f3458df},
-      {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17},
-      {0xea9c227723ee8bcb, 0x465e15a979c1cadd},
-      {0x92a1958a7675175f, 0x0bfacd89ec191eca},
-      {0xb749faed14125d36, 0xcef980ec671f667c},
-      {0xe51c79a85916f484, 0x82b7e12780e7401b},
-      {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811},
-      {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16},
-      {0xdfbdcece67006ac9, 0x67a791e093e1d49b},
-      {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1},
-      {0xaecc49914078536d, 0x58fae9f773886e19},
-      {0xda7f5bf590966848, 0xaf39a475506a899f},
-      {0x888f99797a5e012d, 0x6d8406c952429604},
-      {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84},
-      {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65},
-      {0x855c3be0a17fcd26, 0x5cf2eea09a550680},
-      {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f},
-      {0xd0601d8efc57b08b, 0xf13b94daf124da27},
-      {0x823c12795db6ce57, 0x76c53d08d6b70859},
-      {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f},
-      {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a},
-      {0xfe5d54150b090b02, 0xd3f93b35435d7c4d},
-      {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0},
-      {0xc6b8e9b0709f109a, 0x359ab6419ca1091c},
-      {0xf867241c8cc6d4c0, 0xc30163d203c94b63},
-      {0x9b407691d7fc44f8, 0x79e0de63425dcf1e},
-      {0xc21094364dfb5636, 0x985915fc12f542e5},
-      {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e},
-      {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43},
-      {0xbd8430bd08277231, 0x50c6ff782a838354},
-      {0xece53cec4a314ebd, 0xa4f8bf5635246429},
-      {0x940f4613ae5ed136, 0x871b7795e136be9a},
-      {0xb913179899f68584, 0x28e2557b59846e40},
-      {0xe757dd7ec07426e5, 0x331aeada2fe589d0},
-      {0x9096ea6f3848984f, 0x3ff0d2c85def7622},
-      {0xb4bca50b065abe63, 0x0fed077a756b53aa},
-      {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895},
-      {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d},
-      {0xb080392cc4349dec, 0xbd8d794d96aacfb4},
-      {0xdca04777f541c567, 0xecf0d7a0fc5583a1},
-      {0x89e42caaf9491b60, 0xf41686c49db57245},
-      {0xac5d37d5b79b6239, 0x311c2875c522ced6},
-      {0xd77485cb25823ac7, 0x7d633293366b828c},
-      {0x86a8d39ef77164bc, 0xae5dff9c02033198},
-      {0xa8530886b54dbdeb, 0xd9f57f830283fdfd},
-      {0xd267caa862a12d66, 0xd072df63c324fd7c},
-      {0x8380dea93da4bc60, 0x4247cb9e59f71e6e},
-      {0xa46116538d0deb78, 0x52d9be85f074e609},
-      {0xcd795be870516656, 0x67902e276c921f8c},
-      {0x806bd9714632dff6, 0x00ba1cd8a3db53b7},
-      {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5},
-      {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce},
-      {0xfad2a4b13d1b5d6c, 0x796b805720085f82},
-      {0x9cc3a6eec6311a63, 0xcbe3303674053bb1},
-      {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d},
-      {0xf4f1b4d515acb93b, 0xee92fb5515482d45},
-      {0x991711052d8bf3c5, 0x751bdd152d4d1c4b},
-      {0xbf5cd54678eef0b6, 0xd262d45a78a0635e},
-      {0xef340a98172aace4, 0x86fb897116c87c35},
-      {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1},
-      {0xbae0a846d2195712, 0x8974836059cca10a},
-      {0xe998d258869facd7, 0x2bd1a438703fc94c},
-      {0x91ff83775423cc06, 0x7b6306a34627ddd0},
-      {0xb67f6455292cbf08, 0x1a3bc84c17b1d543},
-      {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94},
-      {0x8e938662882af53e, 0x547eb47b7282ee9d},
-      {0xb23867fb2a35b28d, 0xe99e619a4f23aa44},
-      {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5},
-      {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05},
-      {0xae0b158b4738705e, 0x9624ab50b148d446},
-      {0xd98ddaee19068c76, 0x3badd624dd9b0958},
-      {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7},
-      {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d},
-      {0xd47487cc8470652b, 0x7647c32000696720},
-      {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074},
-      {0xa5fb0a17c777cf09, 0xf468107100525891},
-      {0xcf79cc9db955c2cc, 0x7182148d4066eeb5},
-      {0x81ac1fe293d599bf, 0xc6f14cd848405531},
-      {0xa21727db38cb002f, 0xb8ada00e5a506a7d},
-      {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d},
-      {0xfd442e4688bd304a, 0x908f4a166d1da664},
-      {0x9e4a9cec15763e2e, 0x9a598e4e043287ff},
-      {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe},
-      {0xf7549530e188c128, 0xd12bee59e68ef47d},
-      {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf},
-      {0xc13a148e3032d6e7, 0xe36a52363c1faf02},
-      {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2},
-      {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba},
-      {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8},
-      {0xebdf661791d60f56, 0x111b495b3464ad22},
-      {0x936b9fcebb25c995, 0xcab10dd900beec35},
-      {0xb84687c269ef3bfb, 0x3d5d514f40eea743},
-      {0xe65829b3046b0afa, 0x0cb4a5a3112a5113},
-      {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac},
-      {0xb3f4e093db73a093, 0x59ed216765690f57},
-      {0xe0f218b8d25088b8, 0x306869c13ec3532d},
-      {0x8c974f7383725573, 0x1e414218c73a13fc},
-      {0xafbd2350644eeacf, 0xe5d1929ef90898fb},
-      {0xdbac6c247d62a583, 0xdf45f746b74abf3a},
-      {0x894bc396ce5da772, 0x6b8bba8c328eb784},
-      {0xab9eb47c81f5114f, 0x066ea92f3f326565},
-      {0xd686619ba27255a2, 0xc80a537b0efefebe},
-      {0x8613fd0145877585, 0xbd06742ce95f5f37},
-      {0xa798fc4196e952e7, 0x2c48113823b73705},
-      {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6},
-      {0x82ef85133de648c4, 0x9a984d73dbe722fc},
-      {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb},
-      {0xcc963fee10b7d1b3, 0x318df905079926a9},
-      {0xffbbcfe994e5c61f, 0xfdf17746497f7053},
-      {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634},
-      {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1},
-      {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1},
-      {0x9c1661a651213e2d, 0x06bea10ca65c084f},
-      {0xc31bfa0fe5698db8, 0x486e494fcff30a63},
-      {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb},
-      {0x986ddb5c6b3a76b7, 0xf89629465a75e01d},
-      {0xbe89523386091465, 0xf6bbb397f1135824},
-      {0xee2ba6c0678b597f, 0x746aa07ded582e2d},
-      {0x94db483840b717ef, 0xa8c2a44eb4571cdd},
-      {0xba121a4650e4ddeb, 0x92f34d62616ce414},
-      {0xe896a0d7e51e1566, 0x77b020baf9c81d18},
-      {0x915e2486ef32cd60, 0x0ace1474dc1d122f},
-      {0xb5b5ada8aaff80b8, 0x0d819992132456bb},
-      {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a},
-      {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2},
-      {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3},
-      {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf},
-      {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c},
-      {0xad4ab7112eb3929d, 0x86c16c98d2c953c7},
-      {0xd89d64d57a607744, 0xe871c7bf077ba8b8},
-      {0x87625f056c7c4a8b, 0x11471cd764ad4973},
-      {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0},
-      {0xd389b47879823479, 0x4aff1d108d4ec2c4},
-      {0x843610cb4bf160cb, 0xcedf722a585139bb},
-      {0xa54394fe1eedb8fe, 0xc2974eb4ee658829},
-      {0xce947a3da6a9273e, 0x733d226229feea33},
-      {0x811ccc668829b887, 0x0806357d5a3f5260},
-      {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8},
-      {0xc9bcff6034c13052, 0xfc89b393dd02f0b6},
-      {0xfc2c3f3841f17c67, 0xbbac2078d443ace3},
-      {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e},
-      {0xc5029163f384a931, 0x0a9e795e65d4df12},
-      {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6},
-      {0x99ea0196163fa42e, 0x504bced1bf8e4e46},
-      {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7},
-      {0xf07da27a82c37088, 0x5d767327bb4e5a4d},
-      {0x964e858c91ba2655, 0x3a6a07f8d510f870},
-      {0xbbe226efb628afea, 0x890489f70a55368c},
-      {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f},
-      {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e},
-      {0xb77ada0617e3bbcb, 0x09ce6ebb40173745},
-      {0xe55990879ddcaabd, 0xcc420a6a101d0516},
-      {0x8f57fa54c2a9eab6, 0x9fa946824a12232e},
-      {0xb32df8e9f3546564, 0x47939822dc96abfa},
-      {0xdff9772470297ebd, 0x59787e2b93bc56f8},
-      {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b},
-      {0xaefae51477a06b03, 0xede622920b6b23f2},
-      {0xdab99e59958885c4, 0xe95fab368e45ecee},
-      {0x88b402f7fd75539b, 0x11dbcb0218ebb415},
-      {0xaae103b5fcd2a881, 0xd652bdc29f26a11a},
-      {0xd59944a37c0752a2, 0x4be76d3346f04960},
-      {0x857fcae62d8493a5, 0x6f70a4400c562ddc},
-      {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953},
-      {0xd097ad07a71f26b2, 0x7e2000a41346a7a8},
-      {0x825ecc24c873782f, 0x8ed400668c0c28c9},
-      {0xa2f67f2dfa90563b, 0x728900802f0f32fb},
-      {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba},
-      {0xfea126b7d78186bc, 0xe2f610c84987bfa9},
-      {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca},
-      {0xc6ede63fa05d3143, 0x91503d1c79720dbc},
-      {0xf8a95fcf88747d94, 0x75a44c6397ce912b},
-      {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb},
-      {0xc24452da229b021b, 0xfbe85badce996169},
-      {0xf2d56790ab41c2a2, 0xfae27299423fb9c4},
-      {0x97c560ba6b0919a5, 0xdccd879fc967d41b},
-      {0xbdb6b8e905cb600f, 0x5400e987bbc1c921},
-      {0xed246723473e3813, 0x290123e9aab23b69},
-      {0x9436c0760c86e30b, 0xf9a0b6720aaf6522},
-      {0xb94470938fa89bce, 0xf808e40e8d5b3e6a},
-      {0xe7958cb87392c2c2, 0xb60b1d1230b20e05},
-      {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3},
-      {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4},
-      {0xe2280b6c20dd5232, 0x25c6da63c38de1b1},
-      {0x8d590723948a535f, 0x579c487e5a38ad0f},
-      {0xb0af48ec79ace837, 0x2d835a9df0c6d852},
-      {0xdcdb1b2798182244, 0xf8e431456cf88e66},
-      {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900},
-      {0xac8b2d36eed2dac5, 0xe272467e3d222f40},
-      {0xd7adf884aa879177, 0x5b0ed81dcc6abb10},
-      {0x86ccbb52ea94baea, 0x98e947129fc2b4ea},
-      {0xa87fea27a539e9a5, 0x3f2398d747b36225},
-      {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae},
-      {0x83a3eeeef9153e89, 0x1953cf68300424ad},
-      {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8},
-      {0xcdb02555653131b6, 0x3792f412cb06794e},
-      {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1},
-      {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5},
-      {0xc8de047564d20a8b, 0xf245825a5a445276},
-      {0xfb158592be068d2e, 0xeed6e2f0f0d56713},
-      {0x9ced737bb6c4183d, 0x55464dd69685606c},
-      {0xc428d05aa4751e4c, 0xaa97e14c3c26b887},
-      {0xf53304714d9265df, 0xd53dd99f4b3066a9},
-      {0x993fe2c6d07b7fab, 0xe546a8038efe402a},
-      {0xbf8fdb78849a5f96, 0xde98520472bdd034},
-      {0xef73d256a5c0f77c, 0x963e66858f6d4441},
-      {0x95a8637627989aad, 0xdde7001379a44aa9},
-      {0xbb127c53b17ec159, 0x5560c018580d5d53},
-      {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7},
-      {0x9226712162ab070d, 0xcab3961304ca70e9},
-      {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23},
-      {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b},
-      {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243},
-      {0xb267ed1940f1c61c, 0x55f038b237591ed4},
-      {0xdf01e85f912e37a3, 0x6b6c46dec52f6689},
-      {0x8b61313bbabce2c6, 0x2323ac4b3b3da016},
-      {0xae397d8aa96c1b77, 0xabec975e0a0d081b},
-      {0xd9c7dced53c72255, 0x96e7bd358c904a22},
-      {0x881cea14545c7575, 0x7e50d64177da2e55},
-      {0xaa242499697392d2, 0xdde50bd1d5d0b9ea},
-      {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865},
-      {0x84ec3c97da624ab4, 0xbd5af13bef0b113f},
-      {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f},
-      {0xcfb11ead453994ba, 0x67de18eda5814af3},
-      {0x81ceb32c4b43fcf4, 0x80eacf948770ced8},
-      {0xa2425ff75e14fc31, 0xa1258379a94d028e},
-      {0xcad2f7f5359a3b3e, 0x096ee45813a04331},
-      {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd},
-      {0x9e74d1b791e07e48, 0x775ea264cf55347e},
-      {0xc612062576589dda, 0x95364afe032a819e},
-      {0xf79687aed3eec551, 0x3a83ddbd83f52205},
-      {0x9abe14cd44753b52, 0xc4926a9672793543},
-      {0xc16d9a0095928a27, 0x75b7053c0f178294},
-      {0xf1c90080baf72cb1, 0x5324c68b12dd6339},
-      {0x971da05074da7bee, 0xd3f6fc16ebca5e04},
-      {0xbce5086492111aea, 0x88f4bb1ca6bcf585},
-      {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6},
-      {0x9392ee8e921d5d07, 0x3aff322e62439fd0},
-      {0xb877aa3236a4b449, 0x09befeb9fad487c3},
-      {0xe69594bec44de15b, 0x4c2ebe687989a9b4},
-      {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11},
-      {0xb424dc35095cd80f, 0x538484c19ef38c95},
-      {0xe12e13424bb40e13, 0x2865a5f206b06fba},
-      {0x8cbccc096f5088cb, 0xf93f87b7442e45d4},
-      {0xafebff0bcb24aafe, 0xf78f69a51539d749},
-      {0xdbe6fecebdedd5be, 0xb573440e5a884d1c},
-      {0x89705f4136b4a597, 0x31680a88f8953031},
-      {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e},
-      {0xd6bf94d5e57a42bc, 0x3d32907604691b4d},
-      {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110},
-      {0xa7c5ac471b478423, 0x0fcf80dc33721d54},
-      {0xd1b71758e219652b, 0xd3c36113404ea4a9},
-      {0x83126e978d4fdf3b, 0x645a1cac083126ea},
-      {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4},
-      {0xcccccccccccccccc, 0xcccccccccccccccd},
-      {0x8000000000000000, 0x0000000000000000},
-      {0xa000000000000000, 0x0000000000000000},
-      {0xc800000000000000, 0x0000000000000000},
-      {0xfa00000000000000, 0x0000000000000000},
-      {0x9c40000000000000, 0x0000000000000000},
-      {0xc350000000000000, 0x0000000000000000},
-      {0xf424000000000000, 0x0000000000000000},
-      {0x9896800000000000, 0x0000000000000000},
-      {0xbebc200000000000, 0x0000000000000000},
-      {0xee6b280000000000, 0x0000000000000000},
-      {0x9502f90000000000, 0x0000000000000000},
-      {0xba43b74000000000, 0x0000000000000000},
-      {0xe8d4a51000000000, 0x0000000000000000},
-      {0x9184e72a00000000, 0x0000000000000000},
-      {0xb5e620f480000000, 0x0000000000000000},
-      {0xe35fa931a0000000, 0x0000000000000000},
-      {0x8e1bc9bf04000000, 0x0000000000000000},
-      {0xb1a2bc2ec5000000, 0x0000000000000000},
-      {0xde0b6b3a76400000, 0x0000000000000000},
-      {0x8ac7230489e80000, 0x0000000000000000},
-      {0xad78ebc5ac620000, 0x0000000000000000},
-      {0xd8d726b7177a8000, 0x0000000000000000},
-      {0x878678326eac9000, 0x0000000000000000},
-      {0xa968163f0a57b400, 0x0000000000000000},
-      {0xd3c21bcecceda100, 0x0000000000000000},
-      {0x84595161401484a0, 0x0000000000000000},
-      {0xa56fa5b99019a5c8, 0x0000000000000000},
-      {0xcecb8f27f4200f3a, 0x0000000000000000},
-      {0x813f3978f8940984, 0x4000000000000000},
-      {0xa18f07d736b90be5, 0x5000000000000000},
-      {0xc9f2c9cd04674ede, 0xa400000000000000},
-      {0xfc6f7c4045812296, 0x4d00000000000000},
-      {0x9dc5ada82b70b59d, 0xf020000000000000},
-      {0xc5371912364ce305, 0x6c28000000000000},
-      {0xf684df56c3e01bc6, 0xc732000000000000},
-      {0x9a130b963a6c115c, 0x3c7f400000000000},
-      {0xc097ce7bc90715b3, 0x4b9f100000000000},
-      {0xf0bdc21abb48db20, 0x1e86d40000000000},
-      {0x96769950b50d88f4, 0x1314448000000000},
-      {0xbc143fa4e250eb31, 0x17d955a000000000},
-      {0xeb194f8e1ae525fd, 0x5dcfab0800000000},
-      {0x92efd1b8d0cf37be, 0x5aa1cae500000000},
-      {0xb7abc627050305ad, 0xf14a3d9e40000000},
-      {0xe596b7b0c643c719, 0x6d9ccd05d0000000},
-      {0x8f7e32ce7bea5c6f, 0xe4820023a2000000},
-      {0xb35dbf821ae4f38b, 0xdda2802c8a800000},
-      {0xe0352f62a19e306e, 0xd50b2037ad200000},
-      {0x8c213d9da502de45, 0x4526f422cc340000},
-      {0xaf298d050e4395d6, 0x9670b12b7f410000},
-      {0xdaf3f04651d47b4c, 0x3c0cdd765f114000},
-      {0x88d8762bf324cd0f, 0xa5880a69fb6ac800},
-      {0xab0e93b6efee0053, 0x8eea0d047a457a00},
-      {0xd5d238a4abe98068, 0x72a4904598d6d880},
-      {0x85a36366eb71f041, 0x47a6da2b7f864750},
-      {0xa70c3c40a64e6c51, 0x999090b65f67d924},
-      {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d},
-      {0x82818f1281ed449f, 0xbff8f10e7a8921a5},
-      {0xa321f2d7226895c7, 0xaff72d52192b6a0e},
-      {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764491},
-      {0xfee50b7025c36a08, 0x02f236d04753d5b5},
-      {0x9f4f2726179a2245, 0x01d762422c946591},
-      {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef6},
-      {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb3},
-      {0x9b934c3b330c8577, 0x63cc55f49f88eb30},
-      {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fc},
-      {0xf316271c7fc3908a, 0x8bef464e3945ef7b},
-      {0x97edd871cfda3a56, 0x97758bf0e3cbb5ad},
-      {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea318},
-      {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bde},
-      {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6b},
-      {0xb975d6b6ee39e436, 0xb3e2fd538e122b45},
-      {0xe7d34c64a9c85d44, 0x60dbbca87196b617},
-      {0x90e40fbeea1d3a4a, 0xbc8955e946fe31ce},
-      {0xb51d13aea4a488dd, 0x6babab6398bdbe42},
-      {0xe264589a4dcdab14, 0xc696963c7eed2dd2},
-      {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca3},
-      {0xb0de65388cc8ada8, 0x3b25a55f43294bcc},
-      {0xdd15fe86affad912, 0x49ef0eb713f39ebf},
-      {0x8a2dbf142dfcc7ab, 0x6e3569326c784338},
-      {0xacb92ed9397bf996, 0x49c2c37f07965405},
-      {0xd7e77a8f87daf7fb, 0xdc33745ec97be907},
-      {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a4},
-      {0xa8acd7c0222311bc, 0xc40832ea0d68ce0d},
-      {0xd2d80db02aabd62b, 0xf50a3fa490c30191},
-      {0x83c7088e1aab65db, 0x792667c6da79e0fb},
-      {0xa4b8cab1a1563f52, 0x577001b891185939},
-      {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87},
-      {0x80b05e5ac60b6178, 0x544f8158315b05b5},
-      {0xa0dc75f1778e39d6, 0x696361ae3db1c722},
-      {0xc913936dd571c84c, 0x03bc3a19cd1e38ea},
-      {0xfb5878494ace3a5f, 0x04ab48a04065c724},
-      {0x9d174b2dcec0e47b, 0x62eb0d64283f9c77},
-      {0xc45d1df942711d9a, 0x3ba5d0bd324f8395},
-      {0xf5746577930d6500, 0xca8f44ec7ee3647a},
-      {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecc},
-      {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67f},
-      {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101f},
-      {0x95d04aee3b80ece5, 0xbba1f1d158724a13},
-      {0xbb445da9ca61281f, 0x2a8a6e45ae8edc98},
-      {0xea1575143cf97226, 0xf52d09d71a3293be},
-      {0x924d692ca61be758, 0x593c2626705f9c57},
-      {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836d},
-      {0xe498f455c38b997a, 0x0b6dfb9c0f956448},
-      {0x8edf98b59a373fec, 0x4724bd4189bd5ead},
-      {0xb2977ee300c50fe7, 0x58edec91ec2cb658},
-      {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ee},
-      {0x8b865b215899f46c, 0xbd79e0d20082ee75},
-      {0xae67f1e9aec07187, 0xecd8590680a3aa12},
-      {0xda01ee641a708de9, 0xe80e6f4820cc9496},
-      {0x884134fe908658b2, 0x3109058d147fdcde},
-      {0xaa51823e34a7eede, 0xbd4b46f0599fd416},
-      {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91b},
-      {0x850fadc09923329e, 0x03e2cf6bc604ddb1},
-      {0xa6539930bf6bff45, 0x84db8346b786151d},
-      {0xcfe87f7cef46ff16, 0xe612641865679a64},
-      {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07f},
-      {0xa26da3999aef7749, 0xe3be5e330f38f09e},
-      {0xcb090c8001ab551c, 0x5cadf5bfd3072cc6},
-      {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f7},
-      {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afb},
-      {0xc646d63501a1511d, 0xb281e1fd541501b9},
-      {0xf7d88bc24209a565, 0x1f225a7ca91a4227},
-      {0x9ae757596946075f, 0x3375788de9b06959},
-      {0xc1a12d2fc3978937, 0x0052d6b1641c83af},
-      {0xf209787bb47d6b84, 0xc0678c5dbd23a49b},
-      {0x9745eb4d50ce6332, 0xf840b7ba963646e1},
-      {0xbd176620a501fbff, 0xb650e5a93bc3d899},
-      {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebf},
-      {0x93ba47c980e98cdf, 0xc66f336c36b10138},
-      {0xb8a8d9bbe123f017, 0xb80b0047445d4185},
-      {0xe6d3102ad96cec1d, 0xa60dc059157491e6},
-      {0x9043ea1ac7e41392, 0x87c89837ad68db30},
-      {0xb454e4a179dd1877, 0x29babe4598c311fc},
-      {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67b},
-      {0x8ce2529e2734bb1d, 0x1899e4a65f58660d},
-      {0xb01ae745b101e9e4, 0x5ec05dcff72e7f90},
-      {0xdc21a1171d42645d, 0x76707543f4fa1f74},
-      {0x899504ae72497eba, 0x6a06494a791c53a9},
-      {0xabfa45da0edbde69, 0x0487db9d17636893},
-      {0xd6f8d7509292d603, 0x45a9d2845d3c42b7},
-      {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3},
-      {0xa7f26836f282b732, 0x8e6cac7768d7141f},
-      {0xd1ef0244af2364ff, 0x3207d795430cd927},
-      {0x8335616aed761f1f, 0x7f44e6bd49e807b9},
-      {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a7},
-      {0xcd036837130890a1, 0x36dba887c37a8c10},
-      {0x802221226be55a64, 0xc2494954da2c978a},
-      {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6d},
-      {0xc83553c5c8965d3d, 0x6f92829494e5acc8},
-      {0xfa42a8b73abbf48c, 0xcb772339ba1f17fa},
-      {0x9c69a97284b578d7, 0xff2a760414536efc},
-      {0xc38413cf25e2d70d, 0xfef5138519684abb},
-      {0xf46518c2ef5b8cd1, 0x7eb258665fc25d6a},
-      {0x98bf2f79d5993802, 0xef2f773ffbd97a62},
-      {0xbeeefb584aff8603, 0xaafb550ffacfd8fb},
-      {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf39},
-      {0x952ab45cfa97a0b2, 0xdd945a747bf26184},
-      {0xba756174393d88df, 0x94f971119aeef9e5},
-      {0xe912b9d1478ceb17, 0x7a37cd5601aab85e},
-      {0x91abb422ccb812ee, 0xac62e055c10ab33b},
-      {0xb616a12b7fe617aa, 0x577b986b314d600a},
-      {0xe39c49765fdf9d94, 0xed5a7e85fda0b80c},
-      {0x8e41ade9fbebc27d, 0x14588f13be847308},
-      {0xb1d219647ae6b31c, 0x596eb2d8ae258fc9},
-      {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bc},
-      {0x8aec23d680043bee, 0x25de7bb9480d5855},
-      {0xada72ccc20054ae9, 0xaf561aa79a10ae6b},
-      {0xd910f7ff28069da4, 0x1b2ba1518094da05},
-      {0x87aa9aff79042286, 0x90fb44d2f05d0843},
-      {0xa99541bf57452b28, 0x353a1607ac744a54},
-      {0xd3fa922f2d1675f2, 0x42889b8997915ce9},
-      {0x847c9b5d7c2e09b7, 0x69956135febada12},
-      {0xa59bc234db398c25, 0x43fab9837e699096},
-      {0xcf02b2c21207ef2e, 0x94f967e45e03f4bc},
-      {0x8161afb94b44f57d, 0x1d1be0eebac278f6},
-      {0xa1ba1ba79e1632dc, 0x6462d92a69731733},
-      {0xca28a291859bbf93, 0x7d7b8f7503cfdcff},
-      {0xfcb2cb35e702af78, 0x5cda735244c3d43f},
-      {0x9defbf01b061adab, 0x3a0888136afa64a8},
-      {0xc56baec21c7a1916, 0x088aaa1845b8fdd1},
-      {0xf6c69a72a3989f5b, 0x8aad549e57273d46},
-      {0x9a3c2087a63f6399, 0x36ac54e2f678864c},
-      {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7de},
-      {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d6},
-      {0x969eb7c47859e743, 0x9f644ae5a4b1b326},
-      {0xbc4665b596706114, 0x873d5d9f0dde1fef},
-      {0xeb57ff22fc0c7959, 0xa90cb506d155a7eb},
-      {0x9316ff75dd87cbd8, 0x09a7f12442d588f3},
-      {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb30},
-      {0xe5d3ef282a242e81, 0x8f1668c8a86da5fb},
-      {0x8fa475791a569d10, 0xf96e017d694487bd},
-      {0xb38d92d760ec4455, 0x37c981dcc395a9ad},
-      {0xe070f78d3927556a, 0x85bbe253f47b1418},
-      {0x8c469ab843b89562, 0x93956d7478ccec8f},
-      {0xaf58416654a6babb, 0x387ac8d1970027b3},
-      {0xdb2e51bfe9d0696a, 0x06997b05fcc0319f},
-      {0x88fcf317f22241e2, 0x441fece3bdf81f04},
-      {0xab3c2fddeeaad25a, 0xd527e81cad7626c4},
-      {0xd60b3bd56a5586f1, 0x8a71e223d8d3b075},
-      {0x85c7056562757456, 0xf6872d5667844e4a},
-      {0xa738c6bebb12d16c, 0xb428f8ac016561dc},
-      {0xd106f86e69d785c7, 0xe13336d701beba53},
-      {0x82a45b450226b39c, 0xecc0024661173474},
-      {0xa34d721642b06084, 0x27f002d7f95d0191},
-      {0xcc20ce9bd35c78a5, 0x31ec038df7b441f5},
-      {0xff290242c83396ce, 0x7e67047175a15272},
-      {0x9f79a169bd203e41, 0x0f0062c6e984d387},
-      {0xc75809c42c684dd1, 0x52c07b78a3e60869},
-      {0xf92e0c3537826145, 0xa7709a56ccdf8a83},
-      {0x9bbcc7a142b17ccb, 0x88a66076400bb692},
-      {0xc2abf989935ddbfe, 0x6acff893d00ea436},
-      {0xf356f7ebf83552fe, 0x0583f6b8c4124d44},
-      {0x98165af37b2153de, 0xc3727a337a8b704b},
-      {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5d},
-      {0xeda2ee1c7064130c, 0x1162def06f79df74},
-      {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba9},
-      {0xb9a74a0637ce2ee1, 0x6d953e2bd7173693},
-      {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0438},
-      {0x910ab1d4db9914a0, 0x1d9c9892400a22a3},
-      {0xb54d5e4a127f59c8, 0x2503beb6d00cab4c},
-      {0xe2a0b5dc971f303a, 0x2e44ae64840fd61e},
-      {0x8da471a9de737e24, 0x5ceaecfed289e5d3},
-      {0xb10d8e1456105dad, 0x7425a83e872c5f48},
-      {0xdd50f1996b947518, 0xd12f124e28f7771a},
-      {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa70},
-      {0xace73cbfdc0bfb7b, 0x636cc64d1001550c},
-      {0xd8210befd30efa5a, 0x3c47f7e05401aa4f},
-      {0x8714a775e3e95c78, 0x65acfaec34810a72},
-      {0xa8d9d1535ce3b396, 0x7f1839a741a14d0e},
-      {0xd31045a8341ca07c, 0x1ede48111209a051},
-      {0x83ea2b892091e44d, 0x934aed0aab460433},
-      {0xa4e4b66b68b65d60, 0xf81da84d56178540},
-      {0xce1de40642e3f4b9, 0x36251260ab9d668f},
-      {0x80d2ae83e9ce78f3, 0xc1d72b7c6b42601a},
-      {0xa1075a24e4421730, 0xb24cf65b8612f820},
-      {0xc94930ae1d529cfc, 0xdee033f26797b628},
-      {0xfb9b7cd9a4a7443c, 0x169840ef017da3b2},
-      {0x9d412e0806e88aa5, 0x8e1f289560ee864f},
-      {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e3},
-      {0xf5b5d7ec8acb58a2, 0xae10af696774b1dc},
-      {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef2a},
-      {0xbff610b0cc6edd3f, 0x17fd090a58d32af4},
-      {0xeff394dcff8a948e, 0xddfc4b4cef07f5b1},
-      {0x95f83d0a1fb69cd9, 0x4abdaf101564f98f},
-      {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f2},
-      {0xea53df5fd18d5513, 0x84c86189216dc5ee},
-      {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb5},
-      {0xb7118682dbb66a77, 0x3fbc8c33221dc2a2},
-      {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
-      {0x8f05b1163ba6832d, 0x29cb4d87f2a7400f},
-      {0xb2c71d5bca9023f8, 0x743e20e9ef511013},
-      {0xdf78e4b2bd342cf6, 0x914da9246b255417},
-      {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548f},
-      {0xae9672aba3d0c320, 0xa184ac2473b529b2},
-      {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741f},
-      {0x8865899617fb1871, 0x7e2fa67c7a658893},
-      {0xaa7eebfb9df9de8d, 0xddbb901b98feeab8},
-      {0xd51ea6fa85785631, 0x552a74227f3ea566},
-      {0x8533285c936b35de, 0xd53a88958f872760},
-      {0xa67ff273b8460356, 0x8a892abaf368f138},
-      {0xd01fef10a657842c, 0x2d2b7569b0432d86},
-      {0x8213f56a67f6b29b, 0x9c3b29620e29fc74},
-      {0xa298f2c501f45f42, 0x8349f3ba91b47b90},
-      {0xcb3f2f7642717713, 0x241c70a936219a74},
-      {0xfe0efb53d30dd4d7, 0xed238cd383aa0111},
-      {0x9ec95d1463e8a506, 0xf4363804324a40ab},
-      {0xc67bb4597ce2ce48, 0xb143c6053edcd0d6},
-      {0xf81aa16fdc1b81da, 0xdd94b7868e94050b},
-      {0x9b10a4e5e9913128, 0xca7cf2b4191c8327},
-      {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f1},
-      {0xf24a01a73cf2dccf, 0xbc633b39673c8ced},
-      {0x976e41088617ca01, 0xd5be0503e085d814},
-      {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e19},
-      {0xec9c459d51852ba2, 0xddf8e7d60ed1219f},
-      {0x93e1ab8252f33b45, 0xcabb90e5c942b504},
-      {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
-      {0xe7109bfba19c0c9d, 0x0cc512670a783ad5},
-      {0x906a617d450187e2, 0x27fb2b80668b24c6},
-      {0xb484f9dc9641e9da, 0xb1f9f660802dedf7},
-      {0xe1a63853bbd26451, 0x5e7873f8a0396974},
-      {0x8d07e33455637eb2, 0xdb0b487b6423e1e9},
-      {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda63},
-      {0xdc5c5301c56b75f7, 0x7641a140cc7810fc},
-      {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9e},
-      {0xac2820d9623bf429, 0x546345fa9fbdcd45},
-      {0xd732290fbacaf133, 0xa97c177947ad4096},
-      {0x867f59a9d4bed6c0, 0x49ed8eabcccc485e},
-      {0xa81f301449ee8c70, 0x5c68f256bfff5a75},
-      {0xd226fc195c6a2f8c, 0x73832eec6fff3112},
-      {0x83585d8fd9c25db7, 0xc831fd53c5ff7eac},
-      {0xa42e74f3d032f525, 0xba3e7ca8b77f5e56},
-      {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35ec},
-      {0x80444b5e7aa7cf85, 0x7980d163cf5b81b4},
-      {0xa0555e361951c366, 0xd7e105bcc3326220},
-      {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa8},
-      {0xfa856334878fc150, 0xb14f98f6f0feb952},
-      {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d4},
-      {0xc3b8358109e84f07, 0x0a862f80ec4700c9},
-      {0xf4a642e14c6262c8, 0xcd27bb612758c0fb},
-      {0x98e7e9cccfbd7dbd, 0x8038d51cb897789d},
-      {0xbf21e44003acdd2c, 0xe0470a63e6bd56c4},
-      {0xeeea5d5004981478, 0x1858ccfce06cac75},
-      {0x95527a5202df0ccb, 0x0f37801e0c43ebc9},
-      {0xbaa718e68396cffd, 0xd30560258f54e6bb},
-      {0xe950df20247c83fd, 0x47c6b82ef32a206a},
-      {0x91d28b7416cdd27e, 0x4cdc331d57fa5442},
-      {0xb6472e511c81471d, 0xe0133fe4adf8e953},
-      {0xe3d8f9e563a198e5, 0x58180fddd97723a7},
-      {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7649},
-      {0xb201833b35d63f73, 0x2cd2cc6551e513db},
-      {0xde81e40a034bcf4f, 0xf8077f7ea65e58d2},
-      {0x8b112e86420f6191, 0xfb04afaf27faf783},
-      {0xadd57a27d29339f6, 0x79c5db9af1f9b564},
-      {0xd94ad8b1c7380874, 0x18375281ae7822bd},
-      {0x87cec76f1c830548, 0x8f2293910d0b15b6},
-      {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb23},
-      {0xd433179d9c8cb841, 0x5fa60692a46151ec},
-      {0x849feec281d7f328, 0xdbc7c41ba6bcd334},
-      {0xa5c7ea73224deff3, 0x12b9b522906c0801},
-      {0xcf39e50feae16bef, 0xd768226b34870a01},
-      {0x81842f29f2cce375, 0xe6a1158300d46641},
-      {0xa1e53af46f801c53, 0x60495ae3c1097fd1},
-      {0xca5e89b18b602368, 0x385bb19cb14bdfc5},
-      {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6},
-      {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2},
-      {0xc5a05277621be293, 0xc7098b7305241886},
-      { 0xf70867153aa2db38,
-        0xb8cbee4fc66d1ea8 }
+                {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
+                {0x9faacf3df73609b1, 0x77b191618c54e9ad},
+                {0xc795830d75038c1d, 0xd59df5b9ef6a2418},
+                {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e},
+                {0x9becce62836ac577, 0x4ee367f9430aec33},
+                {0xc2e801fb244576d5, 0x229c41f793cda740},
+                {0xf3a20279ed56d48a, 0x6b43527578c11110},
+                {0x9845418c345644d6, 0x830a13896b78aaaa},
+                {0xbe5691ef416bd60c, 0x23cc986bc656d554},
+                {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9},
+                {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa},
+                {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54},
+                {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69},
+                {0x91376c36d99995be, 0x23100809b9c21fa2},
+                {0xb58547448ffffb2d, 0xabd40a0c2832a78b},
+                {0xe2e69915b3fff9f9, 0x16c90c8f323f516d},
+                {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4},
+                {0xb1442798f49ffb4a, 0x99cd11cfdf41779d},
+                {0xdd95317f31c7fa1d, 0x40405643d711d584},
+                {0x8a7d3eef7f1cfc52, 0x482835ea666b2573},
+                {0xad1c8eab5ee43b66, 0xda3243650005eed0},
+                {0xd863b256369d4a40, 0x90bed43e40076a83},
+                {0x873e4f75e2224e68, 0x5a7744a6e804a292},
+                {0xa90de3535aaae202, 0x711515d0a205cb37},
+                {0xd3515c2831559a83, 0x0d5a5b44ca873e04},
+                {0x8412d9991ed58091, 0xe858790afe9486c3},
+                {0xa5178fff668ae0b6, 0x626e974dbe39a873},
+                {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
+                {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a},
+                {0xa139029f6a239f72, 0x1c1fffc1ebc44e81},
+                {0xc987434744ac874e, 0xa327ffb266b56221},
+                {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9},
+                {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa},
+                {0xc4ce17b399107c22, 0xcb550fb4384d21d4},
+                {0xf6019da07f549b2b, 0x7e2a53a146606a49},
+                {0x99c102844f94e0fb, 0x2eda7444cbfc426e},
+                {0xc0314325637a1939, 0xfa911155fefb5309},
+                {0xf03d93eebc589f88, 0x793555ab7eba27cb},
+                {0x96267c7535b763b5, 0x4bc1558b2f3458df},
+                {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17},
+                {0xea9c227723ee8bcb, 0x465e15a979c1cadd},
+                {0x92a1958a7675175f, 0x0bfacd89ec191eca},
+                {0xb749faed14125d36, 0xcef980ec671f667c},
+                {0xe51c79a85916f484, 0x82b7e12780e7401b},
+                {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811},
+                {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16},
+                {0xdfbdcece67006ac9, 0x67a791e093e1d49b},
+                {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1},
+                {0xaecc49914078536d, 0x58fae9f773886e19},
+                {0xda7f5bf590966848, 0xaf39a475506a899f},
+                {0x888f99797a5e012d, 0x6d8406c952429604},
+                {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84},
+                {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65},
+                {0x855c3be0a17fcd26, 0x5cf2eea09a550680},
+                {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f},
+                {0xd0601d8efc57b08b, 0xf13b94daf124da27},
+                {0x823c12795db6ce57, 0x76c53d08d6b70859},
+                {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f},
+                {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a},
+                {0xfe5d54150b090b02, 0xd3f93b35435d7c4d},
+                {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0},
+                {0xc6b8e9b0709f109a, 0x359ab6419ca1091c},
+                {0xf867241c8cc6d4c0, 0xc30163d203c94b63},
+                {0x9b407691d7fc44f8, 0x79e0de63425dcf1e},
+                {0xc21094364dfb5636, 0x985915fc12f542e5},
+                {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e},
+                {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43},
+                {0xbd8430bd08277231, 0x50c6ff782a838354},
+                {0xece53cec4a314ebd, 0xa4f8bf5635246429},
+                {0x940f4613ae5ed136, 0x871b7795e136be9a},
+                {0xb913179899f68584, 0x28e2557b59846e40},
+                {0xe757dd7ec07426e5, 0x331aeada2fe589d0},
+                {0x9096ea6f3848984f, 0x3ff0d2c85def7622},
+                {0xb4bca50b065abe63, 0x0fed077a756b53aa},
+                {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895},
+                {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d},
+                {0xb080392cc4349dec, 0xbd8d794d96aacfb4},
+                {0xdca04777f541c567, 0xecf0d7a0fc5583a1},
+                {0x89e42caaf9491b60, 0xf41686c49db57245},
+                {0xac5d37d5b79b6239, 0x311c2875c522ced6},
+                {0xd77485cb25823ac7, 0x7d633293366b828c},
+                {0x86a8d39ef77164bc, 0xae5dff9c02033198},
+                {0xa8530886b54dbdeb, 0xd9f57f830283fdfd},
+                {0xd267caa862a12d66, 0xd072df63c324fd7c},
+                {0x8380dea93da4bc60, 0x4247cb9e59f71e6e},
+                {0xa46116538d0deb78, 0x52d9be85f074e609},
+                {0xcd795be870516656, 0x67902e276c921f8c},
+                {0x806bd9714632dff6, 0x00ba1cd8a3db53b7},
+                {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5},
+                {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce},
+                {0xfad2a4b13d1b5d6c, 0x796b805720085f82},
+                {0x9cc3a6eec6311a63, 0xcbe3303674053bb1},
+                {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d},
+                {0xf4f1b4d515acb93b, 0xee92fb5515482d45},
+                {0x991711052d8bf3c5, 0x751bdd152d4d1c4b},
+                {0xbf5cd54678eef0b6, 0xd262d45a78a0635e},
+                {0xef340a98172aace4, 0x86fb897116c87c35},
+                {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1},
+                {0xbae0a846d2195712, 0x8974836059cca10a},
+                {0xe998d258869facd7, 0x2bd1a438703fc94c},
+                {0x91ff83775423cc06, 0x7b6306a34627ddd0},
+                {0xb67f6455292cbf08, 0x1a3bc84c17b1d543},
+                {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94},
+                {0x8e938662882af53e, 0x547eb47b7282ee9d},
+                {0xb23867fb2a35b28d, 0xe99e619a4f23aa44},
+                {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5},
+                {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05},
+                {0xae0b158b4738705e, 0x9624ab50b148d446},
+                {0xd98ddaee19068c76, 0x3badd624dd9b0958},
+                {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7},
+                {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d},
+                {0xd47487cc8470652b, 0x7647c32000696720},
+                {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074},
+                {0xa5fb0a17c777cf09, 0xf468107100525891},
+                {0xcf79cc9db955c2cc, 0x7182148d4066eeb5},
+                {0x81ac1fe293d599bf, 0xc6f14cd848405531},
+                {0xa21727db38cb002f, 0xb8ada00e5a506a7d},
+                {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d},
+                {0xfd442e4688bd304a, 0x908f4a166d1da664},
+                {0x9e4a9cec15763e2e, 0x9a598e4e043287ff},
+                {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe},
+                {0xf7549530e188c128, 0xd12bee59e68ef47d},
+                {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf},
+                {0xc13a148e3032d6e7, 0xe36a52363c1faf02},
+                {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2},
+                {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba},
+                {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8},
+                {0xebdf661791d60f56, 0x111b495b3464ad22},
+                {0x936b9fcebb25c995, 0xcab10dd900beec35},
+                {0xb84687c269ef3bfb, 0x3d5d514f40eea743},
+                {0xe65829b3046b0afa, 0x0cb4a5a3112a5113},
+                {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac},
+                {0xb3f4e093db73a093, 0x59ed216765690f57},
+                {0xe0f218b8d25088b8, 0x306869c13ec3532d},
+                {0x8c974f7383725573, 0x1e414218c73a13fc},
+                {0xafbd2350644eeacf, 0xe5d1929ef90898fb},
+                {0xdbac6c247d62a583, 0xdf45f746b74abf3a},
+                {0x894bc396ce5da772, 0x6b8bba8c328eb784},
+                {0xab9eb47c81f5114f, 0x066ea92f3f326565},
+                {0xd686619ba27255a2, 0xc80a537b0efefebe},
+                {0x8613fd0145877585, 0xbd06742ce95f5f37},
+                {0xa798fc4196e952e7, 0x2c48113823b73705},
+                {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6},
+                {0x82ef85133de648c4, 0x9a984d73dbe722fc},
+                {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb},
+                {0xcc963fee10b7d1b3, 0x318df905079926a9},
+                {0xffbbcfe994e5c61f, 0xfdf17746497f7053},
+                {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634},
+                {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1},
+                {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1},
+                {0x9c1661a651213e2d, 0x06bea10ca65c084f},
+                {0xc31bfa0fe5698db8, 0x486e494fcff30a63},
+                {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb},
+                {0x986ddb5c6b3a76b7, 0xf89629465a75e01d},
+                {0xbe89523386091465, 0xf6bbb397f1135824},
+                {0xee2ba6c0678b597f, 0x746aa07ded582e2d},
+                {0x94db483840b717ef, 0xa8c2a44eb4571cdd},
+                {0xba121a4650e4ddeb, 0x92f34d62616ce414},
+                {0xe896a0d7e51e1566, 0x77b020baf9c81d18},
+                {0x915e2486ef32cd60, 0x0ace1474dc1d122f},
+                {0xb5b5ada8aaff80b8, 0x0d819992132456bb},
+                {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a},
+                {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2},
+                {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3},
+                {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf},
+                {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c},
+                {0xad4ab7112eb3929d, 0x86c16c98d2c953c7},
+                {0xd89d64d57a607744, 0xe871c7bf077ba8b8},
+                {0x87625f056c7c4a8b, 0x11471cd764ad4973},
+                {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0},
+                {0xd389b47879823479, 0x4aff1d108d4ec2c4},
+                {0x843610cb4bf160cb, 0xcedf722a585139bb},
+                {0xa54394fe1eedb8fe, 0xc2974eb4ee658829},
+                {0xce947a3da6a9273e, 0x733d226229feea33},
+                {0x811ccc668829b887, 0x0806357d5a3f5260},
+                {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8},
+                {0xc9bcff6034c13052, 0xfc89b393dd02f0b6},
+                {0xfc2c3f3841f17c67, 0xbbac2078d443ace3},
+                {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e},
+                {0xc5029163f384a931, 0x0a9e795e65d4df12},
+                {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6},
+                {0x99ea0196163fa42e, 0x504bced1bf8e4e46},
+                {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7},
+                {0xf07da27a82c37088, 0x5d767327bb4e5a4d},
+                {0x964e858c91ba2655, 0x3a6a07f8d510f870},
+                {0xbbe226efb628afea, 0x890489f70a55368c},
+                {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f},
+                {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e},
+                {0xb77ada0617e3bbcb, 0x09ce6ebb40173745},
+                {0xe55990879ddcaabd, 0xcc420a6a101d0516},
+                {0x8f57fa54c2a9eab6, 0x9fa946824a12232e},
+                {0xb32df8e9f3546564, 0x47939822dc96abfa},
+                {0xdff9772470297ebd, 0x59787e2b93bc56f8},
+                {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b},
+                {0xaefae51477a06b03, 0xede622920b6b23f2},
+                {0xdab99e59958885c4, 0xe95fab368e45ecee},
+                {0x88b402f7fd75539b, 0x11dbcb0218ebb415},
+                {0xaae103b5fcd2a881, 0xd652bdc29f26a11a},
+                {0xd59944a37c0752a2, 0x4be76d3346f04960},
+                {0x857fcae62d8493a5, 0x6f70a4400c562ddc},
+                {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953},
+                {0xd097ad07a71f26b2, 0x7e2000a41346a7a8},
+                {0x825ecc24c873782f, 0x8ed400668c0c28c9},
+                {0xa2f67f2dfa90563b, 0x728900802f0f32fb},
+                {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba},
+                {0xfea126b7d78186bc, 0xe2f610c84987bfa9},
+                {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca},
+                {0xc6ede63fa05d3143, 0x91503d1c79720dbc},
+                {0xf8a95fcf88747d94, 0x75a44c6397ce912b},
+                {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb},
+                {0xc24452da229b021b, 0xfbe85badce996169},
+                {0xf2d56790ab41c2a2, 0xfae27299423fb9c4},
+                {0x97c560ba6b0919a5, 0xdccd879fc967d41b},
+                {0xbdb6b8e905cb600f, 0x5400e987bbc1c921},
+                {0xed246723473e3813, 0x290123e9aab23b69},
+                {0x9436c0760c86e30b, 0xf9a0b6720aaf6522},
+                {0xb94470938fa89bce, 0xf808e40e8d5b3e6a},
+                {0xe7958cb87392c2c2, 0xb60b1d1230b20e05},
+                {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3},
+                {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4},
+                {0xe2280b6c20dd5232, 0x25c6da63c38de1b1},
+                {0x8d590723948a535f, 0x579c487e5a38ad0f},
+                {0xb0af48ec79ace837, 0x2d835a9df0c6d852},
+                {0xdcdb1b2798182244, 0xf8e431456cf88e66},
+                {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900},
+                {0xac8b2d36eed2dac5, 0xe272467e3d222f40},
+                {0xd7adf884aa879177, 0x5b0ed81dcc6abb10},
+                {0x86ccbb52ea94baea, 0x98e947129fc2b4ea},
+                {0xa87fea27a539e9a5, 0x3f2398d747b36225},
+                {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae},
+                {0x83a3eeeef9153e89, 0x1953cf68300424ad},
+                {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8},
+                {0xcdb02555653131b6, 0x3792f412cb06794e},
+                {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1},
+                {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5},
+                {0xc8de047564d20a8b, 0xf245825a5a445276},
+                {0xfb158592be068d2e, 0xeed6e2f0f0d56713},
+                {0x9ced737bb6c4183d, 0x55464dd69685606c},
+                {0xc428d05aa4751e4c, 0xaa97e14c3c26b887},
+                {0xf53304714d9265df, 0xd53dd99f4b3066a9},
+                {0x993fe2c6d07b7fab, 0xe546a8038efe402a},
+                {0xbf8fdb78849a5f96, 0xde98520472bdd034},
+                {0xef73d256a5c0f77c, 0x963e66858f6d4441},
+                {0x95a8637627989aad, 0xdde7001379a44aa9},
+                {0xbb127c53b17ec159, 0x5560c018580d5d53},
+                {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7},
+                {0x9226712162ab070d, 0xcab3961304ca70e9},
+                {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23},
+                {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b},
+                {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243},
+                {0xb267ed1940f1c61c, 0x55f038b237591ed4},
+                {0xdf01e85f912e37a3, 0x6b6c46dec52f6689},
+                {0x8b61313bbabce2c6, 0x2323ac4b3b3da016},
+                {0xae397d8aa96c1b77, 0xabec975e0a0d081b},
+                {0xd9c7dced53c72255, 0x96e7bd358c904a22},
+                {0x881cea14545c7575, 0x7e50d64177da2e55},
+                {0xaa242499697392d2, 0xdde50bd1d5d0b9ea},
+                {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865},
+                {0x84ec3c97da624ab4, 0xbd5af13bef0b113f},
+                {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f},
+                {0xcfb11ead453994ba, 0x67de18eda5814af3},
+                {0x81ceb32c4b43fcf4, 0x80eacf948770ced8},
+                {0xa2425ff75e14fc31, 0xa1258379a94d028e},
+                {0xcad2f7f5359a3b3e, 0x096ee45813a04331},
+                {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd},
+                {0x9e74d1b791e07e48, 0x775ea264cf55347e},
+                {0xc612062576589dda, 0x95364afe032a819e},
+                {0xf79687aed3eec551, 0x3a83ddbd83f52205},
+                {0x9abe14cd44753b52, 0xc4926a9672793543},
+                {0xc16d9a0095928a27, 0x75b7053c0f178294},
+                {0xf1c90080baf72cb1, 0x5324c68b12dd6339},
+                {0x971da05074da7bee, 0xd3f6fc16ebca5e04},
+                {0xbce5086492111aea, 0x88f4bb1ca6bcf585},
+                {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6},
+                {0x9392ee8e921d5d07, 0x3aff322e62439fd0},
+                {0xb877aa3236a4b449, 0x09befeb9fad487c3},
+                {0xe69594bec44de15b, 0x4c2ebe687989a9b4},
+                {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11},
+                {0xb424dc35095cd80f, 0x538484c19ef38c95},
+                {0xe12e13424bb40e13, 0x2865a5f206b06fba},
+                {0x8cbccc096f5088cb, 0xf93f87b7442e45d4},
+                {0xafebff0bcb24aafe, 0xf78f69a51539d749},
+                {0xdbe6fecebdedd5be, 0xb573440e5a884d1c},
+                {0x89705f4136b4a597, 0x31680a88f8953031},
+                {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e},
+                {0xd6bf94d5e57a42bc, 0x3d32907604691b4d},
+                {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110},
+                {0xa7c5ac471b478423, 0x0fcf80dc33721d54},
+                {0xd1b71758e219652b, 0xd3c36113404ea4a9},
+                {0x83126e978d4fdf3b, 0x645a1cac083126ea},
+                {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4},
+                {0xcccccccccccccccc, 0xcccccccccccccccd},
+                {0x8000000000000000, 0x0000000000000000},
+                {0xa000000000000000, 0x0000000000000000},
+                {0xc800000000000000, 0x0000000000000000},
+                {0xfa00000000000000, 0x0000000000000000},
+                {0x9c40000000000000, 0x0000000000000000},
+                {0xc350000000000000, 0x0000000000000000},
+                {0xf424000000000000, 0x0000000000000000},
+                {0x9896800000000000, 0x0000000000000000},
+                {0xbebc200000000000, 0x0000000000000000},
+                {0xee6b280000000000, 0x0000000000000000},
+                {0x9502f90000000000, 0x0000000000000000},
+                {0xba43b74000000000, 0x0000000000000000},
+                {0xe8d4a51000000000, 0x0000000000000000},
+                {0x9184e72a00000000, 0x0000000000000000},
+                {0xb5e620f480000000, 0x0000000000000000},
+                {0xe35fa931a0000000, 0x0000000000000000},
+                {0x8e1bc9bf04000000, 0x0000000000000000},
+                {0xb1a2bc2ec5000000, 0x0000000000000000},
+                {0xde0b6b3a76400000, 0x0000000000000000},
+                {0x8ac7230489e80000, 0x0000000000000000},
+                {0xad78ebc5ac620000, 0x0000000000000000},
+                {0xd8d726b7177a8000, 0x0000000000000000},
+                {0x878678326eac9000, 0x0000000000000000},
+                {0xa968163f0a57b400, 0x0000000000000000},
+                {0xd3c21bcecceda100, 0x0000000000000000},
+                {0x84595161401484a0, 0x0000000000000000},
+                {0xa56fa5b99019a5c8, 0x0000000000000000},
+                {0xcecb8f27f4200f3a, 0x0000000000000000},
+                {0x813f3978f8940984, 0x4000000000000000},
+                {0xa18f07d736b90be5, 0x5000000000000000},
+                {0xc9f2c9cd04674ede, 0xa400000000000000},
+                {0xfc6f7c4045812296, 0x4d00000000000000},
+                {0x9dc5ada82b70b59d, 0xf020000000000000},
+                {0xc5371912364ce305, 0x6c28000000000000},
+                {0xf684df56c3e01bc6, 0xc732000000000000},
+                {0x9a130b963a6c115c, 0x3c7f400000000000},
+                {0xc097ce7bc90715b3, 0x4b9f100000000000},
+                {0xf0bdc21abb48db20, 0x1e86d40000000000},
+                {0x96769950b50d88f4, 0x1314448000000000},
+                {0xbc143fa4e250eb31, 0x17d955a000000000},
+                {0xeb194f8e1ae525fd, 0x5dcfab0800000000},
+                {0x92efd1b8d0cf37be, 0x5aa1cae500000000},
+                {0xb7abc627050305ad, 0xf14a3d9e40000000},
+                {0xe596b7b0c643c719, 0x6d9ccd05d0000000},
+                {0x8f7e32ce7bea5c6f, 0xe4820023a2000000},
+                {0xb35dbf821ae4f38b, 0xdda2802c8a800000},
+                {0xe0352f62a19e306e, 0xd50b2037ad200000},
+                {0x8c213d9da502de45, 0x4526f422cc340000},
+                {0xaf298d050e4395d6, 0x9670b12b7f410000},
+                {0xdaf3f04651d47b4c, 0x3c0cdd765f114000},
+                {0x88d8762bf324cd0f, 0xa5880a69fb6ac800},
+                {0xab0e93b6efee0053, 0x8eea0d047a457a00},
+                {0xd5d238a4abe98068, 0x72a4904598d6d880},
+                {0x85a36366eb71f041, 0x47a6da2b7f864750},
+                {0xa70c3c40a64e6c51, 0x999090b65f67d924},
+                {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d},
+                {0x82818f1281ed449f, 0xbff8f10e7a8921a5},
+                {0xa321f2d7226895c7, 0xaff72d52192b6a0e},
+                {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764491},
+                {0xfee50b7025c36a08, 0x02f236d04753d5b5},
+                {0x9f4f2726179a2245, 0x01d762422c946591},
+                {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef6},
+                {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb3},
+                {0x9b934c3b330c8577, 0x63cc55f49f88eb30},
+                {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fc},
+                {0xf316271c7fc3908a, 0x8bef464e3945ef7b},
+                {0x97edd871cfda3a56, 0x97758bf0e3cbb5ad},
+                {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea318},
+                {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bde},
+                {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6b},
+                {0xb975d6b6ee39e436, 0xb3e2fd538e122b45},
+                {0xe7d34c64a9c85d44, 0x60dbbca87196b617},
+                {0x90e40fbeea1d3a4a, 0xbc8955e946fe31ce},
+                {0xb51d13aea4a488dd, 0x6babab6398bdbe42},
+                {0xe264589a4dcdab14, 0xc696963c7eed2dd2},
+                {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca3},
+                {0xb0de65388cc8ada8, 0x3b25a55f43294bcc},
+                {0xdd15fe86affad912, 0x49ef0eb713f39ebf},
+                {0x8a2dbf142dfcc7ab, 0x6e3569326c784338},
+                {0xacb92ed9397bf996, 0x49c2c37f07965405},
+                {0xd7e77a8f87daf7fb, 0xdc33745ec97be907},
+                {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a4},
+                {0xa8acd7c0222311bc, 0xc40832ea0d68ce0d},
+                {0xd2d80db02aabd62b, 0xf50a3fa490c30191},
+                {0x83c7088e1aab65db, 0x792667c6da79e0fb},
+                {0xa4b8cab1a1563f52, 0x577001b891185939},
+                {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87},
+                {0x80b05e5ac60b6178, 0x544f8158315b05b5},
+                {0xa0dc75f1778e39d6, 0x696361ae3db1c722},
+                {0xc913936dd571c84c, 0x03bc3a19cd1e38ea},
+                {0xfb5878494ace3a5f, 0x04ab48a04065c724},
+                {0x9d174b2dcec0e47b, 0x62eb0d64283f9c77},
+                {0xc45d1df942711d9a, 0x3ba5d0bd324f8395},
+                {0xf5746577930d6500, 0xca8f44ec7ee3647a},
+                {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecc},
+                {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67f},
+                {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101f},
+                {0x95d04aee3b80ece5, 0xbba1f1d158724a13},
+                {0xbb445da9ca61281f, 0x2a8a6e45ae8edc98},
+                {0xea1575143cf97226, 0xf52d09d71a3293be},
+                {0x924d692ca61be758, 0x593c2626705f9c57},
+                {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836d},
+                {0xe498f455c38b997a, 0x0b6dfb9c0f956448},
+                {0x8edf98b59a373fec, 0x4724bd4189bd5ead},
+                {0xb2977ee300c50fe7, 0x58edec91ec2cb658},
+                {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ee},
+                {0x8b865b215899f46c, 0xbd79e0d20082ee75},
+                {0xae67f1e9aec07187, 0xecd8590680a3aa12},
+                {0xda01ee641a708de9, 0xe80e6f4820cc9496},
+                {0x884134fe908658b2, 0x3109058d147fdcde},
+                {0xaa51823e34a7eede, 0xbd4b46f0599fd416},
+                {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91b},
+                {0x850fadc09923329e, 0x03e2cf6bc604ddb1},
+                {0xa6539930bf6bff45, 0x84db8346b786151d},
+                {0xcfe87f7cef46ff16, 0xe612641865679a64},
+                {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07f},
+                {0xa26da3999aef7749, 0xe3be5e330f38f09e},
+                {0xcb090c8001ab551c, 0x5cadf5bfd3072cc6},
+                {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f7},
+                {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afb},
+                {0xc646d63501a1511d, 0xb281e1fd541501b9},
+                {0xf7d88bc24209a565, 0x1f225a7ca91a4227},
+                {0x9ae757596946075f, 0x3375788de9b06959},
+                {0xc1a12d2fc3978937, 0x0052d6b1641c83af},
+                {0xf209787bb47d6b84, 0xc0678c5dbd23a49b},
+                {0x9745eb4d50ce6332, 0xf840b7ba963646e1},
+                {0xbd176620a501fbff, 0xb650e5a93bc3d899},
+                {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebf},
+                {0x93ba47c980e98cdf, 0xc66f336c36b10138},
+                {0xb8a8d9bbe123f017, 0xb80b0047445d4185},
+                {0xe6d3102ad96cec1d, 0xa60dc059157491e6},
+                {0x9043ea1ac7e41392, 0x87c89837ad68db30},
+                {0xb454e4a179dd1877, 0x29babe4598c311fc},
+                {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67b},
+                {0x8ce2529e2734bb1d, 0x1899e4a65f58660d},
+                {0xb01ae745b101e9e4, 0x5ec05dcff72e7f90},
+                {0xdc21a1171d42645d, 0x76707543f4fa1f74},
+                {0x899504ae72497eba, 0x6a06494a791c53a9},
+                {0xabfa45da0edbde69, 0x0487db9d17636893},
+                {0xd6f8d7509292d603, 0x45a9d2845d3c42b7},
+                {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3},
+                {0xa7f26836f282b732, 0x8e6cac7768d7141f},
+                {0xd1ef0244af2364ff, 0x3207d795430cd927},
+                {0x8335616aed761f1f, 0x7f44e6bd49e807b9},
+                {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a7},
+                {0xcd036837130890a1, 0x36dba887c37a8c10},
+                {0x802221226be55a64, 0xc2494954da2c978a},
+                {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6d},
+                {0xc83553c5c8965d3d, 0x6f92829494e5acc8},
+                {0xfa42a8b73abbf48c, 0xcb772339ba1f17fa},
+                {0x9c69a97284b578d7, 0xff2a760414536efc},
+                {0xc38413cf25e2d70d, 0xfef5138519684abb},
+                {0xf46518c2ef5b8cd1, 0x7eb258665fc25d6a},
+                {0x98bf2f79d5993802, 0xef2f773ffbd97a62},
+                {0xbeeefb584aff8603, 0xaafb550ffacfd8fb},
+                {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf39},
+                {0x952ab45cfa97a0b2, 0xdd945a747bf26184},
+                {0xba756174393d88df, 0x94f971119aeef9e5},
+                {0xe912b9d1478ceb17, 0x7a37cd5601aab85e},
+                {0x91abb422ccb812ee, 0xac62e055c10ab33b},
+                {0xb616a12b7fe617aa, 0x577b986b314d600a},
+                {0xe39c49765fdf9d94, 0xed5a7e85fda0b80c},
+                {0x8e41ade9fbebc27d, 0x14588f13be847308},
+                {0xb1d219647ae6b31c, 0x596eb2d8ae258fc9},
+                {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bc},
+                {0x8aec23d680043bee, 0x25de7bb9480d5855},
+                {0xada72ccc20054ae9, 0xaf561aa79a10ae6b},
+                {0xd910f7ff28069da4, 0x1b2ba1518094da05},
+                {0x87aa9aff79042286, 0x90fb44d2f05d0843},
+                {0xa99541bf57452b28, 0x353a1607ac744a54},
+                {0xd3fa922f2d1675f2, 0x42889b8997915ce9},
+                {0x847c9b5d7c2e09b7, 0x69956135febada12},
+                {0xa59bc234db398c25, 0x43fab9837e699096},
+                {0xcf02b2c21207ef2e, 0x94f967e45e03f4bc},
+                {0x8161afb94b44f57d, 0x1d1be0eebac278f6},
+                {0xa1ba1ba79e1632dc, 0x6462d92a69731733},
+                {0xca28a291859bbf93, 0x7d7b8f7503cfdcff},
+                {0xfcb2cb35e702af78, 0x5cda735244c3d43f},
+                {0x9defbf01b061adab, 0x3a0888136afa64a8},
+                {0xc56baec21c7a1916, 0x088aaa1845b8fdd1},
+                {0xf6c69a72a3989f5b, 0x8aad549e57273d46},
+                {0x9a3c2087a63f6399, 0x36ac54e2f678864c},
+                {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7de},
+                {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d6},
+                {0x969eb7c47859e743, 0x9f644ae5a4b1b326},
+                {0xbc4665b596706114, 0x873d5d9f0dde1fef},
+                {0xeb57ff22fc0c7959, 0xa90cb506d155a7eb},
+                {0x9316ff75dd87cbd8, 0x09a7f12442d588f3},
+                {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb30},
+                {0xe5d3ef282a242e81, 0x8f1668c8a86da5fb},
+                {0x8fa475791a569d10, 0xf96e017d694487bd},
+                {0xb38d92d760ec4455, 0x37c981dcc395a9ad},
+                {0xe070f78d3927556a, 0x85bbe253f47b1418},
+                {0x8c469ab843b89562, 0x93956d7478ccec8f},
+                {0xaf58416654a6babb, 0x387ac8d1970027b3},
+                {0xdb2e51bfe9d0696a, 0x06997b05fcc0319f},
+                {0x88fcf317f22241e2, 0x441fece3bdf81f04},
+                {0xab3c2fddeeaad25a, 0xd527e81cad7626c4},
+                {0xd60b3bd56a5586f1, 0x8a71e223d8d3b075},
+                {0x85c7056562757456, 0xf6872d5667844e4a},
+                {0xa738c6bebb12d16c, 0xb428f8ac016561dc},
+                {0xd106f86e69d785c7, 0xe13336d701beba53},
+                {0x82a45b450226b39c, 0xecc0024661173474},
+                {0xa34d721642b06084, 0x27f002d7f95d0191},
+                {0xcc20ce9bd35c78a5, 0x31ec038df7b441f5},
+                {0xff290242c83396ce, 0x7e67047175a15272},
+                {0x9f79a169bd203e41, 0x0f0062c6e984d387},
+                {0xc75809c42c684dd1, 0x52c07b78a3e60869},
+                {0xf92e0c3537826145, 0xa7709a56ccdf8a83},
+                {0x9bbcc7a142b17ccb, 0x88a66076400bb692},
+                {0xc2abf989935ddbfe, 0x6acff893d00ea436},
+                {0xf356f7ebf83552fe, 0x0583f6b8c4124d44},
+                {0x98165af37b2153de, 0xc3727a337a8b704b},
+                {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5d},
+                {0xeda2ee1c7064130c, 0x1162def06f79df74},
+                {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba9},
+                {0xb9a74a0637ce2ee1, 0x6d953e2bd7173693},
+                {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0438},
+                {0x910ab1d4db9914a0, 0x1d9c9892400a22a3},
+                {0xb54d5e4a127f59c8, 0x2503beb6d00cab4c},
+                {0xe2a0b5dc971f303a, 0x2e44ae64840fd61e},
+                {0x8da471a9de737e24, 0x5ceaecfed289e5d3},
+                {0xb10d8e1456105dad, 0x7425a83e872c5f48},
+                {0xdd50f1996b947518, 0xd12f124e28f7771a},
+                {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa70},
+                {0xace73cbfdc0bfb7b, 0x636cc64d1001550c},
+                {0xd8210befd30efa5a, 0x3c47f7e05401aa4f},
+                {0x8714a775e3e95c78, 0x65acfaec34810a72},
+                {0xa8d9d1535ce3b396, 0x7f1839a741a14d0e},
+                {0xd31045a8341ca07c, 0x1ede48111209a051},
+                {0x83ea2b892091e44d, 0x934aed0aab460433},
+                {0xa4e4b66b68b65d60, 0xf81da84d56178540},
+                {0xce1de40642e3f4b9, 0x36251260ab9d668f},
+                {0x80d2ae83e9ce78f3, 0xc1d72b7c6b42601a},
+                {0xa1075a24e4421730, 0xb24cf65b8612f820},
+                {0xc94930ae1d529cfc, 0xdee033f26797b628},
+                {0xfb9b7cd9a4a7443c, 0x169840ef017da3b2},
+                {0x9d412e0806e88aa5, 0x8e1f289560ee864f},
+                {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e3},
+                {0xf5b5d7ec8acb58a2, 0xae10af696774b1dc},
+                {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef2a},
+                {0xbff610b0cc6edd3f, 0x17fd090a58d32af4},
+                {0xeff394dcff8a948e, 0xddfc4b4cef07f5b1},
+                {0x95f83d0a1fb69cd9, 0x4abdaf101564f98f},
+                {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f2},
+                {0xea53df5fd18d5513, 0x84c86189216dc5ee},
+                {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb5},
+                {0xb7118682dbb66a77, 0x3fbc8c33221dc2a2},
+                {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
+                {0x8f05b1163ba6832d, 0x29cb4d87f2a7400f},
+                {0xb2c71d5bca9023f8, 0x743e20e9ef511013},
+                {0xdf78e4b2bd342cf6, 0x914da9246b255417},
+                {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548f},
+                {0xae9672aba3d0c320, 0xa184ac2473b529b2},
+                {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741f},
+                {0x8865899617fb1871, 0x7e2fa67c7a658893},
+                {0xaa7eebfb9df9de8d, 0xddbb901b98feeab8},
+                {0xd51ea6fa85785631, 0x552a74227f3ea566},
+                {0x8533285c936b35de, 0xd53a88958f872760},
+                {0xa67ff273b8460356, 0x8a892abaf368f138},
+                {0xd01fef10a657842c, 0x2d2b7569b0432d86},
+                {0x8213f56a67f6b29b, 0x9c3b29620e29fc74},
+                {0xa298f2c501f45f42, 0x8349f3ba91b47b90},
+                {0xcb3f2f7642717713, 0x241c70a936219a74},
+                {0xfe0efb53d30dd4d7, 0xed238cd383aa0111},
+                {0x9ec95d1463e8a506, 0xf4363804324a40ab},
+                {0xc67bb4597ce2ce48, 0xb143c6053edcd0d6},
+                {0xf81aa16fdc1b81da, 0xdd94b7868e94050b},
+                {0x9b10a4e5e9913128, 0xca7cf2b4191c8327},
+                {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f1},
+                {0xf24a01a73cf2dccf, 0xbc633b39673c8ced},
+                {0x976e41088617ca01, 0xd5be0503e085d814},
+                {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e19},
+                {0xec9c459d51852ba2, 0xddf8e7d60ed1219f},
+                {0x93e1ab8252f33b45, 0xcabb90e5c942b504},
+                {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
+                {0xe7109bfba19c0c9d, 0x0cc512670a783ad5},
+                {0x906a617d450187e2, 0x27fb2b80668b24c6},
+                {0xb484f9dc9641e9da, 0xb1f9f660802dedf7},
+                {0xe1a63853bbd26451, 0x5e7873f8a0396974},
+                {0x8d07e33455637eb2, 0xdb0b487b6423e1e9},
+                {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda63},
+                {0xdc5c5301c56b75f7, 0x7641a140cc7810fc},
+                {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9e},
+                {0xac2820d9623bf429, 0x546345fa9fbdcd45},
+                {0xd732290fbacaf133, 0xa97c177947ad4096},
+                {0x867f59a9d4bed6c0, 0x49ed8eabcccc485e},
+                {0xa81f301449ee8c70, 0x5c68f256bfff5a75},
+                {0xd226fc195c6a2f8c, 0x73832eec6fff3112},
+                {0x83585d8fd9c25db7, 0xc831fd53c5ff7eac},
+                {0xa42e74f3d032f525, 0xba3e7ca8b77f5e56},
+                {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35ec},
+                {0x80444b5e7aa7cf85, 0x7980d163cf5b81b4},
+                {0xa0555e361951c366, 0xd7e105bcc3326220},
+                {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa8},
+                {0xfa856334878fc150, 0xb14f98f6f0feb952},
+                {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d4},
+                {0xc3b8358109e84f07, 0x0a862f80ec4700c9},
+                {0xf4a642e14c6262c8, 0xcd27bb612758c0fb},
+                {0x98e7e9cccfbd7dbd, 0x8038d51cb897789d},
+                {0xbf21e44003acdd2c, 0xe0470a63e6bd56c4},
+                {0xeeea5d5004981478, 0x1858ccfce06cac75},
+                {0x95527a5202df0ccb, 0x0f37801e0c43ebc9},
+                {0xbaa718e68396cffd, 0xd30560258f54e6bb},
+                {0xe950df20247c83fd, 0x47c6b82ef32a206a},
+                {0x91d28b7416cdd27e, 0x4cdc331d57fa5442},
+                {0xb6472e511c81471d, 0xe0133fe4adf8e953},
+                {0xe3d8f9e563a198e5, 0x58180fddd97723a7},
+                {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7649},
+                {0xb201833b35d63f73, 0x2cd2cc6551e513db},
+                {0xde81e40a034bcf4f, 0xf8077f7ea65e58d2},
+                {0x8b112e86420f6191, 0xfb04afaf27faf783},
+                {0xadd57a27d29339f6, 0x79c5db9af1f9b564},
+                {0xd94ad8b1c7380874, 0x18375281ae7822bd},
+                {0x87cec76f1c830548, 0x8f2293910d0b15b6},
+                {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb23},
+                {0xd433179d9c8cb841, 0x5fa60692a46151ec},
+                {0x849feec281d7f328, 0xdbc7c41ba6bcd334},
+                {0xa5c7ea73224deff3, 0x12b9b522906c0801},
+                {0xcf39e50feae16bef, 0xd768226b34870a01},
+                {0x81842f29f2cce375, 0xe6a1158300d46641},
+                {0xa1e53af46f801c53, 0x60495ae3c1097fd1},
+                {0xca5e89b18b602368, 0x385bb19cb14bdfc5},
+                {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6},
+                {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2},
+                {0xc5a05277621be293, 0xc7098b7305241886},
+                {0xf70867153aa2db38, 0xb8cbee4fc66d1ea8}
 #else
-      {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
-      {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
-      {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f},
-      {0x86a8d39ef77164bc, 0xae5dff9c02033198},
-      {0xd98ddaee19068c76, 0x3badd624dd9b0958},
-      {0xafbd2350644eeacf, 0xe5d1929ef90898fb},
-      {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2},
-      {0xe55990879ddcaabd, 0xcc420a6a101d0516},
-      {0xb94470938fa89bce, 0xf808e40e8d5b3e6a},
-      {0x95a8637627989aad, 0xdde7001379a44aa9},
-      {0xf1c90080baf72cb1, 0x5324c68b12dd6339},
-      {0xc350000000000000, 0x0000000000000000},
-      {0x9dc5ada82b70b59d, 0xf020000000000000},
-      {0xfee50b7025c36a08, 0x02f236d04753d5b5},
-      {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87},
-      {0xa6539930bf6bff45, 0x84db8346b786151d},
-      {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3},
-      {0xd910f7ff28069da4, 0x1b2ba1518094da05},
-      {0xaf58416654a6babb, 0x387ac8d1970027b3},
-      {0x8da471a9de737e24, 0x5ceaecfed289e5d3},
-      {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
-      {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
-      { 0x95527a5202df0ccb,
-        0x0f37801e0c43ebc9 }
+                {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
+                {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
+                {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f},
+                {0x86a8d39ef77164bc, 0xae5dff9c02033198},
+                {0xd98ddaee19068c76, 0x3badd624dd9b0958},
+                {0xafbd2350644eeacf, 0xe5d1929ef90898fb},
+                {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2},
+                {0xe55990879ddcaabd, 0xcc420a6a101d0516},
+                {0xb94470938fa89bce, 0xf808e40e8d5b3e6a},
+                {0x95a8637627989aad, 0xdde7001379a44aa9},
+                {0xf1c90080baf72cb1, 0x5324c68b12dd6339},
+                {0xc350000000000000, 0x0000000000000000},
+                {0x9dc5ada82b70b59d, 0xf020000000000000},
+                {0xfee50b7025c36a08, 0x02f236d04753d5b5},
+                {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87},
+                {0xa6539930bf6bff45, 0x84db8346b786151d},
+                {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3},
+                {0xd910f7ff28069da4, 0x1b2ba1518094da05},
+                {0xaf58416654a6babb, 0x387ac8d1970027b3},
+                {0x8da471a9de737e24, 0x5ceaecfed289e5d3},
+                {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
+                {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
+                { 0x95527a5202df0ccb,
+                  0x0f37801e0c43ebc9 }
 #endif
-    };
+            };
 
 #if FMT_USE_FULL_CACHE_DRAGONBOX
-    return pow10_significands[k - float_info<double>::min_k];
+            return pow10_significands[k - float_info<double>::min_k];
 #else
-    static constexpr const uint64_t powers_of_5_64[] = {
-        0x0000000000000001, 0x0000000000000005, 0x0000000000000019,
-        0x000000000000007d, 0x0000000000000271, 0x0000000000000c35,
-        0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1,
-        0x00000000001dcd65, 0x00000000009502f9, 0x0000000002e90edd,
-        0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9,
-        0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5,
-        0x000003782dace9d9, 0x00001158e460913d, 0x000056bc75e2d631,
-        0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed,
-        0x00d3c21bcecceda1, 0x0422ca8b0a00a425, 0x14adf4b7320334b9};
-
-    static const int compression_ratio = 27;
-
-    // Compute base index.
-    int cache_index = (k - float_info<double>::min_k) / compression_ratio;
-    int kb = cache_index * compression_ratio + float_info<double>::min_k;
-    int offset = k - kb;
-
-    // Get base cache.
-    uint128_fallback base_cache = pow10_significands[cache_index];
-    if (offset == 0) return base_cache;
-
-    // Compute the required amount of bit-shift.
-    int alpha = floor_log2_pow10(kb + offset) - floor_log2_pow10(kb) - offset;
-    FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected");
-
-    // Try to recover the real cache.
-    uint64_t pow5 = powers_of_5_64[offset];
-    uint128_fallback recovered_cache = umul128(base_cache.high(), pow5);
-    uint128_fallback middle_low = umul128(base_cache.low(), pow5);
-
-    recovered_cache += middle_low.high();
-
-    uint64_t high_to_middle = recovered_cache.high() << (64 - alpha);
-    uint64_t middle_to_low = recovered_cache.low() << (64 - alpha);
-
-    recovered_cache =
-        uint128_fallback{(recovered_cache.low() >> alpha) | high_to_middle,
-                         ((middle_low.low() >> alpha) | middle_to_low)};
-    FMT_ASSERT(recovered_cache.low() + 1 != 0, "");
-    return {recovered_cache.high(), recovered_cache.low() + 1};
+            static constexpr const uint64_t powers_of_5_64[] = {
+                0x0000000000000001, 0x0000000000000005, 0x0000000000000019, 0x000000000000007d, 0x0000000000000271,
+                0x0000000000000c35, 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, 0x00000000001dcd65,
+                0x00000000009502f9, 0x0000000002e90edd, 0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9,
+                0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5, 0x000003782dace9d9, 0x00001158e460913d,
+                0x000056bc75e2d631, 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, 0x00d3c21bcecceda1,
+                0x0422ca8b0a00a425, 0x14adf4b7320334b9};
+
+            static const int compression_ratio = 27;
+
+            // Compute base index.
+            int cache_index = (k - float_info<double>::min_k) / compression_ratio;
+            int kb          = cache_index * compression_ratio + float_info<double>::min_k;
+            int offset      = k - kb;
+
+            // Get base cache.
+            uint128_fallback base_cache = pow10_significands[cache_index];
+            if(offset == 0)
+                return base_cache;
+
+            // Compute the required amount of bit-shift.
+            int alpha = floor_log2_pow10(kb + offset) - floor_log2_pow10(kb) - offset;
+            FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected");
+
+            // Try to recover the real cache.
+            uint64_t         pow5            = powers_of_5_64[offset];
+            uint128_fallback recovered_cache = umul128(base_cache.high(), pow5);
+            uint128_fallback middle_low      = umul128(base_cache.low(), pow5);
+
+            recovered_cache += middle_low.high();
+
+            uint64_t high_to_middle = recovered_cache.high() << (64 - alpha);
+            uint64_t middle_to_low  = recovered_cache.low() << (64 - alpha);
+
+            recovered_cache = uint128_fallback{
+                (recovered_cache.low() >> alpha) | high_to_middle, ((middle_low.low() >> alpha) | middle_to_low)};
+            FMT_ASSERT(recovered_cache.low() + 1 != 0, "");
+            return {recovered_cache.high(), recovered_cache.low() + 1};
 #endif
-  }
-
-  struct compute_mul_result {
-    carrier_uint result;
-    bool is_integer;
-  };
-  struct compute_mul_parity_result {
-    bool parity;
-    bool is_integer;
-  };
-
-  static compute_mul_result compute_mul(
-      carrier_uint u, const cache_entry_type& cache) noexcept {
-    auto r = umul192_upper128(u, cache);
-    return {r.high(), r.low() == 0};
-  }
-
-  static uint32_t compute_delta(cache_entry_type const& cache,
-                                int beta) noexcept {
-    return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
-  }
-
-  static compute_mul_parity_result compute_mul_parity(
-      carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
-    FMT_ASSERT(beta >= 1, "");
-    FMT_ASSERT(beta < 64, "");
-
-    auto r = umul192_lower128(two_f, cache);
-    return {((r.high() >> (64 - beta)) & 1) != 0,
-            ((r.high() << beta) | (r.low() >> (64 - beta))) == 0};
-  }
-
-  static carrier_uint compute_left_endpoint_for_shorter_interval_case(
-      const cache_entry_type& cache, int beta) noexcept {
-    return (cache.high() -
-            (cache.high() >> (num_significand_bits<double>() + 2))) >>
-           (64 - num_significand_bits<double>() - 1 - beta);
-  }
-
-  static carrier_uint compute_right_endpoint_for_shorter_interval_case(
-      const cache_entry_type& cache, int beta) noexcept {
-    return (cache.high() +
-            (cache.high() >> (num_significand_bits<double>() + 1))) >>
-           (64 - num_significand_bits<double>() - 1 - beta);
-  }
-
-  static carrier_uint compute_round_up_for_shorter_interval_case(
-      const cache_entry_type& cache, int beta) noexcept {
-    return ((cache.high() >> (64 - num_significand_bits<double>() - 2 - beta)) +
-            1) /
-           2;
-  }
-};
-
-// Various integer checks
-template <class T>
-bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
-  const int case_shorter_interval_left_endpoint_lower_threshold = 2;
-  const int case_shorter_interval_left_endpoint_upper_threshold = 3;
-  return exponent >= case_shorter_interval_left_endpoint_lower_threshold &&
-         exponent <= case_shorter_interval_left_endpoint_upper_threshold;
-}
-
-// Remove trailing zeros from n and return the number of zeros removed (float)
-FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept {
-  FMT_ASSERT(n != 0, "");
-  const uint32_t mod_inv_5 = 0xcccccccd;
-  const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
-
-  int s = 0;
-  while (true) {
-    auto q = rotr(n * mod_inv_25, 2);
-    if (q > max_value<uint32_t>() / 100) break;
-    n = q;
-    s += 2;
-  }
-  auto q = rotr(n * mod_inv_5, 1);
-  if (q <= max_value<uint32_t>() / 10) {
-    n = q;
-    s |= 1;
-  }
-
-  return s;
-}
-
-// Removes trailing zeros and returns the number of zeros removed (double)
-FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
-  FMT_ASSERT(n != 0, "");
-
-  // This magic number is ceil(2^90 / 10^8).
-  constexpr uint64_t magic_number = 12379400392853802749ull;
-  auto nm = umul128(n, magic_number);
-
-  // Is n is divisible by 10^8?
-  if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
-    // If yes, work with the quotient.
-    auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
-
-    const uint32_t mod_inv_5 = 0xcccccccd;
-    const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
+        }
+
+        struct compute_mul_result
+        {
+            carrier_uint result;
+            bool         is_integer;
+        };
+        struct compute_mul_parity_result
+        {
+            bool parity;
+            bool is_integer;
+        };
+
+        static compute_mul_result compute_mul(carrier_uint u, const cache_entry_type &cache) noexcept
+        {
+            auto r = umul192_upper128(u, cache);
+            return {r.high(), r.low() == 0};
+        }
+
+        static uint32_t compute_delta(cache_entry_type const &cache, int beta) noexcept
+        {
+            return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
+        }
+
+        static compute_mul_parity_result
+        compute_mul_parity(carrier_uint two_f, const cache_entry_type &cache, int beta) noexcept
+        {
+            FMT_ASSERT(beta >= 1, "");
+            FMT_ASSERT(beta < 64, "");
+
+            auto r = umul192_lower128(two_f, cache);
+            return {((r.high() >> (64 - beta)) & 1) != 0, ((r.high() << beta) | (r.low() >> (64 - beta))) == 0};
+        }
+
+        static carrier_uint
+        compute_left_endpoint_for_shorter_interval_case(const cache_entry_type &cache, int beta) noexcept
+        {
+            return (cache.high() - (cache.high() >> (num_significand_bits<double>() + 2))) >>
+                   (64 - num_significand_bits<double>() - 1 - beta);
+        }
+
+        static carrier_uint
+        compute_right_endpoint_for_shorter_interval_case(const cache_entry_type &cache, int beta) noexcept
+        {
+            return (cache.high() + (cache.high() >> (num_significand_bits<double>() + 1))) >>
+                   (64 - num_significand_bits<double>() - 1 - beta);
+        }
+
+        static carrier_uint compute_round_up_for_shorter_interval_case(const cache_entry_type &cache, int beta) noexcept
+        {
+            return ((cache.high() >> (64 - num_significand_bits<double>() - 2 - beta)) + 1) / 2;
+        }
+    };
 
-    int s = 8;
-    while (true) {
-      auto q = rotr(n32 * mod_inv_25, 2);
-      if (q > max_value<uint32_t>() / 100) break;
-      n32 = q;
-      s += 2;
+    // Various integer checks
+    template <class T>
+    bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept
+    {
+        const int case_shorter_interval_left_endpoint_lower_threshold = 2;
+        const int case_shorter_interval_left_endpoint_upper_threshold = 3;
+        return exponent >= case_shorter_interval_left_endpoint_lower_threshold &&
+               exponent <= case_shorter_interval_left_endpoint_upper_threshold;
     }
-    auto q = rotr(n32 * mod_inv_5, 1);
-    if (q <= max_value<uint32_t>() / 10) {
-      n32 = q;
-      s |= 1;
+
+    // Remove trailing zeros from n and return the number of zeros removed (float)
+    FMT_INLINE int remove_trailing_zeros(uint32_t &n) noexcept
+    {
+        FMT_ASSERT(n != 0, "");
+        const uint32_t mod_inv_5  = 0xcccccccd;
+        const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
+
+        int s = 0;
+        while(true)
+        {
+            auto q = rotr(n * mod_inv_25, 2);
+            if(q > max_value<uint32_t>() / 100)
+                break;
+            n = q;
+            s += 2;
+        }
+        auto q = rotr(n * mod_inv_5, 1);
+        if(q <= max_value<uint32_t>() / 10)
+        {
+            n = q;
+            s |= 1;
+        }
+
+        return s;
     }
 
-    n = n32;
-    return s;
-  }
-
-  // If n is not divisible by 10^8, work with n itself.
-  const uint64_t mod_inv_5 = 0xcccccccccccccccd;
-  const uint64_t mod_inv_25 = mod_inv_5 * mod_inv_5;
-
-  int s = 0;
-  while (true) {
-    auto q = rotr(n * mod_inv_25, 2);
-    if (q > max_value<uint64_t>() / 100) break;
-    n = q;
-    s += 2;
-  }
-  auto q = rotr(n * mod_inv_5, 1);
-  if (q <= max_value<uint64_t>() / 10) {
-    n = q;
-    s |= 1;
-  }
-
-  return s;
-}
+    // Removes trailing zeros and returns the number of zeros removed (double)
+    FMT_INLINE int remove_trailing_zeros(uint64_t &n) noexcept
+    {
+        FMT_ASSERT(n != 0, "");
+
+        // This magic number is ceil(2^90 / 10^8).
+        constexpr uint64_t magic_number = 12379400392853802749ull;
+        auto               nm           = umul128(n, magic_number);
+
+        // Is n is divisible by 10^8?
+        if((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number)
+        {
+            // If yes, work with the quotient.
+            auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
+
+            const uint32_t mod_inv_5  = 0xcccccccd;
+            const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
+
+            int s = 8;
+            while(true)
+            {
+                auto q = rotr(n32 * mod_inv_25, 2);
+                if(q > max_value<uint32_t>() / 100)
+                    break;
+                n32 = q;
+                s += 2;
+            }
+            auto q = rotr(n32 * mod_inv_5, 1);
+            if(q <= max_value<uint32_t>() / 10)
+            {
+                n32 = q;
+                s |= 1;
+            }
+
+            n = n32;
+            return s;
+        }
+
+        // If n is not divisible by 10^8, work with n itself.
+        const uint64_t mod_inv_5  = 0xcccccccccccccccd;
+        const uint64_t mod_inv_25 = mod_inv_5 * mod_inv_5;
+
+        int s = 0;
+        while(true)
+        {
+            auto q = rotr(n * mod_inv_25, 2);
+            if(q > max_value<uint64_t>() / 100)
+                break;
+            n = q;
+            s += 2;
+        }
+        auto q = rotr(n * mod_inv_5, 1);
+        if(q <= max_value<uint64_t>() / 10)
+        {
+            n = q;
+            s |= 1;
+        }
+
+        return s;
+    }
 
-// The main algorithm for shorter interval case
-template <class T>
-FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
-  decimal_fp<T> ret_value;
-  // Compute k and beta
-  const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
-  const int beta = exponent + floor_log2_pow10(-minus_k);
-
-  // Compute xi and zi
-  using cache_entry_type = typename cache_accessor<T>::cache_entry_type;
-  const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k);
-
-  auto xi = cache_accessor<T>::compute_left_endpoint_for_shorter_interval_case(
-      cache, beta);
-  auto zi = cache_accessor<T>::compute_right_endpoint_for_shorter_interval_case(
-      cache, beta);
-
-  // If the left endpoint is not an integer, increase it
-  if (!is_left_endpoint_integer_shorter_interval<T>(exponent)) ++xi;
-
-  // Try bigger divisor
-  ret_value.significand = zi / 10;
-
-  // If succeed, remove trailing zeros if necessary and return
-  if (ret_value.significand * 10 >= xi) {
-    ret_value.exponent = minus_k + 1;
-    ret_value.exponent += remove_trailing_zeros(ret_value.significand);
-    return ret_value;
-  }
-
-  // Otherwise, compute the round-up of y
-  ret_value.significand =
-      cache_accessor<T>::compute_round_up_for_shorter_interval_case(cache,
-                                                                    beta);
-  ret_value.exponent = minus_k;
-
-  // When tie occurs, choose one of them according to the rule
-  if (exponent >= float_info<T>::shorter_interval_tie_lower_threshold &&
-      exponent <= float_info<T>::shorter_interval_tie_upper_threshold) {
-    ret_value.significand = ret_value.significand % 2 == 0
-                                ? ret_value.significand
-                                : ret_value.significand - 1;
-  } else if (ret_value.significand < xi) {
-    ++ret_value.significand;
-  }
-  return ret_value;
-}
+    // The main algorithm for shorter interval case
+    template <class T>
+    FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept
+    {
+        decimal_fp<T> ret_value;
+        // Compute k and beta
+        const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
+        const int beta    = exponent + floor_log2_pow10(-minus_k);
+
+        // Compute xi and zi
+        using cache_entry_type       = typename cache_accessor<T>::cache_entry_type;
+        const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k);
+
+        auto xi = cache_accessor<T>::compute_left_endpoint_for_shorter_interval_case(cache, beta);
+        auto zi = cache_accessor<T>::compute_right_endpoint_for_shorter_interval_case(cache, beta);
+
+        // If the left endpoint is not an integer, increase it
+        if(!is_left_endpoint_integer_shorter_interval<T>(exponent))
+            ++xi;
+
+        // Try bigger divisor
+        ret_value.significand = zi / 10;
+
+        // If succeed, remove trailing zeros if necessary and return
+        if(ret_value.significand * 10 >= xi)
+        {
+            ret_value.exponent = minus_k + 1;
+            ret_value.exponent += remove_trailing_zeros(ret_value.significand);
+            return ret_value;
+        }
+
+        // Otherwise, compute the round-up of y
+        ret_value.significand = cache_accessor<T>::compute_round_up_for_shorter_interval_case(cache, beta);
+        ret_value.exponent    = minus_k;
+
+        // When tie occurs, choose one of them according to the rule
+        if(exponent >= float_info<T>::shorter_interval_tie_lower_threshold &&
+           exponent <= float_info<T>::shorter_interval_tie_upper_threshold)
+        {
+            ret_value.significand = ret_value.significand % 2 == 0 ? ret_value.significand : ret_value.significand - 1;
+        }
+        else if(ret_value.significand < xi)
+        {
+            ++ret_value.significand;
+        }
+        return ret_value;
+    }
 
-template <typename T> decimal_fp<T> to_decimal(T x) noexcept {
-  // Step 1: integer promotion & Schubfach multiplier calculation.
-
-  using carrier_uint = typename float_info<T>::carrier_uint;
-  using cache_entry_type = typename cache_accessor<T>::cache_entry_type;
-  auto br = bit_cast<carrier_uint>(x);
-
-  // Extract significand bits and exponent bits.
-  const carrier_uint significand_mask =
-      (static_cast<carrier_uint>(1) << num_significand_bits<T>()) - 1;
-  carrier_uint significand = (br & significand_mask);
-  int exponent =
-      static_cast<int>((br & exponent_mask<T>()) >> num_significand_bits<T>());
-
-  if (exponent != 0) {  // Check if normal.
-    exponent -= exponent_bias<T>() + num_significand_bits<T>();
-
-    // Shorter interval case; proceed like Schubfach.
-    // In fact, when exponent == 1 and significand == 0, the interval is
-    // regular. However, it can be shown that the end-results are anyway same.
-    if (significand == 0) return shorter_interval_case<T>(exponent);
-
-    significand |= (static_cast<carrier_uint>(1) << num_significand_bits<T>());
-  } else {
-    // Subnormal case; the interval is always regular.
-    if (significand == 0) return {0, 0};
-    exponent =
-        std::numeric_limits<T>::min_exponent - num_significand_bits<T>() - 1;
-  }
-
-  const bool include_left_endpoint = (significand % 2 == 0);
-  const bool include_right_endpoint = include_left_endpoint;
-
-  // Compute k and beta.
-  const int minus_k = floor_log10_pow2(exponent) - float_info<T>::kappa;
-  const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k);
-  const int beta = exponent + floor_log2_pow10(-minus_k);
-
-  // Compute zi and deltai.
-  // 10^kappa <= deltai < 10^(kappa + 1)
-  const uint32_t deltai = cache_accessor<T>::compute_delta(cache, beta);
-  const carrier_uint two_fc = significand << 1;
-
-  // For the case of binary32, the result of integer check is not correct for
-  // 29711844 * 2^-82
-  // = 6.1442653300000000008655037797566933477355632930994033813476... * 10^-18
-  // and 29711844 * 2^-81
-  // = 1.2288530660000000001731007559513386695471126586198806762695... * 10^-17,
-  // and they are the unique counterexamples. However, since 29711844 is even,
-  // this does not cause any problem for the endpoints calculations; it can only
-  // cause a problem when we need to perform integer check for the center.
-  // Fortunately, with these inputs, that branch is never executed, so we are
-  // fine.
-  const typename cache_accessor<T>::compute_mul_result z_mul =
-      cache_accessor<T>::compute_mul((two_fc | 1) << beta, cache);
-
-  // Step 2: Try larger divisor; remove trailing zeros if necessary.
-
-  // Using an upper bound on zi, we might be able to optimize the division
-  // better than the compiler; we are computing zi / big_divisor here.
-  decimal_fp<T> ret_value;
-  ret_value.significand = divide_by_10_to_kappa_plus_1(z_mul.result);
-  uint32_t r = static_cast<uint32_t>(z_mul.result - float_info<T>::big_divisor *
-                                                        ret_value.significand);
-
-  if (r < deltai) {
-    // Exclude the right endpoint if necessary.
-    if (r == 0 && (z_mul.is_integer & !include_right_endpoint)) {
-      --ret_value.significand;
-      r = float_info<T>::big_divisor;
-      goto small_divisor_case_label;
+    template <typename T>
+    decimal_fp<T> to_decimal(T x) noexcept
+    {
+        // Step 1: integer promotion & Schubfach multiplier calculation.
+
+        using carrier_uint     = typename float_info<T>::carrier_uint;
+        using cache_entry_type = typename cache_accessor<T>::cache_entry_type;
+        auto br                = bit_cast<carrier_uint>(x);
+
+        // Extract significand bits and exponent bits.
+        const carrier_uint significand_mask = (static_cast<carrier_uint>(1) << num_significand_bits<T>()) - 1;
+        carrier_uint       significand      = (br & significand_mask);
+        int                exponent         = static_cast<int>((br & exponent_mask<T>()) >> num_significand_bits<T>());
+
+        if(exponent != 0)
+        { // Check if normal.
+            exponent -= exponent_bias<T>() + num_significand_bits<T>();
+
+            // Shorter interval case; proceed like Schubfach.
+            // In fact, when exponent == 1 and significand == 0, the interval is
+            // regular. However, it can be shown that the end-results are anyway same.
+            if(significand == 0)
+                return shorter_interval_case<T>(exponent);
+
+            significand |= (static_cast<carrier_uint>(1) << num_significand_bits<T>());
+        }
+        else
+        {
+            // Subnormal case; the interval is always regular.
+            if(significand == 0)
+                return {0, 0};
+            exponent = std::numeric_limits<T>::min_exponent - num_significand_bits<T>() - 1;
+        }
+
+        const bool include_left_endpoint  = (significand % 2 == 0);
+        const bool include_right_endpoint = include_left_endpoint;
+
+        // Compute k and beta.
+        const int              minus_k = floor_log10_pow2(exponent) - float_info<T>::kappa;
+        const cache_entry_type cache   = cache_accessor<T>::get_cached_power(-minus_k);
+        const int              beta    = exponent + floor_log2_pow10(-minus_k);
+
+        // Compute zi and deltai.
+        // 10^kappa <= deltai < 10^(kappa + 1)
+        const uint32_t     deltai = cache_accessor<T>::compute_delta(cache, beta);
+        const carrier_uint two_fc = significand << 1;
+
+        // For the case of binary32, the result of integer check is not correct for
+        // 29711844 * 2^-82
+        // = 6.1442653300000000008655037797566933477355632930994033813476... * 10^-18
+        // and 29711844 * 2^-81
+        // = 1.2288530660000000001731007559513386695471126586198806762695... * 10^-17,
+        // and they are the unique counterexamples. However, since 29711844 is even,
+        // this does not cause any problem for the endpoints calculations; it can only
+        // cause a problem when we need to perform integer check for the center.
+        // Fortunately, with these inputs, that branch is never executed, so we are
+        // fine.
+        const typename cache_accessor<T>::compute_mul_result z_mul =
+            cache_accessor<T>::compute_mul((two_fc | 1) << beta, cache);
+
+        // Step 2: Try larger divisor; remove trailing zeros if necessary.
+
+        // Using an upper bound on zi, we might be able to optimize the division
+        // better than the compiler; we are computing zi / big_divisor here.
+        decimal_fp<T> ret_value;
+        ret_value.significand = divide_by_10_to_kappa_plus_1(z_mul.result);
+        uint32_t r = static_cast<uint32_t>(z_mul.result - float_info<T>::big_divisor * ret_value.significand);
+
+        if(r < deltai)
+        {
+            // Exclude the right endpoint if necessary.
+            if(r == 0 && (z_mul.is_integer & !include_right_endpoint))
+            {
+                --ret_value.significand;
+                r = float_info<T>::big_divisor;
+                goto small_divisor_case_label;
+            }
+        }
+        else if(r > deltai)
+        {
+            goto small_divisor_case_label;
+        }
+        else
+        {
+            // r == deltai; compare fractional parts.
+            const typename cache_accessor<T>::compute_mul_parity_result x_mul =
+                cache_accessor<T>::compute_mul_parity(two_fc - 1, cache, beta);
+
+            if(!(x_mul.parity | (x_mul.is_integer & include_left_endpoint)))
+                goto small_divisor_case_label;
+        }
+        ret_value.exponent = minus_k + float_info<T>::kappa + 1;
+
+        // We may need to remove trailing zeros.
+        ret_value.exponent += remove_trailing_zeros(ret_value.significand);
+        return ret_value;
+
+        // Step 3: Find the significand with the smaller divisor.
+
+    small_divisor_case_label:
+        ret_value.significand *= 10;
+        ret_value.exponent = minus_k + float_info<T>::kappa;
+
+        uint32_t   dist            = r - (deltai / 2) + (float_info<T>::small_divisor / 2);
+        const bool approx_y_parity = ((dist ^ (float_info<T>::small_divisor / 2)) & 1) != 0;
+
+        // Is dist divisible by 10^kappa?
+        const bool divisible_by_small_divisor = check_divisibility_and_divide_by_pow10<float_info<T>::kappa>(dist);
+
+        // Add dist / 10^kappa to the significand.
+        ret_value.significand += dist;
+
+        if(!divisible_by_small_divisor)
+            return ret_value;
+
+        // Check z^(f) >= epsilon^(f).
+        // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1,
+        // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f).
+        // Since there are only 2 possibilities, we only need to care about the
+        // parity. Also, zi and r should have the same parity since the divisor
+        // is an even number.
+        const auto y_mul = cache_accessor<T>::compute_mul_parity(two_fc, cache, beta);
+
+        // If z^(f) >= epsilon^(f), we might have a tie when z^(f) == epsilon^(f),
+        // or equivalently, when y is an integer.
+        if(y_mul.parity != approx_y_parity)
+            --ret_value.significand;
+        else if(y_mul.is_integer & (ret_value.significand % 2 != 0))
+            --ret_value.significand;
+        return ret_value;
     }
-  } else if (r > deltai) {
-    goto small_divisor_case_label;
-  } else {
-    // r == deltai; compare fractional parts.
-    const typename cache_accessor<T>::compute_mul_parity_result x_mul =
-        cache_accessor<T>::compute_mul_parity(two_fc - 1, cache, beta);
-
-    if (!(x_mul.parity | (x_mul.is_integer & include_left_endpoint)))
-      goto small_divisor_case_label;
-  }
-  ret_value.exponent = minus_k + float_info<T>::kappa + 1;
-
-  // We may need to remove trailing zeros.
-  ret_value.exponent += remove_trailing_zeros(ret_value.significand);
-  return ret_value;
-
-  // Step 3: Find the significand with the smaller divisor.
-
-small_divisor_case_label:
-  ret_value.significand *= 10;
-  ret_value.exponent = minus_k + float_info<T>::kappa;
-
-  uint32_t dist = r - (deltai / 2) + (float_info<T>::small_divisor / 2);
-  const bool approx_y_parity =
-      ((dist ^ (float_info<T>::small_divisor / 2)) & 1) != 0;
-
-  // Is dist divisible by 10^kappa?
-  const bool divisible_by_small_divisor =
-      check_divisibility_and_divide_by_pow10<float_info<T>::kappa>(dist);
-
-  // Add dist / 10^kappa to the significand.
-  ret_value.significand += dist;
-
-  if (!divisible_by_small_divisor) return ret_value;
-
-  // Check z^(f) >= epsilon^(f).
-  // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1,
-  // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f).
-  // Since there are only 2 possibilities, we only need to care about the
-  // parity. Also, zi and r should have the same parity since the divisor
-  // is an even number.
-  const auto y_mul = cache_accessor<T>::compute_mul_parity(two_fc, cache, beta);
-
-  // If z^(f) >= epsilon^(f), we might have a tie when z^(f) == epsilon^(f),
-  // or equivalently, when y is an integer.
-  if (y_mul.parity != approx_y_parity)
-    --ret_value.significand;
-  else if (y_mul.is_integer & (ret_value.significand % 2 != 0))
-    --ret_value.significand;
-  return ret_value;
-}
-}  // namespace dragonbox
+} // namespace dragonbox
 
 #ifdef _MSC_VER
-FMT_FUNC auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...)
-    -> int {
-  auto args = va_list();
-  va_start(args, fmt);
-  int result = vsnprintf_s(buf, size, _TRUNCATE, fmt, args);
-  va_end(args);
-  return result;
+FMT_FUNC auto fmt_snprintf(char *buf, size_t size, const char *fmt, ...) -> int
+{
+    auto args = va_list();
+    va_start(args, fmt);
+    int result = vsnprintf_s(buf, size, _TRUNCATE, fmt, args);
+    va_end(args);
+    return result;
 }
 #endif
-}  // namespace detail
-
-template <> struct formatter<detail::bigint> {
-  FMT_CONSTEXPR auto parse(format_parse_context& ctx)
-      -> format_parse_context::iterator {
-    return ctx.begin();
-  }
-
-  template <typename FormatContext>
-  auto format(const detail::bigint& n, FormatContext& ctx) const ->
-      typename FormatContext::iterator {
-    auto out = ctx.out();
-    bool first = true;
-    for (auto i = n.bigits_.size(); i > 0; --i) {
-      auto value = n.bigits_[i - 1u];
-      if (first) {
-        out = format_to(out, FMT_STRING("{:x}"), value);
-        first = false;
-        continue;
-      }
-      out = format_to(out, FMT_STRING("{:08x}"), value);
+} // namespace detail
+
+template <>
+struct formatter<detail::bigint>
+{
+    FMT_CONSTEXPR auto parse(format_parse_context &ctx) -> format_parse_context::iterator { return ctx.begin(); }
+
+    template <typename FormatContext>
+    auto format(const detail::bigint &n, FormatContext &ctx) const -> typename FormatContext::iterator
+    {
+        auto out   = ctx.out();
+        bool first = true;
+        for(auto i = n.bigits_.size(); i > 0; --i)
+        {
+            auto value = n.bigits_[i - 1u];
+            if(first)
+            {
+                out   = format_to(out, FMT_STRING("{:x}"), value);
+                first = false;
+                continue;
+            }
+            out = format_to(out, FMT_STRING("{:08x}"), value);
+        }
+        if(n.exp_ > 0)
+            out = format_to(out, FMT_STRING("p{}"), n.exp_ * detail::bigint::bigit_bits);
+        return out;
     }
-    if (n.exp_ > 0)
-      out = format_to(out, FMT_STRING("p{}"),
-                      n.exp_ * detail::bigint::bigit_bits);
-    return out;
-  }
 };
 
-FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
-  for_each_codepoint(s, [this](uint32_t cp, string_view) {
-    if (cp == invalid_code_point) FMT_THROW(std::runtime_error("invalid utf8"));
-    if (cp <= 0xFFFF) {
-      buffer_.push_back(static_cast<wchar_t>(cp));
-    } else {
-      cp -= 0x10000;
-      buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
-      buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
-    }
-    return true;
-  });
-  buffer_.push_back(0);
+FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s)
+{
+    for_each_codepoint(
+        s,
+        [this](uint32_t cp, string_view)
+        {
+            if(cp == invalid_code_point)
+                FMT_THROW(std::runtime_error("invalid utf8"));
+            if(cp <= 0xFFFF)
+            {
+                buffer_.push_back(static_cast<wchar_t>(cp));
+            }
+            else
+            {
+                cp -= 0x10000;
+                buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
+                buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
+            }
+            return true;
+        });
+    buffer_.push_back(0);
 }
 
-FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
-                                  const char* message) noexcept {
-  FMT_TRY {
-    auto ec = std::error_code(error_code, std::generic_category());
-    write(std::back_inserter(out), std::system_error(ec, message).what());
-    return;
-  }
-  FMT_CATCH(...) {}
-  format_error_code(out, error_code, message);
+FMT_FUNC void format_system_error(detail::buffer<char> &out, int error_code, const char *message) noexcept
+{
+    FMT_TRY
+    {
+        auto ec = std::error_code(error_code, std::generic_category());
+        write(std::back_inserter(out), std::system_error(ec, message).what());
+        return;
+    }
+    FMT_CATCH(...) {}
+    format_error_code(out, error_code, message);
 }
 
-FMT_FUNC void report_system_error(int error_code,
-                                  const char* message) noexcept {
-  report_error(format_system_error, error_code, message);
+FMT_FUNC void report_system_error(int error_code, const char *message) noexcept
+{
+    report_error(format_system_error, error_code, message);
 }
 
-FMT_FUNC std::string vformat(string_view fmt, format_args args) {
-  // Don't optimize the "{}" case to keep the binary size small and because it
-  // can be better optimized in fmt::format anyway.
-  auto buffer = memory_buffer();
-  detail::vformat_to(buffer, fmt, args);
-  return to_string(buffer);
+FMT_FUNC std::string vformat(string_view fmt, format_args args)
+{
+    // Don't optimize the "{}" case to keep the binary size small and because it
+    // can be better optimized in fmt::format anyway.
+    auto buffer = memory_buffer();
+    detail::vformat_to(buffer, fmt, args);
+    return to_string(buffer);
 }
 
-namespace detail {
+namespace detail
+{
 #ifdef _WIN32
 using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
-extern "C" __declspec(dllimport) int __stdcall WriteConsoleW(  //
-    void*, const void*, dword, dword*, void*);
-
-FMT_FUNC bool write_console(std::FILE* f, string_view text) {
-  auto fd = _fileno(f);
-  if (_isatty(fd)) {
-    detail::utf8_to_utf16 u16(string_view(text.data(), text.size()));
-    auto written = detail::dword();
-    if (detail::WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)),
-                              u16.c_str(), static_cast<uint32_t>(u16.size()),
-                              &written, nullptr)) {
-      return true;
+extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
+    void *,
+    const void *,
+    dword,
+    dword *,
+    void *);
+
+FMT_FUNC bool write_console(std::FILE *f, string_view text)
+{
+    auto fd = _fileno(f);
+    if(_isatty(fd))
+    {
+        detail::utf8_to_utf16 u16(string_view(text.data(), text.size()));
+        auto                  written = detail::dword();
+        if(detail::WriteConsoleW(
+               reinterpret_cast<void *>(_get_osfhandle(fd)),
+               u16.c_str(),
+               static_cast<uint32_t>(u16.size()),
+               &written,
+               nullptr))
+        {
+            return true;
+        }
     }
-  }
-  // We return false if the file descriptor was not TTY, or it was but
-  // SetConsoleW failed which can happen if the output has been redirected to
-  // NUL. In both cases when we return false, we should attempt to do regular
-  // write via fwrite or std::ostream::write.
-  return false;
+    // We return false if the file descriptor was not TTY, or it was but
+    // SetConsoleW failed which can happen if the output has been redirected to
+    // NUL. In both cases when we return false, we should attempt to do regular
+    // write via fwrite or std::ostream::write.
+    return false;
 }
 #endif
 
-FMT_FUNC void print(std::FILE* f, string_view text) {
+FMT_FUNC void print(std::FILE *f, string_view text)
+{
 #ifdef _WIN32
-  if (write_console(f, text)) return;
+    if(write_console(f, text))
+        return;
 #endif
-  detail::fwrite_fully(text.data(), 1, text.size(), f);
+    detail::fwrite_fully(text.data(), 1, text.size(), f);
 }
-}  // namespace detail
+} // namespace detail
 
-FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
-  memory_buffer buffer;
-  detail::vformat_to(buffer, format_str, args);
-  detail::print(f, {buffer.data(), buffer.size()});
+FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args)
+{
+    memory_buffer buffer;
+    detail::vformat_to(buffer, format_str, args);
+    detail::print(f, {buffer.data(), buffer.size()});
 }
 
 #ifdef _WIN32
 // Print assuming legacy (non-Unicode) encoding.
-FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str,
-                                      format_args args) {
-  memory_buffer buffer;
-  detail::vformat_to(buffer, format_str,
-                     basic_format_args<buffer_context<char>>(args));
-  fwrite_fully(buffer.data(), 1, buffer.size(), f);
+FMT_FUNC void detail::vprint_mojibake(std::FILE *f, string_view format_str, format_args args)
+{
+    memory_buffer buffer;
+    detail::vformat_to(buffer, format_str, basic_format_args<buffer_context<char>>(args));
+    fwrite_fully(buffer.data(), 1, buffer.size(), f);
 }
 #endif
 
-FMT_FUNC void vprint(string_view format_str, format_args args) {
-  vprint(stdout, format_str, args);
+FMT_FUNC void vprint(string_view format_str, format_args args)
+{
+    vprint(stdout, format_str, args);
 }
 
-namespace detail {
+namespace detail
+{
 
-struct singleton {
-  unsigned char upper;
-  unsigned char lower_count;
+struct singleton
+{
+    unsigned char upper;
+    unsigned char lower_count;
 };
 
-inline auto is_printable(uint16_t x, const singleton* singletons,
-                         size_t singletons_size,
-                         const unsigned char* singleton_lowers,
-                         const unsigned char* normal, size_t normal_size)
-    -> bool {
-  auto upper = x >> 8;
-  auto lower_start = 0;
-  for (size_t i = 0; i < singletons_size; ++i) {
-    auto s = singletons[i];
-    auto lower_end = lower_start + s.lower_count;
-    if (upper < s.upper) break;
-    if (upper == s.upper) {
-      for (auto j = lower_start; j < lower_end; ++j) {
-        if (singleton_lowers[j] == (x & 0xff)) return false;
-      }
+inline auto is_printable(
+    uint16_t             x,
+    const singleton     *singletons,
+    size_t               singletons_size,
+    const unsigned char *singleton_lowers,
+    const unsigned char *normal,
+    size_t               normal_size) -> bool
+{
+    auto upper       = x >> 8;
+    auto lower_start = 0;
+    for(size_t i = 0; i < singletons_size; ++i)
+    {
+        auto s         = singletons[i];
+        auto lower_end = lower_start + s.lower_count;
+        if(upper < s.upper)
+            break;
+        if(upper == s.upper)
+        {
+            for(auto j = lower_start; j < lower_end; ++j)
+            {
+                if(singleton_lowers[j] == (x & 0xff))
+                    return false;
+            }
+        }
+        lower_start = lower_end;
+    }
+
+    auto xsigned = static_cast<int>(x);
+    auto current = true;
+    for(size_t i = 0; i < normal_size; ++i)
+    {
+        auto v   = static_cast<int>(normal[i]);
+        auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v;
+        xsigned -= len;
+        if(xsigned < 0)
+            break;
+        current = !current;
     }
-    lower_start = lower_end;
-  }
-
-  auto xsigned = static_cast<int>(x);
-  auto current = true;
-  for (size_t i = 0; i < normal_size; ++i) {
-    auto v = static_cast<int>(normal[i]);
-    auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v;
-    xsigned -= len;
-    if (xsigned < 0) break;
-    current = !current;
-  }
-  return current;
+    return current;
 }
 
 // This code is generated by support/printable.py.
-FMT_FUNC auto is_printable(uint32_t cp) -> bool {
-  static constexpr singleton singletons0[] = {
-      {0x00, 1},  {0x03, 5},  {0x05, 6},  {0x06, 3},  {0x07, 6},  {0x08, 8},
-      {0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13},
-      {0x0f, 4},  {0x10, 3},  {0x12, 18}, {0x13, 9},  {0x16, 1},  {0x17, 5},
-      {0x18, 2},  {0x19, 3},  {0x1a, 7},  {0x1c, 2},  {0x1d, 1},  {0x1f, 22},
-      {0x20, 3},  {0x2b, 3},  {0x2c, 2},  {0x2d, 11}, {0x2e, 1},  {0x30, 3},
-      {0x31, 2},  {0x32, 1},  {0xa7, 2},  {0xa9, 2},  {0xaa, 4},  {0xab, 8},
-      {0xfa, 2},  {0xfb, 5},  {0xfd, 4},  {0xfe, 3},  {0xff, 9},
-  };
-  static constexpr unsigned char singletons0_lower[] = {
-      0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90,
-      0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f,
-      0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
-      0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04,
-      0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d,
-      0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
-      0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
-      0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d,
-      0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
-      0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d,
-      0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5,
-      0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
-      0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49,
-      0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7,
-      0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
-      0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e,
-      0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16,
-      0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
-      0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f,
-      0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf,
-      0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
-      0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27,
-      0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91,
-      0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
-      0xfe, 0xff,
-  };
-  static constexpr singleton singletons1[] = {
-      {0x00, 6},  {0x01, 1}, {0x03, 1},  {0x04, 2}, {0x08, 8},  {0x09, 2},
-      {0x0a, 5},  {0x0b, 2}, {0x0e, 4},  {0x10, 1}, {0x11, 2},  {0x12, 5},
-      {0x13, 17}, {0x14, 1}, {0x15, 2},  {0x17, 2}, {0x19, 13}, {0x1c, 5},
-      {0x1d, 8},  {0x24, 1}, {0x6a, 3},  {0x6b, 2}, {0xbc, 2},  {0xd1, 2},
-      {0xd4, 12}, {0xd5, 9}, {0xd6, 2},  {0xd7, 2}, {0xda, 1},  {0xe0, 5},
-      {0xe1, 2},  {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2},  {0xf9, 2},
-      {0xfa, 2},  {0xfb, 1},
-  };
-  static constexpr unsigned char singletons1_lower[] = {
-      0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07,
-      0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36,
-      0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
-      0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
-      0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b,
-      0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
-      0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66,
-      0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27,
-      0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
-      0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7,
-      0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6,
-      0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
-      0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66,
-      0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0,
-      0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
-  };
-  static constexpr unsigned char normal0[] = {
-      0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04,
-      0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0,
-      0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
-      0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03,
-      0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03,
-      0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
-      0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15,
-      0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f,
-      0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
-      0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07,
-      0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06,
-      0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
-      0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac,
-      0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c,
-      0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
-      0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c,
-      0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b,
-      0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
-      0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03,
-      0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80,
-      0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
-      0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c,
-      0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17,
-      0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
-      0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80,
-      0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d,
-  };
-  static constexpr unsigned char normal1[] = {
-      0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f,
-      0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e,
-      0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
-      0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09,
-      0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16,
-      0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
-      0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36,
-      0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33,
-      0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
-      0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e,
-      0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41,
-      0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
-      0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22,
-      0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04,
-      0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
-      0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03,
-      0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81,
-      0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
-      0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1,
-      0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a,
-      0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
-      0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09,
-      0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89,
-      0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
-      0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09,
-      0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50,
-      0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
-      0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83,
-      0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05,
-      0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
-      0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80,
-      0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07,
-      0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
-      0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07,
-      0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06,
-  };
-  auto lower = static_cast<uint16_t>(cp);
-  if (cp < 0x10000) {
-    return is_printable(lower, singletons0,
-                        sizeof(singletons0) / sizeof(*singletons0),
-                        singletons0_lower, normal0, sizeof(normal0));
-  }
-  if (cp < 0x20000) {
-    return is_printable(lower, singletons1,
-                        sizeof(singletons1) / sizeof(*singletons1),
-                        singletons1_lower, normal1, sizeof(normal1));
-  }
-  if (0x2a6de <= cp && cp < 0x2a700) return false;
-  if (0x2b735 <= cp && cp < 0x2b740) return false;
-  if (0x2b81e <= cp && cp < 0x2b820) return false;
-  if (0x2cea2 <= cp && cp < 0x2ceb0) return false;
-  if (0x2ebe1 <= cp && cp < 0x2f800) return false;
-  if (0x2fa1e <= cp && cp < 0x30000) return false;
-  if (0x3134b <= cp && cp < 0xe0100) return false;
-  if (0xe01f0 <= cp && cp < 0x110000) return false;
-  return cp < 0x110000;
+FMT_FUNC auto is_printable(uint32_t cp) -> bool
+{
+    static constexpr singleton singletons0[] = {
+        {0x00, 1},  {0x03, 5},  {0x05, 6},  {0x06, 3}, {0x07, 6}, {0x08, 8},  {0x09, 17}, {0x0a, 28}, {0x0b, 25},
+        {0x0c, 20}, {0x0d, 16}, {0x0e, 13}, {0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9},  {0x16, 1},  {0x17, 5},
+        {0x18, 2},  {0x19, 3},  {0x1a, 7},  {0x1c, 2}, {0x1d, 1}, {0x1f, 22}, {0x20, 3},  {0x2b, 3},  {0x2c, 2},
+        {0x2d, 11}, {0x2e, 1},  {0x30, 3},  {0x31, 2}, {0x32, 1}, {0xa7, 2},  {0xa9, 2},  {0xaa, 4},  {0xab, 8},
+        {0xfa, 2},  {0xfb, 5},  {0xfd, 4},  {0xfe, 3}, {0xff, 9},
+    };
+    static constexpr unsigned char singletons0_lower[] = {
+        0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90, 0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b,
+        0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f, 0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
+        0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04, 0x11, 0x12, 0x29, 0x31, 0x34, 0x37,
+        0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d, 0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
+        0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, 0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e,
+        0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d, 0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
+        0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d, 0x11, 0x45, 0x49, 0x64, 0x65, 0x80,
+        0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, 0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
+        0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, 0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f,
+        0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7, 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
+        0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e, 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e,
+        0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16, 0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
+        0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f, 0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26,
+        0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf, 0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
+        0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, 0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37,
+        0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91, 0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
+        0xfe, 0xff,
+    };
+    static constexpr singleton singletons1[] = {
+        {0x00, 6}, {0x01, 1}, {0x03, 1},  {0x04, 2}, {0x08, 8},  {0x09, 2}, {0x0a, 5},  {0x0b, 2}, {0x0e, 4}, {0x10, 1},
+        {0x11, 2}, {0x12, 5}, {0x13, 17}, {0x14, 1}, {0x15, 2},  {0x17, 2}, {0x19, 13}, {0x1c, 5}, {0x1d, 8}, {0x24, 1},
+        {0x6a, 3}, {0x6b, 2}, {0xbc, 2},  {0xd1, 2}, {0xd4, 12}, {0xd5, 9}, {0xd6, 2},  {0xd7, 2}, {0xda, 1}, {0xe0, 5},
+        {0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2},  {0xf9, 2}, {0xfa, 2},  {0xfb, 1},
+    };
+    static constexpr unsigned char singletons1_lower[] = {
+        0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07, 0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3,
+        0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36, 0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
+        0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, 0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f,
+        0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b, 0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
+        0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66, 0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee,
+        0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27, 0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
+        0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, 0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a,
+        0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6, 0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
+        0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66, 0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a,
+        0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0, 0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
+    };
+    static constexpr unsigned char normal0[] = {
+        0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04, 0x06, 0x11, 0x81, 0xac, 0x0e, 0x80,
+        0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0, 0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
+        0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03, 0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08,
+        0x03, 0x07, 0x03, 0x02, 0x03, 0x03, 0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
+        0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15, 0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d,
+        0x03, 0x01, 0x04, 0x11, 0x06, 0x0f, 0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
+        0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07, 0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c,
+        0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06, 0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
+        0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac, 0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c,
+        0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c, 0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
+        0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c, 0x04, 0x82, 0x97, 0x19, 0x0b, 0x15,
+        0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b, 0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
+        0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03, 0x84, 0x8d, 0x03, 0x37, 0x09, 0x81,
+        0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80, 0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
+        0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c, 0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80,
+        0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17, 0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
+        0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80, 0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b,
+        0x03, 0x0f, 0x0d,
+    };
+    static constexpr unsigned char normal1[] = {
+        0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f, 0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31,
+        0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e, 0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
+        0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09, 0x16, 0x0a, 0x08, 0x80, 0x98, 0x39,
+        0x03, 0x63, 0x08, 0x09, 0x30, 0x16, 0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
+        0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36, 0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04,
+        0x0c, 0x07, 0x50, 0x49, 0x37, 0x33, 0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
+        0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e, 0x19, 0x07, 0x0a, 0x06, 0x48, 0x08,
+        0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41, 0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
+        0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22, 0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13,
+        0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04, 0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
+        0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03, 0x47, 0x49, 0x37, 0x03, 0x0e, 0x08,
+        0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81, 0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
+        0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1, 0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04,
+        0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a, 0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
+        0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09, 0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03,
+        0x11, 0x04, 0x08, 0x81, 0x8c, 0x89, 0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
+        0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09, 0x19, 0x80, 0x87, 0x81, 0x47, 0x03,
+        0x85, 0x42, 0x0f, 0x15, 0x85, 0x50, 0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
+        0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83, 0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2,
+        0x3c, 0x06, 0x01, 0x04, 0x55, 0x05, 0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
+        0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80, 0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03,
+        0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07, 0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
+        0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07, 0x09, 0x03, 0x0d, 0x07, 0x29, 0x80,
+        0xcb, 0x25, 0x0a, 0x84, 0x06,
+    };
+    auto lower = static_cast<uint16_t>(cp);
+    if(cp < 0x10000)
+    {
+        return is_printable(
+            lower,
+            singletons0,
+            sizeof(singletons0) / sizeof(*singletons0),
+            singletons0_lower,
+            normal0,
+            sizeof(normal0));
+    }
+    if(cp < 0x20000)
+    {
+        return is_printable(
+            lower,
+            singletons1,
+            sizeof(singletons1) / sizeof(*singletons1),
+            singletons1_lower,
+            normal1,
+            sizeof(normal1));
+    }
+    if(0x2a6de <= cp && cp < 0x2a700)
+        return false;
+    if(0x2b735 <= cp && cp < 0x2b740)
+        return false;
+    if(0x2b81e <= cp && cp < 0x2b820)
+        return false;
+    if(0x2cea2 <= cp && cp < 0x2ceb0)
+        return false;
+    if(0x2ebe1 <= cp && cp < 0x2f800)
+        return false;
+    if(0x2fa1e <= cp && cp < 0x30000)
+        return false;
+    if(0x3134b <= cp && cp < 0xe0100)
+        return false;
+    if(0xe01f0 <= cp && cp < 0x110000)
+        return false;
+    return cp < 0x110000;
 }
 
-}  // namespace detail
+} // namespace detail
 
 FMT_END_NAMESPACE
 
-#endif  // FMT_FORMAT_INL_H_
+#endif // FMT_FORMAT_INL_H_
diff --git a/include/spdlog/fmt/bundled/format.h b/include/spdlog/fmt/bundled/format.h
index 7c607dbd3..6d296484b 100644
--- a/include/spdlog/fmt/bundled/format.h
+++ b/include/spdlog/fmt/bundled/format.h
@@ -33,100 +33,104 @@
 #ifndef FMT_FORMAT_H_
 #define FMT_FORMAT_H_
 
-#include <cmath>         // std::signbit
-#include <cstdint>       // uint32_t
-#include <cstring>       // std::memcpy
-#include <limits>        // std::numeric_limits
-#include <memory>        // std::uninitialized_copy
-#include <stdexcept>     // std::runtime_error
-#include <system_error>  // std::system_error
+#include <cmath>        // std::signbit
+#include <cstdint>      // uint32_t
+#include <cstring>      // std::memcpy
+#include <limits>       // std::numeric_limits
+#include <memory>       // std::uninitialized_copy
+#include <stdexcept>    // std::runtime_error
+#include <system_error> // std::system_error
 
 #ifdef __cpp_lib_bit_cast
-#  include <bit>  // std::bitcast
+#include <bit> // std::bitcast
 #endif
 
 #include "core.h"
 
 #if FMT_GCC_VERSION
-#  define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden")))
+#define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden")))
 #else
-#  define FMT_GCC_VISIBILITY_HIDDEN
+#define FMT_GCC_VISIBILITY_HIDDEN
 #endif
 
 #ifdef __NVCC__
-#  define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__)
+#define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__)
 #else
-#  define FMT_CUDA_VERSION 0
+#define FMT_CUDA_VERSION 0
 #endif
 
 #ifdef __has_builtin
-#  define FMT_HAS_BUILTIN(x) __has_builtin(x)
+#define FMT_HAS_BUILTIN(x) __has_builtin(x)
 #else
-#  define FMT_HAS_BUILTIN(x) 0
+#define FMT_HAS_BUILTIN(x) 0
 #endif
 
 #if FMT_GCC_VERSION || FMT_CLANG_VERSION
-#  define FMT_NOINLINE __attribute__((noinline))
+#define FMT_NOINLINE __attribute__((noinline))
 #else
-#  define FMT_NOINLINE
+#define FMT_NOINLINE
 #endif
 
 #if FMT_MSC_VERSION
-#  define FMT_MSC_DEFAULT = default
+#define FMT_MSC_DEFAULT = default
 #else
-#  define FMT_MSC_DEFAULT
+#define FMT_MSC_DEFAULT
 #endif
 
 #ifndef FMT_THROW
-#  if FMT_EXCEPTIONS
-#    if FMT_MSC_VERSION || defined(__NVCC__)
+#if FMT_EXCEPTIONS
+#if FMT_MSC_VERSION || defined(__NVCC__)
 FMT_BEGIN_NAMESPACE
-namespace detail {
-template <typename Exception> inline void do_throw(const Exception& x) {
-  // Silence unreachable code warnings in MSVC and NVCC because these
-  // are nearly impossible to fix in a generic code.
-  volatile bool b = true;
-  if (b) throw x;
-}
-}  // namespace detail
+namespace detail
+{
+template <typename Exception>
+inline void do_throw(const Exception &x)
+{
+    // Silence unreachable code warnings in MSVC and NVCC because these
+    // are nearly impossible to fix in a generic code.
+    volatile bool b = true;
+    if(b)
+        throw x;
+}
+} // namespace detail
 FMT_END_NAMESPACE
-#      define FMT_THROW(x) detail::do_throw(x)
-#    else
-#      define FMT_THROW(x) throw x
-#    endif
-#  else
-#    define FMT_THROW(x)               \
-      do {                             \
-        FMT_ASSERT(false, (x).what()); \
-      } while (false)
-#  endif
+#define FMT_THROW(x) detail::do_throw(x)
+#else
+#define FMT_THROW(x) throw x
+#endif
+#else
+#define FMT_THROW(x)                                                                                                   \
+    do                                                                                                                 \
+    {                                                                                                                  \
+        FMT_ASSERT(false, (x).what());                                                                                 \
+    } while(false)
+#endif
 #endif
 
 #if FMT_EXCEPTIONS
-#  define FMT_TRY try
-#  define FMT_CATCH(x) catch (x)
+#define FMT_TRY      try
+#define FMT_CATCH(x) catch(x)
 #else
-#  define FMT_TRY if (true)
-#  define FMT_CATCH(x) if (false)
+#define FMT_TRY      if(true)
+#define FMT_CATCH(x) if(false)
 #endif
 
 #ifndef FMT_MAYBE_UNUSED
-#  if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
-#    define FMT_MAYBE_UNUSED [[maybe_unused]]
-#  else
-#    define FMT_MAYBE_UNUSED
-#  endif
+#if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
+#define FMT_MAYBE_UNUSED [[maybe_unused]]
+#else
+#define FMT_MAYBE_UNUSED
+#endif
 #endif
 
 #ifndef FMT_USE_USER_DEFINED_LITERALS
 // EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs.
-#  if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \
-       FMT_MSC_VERSION >= 1900) &&                                     \
-      (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480)
-#    define FMT_USE_USER_DEFINED_LITERALS 1
-#  else
-#    define FMT_USE_USER_DEFINED_LITERALS 0
-#  endif
+#if(FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || FMT_MSC_VERSION >= 1900) &&                        \
+    (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480)
+#define FMT_USE_USER_DEFINED_LITERALS 1
+#else
+#define FMT_USE_USER_DEFINED_LITERALS 0
+#endif
 #endif
 
 // Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of
@@ -134,126 +138,133 @@ FMT_END_NAMESPACE
 // largest integer type. This results in a reduction in binary size but will
 // cause a decrease in integer formatting performance.
 #if !defined(FMT_REDUCE_INT_INSTANTIATIONS)
-#  define FMT_REDUCE_INT_INSTANTIATIONS 0
+#define FMT_REDUCE_INT_INSTANTIATIONS 0
 #endif
 
 // __builtin_clz is broken in clang with Microsoft CodeGen:
 // https://github.com/fmtlib/fmt/issues/519.
 #if !FMT_MSC_VERSION
-#  if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION
-#    define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
-#  endif
-#  if FMT_HAS_BUILTIN(__builtin_clzll) || FMT_GCC_VERSION || FMT_ICC_VERSION
-#    define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
-#  endif
+#if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION
+#define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
+#endif
+#if FMT_HAS_BUILTIN(__builtin_clzll) || FMT_GCC_VERSION || FMT_ICC_VERSION
+#define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
+#endif
 #endif
 
 // __builtin_ctz is broken in Intel Compiler Classic on Windows:
 // https://github.com/fmtlib/fmt/issues/2510.
 #ifndef __ICL
-#  if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || \
-      defined(__NVCOMPILER)
-#    define FMT_BUILTIN_CTZ(n) __builtin_ctz(n)
-#  endif
-#  if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || \
-      FMT_ICC_VERSION || defined(__NVCOMPILER)
-#    define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n)
-#  endif
+#if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || defined(__NVCOMPILER)
+#define FMT_BUILTIN_CTZ(n) __builtin_ctz(n)
+#endif
+#if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || FMT_ICC_VERSION || defined(__NVCOMPILER)
+#define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n)
+#endif
 #endif
 
 #if FMT_MSC_VERSION
-#  include <intrin.h>  // _BitScanReverse[64], _BitScanForward[64], _umul128
+#include <intrin.h> // _BitScanReverse[64], _BitScanForward[64], _umul128
 #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.
-#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) && \
-    !defined(FMT_BUILTIN_CTZLL)
+#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) && !defined(FMT_BUILTIN_CTZLL)
 FMT_BEGIN_NAMESPACE
-namespace detail {
+namespace detail
+{
 // Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning.
-#  if !defined(__clang__)
-#    pragma intrinsic(_BitScanForward)
-#    pragma intrinsic(_BitScanReverse)
-#    if defined(_WIN64)
-#      pragma intrinsic(_BitScanForward64)
-#      pragma intrinsic(_BitScanReverse64)
-#    endif
-#  endif
-
-inline auto clz(uint32_t x) -> int {
-  unsigned long r = 0;
-  _BitScanReverse(&r, x);
-  FMT_ASSERT(x != 0, "");
-  // Static analysis complains about using uninitialized data
-  // "r", but the only way that can happen is if "x" is 0,
-  // which the callers guarantee to not happen.
-  FMT_MSC_WARNING(suppress : 6102)
-  return 31 ^ static_cast<int>(r);
-}
-#  define FMT_BUILTIN_CLZ(n) detail::clz(n)
-
-inline auto clzll(uint64_t x) -> int {
-  unsigned long r = 0;
-#  ifdef _WIN64
-  _BitScanReverse64(&r, x);
-#  else
-  // Scan the high 32 bits.
-  if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32))) return 63 ^ (r + 32);
-  // Scan the low 32 bits.
-  _BitScanReverse(&r, static_cast<uint32_t>(x));
-#  endif
-  FMT_ASSERT(x != 0, "");
-  FMT_MSC_WARNING(suppress : 6102)  // Suppress a bogus static analysis warning.
-  return 63 ^ static_cast<int>(r);
-}
-#  define FMT_BUILTIN_CLZLL(n) detail::clzll(n)
-
-inline auto ctz(uint32_t x) -> int {
-  unsigned long r = 0;
-  _BitScanForward(&r, x);
-  FMT_ASSERT(x != 0, "");
-  FMT_MSC_WARNING(suppress : 6102)  // Suppress a bogus static analysis warning.
-  return static_cast<int>(r);
-}
-#  define FMT_BUILTIN_CTZ(n) detail::ctz(n)
-
-inline auto ctzll(uint64_t x) -> int {
-  unsigned long r = 0;
-  FMT_ASSERT(x != 0, "");
-  FMT_MSC_WARNING(suppress : 6102)  // Suppress a bogus static analysis warning.
-#  ifdef _WIN64
-  _BitScanForward64(&r, x);
-#  else
-  // Scan the low 32 bits.
-  if (_BitScanForward(&r, static_cast<uint32_t>(x))) return static_cast<int>(r);
-  // Scan the high 32 bits.
-  _BitScanForward(&r, static_cast<uint32_t>(x >> 32));
-  r += 32;
-#  endif
-  return static_cast<int>(r);
-}
-#  define FMT_BUILTIN_CTZLL(n) detail::ctzll(n)
-}  // namespace detail
+#if !defined(__clang__)
+#pragma intrinsic(_BitScanForward)
+#pragma intrinsic(_BitScanReverse)
+#if defined(_WIN64)
+#pragma intrinsic(_BitScanForward64)
+#pragma intrinsic(_BitScanReverse64)
+#endif
+#endif
+
+inline auto clz(uint32_t x) -> int
+{
+    unsigned long r = 0;
+    _BitScanReverse(&r, x);
+    FMT_ASSERT(x != 0, "");
+    // Static analysis complains about using uninitialized data
+    // "r", but the only way that can happen is if "x" is 0,
+    // which the callers guarantee to not happen.
+    FMT_MSC_WARNING(suppress : 6102)
+    return 31 ^ static_cast<int>(r);
+}
+#define FMT_BUILTIN_CLZ(n) detail::clz(n)
+
+inline auto clzll(uint64_t x) -> int
+{
+    unsigned long r = 0;
+#ifdef _WIN64
+    _BitScanReverse64(&r, x);
+#else
+    // Scan the high 32 bits.
+    if(_BitScanReverse(&r, static_cast<uint32_t>(x >> 32)))
+        return 63 ^ (r + 32);
+    // Scan the low 32 bits.
+    _BitScanReverse(&r, static_cast<uint32_t>(x));
+#endif
+    FMT_ASSERT(x != 0, "");
+    FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
+    return 63 ^ static_cast<int>(r);
+}
+#define FMT_BUILTIN_CLZLL(n) detail::clzll(n)
+
+inline auto ctz(uint32_t x) -> int
+{
+    unsigned long r = 0;
+    _BitScanForward(&r, x);
+    FMT_ASSERT(x != 0, "");
+    FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
+    return static_cast<int>(r);
+}
+#define FMT_BUILTIN_CTZ(n) detail::ctz(n)
+
+inline auto ctzll(uint64_t x) -> int
+{
+    unsigned long r = 0;
+    FMT_ASSERT(x != 0, "");
+    FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
+#ifdef _WIN64
+    _BitScanForward64(&r, x);
+#else
+    // Scan the low 32 bits.
+    if(_BitScanForward(&r, static_cast<uint32_t>(x)))
+        return static_cast<int>(r);
+    // Scan the high 32 bits.
+    _BitScanForward(&r, static_cast<uint32_t>(x >> 32));
+    r += 32;
+#endif
+    return static_cast<int>(r);
+}
+#define FMT_BUILTIN_CTZLL(n) detail::ctzll(n)
+} // namespace detail
 FMT_END_NAMESPACE
 #endif
 
 FMT_BEGIN_NAMESPACE
-namespace detail {
+namespace detail
+{
 
-FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) {
-  ignore_unused(condition);
+FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition)
+{
+    ignore_unused(condition);
 #ifdef FMT_FUZZ
-  if (condition) throw std::runtime_error("fuzzing limit reached");
+    if(condition)
+        throw std::runtime_error("fuzzing limit reached");
 #endif
 }
 
-template <typename CharT, CharT... C> struct string_literal {
-  static constexpr CharT value[sizeof...(C)] = {C...};
-  constexpr operator basic_string_view<CharT>() const {
-    return {value, sizeof...(C)};
-  }
+template <typename CharT, CharT... C>
+struct string_literal
+{
+    static constexpr CharT value[sizeof...(C)] = {C...};
+    constexpr              operator basic_string_view<CharT>() const { return {value, sizeof...(C)}; }
 };
 
 #if FMT_CPLUSPLUS < 201703L
@@ -261,167 +272,182 @@ template <typename CharT, CharT... C>
 constexpr CharT string_literal<CharT, C...>::value[sizeof...(C)];
 #endif
 
-template <typename Streambuf> class formatbuf : public Streambuf {
- private:
-  using char_type = typename Streambuf::char_type;
-  using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
-  using int_type = typename Streambuf::int_type;
-  using traits_type = typename Streambuf::traits_type;
-
-  buffer<char_type>& buffer_;
-
- public:
-  explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {}
-
- protected:
-  // The put area is always empty. This makes the implementation simpler and has
-  // the advantage that the streambuf and the buffer are always in sync and
-  // sputc never writes into uninitialized memory. A disadvantage is that each
-  // call to sputc always results in a (virtual) call to overflow. There is no
-  // disadvantage here for sputn since this always results in a call to xsputn.
-
-  auto overflow(int_type ch) -> int_type override {
-    if (!traits_type::eq_int_type(ch, traits_type::eof()))
-      buffer_.push_back(static_cast<char_type>(ch));
-    return ch;
-  }
-
-  auto xsputn(const char_type* s, streamsize count) -> streamsize override {
-    buffer_.append(s, s + count);
-    return count;
-  }
+template <typename Streambuf>
+class formatbuf : public Streambuf
+{
+private:
+    using char_type   = typename Streambuf::char_type;
+    using streamsize  = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
+    using int_type    = typename Streambuf::int_type;
+    using traits_type = typename Streambuf::traits_type;
+
+    buffer<char_type> &buffer_;
+
+public:
+    explicit formatbuf(buffer<char_type> &buf) : buffer_(buf) {}
+
+protected:
+    // The put area is always empty. This makes the implementation simpler and has
+    // the advantage that the streambuf and the buffer are always in sync and
+    // sputc never writes into uninitialized memory. A disadvantage is that each
+    // call to sputc always results in a (virtual) call to overflow. There is no
+    // disadvantage here for sputn since this always results in a call to xsputn.
+
+    auto overflow(int_type ch) -> int_type override
+    {
+        if(!traits_type::eq_int_type(ch, traits_type::eof()))
+            buffer_.push_back(static_cast<char_type>(ch));
+        return ch;
+    }
+
+    auto xsputn(const char_type *s, streamsize count) -> streamsize override
+    {
+        buffer_.append(s, s + count);
+        return count;
+    }
 };
 
 // Implementation of std::bit_cast for pre-C++20.
 template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) == sizeof(From))>
-FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To {
+FMT_CONSTEXPR20 auto bit_cast(const From &from) -> To
+{
 #ifdef __cpp_lib_bit_cast
-  if (is_constant_evaluated()) return std::bit_cast<To>(from);
+    if(is_constant_evaluated())
+        return std::bit_cast<To>(from);
 #endif
-  auto to = To();
-  // The cast suppresses a bogus -Wclass-memaccess on GCC.
-  std::memcpy(static_cast<void*>(&to), &from, sizeof(to));
-  return to;
+    auto to = To();
+    // The cast suppresses a bogus -Wclass-memaccess on GCC.
+    std::memcpy(static_cast<void *>(&to), &from, sizeof(to));
+    return to;
 }
 
-inline auto is_big_endian() -> bool {
+inline auto is_big_endian() -> bool
+{
 #ifdef _WIN32
-  return false;
+    return false;
 #elif defined(__BIG_ENDIAN__)
-  return true;
+    return true;
 #elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
-  return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__;
+    return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__;
 #else
-  struct bytes {
-    char data[sizeof(int)];
-  };
-  return bit_cast<bytes>(1).data[0] == 0;
+    struct bytes
+    {
+        char data[sizeof(int)];
+    };
+    return bit_cast<bytes>(1).data[0] == 0;
 #endif
 }
 
-class uint128_fallback {
- private:
-  uint64_t lo_, hi_;
-
-  friend uint128_fallback umul128(uint64_t x, uint64_t y) noexcept;
-
- public:
-  constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
-  constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {}
-
-  constexpr uint64_t high() const noexcept { return hi_; }
-  constexpr uint64_t low() const noexcept { return lo_; }
-
-  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-  constexpr explicit operator T() const {
-    return static_cast<T>(lo_);
-  }
-
-  friend constexpr auto operator==(const uint128_fallback& lhs,
-                                   const uint128_fallback& rhs) -> bool {
-    return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_;
-  }
-  friend constexpr auto operator!=(const uint128_fallback& lhs,
-                                   const uint128_fallback& rhs) -> bool {
-    return !(lhs == rhs);
-  }
-  friend constexpr auto operator>(const uint128_fallback& lhs,
-                                  const uint128_fallback& rhs) -> bool {
-    return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_;
-  }
-  friend constexpr auto operator|(const uint128_fallback& lhs,
-                                  const uint128_fallback& rhs)
-      -> uint128_fallback {
-    return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_};
-  }
-  friend constexpr auto operator&(const uint128_fallback& lhs,
-                                  const uint128_fallback& rhs)
-      -> uint128_fallback {
-    return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_};
-  }
-  friend auto operator+(const uint128_fallback& lhs,
-                        const uint128_fallback& rhs) -> uint128_fallback {
-    auto result = uint128_fallback(lhs);
-    result += rhs;
-    return result;
-  }
-  friend auto operator*(const uint128_fallback& lhs, uint32_t rhs)
-      -> uint128_fallback {
-    FMT_ASSERT(lhs.hi_ == 0, "");
-    uint64_t hi = (lhs.lo_ >> 32) * rhs;
-    uint64_t lo = (lhs.lo_ & ~uint32_t()) * rhs;
-    uint64_t new_lo = (hi << 32) + lo;
-    return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo};
-  }
-  friend auto operator-(const uint128_fallback& lhs, uint64_t rhs)
-      -> uint128_fallback {
-    return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs};
-  }
-  FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback {
-    if (shift == 64) return {0, hi_};
-    if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64);
-    return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)};
-  }
-  FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback {
-    if (shift == 64) return {lo_, 0};
-    if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64);
-    return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)};
-  }
-  FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& {
-    return *this = *this >> shift;
-  }
-  FMT_CONSTEXPR void operator+=(uint128_fallback n) {
-    uint64_t new_lo = lo_ + n.lo_;
-    uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0);
-    FMT_ASSERT(new_hi >= hi_, "");
-    lo_ = new_lo;
-    hi_ = new_hi;
-  }
-
-  FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept {
-    if (is_constant_evaluated()) {
-      lo_ += n;
-      hi_ += (lo_ < n ? 1 : 0);
-      return *this;
+class uint128_fallback
+{
+private:
+    uint64_t lo_, hi_;
+
+    friend uint128_fallback umul128(uint64_t x, uint64_t y) noexcept;
+
+public:
+    constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
+    constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {}
+
+    constexpr uint64_t high() const noexcept { return hi_; }
+    constexpr uint64_t low() const noexcept { return lo_; }
+
+    template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+    constexpr explicit operator T() const
+    {
+        return static_cast<T>(lo_);
+    }
+
+    friend constexpr auto operator==(const uint128_fallback &lhs, const uint128_fallback &rhs) -> bool
+    {
+        return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_;
+    }
+    friend constexpr auto operator!=(const uint128_fallback &lhs, const uint128_fallback &rhs) -> bool
+    {
+        return !(lhs == rhs);
+    }
+    friend constexpr auto operator>(const uint128_fallback &lhs, const uint128_fallback &rhs) -> bool
+    {
+        return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_;
+    }
+    friend constexpr auto operator|(const uint128_fallback &lhs, const uint128_fallback &rhs) -> uint128_fallback
+    {
+        return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_};
+    }
+    friend constexpr auto operator&(const uint128_fallback &lhs, const uint128_fallback &rhs) -> uint128_fallback
+    {
+        return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_};
+    }
+    friend auto operator+(const uint128_fallback &lhs, const uint128_fallback &rhs) -> uint128_fallback
+    {
+        auto result = uint128_fallback(lhs);
+        result += rhs;
+        return result;
+    }
+    friend auto operator*(const uint128_fallback &lhs, uint32_t rhs) -> uint128_fallback
+    {
+        FMT_ASSERT(lhs.hi_ == 0, "");
+        uint64_t hi     = (lhs.lo_ >> 32) * rhs;
+        uint64_t lo     = (lhs.lo_ & ~uint32_t()) * rhs;
+        uint64_t new_lo = (hi << 32) + lo;
+        return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo};
+    }
+    friend auto operator-(const uint128_fallback &lhs, uint64_t rhs) -> uint128_fallback
+    {
+        return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs};
     }
+    FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback
+    {
+        if(shift == 64)
+            return {0, hi_};
+        if(shift > 64)
+            return uint128_fallback(0, hi_) >> (shift - 64);
+        return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)};
+    }
+    FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback
+    {
+        if(shift == 64)
+            return {lo_, 0};
+        if(shift > 64)
+            return uint128_fallback(lo_, 0) << (shift - 64);
+        return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)};
+    }
+    FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback & { return *this = *this >> shift; }
+    FMT_CONSTEXPR void operator+=(uint128_fallback n)
+    {
+        uint64_t new_lo = lo_ + n.lo_;
+        uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0);
+        FMT_ASSERT(new_hi >= hi_, "");
+        lo_ = new_lo;
+        hi_ = new_hi;
+    }
+
+    FMT_CONSTEXPR20 uint128_fallback &operator+=(uint64_t n) noexcept
+    {
+        if(is_constant_evaluated())
+        {
+            lo_ += n;
+            hi_ += (lo_ < n ? 1 : 0);
+            return *this;
+        }
 #if FMT_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__)
-    unsigned long long carry;
-    lo_ = __builtin_addcll(lo_, n, 0, &carry);
-    hi_ += carry;
+        unsigned long long carry;
+        lo_ = __builtin_addcll(lo_, n, 0, &carry);
+        hi_ += carry;
 #elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__)
-    unsigned long long result;
-    auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result);
-    lo_ = result;
-    hi_ += carry;
+        unsigned long long result;
+        auto               carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result);
+        lo_                      = result;
+        hi_ += carry;
 #elif defined(_MSC_VER) && defined(_M_X64)
-    auto carry = _addcarry_u64(0, lo_, n, &lo_);
-    _addcarry_u64(carry, hi_, 0, &hi_);
+        auto carry = _addcarry_u64(0, lo_, n, &lo_);
+        _addcarry_u64(carry, hi_, 0, &hi_);
 #else
-    lo_ += n;
-    hi_ += (lo_ < n ? 1 : 0);
+        lo_ += n;
+        hi_ += (lo_ < n ? 1 : 0);
 #endif
-    return *this;
-  }
+        return *this;
+    }
 };
 
 using uint128_t = conditional_t<FMT_USE_INT128, uint128_opt, uint128_fallback>;
@@ -434,68 +460,94 @@ using uintptr_t = uint128_t;
 
 // Returns the largest possible value for type T. Same as
 // std::numeric_limits<T>::max() but shorter and not affected by the max macro.
-template <typename T> constexpr auto max_value() -> T {
-  return (std::numeric_limits<T>::max)();
+template <typename T>
+constexpr auto max_value() -> T
+{
+    return (std::numeric_limits<T>::max)();
 }
-template <typename T> constexpr auto num_bits() -> int {
-  return std::numeric_limits<T>::digits;
+template <typename T>
+constexpr auto num_bits() -> int
+{
+    return std::numeric_limits<T>::digits;
 }
 // std::numeric_limits<T>::digits may return 0 for 128-bit ints.
-template <> constexpr auto num_bits<int128_opt>() -> int { return 128; }
-template <> constexpr auto num_bits<uint128_t>() -> int { return 128; }
+template <>
+constexpr auto num_bits<int128_opt>() -> int
+{
+    return 128;
+}
+template <>
+constexpr auto num_bits<uint128_t>() -> int
+{
+    return 128;
+}
 
 // A heterogeneous bit_cast used for converting 96-bit long double to uint128_t
 // and 128-bit pointers to uint128_fallback.
 template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) > sizeof(From))>
-inline auto bit_cast(const From& from) -> To {
-  constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned));
-  struct data_t {
-    unsigned value[static_cast<unsigned>(size)];
-  } data = bit_cast<data_t>(from);
-  auto result = To();
-  if (const_check(is_big_endian())) {
-    for (int i = 0; i < size; ++i)
-      result = (result << num_bits<unsigned>()) | data.value[i];
-  } else {
-    for (int i = size - 1; i >= 0; --i)
-      result = (result << num_bits<unsigned>()) | data.value[i];
-  }
-  return result;
-}
-
-FMT_INLINE void assume(bool condition) {
-  (void)condition;
+inline auto bit_cast(const From &from) -> To
+{
+    constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned));
+    struct data_t
+    {
+        unsigned value[static_cast<unsigned>(size)];
+    } data      = bit_cast<data_t>(from);
+    auto result = To();
+    if(const_check(is_big_endian()))
+    {
+        for(int i = 0; i < size; ++i)
+            result = (result << num_bits<unsigned>()) | data.value[i];
+    }
+    else
+    {
+        for(int i = size - 1; i >= 0; --i)
+            result = (result << num_bits<unsigned>()) | data.value[i];
+    }
+    return result;
+}
+
+FMT_INLINE void assume(bool condition)
+{
+    (void) condition;
 #if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION
-  __builtin_assume(condition);
+    __builtin_assume(condition);
 #endif
 }
 
 // An approximation of iterator_t for pre-C++20 systems.
 template <typename T>
-using iterator_t = decltype(std::begin(std::declval<T&>()));
-template <typename T> using sentinel_t = decltype(std::end(std::declval<T&>()));
+using iterator_t = decltype(std::begin(std::declval<T &>()));
+template <typename T>
+using sentinel_t = decltype(std::end(std::declval<T &>()));
 
 // A workaround for std::string not having mutable data() until C++17.
 template <typename Char>
-inline auto get_data(std::basic_string<Char>& s) -> Char* {
-  return &s[0];
+inline auto get_data(std::basic_string<Char> &s) -> Char *
+{
+    return &s[0];
 }
 template <typename Container>
-inline auto get_data(Container& c) -> typename Container::value_type* {
-  return c.data();
+inline auto get_data(Container &c) -> typename Container::value_type *
+{
+    return c.data();
 }
 
 #if defined(_SECURE_SCL) && _SECURE_SCL
 // Make a checked iterator to avoid MSVC warnings.
-template <typename T> using checked_ptr = stdext::checked_array_iterator<T*>;
 template <typename T>
-constexpr auto make_checked(T* p, size_t size) -> checked_ptr<T> {
-  return {p, size};
+using checked_ptr = stdext::checked_array_iterator<T *>;
+template <typename T>
+constexpr auto make_checked(T *p, size_t size) -> checked_ptr<T>
+{
+    return {p, size};
 }
 #else
-template <typename T> using checked_ptr = T*;
-template <typename T> constexpr auto make_checked(T* p, size_t) -> T* {
-  return p;
+template <typename T>
+using checked_ptr = T *;
+template <typename T>
+constexpr auto make_checked(T *p, size_t) -> T *
+{
+    return p;
 }
 #endif
 
@@ -506,81 +558,92 @@ template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
 __attribute__((no_sanitize("undefined")))
 #endif
 inline auto
-reserve(std::back_insert_iterator<Container> it, size_t n)
-    -> checked_ptr<typename Container::value_type> {
-  Container& c = get_container(it);
-  size_t size = c.size();
-  c.resize(size + n);
-  return make_checked(get_data(c) + size, n);
+reserve(std::back_insert_iterator<Container> it, size_t n) -> checked_ptr<typename Container::value_type>
+{
+    Container &c    = get_container(it);
+    size_t     size = c.size();
+    c.resize(size + n);
+    return make_checked(get_data(c) + size, n);
 }
 
 template <typename T>
-inline auto reserve(buffer_appender<T> it, size_t n) -> buffer_appender<T> {
-  buffer<T>& buf = get_container(it);
-  buf.try_reserve(buf.size() + n);
-  return it;
+inline auto reserve(buffer_appender<T> it, size_t n) -> buffer_appender<T>
+{
+    buffer<T> &buf = get_container(it);
+    buf.try_reserve(buf.size() + n);
+    return it;
 }
 
 template <typename Iterator>
-constexpr auto reserve(Iterator& it, size_t) -> Iterator& {
-  return it;
+constexpr auto reserve(Iterator &it, size_t) -> Iterator &
+{
+    return it;
 }
 
 template <typename OutputIt>
-using reserve_iterator =
-    remove_reference_t<decltype(reserve(std::declval<OutputIt&>(), 0))>;
+using reserve_iterator = remove_reference_t<decltype(reserve(std::declval<OutputIt &>(), 0))>;
 
 template <typename T, typename OutputIt>
-constexpr auto to_pointer(OutputIt, size_t) -> T* {
-  return nullptr;
+constexpr auto to_pointer(OutputIt, size_t) -> T *
+{
+    return nullptr;
 }
-template <typename T> auto to_pointer(buffer_appender<T> it, size_t n) -> T* {
-  buffer<T>& buf = get_container(it);
-  auto size = buf.size();
-  if (buf.capacity() < size + n) return nullptr;
-  buf.try_resize(size + n);
-  return buf.data() + size;
+template <typename T>
+auto to_pointer(buffer_appender<T> it, size_t n) -> T *
+{
+    buffer<T> &buf  = get_container(it);
+    auto       size = buf.size();
+    if(buf.capacity() < size + n)
+        return nullptr;
+    buf.try_resize(size + n);
+    return buf.data() + size;
 }
 
 template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
-inline auto base_iterator(std::back_insert_iterator<Container>& it,
-                          checked_ptr<typename Container::value_type>)
-    -> std::back_insert_iterator<Container> {
-  return it;
+inline auto base_iterator(std::back_insert_iterator<Container> &it, checked_ptr<typename Container::value_type>)
+    -> std::back_insert_iterator<Container>
+{
+    return it;
 }
 
 template <typename Iterator>
-constexpr auto base_iterator(Iterator, Iterator it) -> Iterator {
-  return it;
+constexpr auto base_iterator(Iterator, Iterator it) -> Iterator
+{
+    return it;
 }
 
 // <algorithm> is spectacularly slow to compile in C++20 so use a simple fill_n
 // instead (#1998).
 template <typename OutputIt, typename Size, typename T>
-FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value)
-    -> OutputIt {
-  for (Size i = 0; i < count; ++i) *out++ = value;
-  return out;
+FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T &value) -> OutputIt
+{
+    for(Size i = 0; i < count; ++i)
+        *out++ = value;
+    return out;
 }
 template <typename T, typename Size>
-FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* {
-  if (is_constant_evaluated()) {
-    return fill_n<T*, Size, T>(out, count, value);
-  }
-  std::memset(out, value, to_unsigned(count));
-  return out + count;
+FMT_CONSTEXPR20 auto fill_n(T *out, Size count, char value) -> T *
+{
+    if(is_constant_evaluated())
+    {
+        return fill_n<T *, Size, T>(out, count, value);
+    }
+    std::memset(out, value, to_unsigned(count));
+    return out + count;
 }
 
 #ifdef __cpp_char8_t
 using char8_type = char8_t;
 #else
-enum char8_type : unsigned char {};
+enum char8_type : unsigned char
+{
+};
 #endif
 
 template <typename OutChar, typename InputIt, typename OutputIt>
-FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end,
-                                                  OutputIt out) -> OutputIt {
-  return copy_str<OutChar>(begin, end, out);
+FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, OutputIt out) -> OutputIt
+{
+    return copy_str<OutChar>(begin, end, out);
 }
 
 // A public domain branchless UTF-8 decoder by Christopher Wellons:
@@ -600,40 +663,40 @@ FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end,
  * occurs, this pointer will be a guess that depends on the particular
  * error, but it will always advance at least one byte.
  */
-FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e)
-    -> const char* {
-  constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07};
-  constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536};
-  constexpr const int shiftc[] = {0, 18, 12, 6, 0};
-  constexpr const int shifte[] = {0, 6, 4, 2, 0};
-
-  int len = code_point_length_impl(*s);
-  // Compute the pointer to the next character early so that the next
-  // iteration can start working on the next character. Neither Clang
-  // nor GCC figure out this reordering on their own.
-  const char* next = s + len + !len;
-
-  using uchar = unsigned char;
-
-  // Assume a four-byte character and load four bytes. Unused bits are
-  // shifted out.
-  *c = uint32_t(uchar(s[0]) & masks[len]) << 18;
-  *c |= uint32_t(uchar(s[1]) & 0x3f) << 12;
-  *c |= uint32_t(uchar(s[2]) & 0x3f) << 6;
-  *c |= uint32_t(uchar(s[3]) & 0x3f) << 0;
-  *c >>= shiftc[len];
-
-  // Accumulate the various error conditions.
-  *e = (*c < mins[len]) << 6;       // non-canonical encoding
-  *e |= ((*c >> 11) == 0x1b) << 7;  // surrogate half?
-  *e |= (*c > 0x10FFFF) << 8;       // out of range?
-  *e |= (uchar(s[1]) & 0xc0) >> 2;
-  *e |= (uchar(s[2]) & 0xc0) >> 4;
-  *e |= uchar(s[3]) >> 6;
-  *e ^= 0x2a;  // top two bits of each tail byte correct?
-  *e >>= shifte[len];
-
-  return next;
+FMT_CONSTEXPR inline auto utf8_decode(const char *s, uint32_t *c, int *e) -> const char *
+{
+    constexpr const int      masks[]  = {0x00, 0x7f, 0x1f, 0x0f, 0x07};
+    constexpr const uint32_t mins[]   = {4194304, 0, 128, 2048, 65536};
+    constexpr const int      shiftc[] = {0, 18, 12, 6, 0};
+    constexpr const int      shifte[] = {0, 6, 4, 2, 0};
+
+    int len = code_point_length_impl(*s);
+    // Compute the pointer to the next character early so that the next
+    // iteration can start working on the next character. Neither Clang
+    // nor GCC figure out this reordering on their own.
+    const char *next = s + len + !len;
+
+    using uchar = unsigned char;
+
+    // Assume a four-byte character and load four bytes. Unused bits are
+    // shifted out.
+    *c = uint32_t(uchar(s[0]) & masks[len]) << 18;
+    *c |= uint32_t(uchar(s[1]) & 0x3f) << 12;
+    *c |= uint32_t(uchar(s[2]) & 0x3f) << 6;
+    *c |= uint32_t(uchar(s[3]) & 0x3f) << 0;
+    *c >>= shiftc[len];
+
+    // Accumulate the various error conditions.
+    *e = (*c < mins[len]) << 6;      // non-canonical encoding
+    *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half?
+    *e |= (*c > 0x10FFFF) << 8;      // out of range?
+    *e |= (uchar(s[1]) & 0xc0) >> 2;
+    *e |= (uchar(s[2]) & 0xc0) >> 4;
+    *e |= uchar(s[3]) >> 6;
+    *e ^= 0x2a; // top two bits of each tail byte correct?
+    *e >>= shifte[len];
+
+    return next;
 }
 
 constexpr uint32_t invalid_code_point = ~uint32_t();
@@ -641,157 +704,183 @@ constexpr uint32_t invalid_code_point = ~uint32_t();
 // Invokes f(cp, sv) for every code point cp in s with sv being the string view
 // corresponding to the code point. cp is invalid_code_point on error.
 template <typename F>
-FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) {
-  auto decode = [f](const char* buf_ptr, const char* ptr) {
-    auto cp = uint32_t();
-    auto error = 0;
-    auto end = utf8_decode(buf_ptr, &cp, &error);
-    bool result = f(error ? invalid_code_point : cp,
-                    string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr)));
-    return result ? (error ? buf_ptr + 1 : end) : nullptr;
-  };
-  auto p = s.data();
-  const size_t block_size = 4;  // utf8_decode always reads blocks of 4 chars.
-  if (s.size() >= block_size) {
-    for (auto end = p + s.size() - block_size + 1; p < end;) {
-      p = decode(p, p);
-      if (!p) return;
-    }
-  }
-  if (auto num_chars_left = s.data() + s.size() - p) {
-    char buf[2 * block_size - 1] = {};
-    copy_str<char>(p, p + num_chars_left, buf);
-    const char* buf_ptr = buf;
-    do {
-      auto end = decode(buf_ptr, p);
-      if (!end) return;
-      p += end - buf_ptr;
-      buf_ptr = end;
-    } while (buf_ptr - buf < num_chars_left);
-  }
+FMT_CONSTEXPR void for_each_codepoint(string_view s, F f)
+{
+    auto decode = [f](const char *buf_ptr, const char *ptr)
+    {
+        auto cp     = uint32_t();
+        auto error  = 0;
+        auto end    = utf8_decode(buf_ptr, &cp, &error);
+        bool result = f(error ? invalid_code_point : cp, string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr)));
+        return result ? (error ? buf_ptr + 1 : end) : nullptr;
+    };
+    auto         p          = s.data();
+    const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars.
+    if(s.size() >= block_size)
+    {
+        for(auto end = p + s.size() - block_size + 1; p < end;)
+        {
+            p = decode(p, p);
+            if(!p)
+                return;
+        }
+    }
+    if(auto num_chars_left = s.data() + s.size() - p)
+    {
+        char buf[2 * block_size - 1] = {};
+        copy_str<char>(p, p + num_chars_left, buf);
+        const char *buf_ptr = buf;
+        do
+        {
+            auto end = decode(buf_ptr, p);
+            if(!end)
+                return;
+            p += end - buf_ptr;
+            buf_ptr = end;
+        } while(buf_ptr - buf < num_chars_left);
+    }
 }
 
 template <typename Char>
-inline auto compute_width(basic_string_view<Char> s) -> size_t {
-  return s.size();
+inline auto compute_width(basic_string_view<Char> s) -> size_t
+{
+    return s.size();
 }
 
 // Computes approximate display width of a UTF-8 string.
-FMT_CONSTEXPR inline size_t compute_width(string_view s) {
-  size_t num_code_points = 0;
-  // It is not a lambda for compatibility with C++14.
-  struct count_code_points {
-    size_t* count;
-    FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool {
-      *count += detail::to_unsigned(
-          1 +
-          (cp >= 0x1100 &&
-           (cp <= 0x115f ||  // Hangul Jamo init. consonants
-            cp == 0x2329 ||  // LEFT-POINTING ANGLE BRACKET
-            cp == 0x232a ||  // RIGHT-POINTING ANGLE BRACKET
-            // CJK ... Yi except IDEOGRAPHIC HALF FILL SPACE:
-            (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) ||
-            (cp >= 0xac00 && cp <= 0xd7a3) ||    // Hangul Syllables
-            (cp >= 0xf900 && cp <= 0xfaff) ||    // CJK Compatibility Ideographs
-            (cp >= 0xfe10 && cp <= 0xfe19) ||    // Vertical Forms
-            (cp >= 0xfe30 && cp <= 0xfe6f) ||    // CJK Compatibility Forms
-            (cp >= 0xff00 && cp <= 0xff60) ||    // Fullwidth Forms
-            (cp >= 0xffe0 && cp <= 0xffe6) ||    // Fullwidth Forms
-            (cp >= 0x20000 && cp <= 0x2fffd) ||  // CJK
-            (cp >= 0x30000 && cp <= 0x3fffd) ||
-            // Miscellaneous Symbols and Pictographs + Emoticons:
-            (cp >= 0x1f300 && cp <= 0x1f64f) ||
-            // Supplemental Symbols and Pictographs:
-            (cp >= 0x1f900 && cp <= 0x1f9ff))));
-      return true;
-    }
-  };
-  for_each_codepoint(s, count_code_points{&num_code_points});
-  return num_code_points;
-}
-
-inline auto compute_width(basic_string_view<char8_type> s) -> size_t {
-  return compute_width(
-      string_view(reinterpret_cast<const char*>(s.data()), s.size()));
+FMT_CONSTEXPR inline size_t compute_width(string_view s)
+{
+    size_t num_code_points = 0;
+    // It is not a lambda for compatibility with C++14.
+    struct count_code_points
+    {
+        size_t            *count;
+        FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool
+        {
+            *count += detail::to_unsigned(
+                1 + (cp >= 0x1100 && (cp <= 0x115f || // Hangul Jamo init. consonants
+                                      cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET
+                                      cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET
+                                      // CJK ... Yi except IDEOGRAPHIC HALF FILL SPACE:
+                                      (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) ||
+                                      (cp >= 0xac00 && cp <= 0xd7a3) ||   // Hangul Syllables
+                                      (cp >= 0xf900 && cp <= 0xfaff) ||   // CJK Compatibility Ideographs
+                                      (cp >= 0xfe10 && cp <= 0xfe19) ||   // Vertical Forms
+                                      (cp >= 0xfe30 && cp <= 0xfe6f) ||   // CJK Compatibility Forms
+                                      (cp >= 0xff00 && cp <= 0xff60) ||   // Fullwidth Forms
+                                      (cp >= 0xffe0 && cp <= 0xffe6) ||   // Fullwidth Forms
+                                      (cp >= 0x20000 && cp <= 0x2fffd) || // CJK
+                                      (cp >= 0x30000 && cp <= 0x3fffd) ||
+                                      // Miscellaneous Symbols and Pictographs + Emoticons:
+                                      (cp >= 0x1f300 && cp <= 0x1f64f) ||
+                                      // Supplemental Symbols and Pictographs:
+                                      (cp >= 0x1f900 && cp <= 0x1f9ff))));
+            return true;
+        }
+    };
+    for_each_codepoint(s, count_code_points{&num_code_points});
+    return num_code_points;
+}
+
+inline auto compute_width(basic_string_view<char8_type> s) -> size_t
+{
+    return compute_width(string_view(reinterpret_cast<const char *>(s.data()), s.size()));
 }
 
 template <typename Char>
-inline auto code_point_index(basic_string_view<Char> s, size_t n) -> size_t {
-  size_t size = s.size();
-  return n < size ? n : size;
+inline auto code_point_index(basic_string_view<Char> s, size_t n) -> size_t
+{
+    size_t size = s.size();
+    return n < size ? n : size;
 }
 
 // Calculates the index of the nth code point in a UTF-8 string.
-inline auto code_point_index(string_view s, size_t n) -> size_t {
-  const char* data = s.data();
-  size_t num_code_points = 0;
-  for (size_t i = 0, size = s.size(); i != size; ++i) {
-    if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i;
-  }
-  return s.size();
+inline auto code_point_index(string_view s, size_t n) -> size_t
+{
+    const char *data            = s.data();
+    size_t      num_code_points = 0;
+    for(size_t i = 0, size = s.size(); i != size; ++i)
+    {
+        if((data[i] & 0xc0) != 0x80 && ++num_code_points > n)
+            return i;
+    }
+    return s.size();
 }
 
-inline auto code_point_index(basic_string_view<char8_type> s, size_t n)
-    -> size_t {
-  return code_point_index(
-      string_view(reinterpret_cast<const char*>(s.data()), s.size()), n);
+inline auto code_point_index(basic_string_view<char8_type> s, size_t n) -> size_t
+{
+    return code_point_index(string_view(reinterpret_cast<const char *>(s.data()), s.size()), n);
 }
 
 #ifndef FMT_USE_FLOAT128
-#  ifdef __SIZEOF_FLOAT128__
-#    define FMT_USE_FLOAT128 1
-#  else
-#    define FMT_USE_FLOAT128 0
-#  endif
+#ifdef __SIZEOF_FLOAT128__
+#define FMT_USE_FLOAT128 1
+#else
+#define FMT_USE_FLOAT128 0
+#endif
 #endif
 #if FMT_USE_FLOAT128
 using float128 = __float128;
 #else
 using float128 = void;
 #endif
-template <typename T> using is_float128 = std::is_same<T, float128>;
+template <typename T>
+using is_float128 = std::is_same<T, float128>;
 
 template <typename T>
-using is_floating_point =
-    bool_constant<std::is_floating_point<T>::value || is_float128<T>::value>;
+using is_floating_point = bool_constant<std::is_floating_point<T>::value || is_float128<T>::value>;
 
 template <typename T, bool = std::is_floating_point<T>::value>
-struct is_fast_float : bool_constant<std::numeric_limits<T>::is_iec559 &&
-                                     sizeof(T) <= sizeof(double)> {};
-template <typename T> struct is_fast_float<T, false> : std::false_type {};
+struct is_fast_float : bool_constant<std::numeric_limits<T>::is_iec559 && sizeof(T) <= sizeof(double)>
+{
+};
+template <typename T>
+struct is_fast_float<T, false> : std::false_type
+{
+};
 
 template <typename T>
 using is_double_double = bool_constant<std::numeric_limits<T>::digits == 106>;
 
 #ifndef FMT_USE_FULL_CACHE_DRAGONBOX
-#  define FMT_USE_FULL_CACHE_DRAGONBOX 0
+#define FMT_USE_FULL_CACHE_DRAGONBOX 0
 #endif
 
 template <typename T>
 template <typename U>
-void buffer<T>::append(const U* begin, const U* end) {
-  while (begin != end) {
-    auto count = to_unsigned(end - begin);
-    try_reserve(size_ + count);
-    auto free_cap = capacity_ - size_;
-    if (free_cap < count) count = free_cap;
-    std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count));
-    size_ += count;
-    begin += count;
-  }
+void buffer<T>::append(const U *begin, const U *end)
+{
+    while(begin != end)
+    {
+        auto count = to_unsigned(end - begin);
+        try_reserve(size_ + count);
+        auto free_cap = capacity_ - size_;
+        if(free_cap < count)
+            count = free_cap;
+        std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count));
+        size_ += count;
+        begin += count;
+    }
 }
 
 template <typename T, typename Enable = void>
-struct is_locale : std::false_type {};
+struct is_locale : std::false_type
+{
+};
 template <typename T>
-struct is_locale<T, void_t<decltype(T::classic())>> : std::true_type {};
-}  // namespace detail
+struct is_locale<T, void_t<decltype(T::classic())>> : std::true_type
+{
+};
+} // namespace detail
 
 FMT_MODULE_EXPORT_BEGIN
 
 // The number of characters to store in the basic_memory_buffer object itself
 // to avoid dynamic memory allocation.
-enum { inline_buffer_size = 500 };
+enum
+{
+    inline_buffer_size = 500
+};
 
 /**
   \rst
@@ -814,950 +903,1057 @@ enum { inline_buffer_size = 500 };
   The output can be converted to an ``std::string`` with ``to_string(out)``.
   \endrst
  */
-template <typename T, size_t SIZE = inline_buffer_size,
-          typename Allocator = std::allocator<T>>
-class basic_memory_buffer final : public detail::buffer<T> {
- private:
-  T store_[SIZE];
-
-  // Don't inherit from Allocator avoid generating type_info for it.
-  Allocator alloc_;
-
-  // Deallocate memory allocated by the buffer.
-  FMT_CONSTEXPR20 void deallocate() {
-    T* data = this->data();
-    if (data != store_) alloc_.deallocate(data, this->capacity());
-  }
-
- protected:
-  FMT_CONSTEXPR20 void grow(size_t size) override;
-
- public:
-  using value_type = T;
-  using const_reference = const T&;
-
-  FMT_CONSTEXPR20 explicit basic_memory_buffer(
-      const Allocator& alloc = Allocator())
-      : alloc_(alloc) {
-    this->set(store_, SIZE);
-    if (detail::is_constant_evaluated()) detail::fill_n(store_, SIZE, T());
-  }
-  FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); }
-
- private:
-  // Move data from other to this buffer.
-  FMT_CONSTEXPR20 void move(basic_memory_buffer& other) {
-    alloc_ = std::move(other.alloc_);
-    T* data = other.data();
-    size_t size = other.size(), capacity = other.capacity();
-    if (data == other.store_) {
-      this->set(store_, capacity);
-      detail::copy_str<T>(other.store_, other.store_ + size,
-                          detail::make_checked(store_, capacity));
-    } else {
-      this->set(data, capacity);
-      // Set pointer to the inline array so that delete is not called
-      // when deallocating.
-      other.set(other.store_, 0);
-      other.clear();
-    }
-    this->resize(size);
-  }
-
- public:
-  /**
-    \rst
-    Constructs a :class:`fmt::basic_memory_buffer` object moving the content
-    of the other object to it.
-    \endrst
-   */
-  FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept {
-    move(other);
-  }
-
-  /**
-    \rst
-    Moves the content of the other ``basic_memory_buffer`` object to this one.
-    \endrst
-   */
-  auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& {
-    FMT_ASSERT(this != &other, "");
-    deallocate();
-    move(other);
-    return *this;
-  }
-
-  // Returns a copy of the allocator associated with this buffer.
-  auto get_allocator() const -> Allocator { return alloc_; }
-
-  /**
-    Resizes the buffer to contain *count* elements. If T is a POD type new
-    elements may not be initialized.
-   */
-  FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); }
-
-  /** Increases the buffer capacity to *new_capacity*. */
-  void reserve(size_t new_capacity) { this->try_reserve(new_capacity); }
-
-  // Directly append data into the buffer
-  using detail::buffer<T>::append;
-  template <typename ContiguousRange>
-  void append(const ContiguousRange& range) {
-    append(range.data(), range.data() + range.size());
-  }
+template <typename T, size_t SIZE = inline_buffer_size, typename Allocator = std::allocator<T>>
+class basic_memory_buffer final : public detail::buffer<T>
+{
+private:
+    T store_[SIZE];
+
+    // Don't inherit from Allocator avoid generating type_info for it.
+    Allocator alloc_;
+
+    // Deallocate memory allocated by the buffer.
+    FMT_CONSTEXPR20 void deallocate()
+    {
+        T *data = this->data();
+        if(data != store_)
+            alloc_.deallocate(data, this->capacity());
+    }
+
+protected:
+    FMT_CONSTEXPR20 void grow(size_t size) override;
+
+public:
+    using value_type      = T;
+    using const_reference = const T &;
+
+    FMT_CONSTEXPR20 explicit basic_memory_buffer(const Allocator &alloc = Allocator()) : alloc_(alloc)
+    {
+        this->set(store_, SIZE);
+        if(detail::is_constant_evaluated())
+            detail::fill_n(store_, SIZE, T());
+    }
+    FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); }
+
+private:
+    // Move data from other to this buffer.
+    FMT_CONSTEXPR20 void move(basic_memory_buffer &other)
+    {
+        alloc_      = std::move(other.alloc_);
+        T     *data = other.data();
+        size_t size = other.size(), capacity = other.capacity();
+        if(data == other.store_)
+        {
+            this->set(store_, capacity);
+            detail::copy_str<T>(other.store_, other.store_ + size, detail::make_checked(store_, capacity));
+        }
+        else
+        {
+            this->set(data, capacity);
+            // Set pointer to the inline array so that delete is not called
+            // when deallocating.
+            other.set(other.store_, 0);
+            other.clear();
+        }
+        this->resize(size);
+    }
+
+public:
+    /**
+      \rst
+      Constructs a :class:`fmt::basic_memory_buffer` object moving the content
+      of the other object to it.
+      \endrst
+     */
+    FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer &&other) noexcept { move(other); }
+
+    /**
+      \rst
+      Moves the content of the other ``basic_memory_buffer`` object to this one.
+      \endrst
+     */
+    auto operator=(basic_memory_buffer &&other) noexcept -> basic_memory_buffer &
+    {
+        FMT_ASSERT(this != &other, "");
+        deallocate();
+        move(other);
+        return *this;
+    }
+
+    // Returns a copy of the allocator associated with this buffer.
+    auto get_allocator() const -> Allocator { return alloc_; }
+
+    /**
+      Resizes the buffer to contain *count* elements. If T is a POD type new
+      elements may not be initialized.
+     */
+    FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); }
+
+    /** Increases the buffer capacity to *new_capacity*. */
+    void reserve(size_t new_capacity) { this->try_reserve(new_capacity); }
+
+    // Directly append data into the buffer
+    using detail::buffer<T>::append;
+    template <typename ContiguousRange>
+    void append(const ContiguousRange &range)
+    {
+        append(range.data(), range.data() + range.size());
+    }
 };
 
 template <typename T, size_t SIZE, typename Allocator>
-FMT_CONSTEXPR20 void basic_memory_buffer<T, SIZE, Allocator>::grow(
-    size_t size) {
-  detail::abort_fuzzing_if(size > 5000);
-  const size_t max_size = std::allocator_traits<Allocator>::max_size(alloc_);
-  size_t old_capacity = this->capacity();
-  size_t new_capacity = old_capacity + old_capacity / 2;
-  if (size > new_capacity)
-    new_capacity = size;
-  else if (new_capacity > max_size)
-    new_capacity = size > max_size ? size : max_size;
-  T* old_data = this->data();
-  T* new_data =
-      std::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
-  // The following code doesn't throw, so the raw pointer above doesn't leak.
-  std::uninitialized_copy(old_data, old_data + this->size(),
-                          detail::make_checked(new_data, new_capacity));
-  this->set(new_data, new_capacity);
-  // deallocate must not throw according to the standard, but even if it does,
-  // the buffer already uses the new storage and will deallocate it in
-  // destructor.
-  if (old_data != store_) alloc_.deallocate(old_data, old_capacity);
+FMT_CONSTEXPR20 void basic_memory_buffer<T, SIZE, Allocator>::grow(size_t size)
+{
+    detail::abort_fuzzing_if(size > 5000);
+    const size_t max_size     = std::allocator_traits<Allocator>::max_size(alloc_);
+    size_t       old_capacity = this->capacity();
+    size_t       new_capacity = old_capacity + old_capacity / 2;
+    if(size > new_capacity)
+        new_capacity = size;
+    else if(new_capacity > max_size)
+        new_capacity = size > max_size ? size : max_size;
+    T *old_data = this->data();
+    T *new_data = std::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
+    // The following code doesn't throw, so the raw pointer above doesn't leak.
+    std::uninitialized_copy(old_data, old_data + this->size(), detail::make_checked(new_data, new_capacity));
+    this->set(new_data, new_capacity);
+    // deallocate must not throw according to the standard, but even if it does,
+    // the buffer already uses the new storage and will deallocate it in
+    // destructor.
+    if(old_data != store_)
+        alloc_.deallocate(old_data, old_capacity);
 }
 
 using memory_buffer = basic_memory_buffer<char>;
 
 template <typename T, size_t SIZE, typename Allocator>
-struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
+struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type
+{
 };
 
-namespace detail {
+namespace detail
+{
 #ifdef _WIN32
-FMT_API bool write_console(std::FILE* f, string_view text);
+FMT_API bool write_console(std::FILE *f, string_view text);
 #endif
-FMT_API void print(std::FILE*, string_view);
-}  // namespace detail
+FMT_API void print(std::FILE *, string_view);
+} // namespace detail
 
 /** A formatting error such as invalid format string. */
 FMT_CLASS_API
-class FMT_API format_error : public std::runtime_error {
- public:
-  explicit format_error(const char* message) : std::runtime_error(message) {}
-  explicit format_error(const std::string& message)
-      : std::runtime_error(message) {}
-  format_error(const format_error&) = default;
-  format_error& operator=(const format_error&) = default;
-  format_error(format_error&&) = default;
-  format_error& operator=(format_error&&) = default;
-  ~format_error() noexcept override FMT_MSC_DEFAULT;
+class FMT_API format_error : public std::runtime_error
+{
+public:
+    explicit format_error(const char *message) : std::runtime_error(message) {}
+    explicit format_error(const std::string &message) : std::runtime_error(message) {}
+    format_error(const format_error &)            = default;
+    format_error &operator=(const format_error &) = default;
+    format_error(format_error &&)                 = default;
+    format_error &operator=(format_error &&)      = default;
+    ~format_error() noexcept override FMT_MSC_DEFAULT;
 };
 
-namespace detail_exported {
+namespace detail_exported
+{
 #if FMT_USE_NONTYPE_TEMPLATE_ARGS
-template <typename Char, size_t N> struct fixed_string {
-  constexpr fixed_string(const Char (&str)[N]) {
-    detail::copy_str<Char, const Char*, Char*>(static_cast<const Char*>(str),
-                                               str + N, data);
-  }
-  Char data[N] = {};
+template <typename Char, size_t N>
+struct fixed_string
+{
+    constexpr fixed_string(const Char (&str)[N])
+    {
+        detail::copy_str<Char, const Char *, Char *>(static_cast<const Char *>(str), str + N, data);
+    }
+    Char data[N] = {};
 };
 #endif
 
 // Converts a compile-time string to basic_string_view.
 template <typename Char, size_t N>
-constexpr auto compile_string_to_view(const Char (&s)[N])
-    -> basic_string_view<Char> {
-  // Remove trailing NUL character if needed. Won't be present if this is used
-  // with a raw character array (i.e. not defined as a string).
-  return {s, N - (std::char_traits<Char>::to_int_type(s[N - 1]) == 0 ? 1 : 0)};
+constexpr auto compile_string_to_view(const Char (&s)[N]) -> basic_string_view<Char>
+{
+    // Remove trailing NUL character if needed. Won't be present if this is used
+    // with a raw character array (i.e. not defined as a string).
+    return {s, N - (std::char_traits<Char>::to_int_type(s[N - 1]) == 0 ? 1 : 0)};
 }
 template <typename Char>
-constexpr auto compile_string_to_view(detail::std_string_view<Char> s)
-    -> basic_string_view<Char> {
-  return {s.data(), s.size()};
+constexpr auto compile_string_to_view(detail::std_string_view<Char> s) -> basic_string_view<Char>
+{
+    return {s.data(), s.size()};
 }
-}  // namespace detail_exported
+} // namespace detail_exported
 
 FMT_BEGIN_DETAIL_NAMESPACE
 
-template <typename T> struct is_integral : std::is_integral<T> {};
-template <> struct is_integral<int128_opt> : std::true_type {};
-template <> struct is_integral<uint128_t> : std::true_type {};
+template <typename T>
+struct is_integral : std::is_integral<T>
+{
+};
+template <>
+struct is_integral<int128_opt> : std::true_type
+{
+};
+template <>
+struct is_integral<uint128_t> : std::true_type
+{
+};
 
 template <typename T>
-using is_signed =
-    std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
-                                     std::is_same<T, int128_opt>::value>;
+using is_signed = std::integral_constant<bool, std::numeric_limits<T>::is_signed || std::is_same<T, int128_opt>::value>;
 
 // Returns true if value is negative, false otherwise.
 // Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
 template <typename T, FMT_ENABLE_IF(is_signed<T>::value)>
-constexpr auto is_negative(T value) -> bool {
-  return value < 0;
+constexpr auto is_negative(T value) -> bool
+{
+    return value < 0;
 }
 template <typename T, FMT_ENABLE_IF(!is_signed<T>::value)>
-constexpr auto is_negative(T) -> bool {
-  return false;
+constexpr auto is_negative(T) -> bool
+{
+    return false;
 }
 
 template <typename T>
-FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool {
-  if (std::is_same<T, float>()) return FMT_USE_FLOAT;
-  if (std::is_same<T, double>()) return FMT_USE_DOUBLE;
-  if (std::is_same<T, long double>()) return FMT_USE_LONG_DOUBLE;
-  return true;
+FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool
+{
+    if(std::is_same<T, float>())
+        return FMT_USE_FLOAT;
+    if(std::is_same<T, double>())
+        return FMT_USE_DOUBLE;
+    if(std::is_same<T, long double>())
+        return FMT_USE_LONG_DOUBLE;
+    return true;
 }
 
 // Smallest of uint32_t, uint64_t, uint128_t that is large enough to
 // represent all values of an integral type T.
 template <typename T>
-using uint32_or_64_or_128_t =
-    conditional_t<num_bits<T>() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS,
-                  uint32_t,
-                  conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;
+using uint32_or_64_or_128_t = conditional_t<
+    num_bits<T>() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS,
+    uint32_t,
+    conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;
 template <typename T>
 using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>;
 
-#define FMT_POWERS_OF_10(factor)                                             \
-  factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
-      (factor)*1000000, (factor)*10000000, (factor)*100000000,               \
-      (factor)*1000000000
+#define FMT_POWERS_OF_10(factor)                                                                                       \
+    factor * 10, (factor) *100, (factor) *1000, (factor) *10000, (factor) *100000, (factor) *1000000,                  \
+        (factor) *10000000, (factor) *100000000, (factor) *1000000000
 
 // Converts value in the range [0, 100) to a string.
-constexpr const char* digits2(size_t value) {
-  // GCC generates slightly better code when value is pointer-size.
-  return &"0001020304050607080910111213141516171819"
-         "2021222324252627282930313233343536373839"
-         "4041424344454647484950515253545556575859"
-         "6061626364656667686970717273747576777879"
-         "8081828384858687888990919293949596979899"[value * 2];
+constexpr const char *digits2(size_t value)
+{
+    // GCC generates slightly better code when value is pointer-size.
+    return &"0001020304050607080910111213141516171819"
+            "2021222324252627282930313233343536373839"
+            "4041424344454647484950515253545556575859"
+            "6061626364656667686970717273747576777879"
+            "8081828384858687888990919293949596979899"[value * 2];
 }
 
 // Sign is a template parameter to workaround a bug in gcc 4.8.
-template <typename Char, typename Sign> constexpr Char sign(Sign s) {
+template <typename Char, typename Sign>
+constexpr Char sign(Sign s)
+{
 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604
-  static_assert(std::is_same<Sign, sign_t>::value, "");
+    static_assert(std::is_same<Sign, sign_t>::value, "");
 #endif
-  return static_cast<Char>("\0-+ "[s]);
-}
-
-template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
-  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
-    // "Three Optimization Tips for C++". See speed-test for a comparison.
-    if (n < 10) return count;
-    if (n < 100) return count + 1;
-    if (n < 1000) return count + 2;
-    if (n < 10000) return count + 3;
-    n /= 10000u;
-    count += 4;
-  }
+    return static_cast<Char>("\0-+ "[s]);
+}
+
+template <typename T>
+FMT_CONSTEXPR auto count_digits_fallback(T n) -> int
+{
+    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
+        // "Three Optimization Tips for C++". See speed-test for a comparison.
+        if(n < 10)
+            return count;
+        if(n < 100)
+            return count + 1;
+        if(n < 1000)
+            return count + 2;
+        if(n < 10000)
+            return count + 3;
+        n /= 10000u;
+        count += 4;
+    }
 }
 #if FMT_USE_INT128
-FMT_CONSTEXPR inline auto count_digits(uint128_opt n) -> int {
-  return count_digits_fallback(n);
+FMT_CONSTEXPR inline auto count_digits(uint128_opt n) -> int
+{
+    return count_digits_fallback(n);
 }
 #endif
 
 #ifdef FMT_BUILTIN_CLZLL
 // It is a separate function rather than a part of count_digits to workaround
 // the lack of static constexpr in constexpr functions.
-inline auto do_count_digits(uint64_t n) -> int {
-  // This has comparable performance to the version by Kendall Willets
-  // (https://github.com/fmtlib/format-benchmark/blob/master/digits10)
-  // but uses smaller tables.
-  // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)).
-  static constexpr uint8_t bsr2log10[] = {
-      1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4,  4,  5,  5,  5,
-      6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  9,  9,  9,  10, 10, 10,
-      10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
-      15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20};
-  auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63];
-  static constexpr const uint64_t zero_or_powers_of_10[] = {
-      0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL),
-      10000000000000000000ULL};
-  return t - (n < zero_or_powers_of_10[t]);
+inline auto do_count_digits(uint64_t n) -> int
+{
+    // This has comparable performance to the version by Kendall Willets
+    // (https://github.com/fmtlib/format-benchmark/blob/master/digits10)
+    // but uses smaller tables.
+    // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)).
+    static constexpr uint8_t        bsr2log10[] = {1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4,  4,  5,  5,  5,
+                                                   6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  9,  9,  9,  10, 10, 10,
+                                                   10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
+                                                   15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20};
+    auto                            t           = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63];
+    static constexpr const uint64_t zero_or_powers_of_10[] = {
+        0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), 10000000000000000000ULL};
+    return t - (n < zero_or_powers_of_10[t]);
 }
 #endif
 
 // Returns the number of decimal digits in n. Leading zeros are not counted
 // except for n == 0 in which case count_digits returns 1.
-FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int {
+FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int
+{
 #ifdef FMT_BUILTIN_CLZLL
-  if (!is_constant_evaluated()) {
-    return do_count_digits(n);
-  }
+    if(!is_constant_evaluated())
+    {
+        return do_count_digits(n);
+    }
 #endif
-  return count_digits_fallback(n);
+    return count_digits_fallback(n);
 }
 
 // Counts the number of digits in n. BITS = log2(radix).
 template <int BITS, typename UInt>
-FMT_CONSTEXPR auto count_digits(UInt n) -> int {
+FMT_CONSTEXPR auto count_digits(UInt n) -> int
+{
 #ifdef FMT_BUILTIN_CLZ
-  if (!is_constant_evaluated() && num_bits<UInt>() == 32)
-    return (FMT_BUILTIN_CLZ(static_cast<uint32_t>(n) | 1) ^ 31) / BITS + 1;
+    if(!is_constant_evaluated() && num_bits<UInt>() == 32)
+        return (FMT_BUILTIN_CLZ(static_cast<uint32_t>(n) | 1) ^ 31) / BITS + 1;
 #endif
-  // Lambda avoids unreachable code warnings from NVHPC.
-  return [](UInt m) {
-    int num_digits = 0;
-    do {
-      ++num_digits;
-    } while ((m >>= BITS) != 0);
-    return num_digits;
-  }(n);
+    // Lambda avoids unreachable code warnings from NVHPC.
+    return [](UInt m)
+    {
+        int num_digits = 0;
+        do
+        {
+            ++num_digits;
+        } while((m >>= BITS) != 0);
+        return num_digits;
+    }(n);
 }
 
 #ifdef FMT_BUILTIN_CLZ
 // It is a separate function rather than a part of count_digits to workaround
 // the lack of static constexpr in constexpr functions.
-FMT_INLINE auto do_count_digits(uint32_t n) -> int {
+FMT_INLINE auto do_count_digits(uint32_t n) -> int
+{
 // An optimization by Kendall Willets from https://bit.ly/3uOIQrB.
 // This increments the upper 32 bits (log10(T) - 1) when >= T is added.
-#  define FMT_INC(T) (((sizeof(#  T) - 1ull) << 32) - T)
-  static constexpr uint64_t table[] = {
-      FMT_INC(0),          FMT_INC(0),          FMT_INC(0),           // 8
-      FMT_INC(10),         FMT_INC(10),         FMT_INC(10),          // 64
-      FMT_INC(100),        FMT_INC(100),        FMT_INC(100),         // 512
-      FMT_INC(1000),       FMT_INC(1000),       FMT_INC(1000),        // 4096
-      FMT_INC(10000),      FMT_INC(10000),      FMT_INC(10000),       // 32k
-      FMT_INC(100000),     FMT_INC(100000),     FMT_INC(100000),      // 256k
-      FMT_INC(1000000),    FMT_INC(1000000),    FMT_INC(1000000),     // 2048k
-      FMT_INC(10000000),   FMT_INC(10000000),   FMT_INC(10000000),    // 16M
-      FMT_INC(100000000),  FMT_INC(100000000),  FMT_INC(100000000),   // 128M
-      FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000),  // 1024M
-      FMT_INC(1000000000), FMT_INC(1000000000)                        // 4B
-  };
-  auto inc = table[FMT_BUILTIN_CLZ(n | 1) ^ 31];
-  return static_cast<int>((n + inc) >> 32);
+#define FMT_INC(T) (((sizeof(#T) - 1ull) << 32) - T)
+    static constexpr uint64_t table[] = {
+        FMT_INC(0),          FMT_INC(0),          FMT_INC(0),          // 8
+        FMT_INC(10),         FMT_INC(10),         FMT_INC(10),         // 64
+        FMT_INC(100),        FMT_INC(100),        FMT_INC(100),        // 512
+        FMT_INC(1000),       FMT_INC(1000),       FMT_INC(1000),       // 4096
+        FMT_INC(10000),      FMT_INC(10000),      FMT_INC(10000),      // 32k
+        FMT_INC(100000),     FMT_INC(100000),     FMT_INC(100000),     // 256k
+        FMT_INC(1000000),    FMT_INC(1000000),    FMT_INC(1000000),    // 2048k
+        FMT_INC(10000000),   FMT_INC(10000000),   FMT_INC(10000000),   // 16M
+        FMT_INC(100000000),  FMT_INC(100000000),  FMT_INC(100000000),  // 128M
+        FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000), // 1024M
+        FMT_INC(1000000000), FMT_INC(1000000000)                       // 4B
+    };
+    auto inc = table[FMT_BUILTIN_CLZ(n | 1) ^ 31];
+    return static_cast<int>((n + inc) >> 32);
 }
 #endif
 
 // Optional version of count_digits for better performance on 32-bit platforms.
-FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int {
+FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int
+{
 #ifdef FMT_BUILTIN_CLZ
-  if (!is_constant_evaluated()) {
-    return do_count_digits(n);
-  }
+    if(!is_constant_evaluated())
+    {
+        return do_count_digits(n);
+    }
 #endif
-  return count_digits_fallback(n);
+    return count_digits_fallback(n);
 }
 
-template <typename Int> constexpr auto digits10() noexcept -> int {
-  return std::numeric_limits<Int>::digits10;
+template <typename Int>
+constexpr auto digits10() noexcept -> int
+{
+    return std::numeric_limits<Int>::digits10;
+}
+template <>
+constexpr auto digits10<int128_opt>() noexcept -> int
+{
+    return 38;
+}
+template <>
+constexpr auto digits10<uint128_t>() noexcept -> int
+{
+    return 38;
 }
-template <> constexpr auto digits10<int128_opt>() noexcept -> int { return 38; }
-template <> constexpr auto digits10<uint128_t>() noexcept -> int { return 38; }
 
-template <typename Char> struct thousands_sep_result {
-  std::string grouping;
-  Char thousands_sep;
+template <typename Char>
+struct thousands_sep_result
+{
+    std::string grouping;
+    Char        thousands_sep;
 };
 
 template <typename Char>
 FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char>;
 template <typename Char>
-inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<Char> {
-  auto result = thousands_sep_impl<char>(loc);
-  return {result.grouping, Char(result.thousands_sep)};
+inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<Char>
+{
+    auto result = thousands_sep_impl<char>(loc);
+    return {result.grouping, Char(result.thousands_sep)};
 }
 template <>
-inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<wchar_t> {
-  return thousands_sep_impl<wchar_t>(loc);
+inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<wchar_t>
+{
+    return thousands_sep_impl<wchar_t>(loc);
 }
 
 template <typename Char>
 FMT_API auto decimal_point_impl(locale_ref loc) -> Char;
-template <typename Char> inline auto decimal_point(locale_ref loc) -> Char {
-  return Char(decimal_point_impl<char>(loc));
+template <typename Char>
+inline auto decimal_point(locale_ref loc) -> Char
+{
+    return Char(decimal_point_impl<char>(loc));
 }
-template <> inline auto decimal_point(locale_ref loc) -> wchar_t {
-  return decimal_point_impl<wchar_t>(loc);
+template <>
+inline auto decimal_point(locale_ref loc) -> wchar_t
+{
+    return decimal_point_impl<wchar_t>(loc);
 }
 
 // Compares two characters for equality.
-template <typename Char> auto equal2(const Char* lhs, const char* rhs) -> bool {
-  return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]);
+template <typename Char>
+auto equal2(const Char *lhs, const char *rhs) -> bool
+{
+    return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]);
 }
-inline auto equal2(const char* lhs, const char* rhs) -> bool {
-  return memcmp(lhs, rhs, 2) == 0;
+inline auto equal2(const char *lhs, const char *rhs) -> bool
+{
+    return memcmp(lhs, rhs, 2) == 0;
 }
 
 // Copies two characters from src to dst.
 template <typename Char>
-FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) {
-  if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) {
-    memcpy(dst, src, 2);
-    return;
-  }
-  *dst++ = static_cast<Char>(*src++);
-  *dst = static_cast<Char>(*src);
-}
-
-template <typename Iterator> struct format_decimal_result {
-  Iterator begin;
-  Iterator end;
+FMT_CONSTEXPR20 FMT_INLINE void copy2(Char *dst, const char *src)
+{
+    if(!is_constant_evaluated() && sizeof(Char) == sizeof(char))
+    {
+        memcpy(dst, src, 2);
+        return;
+    }
+    *dst++ = static_cast<Char>(*src++);
+    *dst   = static_cast<Char>(*src);
+}
+
+template <typename Iterator>
+struct format_decimal_result
+{
+    Iterator begin;
+    Iterator end;
 };
 
 // Formats a decimal unsigned integer value writing into out pointing to a
 // buffer of specified size. The caller must ensure that the buffer is large
 // enough.
 template <typename Char, typename UInt>
-FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size)
-    -> format_decimal_result<Char*> {
-  FMT_ASSERT(size >= count_digits(value), "invalid digit count");
-  out += size;
-  Char* end = out;
-  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
-    // "Three Optimization Tips for C++". See speed-test for a comparison.
+FMT_CONSTEXPR20 auto format_decimal(Char *out, UInt value, int size) -> format_decimal_result<Char *>
+{
+    FMT_ASSERT(size >= count_digits(value), "invalid digit count");
+    out += size;
+    Char *end = out;
+    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
+        // "Three Optimization Tips for C++". See speed-test for a comparison.
+        out -= 2;
+        copy2(out, digits2(static_cast<size_t>(value % 100)));
+        value /= 100;
+    }
+    if(value < 10)
+    {
+        *--out = static_cast<Char>('0' + value);
+        return {out, end};
+    }
     out -= 2;
-    copy2(out, digits2(static_cast<size_t>(value % 100)));
-    value /= 100;
-  }
-  if (value < 10) {
-    *--out = static_cast<Char>('0' + value);
+    copy2(out, digits2(static_cast<size_t>(value)));
     return {out, end};
-  }
-  out -= 2;
-  copy2(out, digits2(static_cast<size_t>(value)));
-  return {out, end};
 }
 
-template <typename Char, typename UInt, typename Iterator,
-          FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)>
-FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size)
-    -> format_decimal_result<Iterator> {
-  // Buffer is large enough to hold all digits (digits10 + 1).
-  Char buffer[digits10<UInt>() + 1];
-  auto end = format_decimal(buffer, value, size).end;
-  return {out, detail::copy_str_noinline<Char>(buffer, end, out)};
+template <
+    typename Char,
+    typename UInt,
+    typename Iterator,
+    FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)>
+FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size) -> format_decimal_result<Iterator>
+{
+    // Buffer is large enough to hold all digits (digits10 + 1).
+    Char buffer[digits10<UInt>() + 1];
+    auto end = format_decimal(buffer, value, size).end;
+    return {out, detail::copy_str_noinline<Char>(buffer, end, out)};
 }
 
 template <unsigned BASE_BITS, typename Char, typename UInt>
-FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits,
-                               bool upper = false) -> Char* {
-  buffer += num_digits;
-  Char* end = buffer;
-  do {
-    const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
-    unsigned digit = static_cast<unsigned>(value & ((1 << BASE_BITS) - 1));
-    *--buffer = static_cast<Char>(BASE_BITS < 4 ? static_cast<char>('0' + digit)
-                                                : digits[digit]);
-  } while ((value >>= BASE_BITS) != 0);
-  return end;
+FMT_CONSTEXPR auto format_uint(Char *buffer, UInt value, int num_digits, bool upper = false) -> Char *
+{
+    buffer += num_digits;
+    Char *end = buffer;
+    do
+    {
+        const char *digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
+        unsigned    digit  = static_cast<unsigned>(value & ((1 << BASE_BITS) - 1));
+        *--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 Char, typename It, typename UInt>
-inline auto format_uint(It out, UInt value, int num_digits, bool upper = false)
-    -> It {
-  if (auto ptr = to_pointer<Char>(out, to_unsigned(num_digits))) {
-    format_uint<BASE_BITS>(ptr, value, num_digits, upper);
-    return out;
-  }
-  // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
-  char buffer[num_bits<UInt>() / BASE_BITS + 1];
-  format_uint<BASE_BITS>(buffer, value, num_digits, upper);
-  return detail::copy_str_noinline<Char>(buffer, buffer + num_digits, out);
+inline auto format_uint(It out, UInt value, int num_digits, bool upper = false) -> It
+{
+    if(auto ptr = to_pointer<Char>(out, to_unsigned(num_digits)))
+    {
+        format_uint<BASE_BITS>(ptr, value, num_digits, upper);
+        return out;
+    }
+    // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
+    char buffer[num_bits<UInt>() / BASE_BITS + 1];
+    format_uint<BASE_BITS>(buffer, value, num_digits, upper);
+    return detail::copy_str_noinline<Char>(buffer, buffer + num_digits, out);
 }
 
 // A converter from UTF-8 to UTF-16.
-class utf8_to_utf16 {
- private:
-  basic_memory_buffer<wchar_t> buffer_;
-
- public:
-  FMT_API explicit utf8_to_utf16(string_view s);
-  operator basic_string_view<wchar_t>() const { return {&buffer_[0], size()}; }
-  auto size() const -> size_t { return buffer_.size() - 1; }
-  auto c_str() const -> const wchar_t* { return &buffer_[0]; }
-  auto str() const -> std::wstring { return {&buffer_[0], size()}; }
+class utf8_to_utf16
+{
+private:
+    basic_memory_buffer<wchar_t> buffer_;
+
+public:
+    FMT_API explicit utf8_to_utf16(string_view s);
+         operator basic_string_view<wchar_t>() const { return {&buffer_[0], size()}; }
+    auto size() const -> size_t { return buffer_.size() - 1; }
+    auto c_str() const -> const wchar_t * { return &buffer_[0]; }
+    auto str() const -> std::wstring { return {&buffer_[0], size()}; }
 };
 
-namespace dragonbox {
+namespace dragonbox
+{
 
 // Type-specific information that Dragonbox uses.
-template <typename T, typename Enable = void> struct float_info;
-
-template <> struct float_info<float> {
-  using carrier_uint = uint32_t;
-  static const int exponent_bits = 8;
-  static const int kappa = 1;
-  static const int big_divisor = 100;
-  static const int small_divisor = 10;
-  static const int min_k = -31;
-  static const int max_k = 46;
-  static const int shorter_interval_tie_lower_threshold = -35;
-  static const int shorter_interval_tie_upper_threshold = -35;
+template <typename T, typename Enable = void>
+struct float_info;
+
+template <>
+struct float_info<float>
+{
+    using carrier_uint                                    = uint32_t;
+    static const int exponent_bits                        = 8;
+    static const int kappa                                = 1;
+    static const int big_divisor                          = 100;
+    static const int small_divisor                        = 10;
+    static const int min_k                                = -31;
+    static const int max_k                                = 46;
+    static const int shorter_interval_tie_lower_threshold = -35;
+    static const int shorter_interval_tie_upper_threshold = -35;
 };
 
-template <> struct float_info<double> {
-  using carrier_uint = uint64_t;
-  static const int exponent_bits = 11;
-  static const int kappa = 2;
-  static const int big_divisor = 1000;
-  static const int small_divisor = 100;
-  static const int min_k = -292;
-  static const int max_k = 326;
-  static const int shorter_interval_tie_lower_threshold = -77;
-  static const int shorter_interval_tie_upper_threshold = -77;
+template <>
+struct float_info<double>
+{
+    using carrier_uint                                    = uint64_t;
+    static const int exponent_bits                        = 11;
+    static const int kappa                                = 2;
+    static const int big_divisor                          = 1000;
+    static const int small_divisor                        = 100;
+    static const int min_k                                = -292;
+    static const int max_k                                = 326;
+    static const int shorter_interval_tie_lower_threshold = -77;
+    static const int shorter_interval_tie_upper_threshold = -77;
 };
 
 // An 80- or 128-bit floating point number.
 template <typename T>
-struct float_info<T, enable_if_t<std::numeric_limits<T>::digits == 64 ||
-                                 std::numeric_limits<T>::digits == 113 ||
-                                 is_float128<T>::value>> {
-  using carrier_uint = detail::uint128_t;
-  static const int exponent_bits = 15;
+struct float_info<
+    T,
+    enable_if_t<std::numeric_limits<T>::digits == 64 || std::numeric_limits<T>::digits == 113 || is_float128<T>::value>>
+{
+    using carrier_uint             = detail::uint128_t;
+    static const int exponent_bits = 15;
 };
 
 // A double-double floating point number.
 template <typename T>
-struct float_info<T, enable_if_t<is_double_double<T>::value>> {
-  using carrier_uint = detail::uint128_t;
+struct float_info<T, enable_if_t<is_double_double<T>::value>>
+{
+    using carrier_uint = detail::uint128_t;
 };
 
-template <typename T> struct decimal_fp {
-  using significand_type = typename float_info<T>::carrier_uint;
-  significand_type significand;
-  int exponent;
+template <typename T>
+struct decimal_fp
+{
+    using significand_type = typename float_info<T>::carrier_uint;
+    significand_type significand;
+    int              exponent;
 };
 
-template <typename T> FMT_API auto to_decimal(T x) noexcept -> decimal_fp<T>;
-}  // namespace dragonbox
+template <typename T>
+FMT_API auto to_decimal(T x) noexcept -> decimal_fp<T>;
+} // namespace dragonbox
 
 // Returns true iff Float has the implicit bit which is not stored.
-template <typename Float> constexpr bool has_implicit_bit() {
-  // An 80-bit FP number has a 64-bit significand an no implicit bit.
-  return std::numeric_limits<Float>::digits != 64;
+template <typename Float>
+constexpr bool has_implicit_bit()
+{
+    // An 80-bit FP number has a 64-bit significand an no implicit bit.
+    return std::numeric_limits<Float>::digits != 64;
 }
 
 // Returns the number of significand bits stored in Float. The implicit bit is
 // not counted since it is not stored.
-template <typename Float> constexpr int num_significand_bits() {
-  // std::numeric_limits may not support __float128.
-  return is_float128<Float>() ? 112
-                              : (std::numeric_limits<Float>::digits -
-                                 (has_implicit_bit<Float>() ? 1 : 0));
+template <typename Float>
+constexpr int num_significand_bits()
+{
+    // std::numeric_limits may not support __float128.
+    return is_float128<Float>() ? 112 : (std::numeric_limits<Float>::digits - (has_implicit_bit<Float>() ? 1 : 0));
 }
 
 template <typename Float>
-constexpr auto exponent_mask() ->
-    typename dragonbox::float_info<Float>::carrier_uint {
-  using uint = typename dragonbox::float_info<Float>::carrier_uint;
-  return ((uint(1) << dragonbox::float_info<Float>::exponent_bits) - 1)
-         << num_significand_bits<Float>();
+constexpr auto exponent_mask() -> typename dragonbox::float_info<Float>::carrier_uint
+{
+    using uint = typename dragonbox::float_info<Float>::carrier_uint;
+    return ((uint(1) << dragonbox::float_info<Float>::exponent_bits) - 1) << num_significand_bits<Float>();
 }
-template <typename Float> constexpr auto exponent_bias() -> int {
-  // std::numeric_limits may not support __float128.
-  return is_float128<Float>() ? 16383
-                              : std::numeric_limits<Float>::max_exponent - 1;
+template <typename Float>
+constexpr auto exponent_bias() -> int
+{
+    // std::numeric_limits may not support __float128.
+    return is_float128<Float>() ? 16383 : std::numeric_limits<Float>::max_exponent - 1;
 }
 
 // Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
 template <typename Char, typename It>
-FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It {
-  FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
-  if (exp < 0) {
-    *it++ = static_cast<Char>('-');
-    exp = -exp;
-  } else {
-    *it++ = static_cast<Char>('+');
-  }
-  if (exp >= 100) {
-    const char* top = digits2(to_unsigned(exp / 100));
-    if (exp >= 1000) *it++ = static_cast<Char>(top[0]);
-    *it++ = static_cast<Char>(top[1]);
-    exp %= 100;
-  }
-  const char* d = digits2(to_unsigned(exp));
-  *it++ = static_cast<Char>(d[0]);
-  *it++ = static_cast<Char>(d[1]);
-  return it;
+FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It
+{
+    FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
+    if(exp < 0)
+    {
+        *it++ = static_cast<Char>('-');
+        exp   = -exp;
+    }
+    else
+    {
+        *it++ = static_cast<Char>('+');
+    }
+    if(exp >= 100)
+    {
+        const char *top = digits2(to_unsigned(exp / 100));
+        if(exp >= 1000)
+            *it++ = static_cast<Char>(top[0]);
+        *it++ = static_cast<Char>(top[1]);
+        exp %= 100;
+    }
+    const char *d = digits2(to_unsigned(exp));
+    *it++         = static_cast<Char>(d[0]);
+    *it++         = static_cast<Char>(d[1]);
+    return it;
 }
 
 // A floating-point number f * pow(2, e) where F is an unsigned type.
-template <typename F> struct basic_fp {
-  F f;
-  int e;
-
-  static constexpr const int num_significand_bits =
-      static_cast<int>(sizeof(F) * num_bits<unsigned char>());
-
-  constexpr basic_fp() : f(0), e(0) {}
-  constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
-
-  // Constructs fp from an IEEE754 floating-point number.
-  template <typename Float> FMT_CONSTEXPR basic_fp(Float n) { assign(n); }
-
-  // Assigns n to this and return true iff predecessor is closer than successor.
-  template <typename Float, FMT_ENABLE_IF(!is_double_double<Float>::value)>
-  FMT_CONSTEXPR auto assign(Float n) -> bool {
-    static_assert(std::numeric_limits<Float>::digits <= 113, "unsupported FP");
-    // Assume Float is in the format [sign][exponent][significand].
-    using carrier_uint = typename dragonbox::float_info<Float>::carrier_uint;
-    const auto num_float_significand_bits =
-        detail::num_significand_bits<Float>();
-    const auto implicit_bit = carrier_uint(1) << num_float_significand_bits;
-    const auto significand_mask = implicit_bit - 1;
-    auto u = bit_cast<carrier_uint>(n);
-    f = static_cast<F>(u & significand_mask);
-    auto biased_e = static_cast<int>((u & exponent_mask<Float>()) >>
-                                     num_float_significand_bits);
-    // The predecessor is closer if n is a normalized power of 2 (f == 0)
-    // other than the smallest normalized number (biased_e > 1).
-    auto is_predecessor_closer = f == 0 && biased_e > 1;
-    if (biased_e == 0)
-      biased_e = 1;  // Subnormals use biased exponent 1 (min exponent).
-    else if (has_implicit_bit<Float>())
-      f += static_cast<F>(implicit_bit);
-    e = biased_e - exponent_bias<Float>() - num_float_significand_bits;
-    if (!has_implicit_bit<Float>()) ++e;
-    return is_predecessor_closer;
-  }
-
-  template <typename Float, FMT_ENABLE_IF(is_double_double<Float>::value)>
-  FMT_CONSTEXPR auto assign(Float n) -> bool {
-    static_assert(std::numeric_limits<double>::is_iec559, "unsupported FP");
-    return assign(static_cast<double>(n));
-  }
+template <typename F>
+struct basic_fp
+{
+    F   f;
+    int e;
+
+    static constexpr const int num_significand_bits = static_cast<int>(sizeof(F) * num_bits<unsigned char>());
+
+    constexpr basic_fp() : f(0), e(0) {}
+    constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
+
+    // Constructs fp from an IEEE754 floating-point number.
+    template <typename Float>
+    FMT_CONSTEXPR basic_fp(Float n)
+    {
+        assign(n);
+    }
+
+    // Assigns n to this and return true iff predecessor is closer than successor.
+    template <typename Float, FMT_ENABLE_IF(!is_double_double<Float>::value)>
+    FMT_CONSTEXPR auto assign(Float n) -> bool
+    {
+        static_assert(std::numeric_limits<Float>::digits <= 113, "unsupported FP");
+        // Assume Float is in the format [sign][exponent][significand].
+        using carrier_uint                    = typename dragonbox::float_info<Float>::carrier_uint;
+        const auto num_float_significand_bits = detail::num_significand_bits<Float>();
+        const auto implicit_bit               = carrier_uint(1) << num_float_significand_bits;
+        const auto significand_mask           = implicit_bit - 1;
+        auto       u                          = bit_cast<carrier_uint>(n);
+        f                                     = static_cast<F>(u & significand_mask);
+        auto biased_e = static_cast<int>((u & exponent_mask<Float>()) >> num_float_significand_bits);
+        // The predecessor is closer if n is a normalized power of 2 (f == 0)
+        // other than the smallest normalized number (biased_e > 1).
+        auto is_predecessor_closer = f == 0 && biased_e > 1;
+        if(biased_e == 0)
+            biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
+        else if(has_implicit_bit<Float>())
+            f += static_cast<F>(implicit_bit);
+        e = biased_e - exponent_bias<Float>() - num_float_significand_bits;
+        if(!has_implicit_bit<Float>())
+            ++e;
+        return is_predecessor_closer;
+    }
+
+    template <typename Float, FMT_ENABLE_IF(is_double_double<Float>::value)>
+    FMT_CONSTEXPR auto assign(Float n) -> bool
+    {
+        static_assert(std::numeric_limits<double>::is_iec559, "unsupported FP");
+        return assign(static_cast<double>(n));
+    }
 };
 
 using fp = basic_fp<unsigned long long>;
 
 // Normalizes the value converted from double and multiplied by (1 << SHIFT).
 template <int SHIFT = 0, typename F>
-FMT_CONSTEXPR basic_fp<F> normalize(basic_fp<F> value) {
-  // Handle subnormals.
-  const auto implicit_bit = F(1) << num_significand_bits<double>();
-  const auto shifted_implicit_bit = implicit_bit << SHIFT;
-  while ((value.f & shifted_implicit_bit) == 0) {
-    value.f <<= 1;
-    --value.e;
-  }
-  // Subtract 1 to account for hidden bit.
-  const auto offset = basic_fp<F>::num_significand_bits -
-                      num_significand_bits<double>() - SHIFT - 1;
-  value.f <<= offset;
-  value.e -= offset;
-  return value;
+FMT_CONSTEXPR basic_fp<F> normalize(basic_fp<F> value)
+{
+    // Handle subnormals.
+    const auto implicit_bit         = F(1) << num_significand_bits<double>();
+    const auto shifted_implicit_bit = implicit_bit << SHIFT;
+    while((value.f & shifted_implicit_bit) == 0)
+    {
+        value.f <<= 1;
+        --value.e;
+    }
+    // Subtract 1 to account for hidden bit.
+    const auto offset = basic_fp<F>::num_significand_bits - num_significand_bits<double>() - SHIFT - 1;
+    value.f <<= offset;
+    value.e -= offset;
+    return value;
 }
 
 // Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
-FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
+FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs)
+{
 #if FMT_USE_INT128
-  auto product = static_cast<__uint128_t>(lhs) * rhs;
-  auto f = static_cast<uint64_t>(product >> 64);
-  return (static_cast<uint64_t>(product) & (1ULL << 63)) != 0 ? f + 1 : f;
+    auto product = static_cast<__uint128_t>(lhs) * rhs;
+    auto f       = static_cast<uint64_t>(product >> 64);
+    return (static_cast<uint64_t>(product) & (1ULL << 63)) != 0 ? f + 1 : f;
 #else
-  // Multiply 32-bit parts of significands.
-  uint64_t mask = (1ULL << 32) - 1;
-  uint64_t a = lhs >> 32, b = lhs & mask;
-  uint64_t c = rhs >> 32, d = rhs & mask;
-  uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
-  // Compute mid 64-bit of result and round.
-  uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
-  return ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
+    // Multiply 32-bit parts of significands.
+    uint64_t mask = (1ULL << 32) - 1;
+    uint64_t a = lhs >> 32, b = lhs & mask;
+    uint64_t c = rhs >> 32, d = rhs & mask;
+    uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
+    // Compute mid 64-bit of result and round.
+    uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
+    return ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
 #endif
 }
 
-FMT_CONSTEXPR inline fp operator*(fp x, fp y) {
-  return {multiply(x.f, y.f), x.e + y.e + 64};
-}
-
-template <typename T = void> struct basic_data {
-  // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
-  // These are generated by support/compute-powers.py.
-  static constexpr uint64_t pow10_significands[87] = {
-      0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
-      0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
-      0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
-      0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
-      0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
-      0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
-      0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
-      0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
-      0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
-      0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
-      0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
-      0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
-      0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
-      0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
-      0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
-      0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
-      0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
-      0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
-      0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
-      0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
-      0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
-      0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
-      0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
-      0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
-      0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
-      0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
-      0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
-      0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
-      0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
-  };
+FMT_CONSTEXPR inline fp operator*(fp x, fp y)
+{
+    return {multiply(x.f, y.f), x.e + y.e + 64};
+}
+
+template <typename T = void>
+struct basic_data
+{
+    // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
+    // These are generated by support/compute-powers.py.
+    static constexpr uint64_t pow10_significands[87] = {
+        0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d,
+        0xe61acf033d1a45df, 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, 0x8dd01fad907ffc3c,
+        0xd3515c2831559a83, 0x9d71ac8fada6c9b5, 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
+        0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, 0xa086cfcd97bf97f4, 0xef340a98172aace5,
+        0xb23867fb2a35b28e, 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, 0xdbac6c247d62a584,
+        0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
+        0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, 0xf8a95fcf88747d94, 0xb94470938fa89bcf,
+        0x8a08f0f8bf0f156b, 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, 0xaa242499697392d3,
+        0xfd87b5f28300ca0e, 0xbce5086492111aeb, 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
+        0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70,
+        0xd5d238a4abe98068, 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, 0x83c7088e1aab65db,
+        0xc45d1df942711d9a, 0x924d692ca61be758, 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
+        0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3,
+        0xa59bc234db398c25, 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, 0xcc20ce9bd35c78a5,
+        0x98165af37b2153df, 0xe2a0b5dc971f303a, 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
+        0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, 0xe7109bfba19c0c9d, 0xac2820d9623bf429,
+        0x80444b5e7aa7cf85, 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, 0x9e19db92b4e31ba9,
+        0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
+    };
 
 #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
-#  pragma GCC diagnostic push
-#  pragma GCC diagnostic ignored "-Wnarrowing"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnarrowing"
 #endif
-  // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
-  // to significands above.
-  static constexpr int16_t pow10_exponents[87] = {
-      -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
-      -927,  -901,  -874,  -847,  -821,  -794,  -768,  -741,  -715,  -688, -661,
-      -635,  -608,  -582,  -555,  -529,  -502,  -475,  -449,  -422,  -396, -369,
-      -343,  -316,  -289,  -263,  -236,  -210,  -183,  -157,  -130,  -103, -77,
-      -50,   -24,   3,     30,    56,    83,    109,   136,   162,   189,  216,
-      242,   269,   295,   322,   348,   375,   402,   428,   455,   481,  508,
-      534,   561,   588,   614,   641,   667,   694,   720,   747,   774,  800,
-      827,   853,   880,   907,   933,   960,   986,   1013,  1039,  1066};
+    // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
+    // to significands above.
+    static constexpr int16_t pow10_exponents[87] = {
+        -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, -927, -901, -874, -847,
+        -821,  -794,  -768,  -741,  -715,  -688,  -661,  -635,  -608,  -582, -555, -529, -502, -475, -449,
+        -422,  -396,  -369,  -343,  -316,  -289,  -263,  -236,  -210,  -183, -157, -130, -103, -77,  -50,
+        -24,   3,     30,    56,    83,    109,   136,   162,   189,   216,  242,  269,  295,  322,  348,
+        375,   402,   428,   455,   481,   508,   534,   561,   588,   614,  641,  667,  694,  720,  747,
+        774,   800,   827,   853,   880,   907,   933,   960,   986,   1013, 1039, 1066};
 #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
-#  pragma GCC diagnostic pop
+#pragma GCC diagnostic pop
 #endif
 
-  static constexpr uint64_t power_of_10_64[20] = {
-      1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL),
-      10000000000000000000ULL};
+    static constexpr uint64_t power_of_10_64[20] =
+        {1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL), 10000000000000000000ULL};
 };
 
 #if FMT_CPLUSPLUS < 201703L
-template <typename T> constexpr uint64_t basic_data<T>::pow10_significands[];
-template <typename T> constexpr int16_t basic_data<T>::pow10_exponents[];
-template <typename T> constexpr uint64_t basic_data<T>::power_of_10_64[];
+template <typename T>
+constexpr uint64_t basic_data<T>::pow10_significands[];
+template <typename T>
+constexpr int16_t basic_data<T>::pow10_exponents[];
+template <typename T>
+constexpr uint64_t basic_data<T>::power_of_10_64[];
 #endif
 
 // This is a struct rather than an alias to avoid shadowing warnings in gcc.
-struct data : basic_data<> {};
+struct data : basic_data<>
+{
+};
 
 // Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
 // (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
-FMT_CONSTEXPR inline fp get_cached_power(int min_exponent,
-                                         int& pow10_exponent) {
-  const int shift = 32;
-  // log10(2) = 0x0.4d104d427de7fbcc...
-  const int64_t significand = 0x4d104d427de7fbcc;
-  int index = static_cast<int>(
-      ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) +
-       ((int64_t(1) << shift) - 1))  // ceil
-      >> 32                          // arithmetic shift
-  );
-  // Decimal exponent of the first (smallest) cached power of 10.
-  const int first_dec_exp = -348;
-  // Difference between 2 consecutive decimal exponents in cached powers of 10.
-  const int dec_exp_step = 8;
-  index = (index - first_dec_exp - 1) / dec_exp_step + 1;
-  pow10_exponent = first_dec_exp + index * dec_exp_step;
-  // Using *(x + index) instead of x[index] avoids an issue with some compilers
-  // using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode).
-  return {*(data::pow10_significands + index),
-          *(data::pow10_exponents + index)};
+FMT_CONSTEXPR inline fp get_cached_power(int min_exponent, int &pow10_exponent)
+{
+    const int shift = 32;
+    // log10(2) = 0x0.4d104d427de7fbcc...
+    const int64_t significand = 0x4d104d427de7fbcc;
+    int           index       = static_cast<int>(
+        ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) + ((int64_t(1) << shift) - 1)) // ceil
+        >> 32 // arithmetic shift
+    );
+    // Decimal exponent of the first (smallest) cached power of 10.
+    const int first_dec_exp = -348;
+    // Difference between 2 consecutive decimal exponents in cached powers of 10.
+    const int dec_exp_step = 8;
+    index                  = (index - first_dec_exp - 1) / dec_exp_step + 1;
+    pow10_exponent         = first_dec_exp + index * dec_exp_step;
+    // Using *(x + index) instead of x[index] avoids an issue with some compilers
+    // using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode).
+    return {*(data::pow10_significands + index), *(data::pow10_exponents + index)};
 }
 
 #ifndef _MSC_VER
-#  define FMT_SNPRINTF snprintf
+#define FMT_SNPRINTF snprintf
 #else
-FMT_API auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...) -> int;
-#  define FMT_SNPRINTF fmt_snprintf
-#endif  // _MSC_VER
+FMT_API auto fmt_snprintf(char *buf, size_t size, const char *fmt, ...) -> int;
+#define FMT_SNPRINTF fmt_snprintf
+#endif // _MSC_VER
 
 // Formats a floating-point number with snprintf using the hexfloat format.
 template <typename T>
-auto snprintf_float(T value, int precision, float_specs specs,
-                    buffer<char>& buf) -> int {
-  // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
-  FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
-  FMT_ASSERT(specs.format == float_format::hex, "");
-  static_assert(!std::is_same<T, float>::value, "");
-
-  // Build the format string.
-  char format[7];  // The longest format is "%#.*Le".
-  char* format_ptr = format;
-  *format_ptr++ = '%';
-  if (specs.showpoint) *format_ptr++ = '#';
-  if (precision >= 0) {
-    *format_ptr++ = '.';
-    *format_ptr++ = '*';
-  }
-  if (std::is_same<T, long double>()) *format_ptr++ = 'L';
-  *format_ptr++ = specs.upper ? 'A' : 'a';
-  *format_ptr = '\0';
-
-  // Format using snprintf.
-  auto offset = buf.size();
-  for (;;) {
-    auto begin = buf.data() + offset;
-    auto capacity = buf.capacity() - offset;
-    abort_fuzzing_if(precision > 100000);
-    // Suppress the warning about a nonliteral format string.
-    // Cannot use auto because of a bug in MinGW (#1532).
-    int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
-    int result = precision >= 0
-                     ? snprintf_ptr(begin, capacity, format, precision, value)
-                     : snprintf_ptr(begin, capacity, format, value);
-    if (result < 0) {
-      // The buffer will grow exponentially.
-      buf.try_reserve(buf.capacity() + 1);
-      continue;
-    }
-    auto size = to_unsigned(result);
-    // Size equal to capacity means that the last character was truncated.
-    if (size < capacity) {
-      buf.try_resize(size + offset);
-      return 0;
-    }
-    buf.try_reserve(size + offset + 1);  // Add 1 for the terminating '\0'.
-  }
+auto snprintf_float(T value, int precision, float_specs specs, buffer<char> &buf) -> int
+{
+    // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
+    FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
+    FMT_ASSERT(specs.format == float_format::hex, "");
+    static_assert(!std::is_same<T, float>::value, "");
+
+    // Build the format string.
+    char  format[7]; // The longest format is "%#.*Le".
+    char *format_ptr = format;
+    *format_ptr++    = '%';
+    if(specs.showpoint)
+        *format_ptr++ = '#';
+    if(precision >= 0)
+    {
+        *format_ptr++ = '.';
+        *format_ptr++ = '*';
+    }
+    if(std::is_same<T, long double>())
+        *format_ptr++ = 'L';
+    *format_ptr++ = specs.upper ? 'A' : 'a';
+    *format_ptr   = '\0';
+
+    // Format using snprintf.
+    auto offset = buf.size();
+    for(;;)
+    {
+        auto begin    = buf.data() + offset;
+        auto capacity = buf.capacity() - offset;
+        abort_fuzzing_if(precision > 100000);
+        // Suppress the warning about a nonliteral format string.
+        // Cannot use auto because of a bug in MinGW (#1532).
+        int (*snprintf_ptr)(char *, size_t, const char *, ...) = FMT_SNPRINTF;
+        int result = precision >= 0 ? snprintf_ptr(begin, capacity, format, precision, value) :
+                                      snprintf_ptr(begin, capacity, format, value);
+        if(result < 0)
+        {
+            // The buffer will grow exponentially.
+            buf.try_reserve(buf.capacity() + 1);
+            continue;
+        }
+        auto size = to_unsigned(result);
+        // Size equal to capacity means that the last character was truncated.
+        if(size < capacity)
+        {
+            buf.try_resize(size + offset);
+            return 0;
+        }
+        buf.try_reserve(size + offset + 1); // Add 1 for the terminating '\0'.
+    }
 }
 
 template <typename T>
-using convert_float_result =
-    conditional_t<std::is_same<T, float>::value || sizeof(T) == sizeof(double),
-                  double, T>;
+using convert_float_result = conditional_t<std::is_same<T, float>::value || sizeof(T) == sizeof(double), double, T>;
 
 template <typename T>
-constexpr auto convert_float(T value) -> convert_float_result<T> {
-  return static_cast<convert_float_result<T>>(value);
+constexpr auto convert_float(T value) -> convert_float_result<T>
+{
+    return static_cast<convert_float_result<T>>(value);
 }
 
 template <typename OutputIt, typename Char>
-FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n,
-                                     const fill_t<Char>& fill) -> OutputIt {
-  auto fill_size = fill.size();
-  if (fill_size == 1) return detail::fill_n(it, n, fill[0]);
-  auto data = fill.data();
-  for (size_t i = 0; i < n; ++i)
-    it = copy_str<Char>(data, data + fill_size, it);
-  return it;
+FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, const fill_t<Char> &fill) -> OutputIt
+{
+    auto fill_size = fill.size();
+    if(fill_size == 1)
+        return detail::fill_n(it, n, fill[0]);
+    auto data = fill.data();
+    for(size_t i = 0; i < n; ++i)
+        it = copy_str<Char>(data, data + fill_size, it);
+    return it;
 }
 
 // Writes the output of f, padded according to format specifications in specs.
 // size: output size in code units.
 // width: output display width in (terminal) column positions.
-template <align::type align = align::left, typename OutputIt, typename Char,
-          typename F>
-FMT_CONSTEXPR auto write_padded(OutputIt out,
-                                const basic_format_specs<Char>& specs,
-                                size_t size, size_t width, F&& f) -> OutputIt {
-  static_assert(align == align::left || align == align::right, "");
-  unsigned spec_width = to_unsigned(specs.width);
-  size_t padding = spec_width > width ? spec_width - width : 0;
-  // Shifts are encoded as string literals because static constexpr is not
-  // supported in constexpr functions.
-  auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01";
-  size_t left_padding = padding >> shifts[specs.align];
-  size_t right_padding = padding - left_padding;
-  auto it = reserve(out, size + padding * specs.fill.size());
-  if (left_padding != 0) it = fill(it, left_padding, specs.fill);
-  it = f(it);
-  if (right_padding != 0) it = fill(it, right_padding, specs.fill);
-  return base_iterator(out, it);
-}
-
-template <align::type align = align::left, typename OutputIt, typename Char,
-          typename F>
-constexpr auto write_padded(OutputIt out, const basic_format_specs<Char>& specs,
-                            size_t size, F&& f) -> OutputIt {
-  return write_padded<align>(out, specs, size, size, f);
+template <align::type align = align::left, typename OutputIt, typename Char, typename F>
+FMT_CONSTEXPR auto write_padded(OutputIt out, const basic_format_specs<Char> &specs, size_t size, size_t width, F &&f)
+    -> OutputIt
+{
+    static_assert(align == align::left || align == align::right, "");
+    unsigned spec_width = to_unsigned(specs.width);
+    size_t   padding    = spec_width > width ? spec_width - width : 0;
+    // Shifts are encoded as string literals because static constexpr is not
+    // supported in constexpr functions.
+    auto  *shifts        = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01";
+    size_t left_padding  = padding >> shifts[specs.align];
+    size_t right_padding = padding - left_padding;
+    auto   it            = reserve(out, size + padding * specs.fill.size());
+    if(left_padding != 0)
+        it = fill(it, left_padding, specs.fill);
+    it = f(it);
+    if(right_padding != 0)
+        it = fill(it, right_padding, specs.fill);
+    return base_iterator(out, it);
+}
+
+template <align::type align = align::left, typename OutputIt, typename Char, typename F>
+constexpr auto write_padded(OutputIt out, const basic_format_specs<Char> &specs, size_t size, F &&f) -> OutputIt
+{
+    return write_padded<align>(out, specs, size, size, f);
 }
 
 template <align::type align = align::left, typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes,
-                               const basic_format_specs<Char>& specs)
-    -> OutputIt {
-  return write_padded<align>(
-      out, specs, bytes.size(), [bytes](reserve_iterator<OutputIt> it) {
-        const char* data = bytes.data();
-        return copy_str<Char>(data, data + bytes.size(), it);
-      });
+FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, const basic_format_specs<Char> &specs) -> OutputIt
+{
+    return write_padded<align>(
+        out,
+        specs,
+        bytes.size(),
+        [bytes](reserve_iterator<OutputIt> it)
+        {
+            const char *data = bytes.data();
+            return copy_str<Char>(data, data + bytes.size(), it);
+        });
 }
 
 template <typename Char, typename OutputIt, typename UIntPtr>
-auto write_ptr(OutputIt out, UIntPtr value,
-               const basic_format_specs<Char>* specs) -> OutputIt {
-  int num_digits = count_digits<4>(value);
-  auto size = to_unsigned(num_digits) + size_t(2);
-  auto write = [=](reserve_iterator<OutputIt> it) {
-    *it++ = static_cast<Char>('0');
-    *it++ = static_cast<Char>('x');
-    return format_uint<4, Char>(it, value, num_digits);
-  };
-  return specs ? write_padded<align::right>(out, *specs, size, write)
-               : base_iterator(out, write(reserve(out, size)));
+auto write_ptr(OutputIt out, UIntPtr value, const basic_format_specs<Char> *specs) -> OutputIt
+{
+    int  num_digits = count_digits<4>(value);
+    auto size       = to_unsigned(num_digits) + size_t(2);
+    auto write      = [=](reserve_iterator<OutputIt> it)
+    {
+        *it++ = static_cast<Char>('0');
+        *it++ = static_cast<Char>('x');
+        return format_uint<4, Char>(it, value, num_digits);
+    };
+    return specs ? write_padded<align::right>(out, *specs, size, write) : base_iterator(out, write(reserve(out, size)));
 }
 
 // Returns true iff the code point cp is printable.
 FMT_API auto is_printable(uint32_t cp) -> bool;
 
-inline auto needs_escape(uint32_t cp) -> bool {
-  return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
-         !is_printable(cp);
+inline auto needs_escape(uint32_t cp) -> bool
+{
+    return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' || !is_printable(cp);
 }
 
-template <typename Char> struct find_escape_result {
-  const Char* begin;
-  const Char* end;
-  uint32_t cp;
+template <typename Char>
+struct find_escape_result
+{
+    const Char *begin;
+    const Char *end;
+    uint32_t    cp;
 };
 
 template <typename Char>
 using make_unsigned_char =
-    typename conditional_t<std::is_integral<Char>::value,
-                           std::make_unsigned<Char>,
-                           type_identity<uint32_t>>::type;
+    typename conditional_t<std::is_integral<Char>::value, std::make_unsigned<Char>, type_identity<uint32_t>>::type;
 
 template <typename Char>
-auto find_escape(const Char* begin, const Char* end)
-    -> find_escape_result<Char> {
-  for (; begin != end; ++begin) {
-    uint32_t cp = static_cast<make_unsigned_char<Char>>(*begin);
-    if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue;
-    if (needs_escape(cp)) return {begin, begin + 1, cp};
-  }
-  return {begin, nullptr, 0};
-}
-
-inline auto find_escape(const char* begin, const char* end)
-    -> find_escape_result<char> {
-  if (!is_utf8()) return find_escape<char>(begin, end);
-  auto result = find_escape_result<char>{end, nullptr, 0};
-  for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
-                     [&](uint32_t cp, string_view sv) {
-                       if (needs_escape(cp)) {
-                         result = {sv.begin(), sv.end(), cp};
-                         return false;
-                       }
-                       return true;
-                     });
-  return result;
-}
-
-#define FMT_STRING_IMPL(s, base, explicit)                                    \
-  [] {                                                                        \
-    /* Use the hidden visibility as a workaround for a GCC bug (#1973). */    \
-    /* Use a macro-like name to avoid shadowing warnings. */                  \
-    struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base {              \
-      using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t<decltype(s[0])>; \
-      FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit                                 \
-      operator fmt::basic_string_view<char_type>() const {                    \
-        return fmt::detail_exported::compile_string_to_view<char_type>(s);    \
-      }                                                                       \
-    };                                                                        \
-    return FMT_COMPILE_STRING();                                              \
-  }()
+auto find_escape(const Char *begin, const Char *end) -> find_escape_result<Char>
+{
+    for(; begin != end; ++begin)
+    {
+        uint32_t cp = static_cast<make_unsigned_char<Char>>(*begin);
+        if(const_check(sizeof(Char) == 1) && cp >= 0x80)
+            continue;
+        if(needs_escape(cp))
+            return {begin, begin + 1, cp};
+    }
+    return {begin, nullptr, 0};
+}
+
+inline auto find_escape(const char *begin, const char *end) -> find_escape_result<char>
+{
+    if(!is_utf8())
+        return find_escape<char>(begin, end);
+    auto result = find_escape_result<char>{end, nullptr, 0};
+    for_each_codepoint(
+        string_view(begin, to_unsigned(end - begin)),
+        [&](uint32_t cp, string_view sv)
+        {
+            if(needs_escape(cp))
+            {
+                result = {sv.begin(), sv.end(), cp};
+                return false;
+            }
+            return true;
+        });
+    return result;
+}
+
+#define FMT_STRING_IMPL(s, base, explicit)                                                                             \
+    []                                                                                                                 \
+    {                                                                                                                  \
+        /* Use the hidden visibility as a workaround for a GCC bug (#1973). */                                         \
+        /* Use a macro-like name to avoid shadowing warnings. */                                                       \
+        struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base                                                     \
+        {                                                                                                              \
+            using char_type                         FMT_MAYBE_UNUSED = fmt::remove_cvref_t<decltype(s[0])>;            \
+            FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit operator fmt::basic_string_view<char_type>() const                 \
+            {                                                                                                          \
+                return fmt::detail_exported::compile_string_to_view<char_type>(s);                                     \
+            }                                                                                                          \
+        };                                                                                                             \
+        return FMT_COMPILE_STRING();                                                                                   \
+    }()
 
 /**
   \rst
@@ -1772,132 +1968,150 @@ inline auto find_escape(const char* begin, const char* end)
 #define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, )
 
 template <size_t width, typename Char, typename OutputIt>
-auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt {
-  *out++ = static_cast<Char>('\\');
-  *out++ = static_cast<Char>(prefix);
-  Char buf[width];
-  fill_n(buf, width, static_cast<Char>('0'));
-  format_uint<4>(buf, cp, width);
-  return copy_str<Char>(buf, buf + width, out);
+auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt
+{
+    *out++ = static_cast<Char>('\\');
+    *out++ = static_cast<Char>(prefix);
+    Char buf[width];
+    fill_n(buf, width, static_cast<Char>('0'));
+    format_uint<4>(buf, cp, width);
+    return copy_str<Char>(buf, buf + width, out);
 }
 
 template <typename OutputIt, typename Char>
-auto write_escaped_cp(OutputIt out, const find_escape_result<Char>& escape)
-    -> OutputIt {
-  auto c = static_cast<Char>(escape.cp);
-  switch (escape.cp) {
-  case '\n':
-    *out++ = static_cast<Char>('\\');
-    c = static_cast<Char>('n');
-    break;
-  case '\r':
-    *out++ = static_cast<Char>('\\');
-    c = static_cast<Char>('r');
-    break;
-  case '\t':
-    *out++ = static_cast<Char>('\\');
-    c = static_cast<Char>('t');
-    break;
-  case '"':
-    FMT_FALLTHROUGH;
-  case '\'':
-    FMT_FALLTHROUGH;
-  case '\\':
-    *out++ = static_cast<Char>('\\');
-    break;
-  default:
-    if (is_utf8()) {
-      if (escape.cp < 0x100) {
-        return write_codepoint<2, Char>(out, 'x', escape.cp);
-      }
-      if (escape.cp < 0x10000) {
-        return write_codepoint<4, Char>(out, 'u', escape.cp);
-      }
-      if (escape.cp < 0x110000) {
-        return write_codepoint<8, Char>(out, 'U', escape.cp);
-      }
-    }
-    for (Char escape_char : basic_string_view<Char>(
-             escape.begin, to_unsigned(escape.end - escape.begin))) {
-      out = write_codepoint<2, Char>(out, 'x',
-                                     static_cast<uint32_t>(escape_char) & 0xFF);
+auto write_escaped_cp(OutputIt out, const find_escape_result<Char> &escape) -> OutputIt
+{
+    auto c = static_cast<Char>(escape.cp);
+    switch(escape.cp)
+    {
+        case '\n':
+            *out++ = static_cast<Char>('\\');
+            c      = static_cast<Char>('n');
+            break;
+        case '\r':
+            *out++ = static_cast<Char>('\\');
+            c      = static_cast<Char>('r');
+            break;
+        case '\t':
+            *out++ = static_cast<Char>('\\');
+            c      = static_cast<Char>('t');
+            break;
+        case '"':
+            FMT_FALLTHROUGH;
+        case '\'':
+            FMT_FALLTHROUGH;
+        case '\\':
+            *out++ = static_cast<Char>('\\');
+            break;
+        default:
+            if(is_utf8())
+            {
+                if(escape.cp < 0x100)
+                {
+                    return write_codepoint<2, Char>(out, 'x', escape.cp);
+                }
+                if(escape.cp < 0x10000)
+                {
+                    return write_codepoint<4, Char>(out, 'u', escape.cp);
+                }
+                if(escape.cp < 0x110000)
+                {
+                    return write_codepoint<8, Char>(out, 'U', escape.cp);
+                }
+            }
+            for(Char escape_char : basic_string_view<Char>(escape.begin, to_unsigned(escape.end - escape.begin)))
+            {
+                out = write_codepoint<2, Char>(out, 'x', static_cast<uint32_t>(escape_char) & 0xFF);
+            }
+            return out;
     }
+    *out++ = c;
     return out;
-  }
-  *out++ = c;
-  return out;
 }
 
 template <typename Char, typename OutputIt>
-auto write_escaped_string(OutputIt out, basic_string_view<Char> str)
-    -> OutputIt {
-  *out++ = static_cast<Char>('"');
-  auto begin = str.begin(), end = str.end();
-  do {
-    auto escape = find_escape(begin, end);
-    out = copy_str<Char>(begin, escape.begin, out);
-    begin = escape.end;
-    if (!begin) break;
-    out = write_escaped_cp<OutputIt, Char>(out, escape);
-  } while (begin != end);
-  *out++ = static_cast<Char>('"');
-  return out;
+auto write_escaped_string(OutputIt out, basic_string_view<Char> str) -> OutputIt
+{
+    *out++     = static_cast<Char>('"');
+    auto begin = str.begin(), end = str.end();
+    do
+    {
+        auto escape = find_escape(begin, end);
+        out         = copy_str<Char>(begin, escape.begin, out);
+        begin       = escape.end;
+        if(!begin)
+            break;
+        out = write_escaped_cp<OutputIt, Char>(out, escape);
+    } while(begin != end);
+    *out++ = static_cast<Char>('"');
+    return out;
 }
 
 template <typename Char, typename OutputIt>
-auto write_escaped_char(OutputIt out, Char v) -> OutputIt {
-  *out++ = static_cast<Char>('\'');
-  if ((needs_escape(static_cast<uint32_t>(v)) && v != static_cast<Char>('"')) ||
-      v == static_cast<Char>('\'')) {
-    out = write_escaped_cp(
-        out, find_escape_result<Char>{&v, &v + 1, static_cast<uint32_t>(v)});
-  } else {
-    *out++ = v;
-  }
-  *out++ = static_cast<Char>('\'');
-  return out;
+auto write_escaped_char(OutputIt out, Char v) -> OutputIt
+{
+    *out++ = static_cast<Char>('\'');
+    if((needs_escape(static_cast<uint32_t>(v)) && v != static_cast<Char>('"')) || v == static_cast<Char>('\''))
+    {
+        out = write_escaped_cp(out, find_escape_result<Char>{&v, &v + 1, static_cast<uint32_t>(v)});
+    }
+    else
+    {
+        *out++ = v;
+    }
+    *out++ = static_cast<Char>('\'');
+    return out;
 }
 
 template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write_char(OutputIt out, Char value,
-                              const basic_format_specs<Char>& specs)
-    -> OutputIt {
-  bool is_debug = specs.type == presentation_type::debug;
-  return write_padded(out, specs, 1, [=](reserve_iterator<OutputIt> it) {
-    if (is_debug) return write_escaped_char(it, value);
-    *it++ = value;
-    return it;
-  });
+FMT_CONSTEXPR auto write_char(OutputIt out, Char value, const basic_format_specs<Char> &specs) -> OutputIt
+{
+    bool is_debug = specs.type == presentation_type::debug;
+    return write_padded(
+        out,
+        specs,
+        1,
+        [=](reserve_iterator<OutputIt> it)
+        {
+            if(is_debug)
+                return write_escaped_char(it, value);
+            *it++ = value;
+            return it;
+        });
 }
 template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write(OutputIt out, Char value,
-                         const basic_format_specs<Char>& specs,
-                         locale_ref loc = {}) -> OutputIt {
-  return check_char_specs(specs)
-             ? write_char(out, value, specs)
-             : write(out, static_cast<int>(value), specs, loc);
+FMT_CONSTEXPR auto write(OutputIt out, Char value, const basic_format_specs<Char> &specs, locale_ref loc = {})
+    -> OutputIt
+{
+    return check_char_specs(specs) ? write_char(out, value, specs) : write(out, static_cast<int>(value), specs, loc);
 }
 
 // Data for write_int that doesn't depend on output iterator type. It is used to
 // avoid template code bloat.
-template <typename Char> struct write_int_data {
-  size_t size;
-  size_t padding;
-
-  FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix,
-                               const basic_format_specs<Char>& specs)
-      : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) {
-    if (specs.align == align::numeric) {
-      auto width = to_unsigned(specs.width);
-      if (width > size) {
-        padding = width - size;
-        size = width;
-      }
-    } else if (specs.precision > num_digits) {
-      size = (prefix >> 24) + to_unsigned(specs.precision);
-      padding = to_unsigned(specs.precision - num_digits);
-    }
-  }
+template <typename Char>
+struct write_int_data
+{
+    size_t size;
+    size_t padding;
+
+    FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix, const basic_format_specs<Char> &specs) :
+        size((prefix >> 24) + to_unsigned(num_digits)), padding(0)
+    {
+        if(specs.align == align::numeric)
+        {
+            auto width = to_unsigned(specs.width);
+            if(width > size)
+            {
+                padding = width - size;
+                size    = width;
+            }
+        }
+        else if(specs.precision > num_digits)
+        {
+            size    = (prefix >> 24) + to_unsigned(specs.precision);
+            padding = to_unsigned(specs.precision - num_digits);
+        }
+    }
 };
 
 // Writes an integer in the format
@@ -1905,1701 +2119,1985 @@ template <typename Char> struct write_int_data {
 // where <digits> are written by write_digits(it).
 // prefix contains chars in three lower bytes and the size in the fourth byte.
 template <typename OutputIt, typename Char, typename W>
-FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits,
-                                        unsigned prefix,
-                                        const basic_format_specs<Char>& specs,
-                                        W write_digits) -> OutputIt {
-  // Slightly faster check for specs.width == 0 && specs.precision == -1.
-  if ((specs.width | (specs.precision + 1)) == 0) {
-    auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24));
-    if (prefix != 0) {
-      for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
-        *it++ = static_cast<Char>(p & 0xff);
-    }
-    return base_iterator(out, write_digits(it));
-  }
-  auto data = write_int_data<Char>(num_digits, prefix, specs);
-  return write_padded<align::right>(
-      out, specs, data.size, [=](reserve_iterator<OutputIt> it) {
-        for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
-          *it++ = static_cast<Char>(p & 0xff);
-        it = detail::fill_n(it, data.padding, static_cast<Char>('0'));
-        return write_digits(it);
-      });
-}
-
-template <typename Char> class digit_grouping {
- private:
-  thousands_sep_result<Char> sep_;
-
-  struct next_state {
-    std::string::const_iterator group;
-    int pos;
-  };
-  next_state initial_state() const { return {sep_.grouping.begin(), 0}; }
-
-  // Returns the next digit group separator position.
-  int next(next_state& state) const {
-    if (!sep_.thousands_sep) return max_value<int>();
-    if (state.group == sep_.grouping.end())
-      return state.pos += sep_.grouping.back();
-    if (*state.group <= 0 || *state.group == max_value<char>())
-      return max_value<int>();
-    state.pos += *state.group++;
-    return state.pos;
-  }
-
- public:
-  explicit digit_grouping(locale_ref loc, bool localized = true) {
-    if (localized)
-      sep_ = thousands_sep<Char>(loc);
-    else
-      sep_.thousands_sep = Char();
-  }
-  explicit digit_grouping(thousands_sep_result<Char> sep) : sep_(sep) {}
-
-  Char separator() const { return sep_.thousands_sep; }
-
-  int count_separators(int num_digits) const {
-    int count = 0;
-    auto state = initial_state();
-    while (num_digits > next(state)) ++count;
-    return count;
-  }
-
-  // Applies grouping to digits and write the output to out.
-  template <typename Out, typename C>
-  Out apply(Out out, basic_string_view<C> digits) const {
-    auto num_digits = static_cast<int>(digits.size());
-    auto separators = basic_memory_buffer<int>();
-    separators.push_back(0);
-    auto state = initial_state();
-    while (int i = next(state)) {
-      if (i >= num_digits) break;
-      separators.push_back(i);
-    }
-    for (int i = 0, sep_index = static_cast<int>(separators.size() - 1);
-         i < num_digits; ++i) {
-      if (num_digits - i == separators[sep_index]) {
-        *out++ = separator();
-        --sep_index;
-      }
-      *out++ = static_cast<Char>(digits[to_unsigned(i)]);
+FMT_CONSTEXPR FMT_INLINE auto
+write_int(OutputIt out, int num_digits, unsigned prefix, const basic_format_specs<Char> &specs, W write_digits)
+    -> OutputIt
+{
+    // Slightly faster check for specs.width == 0 && specs.precision == -1.
+    if((specs.width | (specs.precision + 1)) == 0)
+    {
+        auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24));
+        if(prefix != 0)
+        {
+            for(unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
+                *it++ = static_cast<Char>(p & 0xff);
+        }
+        return base_iterator(out, write_digits(it));
     }
-    return out;
-  }
-};
+    auto data = write_int_data<Char>(num_digits, prefix, specs);
+    return write_padded<align::right>(
+        out,
+        specs,
+        data.size,
+        [=](reserve_iterator<OutputIt> it)
+        {
+            for(unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
+                *it++ = static_cast<Char>(p & 0xff);
+            it = detail::fill_n(it, data.padding, static_cast<Char>('0'));
+            return write_digits(it);
+        });
+}
 
-template <typename OutputIt, typename UInt, typename Char>
-auto write_int_localized(OutputIt out, UInt value, unsigned prefix,
-                         const basic_format_specs<Char>& specs,
-                         const digit_grouping<Char>& grouping) -> OutputIt {
-  static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
-  int num_digits = count_digits(value);
-  char digits[40];
-  format_decimal(digits, value, num_digits);
-  unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits +
-                              grouping.count_separators(num_digits));
-  return write_padded<align::right>(
-      out, specs, size, size, [&](reserve_iterator<OutputIt> it) {
-        if (prefix != 0) {
-          char sign = static_cast<char>(prefix);
-          *it++ = static_cast<Char>(sign);
+template <typename Char>
+class digit_grouping
+{
+private:
+    thousands_sep_result<Char> sep_;
+
+    struct next_state
+    {
+        std::string::const_iterator group;
+        int                         pos;
+    };
+    next_state initial_state() const { return {sep_.grouping.begin(), 0}; }
+
+    // Returns the next digit group separator position.
+    int next(next_state &state) const
+    {
+        if(!sep_.thousands_sep)
+            return max_value<int>();
+        if(state.group == sep_.grouping.end())
+            return state.pos += sep_.grouping.back();
+        if(*state.group <= 0 || *state.group == max_value<char>())
+            return max_value<int>();
+        state.pos += *state.group++;
+        return state.pos;
+    }
+
+public:
+    explicit digit_grouping(locale_ref loc, bool localized = true)
+    {
+        if(localized)
+            sep_ = thousands_sep<Char>(loc);
+        else
+            sep_.thousands_sep = Char();
+    }
+    explicit digit_grouping(thousands_sep_result<Char> sep) : sep_(sep) {}
+
+    Char separator() const { return sep_.thousands_sep; }
+
+    int count_separators(int num_digits) const
+    {
+        int  count = 0;
+        auto state = initial_state();
+        while(num_digits > next(state))
+            ++count;
+        return count;
+    }
+
+    // Applies grouping to digits and write the output to out.
+    template <typename Out, typename C>
+    Out apply(Out out, basic_string_view<C> digits) const
+    {
+        auto num_digits = static_cast<int>(digits.size());
+        auto separators = basic_memory_buffer<int>();
+        separators.push_back(0);
+        auto state = initial_state();
+        while(int i = next(state))
+        {
+            if(i >= num_digits)
+                break;
+            separators.push_back(i);
         }
-        return grouping.apply(it, string_view(digits, to_unsigned(num_digits)));
-      });
-}
+        for(int i = 0, sep_index = static_cast<int>(separators.size() - 1); i < num_digits; ++i)
+        {
+            if(num_digits - i == separators[sep_index])
+            {
+                *out++ = separator();
+                --sep_index;
+            }
+            *out++ = static_cast<Char>(digits[to_unsigned(i)]);
+        }
+        return out;
+    }
+};
 
 template <typename OutputIt, typename UInt, typename Char>
-auto write_int_localized(OutputIt& out, UInt value, unsigned prefix,
-                         const basic_format_specs<Char>& specs, locale_ref loc)
-    -> bool {
-  auto grouping = digit_grouping<Char>(loc);
-  out = write_int_localized(out, value, prefix, specs, grouping);
-  return true;
-}
-
-FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
-  prefix |= prefix != 0 ? value << 8 : value;
-  prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
+auto write_int_localized(
+    OutputIt                        out,
+    UInt                            value,
+    unsigned                        prefix,
+    const basic_format_specs<Char> &specs,
+    const digit_grouping<Char>     &grouping) -> OutputIt
+{
+    static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
+    int  num_digits = count_digits(value);
+    char digits[40];
+    format_decimal(digits, value, num_digits);
+    unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits + grouping.count_separators(num_digits));
+    return write_padded<align::right>(
+        out,
+        specs,
+        size,
+        size,
+        [&](reserve_iterator<OutputIt> it)
+        {
+            if(prefix != 0)
+            {
+                char sign = static_cast<char>(prefix);
+                *it++     = static_cast<Char>(sign);
+            }
+            return grouping.apply(it, string_view(digits, to_unsigned(num_digits)));
+        });
 }
 
-template <typename UInt> struct write_int_arg {
-  UInt abs_value;
-  unsigned prefix;
+template <typename OutputIt, typename UInt, typename Char>
+auto write_int_localized(
+    OutputIt                       &out,
+    UInt                            value,
+    unsigned                        prefix,
+    const basic_format_specs<Char> &specs,
+    locale_ref                      loc) -> bool
+{
+    auto grouping = digit_grouping<Char>(loc);
+    out           = write_int_localized(out, value, prefix, specs, grouping);
+    return true;
+}
+
+FMT_CONSTEXPR inline void prefix_append(unsigned &prefix, unsigned value)
+{
+    prefix |= prefix != 0 ? value << 8 : value;
+    prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
+}
+
+template <typename UInt>
+struct write_int_arg
+{
+    UInt     abs_value;
+    unsigned prefix;
 };
 
 template <typename T>
-FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign)
-    -> write_int_arg<uint32_or_64_or_128_t<T>> {
-  auto prefix = 0u;
-  auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
-  if (is_negative(value)) {
-    prefix = 0x01000000 | '-';
-    abs_value = 0 - abs_value;
-  } else {
-    constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
-                                            0x1000000u | ' '};
-    prefix = prefixes[sign];
-  }
-  return {abs_value, prefix};
+FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) -> write_int_arg<uint32_or_64_or_128_t<T>>
+{
+    auto prefix    = 0u;
+    auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
+    if(is_negative(value))
+    {
+        prefix    = 0x01000000 | '-';
+        abs_value = 0 - abs_value;
+    }
+    else
+    {
+        constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', 0x1000000u | ' '};
+        prefix                               = prefixes[sign];
+    }
+    return {abs_value, prefix};
 }
 
 template <typename Char, typename OutputIt, typename T>
-FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
-                                        const basic_format_specs<Char>& specs,
-                                        locale_ref loc) -> OutputIt {
-  static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
-  auto abs_value = arg.abs_value;
-  auto prefix = arg.prefix;
-  switch (specs.type) {
-  case presentation_type::none:
-  case presentation_type::dec: {
-    if (specs.localized &&
-        write_int_localized(out, static_cast<uint64_or_128_t<T>>(abs_value),
-                            prefix, specs, loc)) {
-      return out;
-    }
-    auto num_digits = count_digits(abs_value);
-    return write_int(
-        out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
-          return format_decimal<Char>(it, abs_value, num_digits).end;
-        });
-  }
-  case presentation_type::hex_lower:
-  case presentation_type::hex_upper: {
-    bool upper = specs.type == presentation_type::hex_upper;
-    if (specs.alt)
-      prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0');
-    int num_digits = count_digits<4>(abs_value);
-    return write_int(
-        out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
-          return format_uint<4, Char>(it, abs_value, num_digits, upper);
-        });
-  }
-  case presentation_type::bin_lower:
-  case presentation_type::bin_upper: {
-    bool upper = specs.type == presentation_type::bin_upper;
-    if (specs.alt)
-      prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0');
-    int num_digits = count_digits<1>(abs_value);
-    return write_int(out, num_digits, prefix, specs,
-                     [=](reserve_iterator<OutputIt> it) {
-                       return format_uint<1, Char>(it, abs_value, num_digits);
-                     });
-  }
-  case presentation_type::oct: {
-    int num_digits = count_digits<3>(abs_value);
-    // Octal prefix '0' is counted as a digit, so only add it if precision
-    // is not greater than the number of digits.
-    if (specs.alt && specs.precision <= num_digits && abs_value != 0)
-      prefix_append(prefix, '0');
-    return write_int(out, num_digits, prefix, specs,
-                     [=](reserve_iterator<OutputIt> it) {
-                       return format_uint<3, Char>(it, abs_value, num_digits);
-                     });
-  }
-  case presentation_type::chr:
-    return write_char(out, static_cast<Char>(abs_value), specs);
-  default:
-    throw_format_error("invalid type specifier");
-  }
-  return out;
+FMT_CONSTEXPR FMT_INLINE auto
+write_int(OutputIt out, write_int_arg<T> arg, const basic_format_specs<Char> &specs, locale_ref loc) -> OutputIt
+{
+    static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
+    auto abs_value = arg.abs_value;
+    auto prefix    = arg.prefix;
+    switch(specs.type)
+    {
+        case presentation_type::none:
+        case presentation_type::dec:
+        {
+            if(specs.localized &&
+               write_int_localized(out, static_cast<uint64_or_128_t<T>>(abs_value), prefix, specs, loc))
+            {
+                return out;
+            }
+            auto num_digits = count_digits(abs_value);
+            return write_int(
+                out,
+                num_digits,
+                prefix,
+                specs,
+                [=](reserve_iterator<OutputIt> it) { return format_decimal<Char>(it, abs_value, num_digits).end; });
+        }
+        case presentation_type::hex_lower:
+        case presentation_type::hex_upper:
+        {
+            bool upper = specs.type == presentation_type::hex_upper;
+            if(specs.alt)
+                prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0');
+            int num_digits = count_digits<4>(abs_value);
+            return write_int(
+                out,
+                num_digits,
+                prefix,
+                specs,
+                [=](reserve_iterator<OutputIt> it) { return format_uint<4, Char>(it, abs_value, num_digits, upper); });
+        }
+        case presentation_type::bin_lower:
+        case presentation_type::bin_upper:
+        {
+            bool upper = specs.type == presentation_type::bin_upper;
+            if(specs.alt)
+                prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0');
+            int num_digits = count_digits<1>(abs_value);
+            return write_int(
+                out,
+                num_digits,
+                prefix,
+                specs,
+                [=](reserve_iterator<OutputIt> it) { return format_uint<1, Char>(it, abs_value, num_digits); });
+        }
+        case presentation_type::oct:
+        {
+            int num_digits = count_digits<3>(abs_value);
+            // Octal prefix '0' is counted as a digit, so only add it if precision
+            // is not greater than the number of digits.
+            if(specs.alt && specs.precision <= num_digits && abs_value != 0)
+                prefix_append(prefix, '0');
+            return write_int(
+                out,
+                num_digits,
+                prefix,
+                specs,
+                [=](reserve_iterator<OutputIt> it) { return format_uint<3, Char>(it, abs_value, num_digits); });
+        }
+        case presentation_type::chr:
+            return write_char(out, static_cast<Char>(abs_value), specs);
+        default:
+            throw_format_error("invalid type specifier");
+    }
+    return out;
 }
 template <typename Char, typename OutputIt, typename T>
-FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(
-    OutputIt out, write_int_arg<T> arg, const basic_format_specs<Char>& specs,
-    locale_ref loc) -> OutputIt {
-  return write_int(out, arg, specs, loc);
-}
-template <typename Char, typename OutputIt, typename T,
-          FMT_ENABLE_IF(is_integral<T>::value &&
-                        !std::is_same<T, bool>::value &&
-                        std::is_same<OutputIt, buffer_appender<Char>>::value)>
-FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
-                                    const basic_format_specs<Char>& specs,
-                                    locale_ref loc) -> OutputIt {
-  return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs,
-                            loc);
+FMT_CONSTEXPR FMT_NOINLINE auto
+write_int_noinline(OutputIt out, write_int_arg<T> arg, const basic_format_specs<Char> &specs, locale_ref loc)
+    -> OutputIt
+{
+    return write_int(out, arg, specs, loc);
+}
+template <
+    typename Char,
+    typename OutputIt,
+    typename T,
+    FMT_ENABLE_IF(
+        is_integral<T>::value && !std::is_same<T, bool>::value && std::is_same<OutputIt, buffer_appender<Char>>::value)>
+FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, const basic_format_specs<Char> &specs, locale_ref loc)
+    -> OutputIt
+{
+    return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs, loc);
 }
 // An inlined version of write used in format string compilation.
-template <typename Char, typename OutputIt, typename T,
-          FMT_ENABLE_IF(is_integral<T>::value &&
-                        !std::is_same<T, bool>::value &&
-                        !std::is_same<OutputIt, buffer_appender<Char>>::value)>
-FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
-                                    const basic_format_specs<Char>& specs,
-                                    locale_ref loc) -> OutputIt {
-  return write_int(out, make_write_int_arg(value, specs.sign), specs, loc);
+template <
+    typename Char,
+    typename OutputIt,
+    typename T,
+    FMT_ENABLE_IF(
+        is_integral<T>::value && !std::is_same<T, bool>::value &&
+        !std::is_same<OutputIt, buffer_appender<Char>>::value)>
+FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, const basic_format_specs<Char> &specs, locale_ref loc)
+    -> OutputIt
+{
+    return write_int(out, make_write_int_arg(value, specs.sign), specs, loc);
 }
 
 // An output iterator that counts the number of objects written to it and
 // discards them.
-class counting_iterator {
- private:
-  size_t count_;
-
- public:
-  using iterator_category = std::output_iterator_tag;
-  using difference_type = std::ptrdiff_t;
-  using pointer = void;
-  using reference = void;
-  FMT_UNCHECKED_ITERATOR(counting_iterator);
-
-  struct value_type {
-    template <typename T> FMT_CONSTEXPR void operator=(const T&) {}
-  };
-
-  FMT_CONSTEXPR counting_iterator() : count_(0) {}
-
-  FMT_CONSTEXPR size_t count() const { return count_; }
-
-  FMT_CONSTEXPR counting_iterator& operator++() {
-    ++count_;
-    return *this;
-  }
-  FMT_CONSTEXPR counting_iterator operator++(int) {
-    auto it = *this;
-    ++*this;
-    return it;
-  }
+class counting_iterator
+{
+private:
+    size_t count_;
+
+public:
+    using iterator_category = std::output_iterator_tag;
+    using difference_type   = std::ptrdiff_t;
+    using pointer           = void;
+    using reference         = void;
+    FMT_UNCHECKED_ITERATOR(counting_iterator);
+
+    struct value_type
+    {
+        template <typename T>
+        FMT_CONSTEXPR void operator=(const T &)
+        {
+        }
+    };
 
-  FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it,
-                                                   difference_type n) {
-    it.count_ += static_cast<size_t>(n);
-    return it;
-  }
+    FMT_CONSTEXPR counting_iterator() : count_(0) {}
+
+    FMT_CONSTEXPR size_t count() const { return count_; }
+
+    FMT_CONSTEXPR counting_iterator &operator++()
+    {
+        ++count_;
+        return *this;
+    }
+    FMT_CONSTEXPR counting_iterator operator++(int)
+    {
+        auto it = *this;
+        ++*this;
+        return it;
+    }
+
+    FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it, difference_type n)
+    {
+        it.count_ += static_cast<size_t>(n);
+        return it;
+    }
 
-  FMT_CONSTEXPR value_type operator*() const { return {}; }
+    FMT_CONSTEXPR value_type operator*() const { return {}; }
 };
 
 template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s,
-                         const basic_format_specs<Char>& specs) -> OutputIt {
-  auto data = s.data();
-  auto size = s.size();
-  if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
-    size = code_point_index(s, to_unsigned(specs.precision));
-  bool is_debug = specs.type == presentation_type::debug;
-  size_t width = 0;
-  if (specs.width != 0) {
-    if (is_debug)
-      width = write_escaped_string(counting_iterator{}, s).count();
-    else
-      width = compute_width(basic_string_view<Char>(data, size));
-  }
-  return write_padded(out, specs, size, width,
-                      [=](reserve_iterator<OutputIt> it) {
-                        if (is_debug) return write_escaped_string(it, s);
-                        return copy_str<Char>(data, data + size, it);
-                      });
+FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s, const basic_format_specs<Char> &specs) -> OutputIt
+{
+    auto data = s.data();
+    auto size = s.size();
+    if(specs.precision >= 0 && to_unsigned(specs.precision) < size)
+        size = code_point_index(s, to_unsigned(specs.precision));
+    bool   is_debug = specs.type == presentation_type::debug;
+    size_t width    = 0;
+    if(specs.width != 0)
+    {
+        if(is_debug)
+            width = write_escaped_string(counting_iterator{}, s).count();
+        else
+            width = compute_width(basic_string_view<Char>(data, size));
+    }
+    return write_padded(
+        out,
+        specs,
+        size,
+        width,
+        [=](reserve_iterator<OutputIt> it)
+        {
+            if(is_debug)
+                return write_escaped_string(it, s);
+            return copy_str<Char>(data, data + size, it);
+        });
 }
 template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write(OutputIt out,
-                         basic_string_view<type_identity_t<Char>> s,
-                         const basic_format_specs<Char>& specs, locale_ref)
-    -> OutputIt {
-  check_string_type_spec(specs.type);
-  return write(out, s, specs);
+FMT_CONSTEXPR auto
+write(OutputIt out, basic_string_view<type_identity_t<Char>> s, const basic_format_specs<Char> &specs, locale_ref)
+    -> OutputIt
+{
+    check_string_type_spec(specs.type);
+    return write(out, s, specs);
 }
 template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write(OutputIt out, const Char* s,
-                         const basic_format_specs<Char>& specs, locale_ref)
-    -> OutputIt {
-  return check_cstring_type_spec(specs.type)
-             ? write(out, basic_string_view<Char>(s), specs, {})
-             : write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
-}
-
-template <typename Char, typename OutputIt, typename T,
-          FMT_ENABLE_IF(is_integral<T>::value &&
-                        !std::is_same<T, bool>::value &&
-                        !std::is_same<T, Char>::value)>
-FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
-  auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
-  bool negative = is_negative(value);
-  // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
-  if (negative) abs_value = ~abs_value + 1;
-  int num_digits = count_digits(abs_value);
-  auto size = (negative ? 1 : 0) + static_cast<size_t>(num_digits);
-  auto it = reserve(out, size);
-  if (auto ptr = to_pointer<Char>(it, size)) {
-    if (negative) *ptr++ = static_cast<Char>('-');
-    format_decimal<Char>(ptr, abs_value, num_digits);
-    return out;
-  }
-  if (negative) *it++ = static_cast<Char>('-');
-  it = format_decimal<Char>(it, abs_value, num_digits).end;
-  return base_iterator(out, it);
+FMT_CONSTEXPR auto write(OutputIt out, const Char *s, const basic_format_specs<Char> &specs, locale_ref) -> OutputIt
+{
+    return check_cstring_type_spec(specs.type) ? write(out, basic_string_view<Char>(s), specs, {}) :
+                                                 write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
+}
+
+template <
+    typename Char,
+    typename OutputIt,
+    typename T,
+    FMT_ENABLE_IF(is_integral<T>::value && !std::is_same<T, bool>::value && !std::is_same<T, Char>::value)>
+FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt
+{
+    auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
+    bool negative  = is_negative(value);
+    // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
+    if(negative)
+        abs_value = ~abs_value + 1;
+    int  num_digits = count_digits(abs_value);
+    auto size       = (negative ? 1 : 0) + static_cast<size_t>(num_digits);
+    auto it         = reserve(out, size);
+    if(auto ptr = to_pointer<Char>(it, size))
+    {
+        if(negative)
+            *ptr++ = static_cast<Char>('-');
+        format_decimal<Char>(ptr, abs_value, num_digits);
+        return out;
+    }
+    if(negative)
+        *it++ = static_cast<Char>('-');
+    it = format_decimal<Char>(it, abs_value, num_digits).end;
+    return base_iterator(out, it);
 }
 
 template <typename Char, typename OutputIt>
-FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan,
-                                     basic_format_specs<Char> specs,
-                                     const float_specs& fspecs) -> OutputIt {
-  auto str =
-      isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf");
-  constexpr size_t str_size = 3;
-  auto sign = fspecs.sign;
-  auto size = str_size + (sign ? 1 : 0);
-  // Replace '0'-padding with space for non-finite values.
-  const bool is_zero_fill =
-      specs.fill.size() == 1 && *specs.fill.data() == static_cast<Char>('0');
-  if (is_zero_fill) specs.fill[0] = static_cast<Char>(' ');
-  return write_padded(out, specs, size, [=](reserve_iterator<OutputIt> it) {
-    if (sign) *it++ = detail::sign<Char>(sign);
-    return copy_str<Char>(str, str + str_size, it);
-  });
+FMT_CONSTEXPR20 auto
+write_nonfinite(OutputIt out, bool isnan, basic_format_specs<Char> specs, const float_specs &fspecs) -> OutputIt
+{
+    auto             str      = isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf");
+    constexpr size_t str_size = 3;
+    auto             sign     = fspecs.sign;
+    auto             size     = str_size + (sign ? 1 : 0);
+    // Replace '0'-padding with space for non-finite values.
+    const bool is_zero_fill = specs.fill.size() == 1 && *specs.fill.data() == static_cast<Char>('0');
+    if(is_zero_fill)
+        specs.fill[0] = static_cast<Char>(' ');
+    return write_padded(
+        out,
+        specs,
+        size,
+        [=](reserve_iterator<OutputIt> it)
+        {
+            if(sign)
+                *it++ = detail::sign<Char>(sign);
+            return copy_str<Char>(str, str + str_size, it);
+        });
 }
 
 // A decimal floating-point number significand * pow(10, exp).
-struct big_decimal_fp {
-  const char* significand;
-  int significand_size;
-  int exponent;
+struct big_decimal_fp
+{
+    const char *significand;
+    int         significand_size;
+    int         exponent;
 };
 
-constexpr auto get_significand_size(const big_decimal_fp& f) -> int {
-  return f.significand_size;
+constexpr auto get_significand_size(const big_decimal_fp &f) -> int
+{
+    return f.significand_size;
 }
 template <typename T>
-inline auto get_significand_size(const dragonbox::decimal_fp<T>& f) -> int {
-  return count_digits(f.significand);
+inline auto get_significand_size(const dragonbox::decimal_fp<T> &f) -> int
+{
+    return count_digits(f.significand);
 }
 
 template <typename Char, typename OutputIt>
-constexpr auto write_significand(OutputIt out, const char* significand,
-                                 int significand_size) -> OutputIt {
-  return copy_str<Char>(significand, significand + significand_size, out);
+constexpr auto write_significand(OutputIt out, const char *significand, int significand_size) -> OutputIt
+{
+    return copy_str<Char>(significand, significand + significand_size, out);
 }
 template <typename Char, typename OutputIt, typename UInt>
-inline auto write_significand(OutputIt out, UInt significand,
-                              int significand_size) -> OutputIt {
-  return format_decimal<Char>(out, significand, significand_size).end;
+inline auto write_significand(OutputIt out, UInt significand, int significand_size) -> OutputIt
+{
+    return format_decimal<Char>(out, significand, significand_size).end;
 }
 template <typename Char, typename OutputIt, typename T, typename Grouping>
-FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
-                                       int significand_size, int exponent,
-                                       const Grouping& grouping) -> OutputIt {
-  if (!grouping.separator()) {
-    out = write_significand<Char>(out, significand, significand_size);
-    return detail::fill_n(out, exponent, static_cast<Char>('0'));
-  }
-  auto buffer = memory_buffer();
-  write_significand<char>(appender(buffer), significand, significand_size);
-  detail::fill_n(appender(buffer), exponent, '0');
-  return grouping.apply(out, string_view(buffer.data(), buffer.size()));
-}
-
-template <typename Char, typename UInt,
-          FMT_ENABLE_IF(std::is_integral<UInt>::value)>
-inline auto write_significand(Char* out, UInt significand, int significand_size,
-                              int integral_size, Char decimal_point) -> Char* {
-  if (!decimal_point)
-    return format_decimal(out, significand, significand_size).end;
-  out += significand_size + 1;
-  Char* end = out;
-  int floating_size = significand_size - integral_size;
-  for (int i = floating_size / 2; i > 0; --i) {
-    out -= 2;
-    copy2(out, digits2(static_cast<std::size_t>(significand % 100)));
-    significand /= 100;
-  }
-  if (floating_size % 2 != 0) {
-    *--out = static_cast<Char>('0' + significand % 10);
-    significand /= 10;
-  }
-  *--out = decimal_point;
-  format_decimal(out - integral_size, significand, integral_size);
-  return end;
-}
-
-template <typename OutputIt, typename UInt, typename Char,
-          FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<OutputIt>>::value)>
-inline auto write_significand(OutputIt out, UInt significand,
-                              int significand_size, int integral_size,
-                              Char decimal_point) -> OutputIt {
-  // Buffer is large enough to hold digits (digits10 + 1) and a decimal point.
-  Char buffer[digits10<UInt>() + 2];
-  auto end = write_significand(buffer, significand, significand_size,
-                               integral_size, decimal_point);
-  return detail::copy_str_noinline<Char>(buffer, end, out);
+FMT_CONSTEXPR20 auto
+write_significand(OutputIt out, T significand, int significand_size, int exponent, const Grouping &grouping) -> OutputIt
+{
+    if(!grouping.separator())
+    {
+        out = write_significand<Char>(out, significand, significand_size);
+        return detail::fill_n(out, exponent, static_cast<Char>('0'));
+    }
+    auto buffer = memory_buffer();
+    write_significand<char>(appender(buffer), significand, significand_size);
+    detail::fill_n(appender(buffer), exponent, '0');
+    return grouping.apply(out, string_view(buffer.data(), buffer.size()));
+}
+
+template <typename Char, typename UInt, FMT_ENABLE_IF(std::is_integral<UInt>::value)>
+inline auto write_significand(Char *out, UInt significand, int significand_size, int integral_size, Char decimal_point)
+    -> Char *
+{
+    if(!decimal_point)
+        return format_decimal(out, significand, significand_size).end;
+    out += significand_size + 1;
+    Char *end           = out;
+    int   floating_size = significand_size - integral_size;
+    for(int i = floating_size / 2; i > 0; --i)
+    {
+        out -= 2;
+        copy2(out, digits2(static_cast<std::size_t>(significand % 100)));
+        significand /= 100;
+    }
+    if(floating_size % 2 != 0)
+    {
+        *--out = static_cast<Char>('0' + significand % 10);
+        significand /= 10;
+    }
+    *--out = decimal_point;
+    format_decimal(out - integral_size, significand, integral_size);
+    return end;
+}
+
+template <
+    typename OutputIt,
+    typename UInt,
+    typename Char,
+    FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<OutputIt>>::value)>
+inline auto
+write_significand(OutputIt out, UInt significand, int significand_size, int integral_size, Char decimal_point)
+    -> OutputIt
+{
+    // Buffer is large enough to hold digits (digits10 + 1) and a decimal point.
+    Char buffer[digits10<UInt>() + 2];
+    auto end = write_significand(buffer, significand, significand_size, integral_size, decimal_point);
+    return detail::copy_str_noinline<Char>(buffer, end, out);
 }
 
 template <typename OutputIt, typename Char>
-FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand,
-                                     int significand_size, int integral_size,
-                                     Char decimal_point) -> OutputIt {
-  out = detail::copy_str_noinline<Char>(significand,
-                                        significand + integral_size, out);
-  if (!decimal_point) return out;
-  *out++ = decimal_point;
-  return detail::copy_str_noinline<Char>(significand + integral_size,
-                                         significand + significand_size, out);
+FMT_CONSTEXPR auto
+write_significand(OutputIt out, const char *significand, int significand_size, int integral_size, Char decimal_point)
+    -> OutputIt
+{
+    out = detail::copy_str_noinline<Char>(significand, significand + integral_size, out);
+    if(!decimal_point)
+        return out;
+    *out++ = decimal_point;
+    return detail::copy_str_noinline<Char>(significand + integral_size, significand + significand_size, out);
 }
 
 template <typename OutputIt, typename Char, typename T, typename Grouping>
-FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
-                                       int significand_size, int integral_size,
-                                       Char decimal_point,
-                                       const Grouping& grouping) -> OutputIt {
-  if (!grouping.separator()) {
-    return write_significand(out, significand, significand_size, integral_size,
-                             decimal_point);
-  }
-  auto buffer = basic_memory_buffer<Char>();
-  write_significand(buffer_appender<Char>(buffer), significand,
-                    significand_size, integral_size, decimal_point);
-  grouping.apply(
-      out, basic_string_view<Char>(buffer.data(), to_unsigned(integral_size)));
-  return detail::copy_str_noinline<Char>(buffer.data() + integral_size,
-                                         buffer.end(), out);
-}
-
-template <typename OutputIt, typename DecimalFP, typename Char,
-          typename Grouping = digit_grouping<Char>>
-FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
-                                    const basic_format_specs<Char>& specs,
-                                    float_specs fspecs, locale_ref loc)
-    -> OutputIt {
-  auto significand = f.significand;
-  int significand_size = get_significand_size(f);
-  const Char zero = static_cast<Char>('0');
-  auto sign = fspecs.sign;
-  size_t size = to_unsigned(significand_size) + (sign ? 1 : 0);
-  using iterator = reserve_iterator<OutputIt>;
-
-  Char decimal_point =
-      fspecs.locale ? detail::decimal_point<Char>(loc) : static_cast<Char>('.');
-
-  int output_exp = f.exponent + significand_size - 1;
-  auto use_exp_format = [=]() {
-    if (fspecs.format == float_format::exp) return true;
-    if (fspecs.format != float_format::general) return false;
-    // Use the fixed notation if the exponent is in [exp_lower, exp_upper),
-    // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation.
-    const int exp_lower = -4, exp_upper = 16;
-    return output_exp < exp_lower ||
-           output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper);
-  };
-  if (use_exp_format()) {
-    int num_zeros = 0;
-    if (fspecs.showpoint) {
-      num_zeros = fspecs.precision - significand_size;
-      if (num_zeros < 0) num_zeros = 0;
-      size += to_unsigned(num_zeros);
-    } else if (significand_size == 1) {
-      decimal_point = Char();
-    }
-    auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp;
-    int exp_digits = 2;
-    if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3;
-
-    size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits);
-    char exp_char = fspecs.upper ? 'E' : 'e';
-    auto write = [=](iterator it) {
-      if (sign) *it++ = detail::sign<Char>(sign);
-      // Insert a decimal point after the first digit and add an exponent.
-      it = write_significand(it, significand, significand_size, 1,
-                             decimal_point);
-      if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero);
-      *it++ = static_cast<Char>(exp_char);
-      return write_exponent<Char>(output_exp, it);
+FMT_CONSTEXPR20 auto write_significand(
+    OutputIt        out,
+    T               significand,
+    int             significand_size,
+    int             integral_size,
+    Char            decimal_point,
+    const Grouping &grouping) -> OutputIt
+{
+    if(!grouping.separator())
+    {
+        return write_significand(out, significand, significand_size, integral_size, decimal_point);
+    }
+    auto buffer = basic_memory_buffer<Char>();
+    write_significand(buffer_appender<Char>(buffer), significand, significand_size, integral_size, decimal_point);
+    grouping.apply(out, basic_string_view<Char>(buffer.data(), to_unsigned(integral_size)));
+    return detail::copy_str_noinline<Char>(buffer.data() + integral_size, buffer.end(), out);
+}
+
+template <typename OutputIt, typename DecimalFP, typename Char, typename Grouping = digit_grouping<Char>>
+FMT_CONSTEXPR20 auto do_write_float(
+    OutputIt                        out,
+    const DecimalFP                &f,
+    const basic_format_specs<Char> &specs,
+    float_specs                     fspecs,
+    locale_ref                      loc) -> OutputIt
+{
+    auto       significand      = f.significand;
+    int        significand_size = get_significand_size(f);
+    const Char zero             = static_cast<Char>('0');
+    auto       sign             = fspecs.sign;
+    size_t     size             = to_unsigned(significand_size) + (sign ? 1 : 0);
+    using iterator              = reserve_iterator<OutputIt>;
+
+    Char decimal_point = fspecs.locale ? detail::decimal_point<Char>(loc) : static_cast<Char>('.');
+
+    int  output_exp     = f.exponent + significand_size - 1;
+    auto use_exp_format = [=]()
+    {
+        if(fspecs.format == float_format::exp)
+            return true;
+        if(fspecs.format != float_format::general)
+            return false;
+        // Use the fixed notation if the exponent is in [exp_lower, exp_upper),
+        // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation.
+        const int exp_lower = -4, exp_upper = 16;
+        return output_exp < exp_lower || output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper);
     };
-    return specs.width > 0 ? write_padded<align::right>(out, specs, size, write)
-                           : base_iterator(out, write(reserve(out, size)));
-  }
-
-  int exp = f.exponent + significand_size;
-  if (f.exponent >= 0) {
-    // 1234e5 -> 123400000[.0+]
-    size += to_unsigned(f.exponent);
-    int num_zeros = fspecs.precision - exp;
-    abort_fuzzing_if(num_zeros > 5000);
-    if (fspecs.showpoint) {
-      ++size;
-      if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1;
-      if (num_zeros > 0) size += to_unsigned(num_zeros);
-    }
-    auto grouping = Grouping(loc, fspecs.locale);
-    size += to_unsigned(grouping.count_separators(exp));
-    return write_padded<align::right>(out, specs, size, [&](iterator it) {
-      if (sign) *it++ = detail::sign<Char>(sign);
-      it = write_significand<Char>(it, significand, significand_size,
-                                   f.exponent, grouping);
-      if (!fspecs.showpoint) return it;
-      *it++ = decimal_point;
-      return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
-    });
-  } else if (exp > 0) {
-    // 1234e-2 -> 12.34[0+]
-    int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0;
-    size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0);
-    auto grouping = Grouping(loc, fspecs.locale);
-    size += to_unsigned(grouping.count_separators(significand_size));
-    return write_padded<align::right>(out, specs, size, [&](iterator it) {
-      if (sign) *it++ = detail::sign<Char>(sign);
-      it = write_significand(it, significand, significand_size, exp,
-                             decimal_point, grouping);
-      return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
-    });
-  }
-  // 1234e-6 -> 0.001234
-  int num_zeros = -exp;
-  if (significand_size == 0 && fspecs.precision >= 0 &&
-      fspecs.precision < num_zeros) {
-    num_zeros = fspecs.precision;
-  }
-  bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint;
-  size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros);
-  return write_padded<align::right>(out, specs, size, [&](iterator it) {
-    if (sign) *it++ = detail::sign<Char>(sign);
-    *it++ = zero;
-    if (!pointy) return it;
-    *it++ = decimal_point;
-    it = detail::fill_n(it, num_zeros, zero);
-    return write_significand<Char>(it, significand, significand_size);
-  });
-}
-
-template <typename Char> class fallback_digit_grouping {
- public:
-  constexpr fallback_digit_grouping(locale_ref, bool) {}
-
-  constexpr Char separator() const { return Char(); }
-
-  constexpr int count_separators(int) const { return 0; }
-
-  template <typename Out, typename C>
-  constexpr Out apply(Out out, basic_string_view<C>) const {
-    return out;
-  }
+    if(use_exp_format())
+    {
+        int num_zeros = 0;
+        if(fspecs.showpoint)
+        {
+            num_zeros = fspecs.precision - significand_size;
+            if(num_zeros < 0)
+                num_zeros = 0;
+            size += to_unsigned(num_zeros);
+        }
+        else if(significand_size == 1)
+        {
+            decimal_point = Char();
+        }
+        auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp;
+        int  exp_digits     = 2;
+        if(abs_output_exp >= 100)
+            exp_digits = abs_output_exp >= 1000 ? 4 : 3;
+
+        size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits);
+        char exp_char = fspecs.upper ? 'E' : 'e';
+        auto write    = [=](iterator it)
+        {
+            if(sign)
+                *it++ = detail::sign<Char>(sign);
+            // Insert a decimal point after the first digit and add an exponent.
+            it = write_significand(it, significand, significand_size, 1, decimal_point);
+            if(num_zeros > 0)
+                it = detail::fill_n(it, num_zeros, zero);
+            *it++ = static_cast<Char>(exp_char);
+            return write_exponent<Char>(output_exp, it);
+        };
+        return specs.width > 0 ? write_padded<align::right>(out, specs, size, write) :
+                                 base_iterator(out, write(reserve(out, size)));
+    }
+
+    int exp = f.exponent + significand_size;
+    if(f.exponent >= 0)
+    {
+        // 1234e5 -> 123400000[.0+]
+        size += to_unsigned(f.exponent);
+        int num_zeros = fspecs.precision - exp;
+        abort_fuzzing_if(num_zeros > 5000);
+        if(fspecs.showpoint)
+        {
+            ++size;
+            if(num_zeros <= 0 && fspecs.format != float_format::fixed)
+                num_zeros = 1;
+            if(num_zeros > 0)
+                size += to_unsigned(num_zeros);
+        }
+        auto grouping = Grouping(loc, fspecs.locale);
+        size += to_unsigned(grouping.count_separators(exp));
+        return write_padded<align::right>(
+            out,
+            specs,
+            size,
+            [&](iterator it)
+            {
+                if(sign)
+                    *it++ = detail::sign<Char>(sign);
+                it = write_significand<Char>(it, significand, significand_size, f.exponent, grouping);
+                if(!fspecs.showpoint)
+                    return it;
+                *it++ = decimal_point;
+                return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
+            });
+    }
+    else if(exp > 0)
+    {
+        // 1234e-2 -> 12.34[0+]
+        int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0;
+        size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0);
+        auto grouping = Grouping(loc, fspecs.locale);
+        size += to_unsigned(grouping.count_separators(significand_size));
+        return write_padded<align::right>(
+            out,
+            specs,
+            size,
+            [&](iterator it)
+            {
+                if(sign)
+                    *it++ = detail::sign<Char>(sign);
+                it = write_significand(it, significand, significand_size, exp, decimal_point, grouping);
+                return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
+            });
+    }
+    // 1234e-6 -> 0.001234
+    int num_zeros = -exp;
+    if(significand_size == 0 && fspecs.precision >= 0 && fspecs.precision < num_zeros)
+    {
+        num_zeros = fspecs.precision;
+    }
+    bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint;
+    size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros);
+    return write_padded<align::right>(
+        out,
+        specs,
+        size,
+        [&](iterator it)
+        {
+            if(sign)
+                *it++ = detail::sign<Char>(sign);
+            *it++ = zero;
+            if(!pointy)
+                return it;
+            *it++ = decimal_point;
+            it    = detail::fill_n(it, num_zeros, zero);
+            return write_significand<Char>(it, significand, significand_size);
+        });
+}
+
+template <typename Char>
+class fallback_digit_grouping
+{
+public:
+    constexpr fallback_digit_grouping(locale_ref, bool) {}
+
+    constexpr Char separator() const { return Char(); }
+
+    constexpr int count_separators(int) const { return 0; }
+
+    template <typename Out, typename C>
+    constexpr Out apply(Out out, basic_string_view<C>) const
+    {
+        return out;
+    }
 };
 
 template <typename OutputIt, typename DecimalFP, typename Char>
-FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f,
-                                 const basic_format_specs<Char>& specs,
-                                 float_specs fspecs, locale_ref loc)
-    -> OutputIt {
-  if (is_constant_evaluated()) {
-    return do_write_float<OutputIt, DecimalFP, Char,
-                          fallback_digit_grouping<Char>>(out, f, specs, fspecs,
-                                                         loc);
-  } else {
-    return do_write_float(out, f, specs, fspecs, loc);
-  }
+FMT_CONSTEXPR20 auto
+write_float(OutputIt out, const DecimalFP &f, const basic_format_specs<Char> &specs, float_specs fspecs, locale_ref loc)
+    -> OutputIt
+{
+    if(is_constant_evaluated())
+    {
+        return do_write_float<OutputIt, DecimalFP, Char, fallback_digit_grouping<Char>>(out, f, specs, fspecs, loc);
+    }
+    else
+    {
+        return do_write_float(out, f, specs, fspecs, loc);
+    }
 }
 
-template <typename T> constexpr bool isnan(T value) {
-  return !(value >= value);  // std::isnan doesn't support __float128.
+template <typename T>
+constexpr bool isnan(T value)
+{
+    return !(value >= value); // std::isnan doesn't support __float128.
 }
 
 template <typename T, typename Enable = void>
-struct has_isfinite : std::false_type {};
+struct has_isfinite : std::false_type
+{
+};
 
 template <typename T>
-struct has_isfinite<T, enable_if_t<sizeof(std::isfinite(T())) != 0>>
-    : std::true_type {};
-
-template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value&&
-                                        has_isfinite<T>::value)>
-FMT_CONSTEXPR20 bool isfinite(T value) {
-  constexpr T inf = T(std::numeric_limits<double>::infinity());
-  if (is_constant_evaluated())
-    return !detail::isnan(value) && value != inf && value != -inf;
-  return std::isfinite(value);
+struct has_isfinite<T, enable_if_t<sizeof(std::isfinite(T())) != 0>> : std::true_type
+{
+};
+
+template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value &&has_isfinite<T>::value)>
+FMT_CONSTEXPR20 bool isfinite(T value)
+{
+    constexpr T inf = T(std::numeric_limits<double>::infinity());
+    if(is_constant_evaluated())
+        return !detail::isnan(value) && value != inf && value != -inf;
+    return std::isfinite(value);
 }
 template <typename T, FMT_ENABLE_IF(!has_isfinite<T>::value)>
-FMT_CONSTEXPR bool isfinite(T value) {
-  T inf = T(std::numeric_limits<double>::infinity());
-  // std::isfinite doesn't support __float128.
-  return !detail::isnan(value) && value != inf && value != -inf;
+FMT_CONSTEXPR bool isfinite(T value)
+{
+    T inf = T(std::numeric_limits<double>::infinity());
+    // std::isfinite doesn't support __float128.
+    return !detail::isnan(value) && value != inf && value != -inf;
 }
 
 template <typename T, FMT_ENABLE_IF(is_floating_point<T>::value)>
-FMT_INLINE FMT_CONSTEXPR bool signbit(T value) {
-  if (is_constant_evaluated()) {
+FMT_INLINE FMT_CONSTEXPR bool signbit(T value)
+{
+    if(is_constant_evaluated())
+    {
 #ifdef __cpp_if_constexpr
-    if constexpr (std::numeric_limits<double>::is_iec559) {
-      auto bits = detail::bit_cast<uint64_t>(static_cast<double>(value));
-      return (bits >> (num_bits<uint64_t>() - 1)) != 0;
-    }
+        if constexpr(std::numeric_limits<double>::is_iec559)
+        {
+            auto bits = detail::bit_cast<uint64_t>(static_cast<double>(value));
+            return (bits >> (num_bits<uint64_t>() - 1)) != 0;
+        }
 #endif
-  }
-  return std::signbit(static_cast<double>(value));
+    }
+    return std::signbit(static_cast<double>(value));
 }
 
-enum class round_direction { unknown, up, down };
+enum class round_direction
+{
+    unknown,
+    up,
+    down
+};
 
 // Given the divisor (normally a power of 10), the remainder = v % divisor for
 // some number v and the error, returns whether v should be rounded up, down, or
 // whether the rounding direction can't be determined due to error.
 // error should be less than divisor / 2.
-FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor,
-                                                         uint64_t remainder,
-                                                         uint64_t error) {
-  FMT_ASSERT(remainder < divisor, "");  // divisor - remainder won't overflow.
-  FMT_ASSERT(error < divisor, "");      // divisor - error won't overflow.
-  FMT_ASSERT(error < divisor - error, "");  // error * 2 won't overflow.
-  // Round down if (remainder + error) * 2 <= divisor.
-  if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
-    return round_direction::down;
-  // Round up if (remainder - error) * 2 >= divisor.
-  if (remainder >= error &&
-      remainder - error >= divisor - (remainder - error)) {
-    return round_direction::up;
-  }
-  return round_direction::unknown;
-}
-
-namespace digits {
-enum result {
-  more,  // Generate more digits.
-  done,  // Done generating digits.
-  error  // Digit generation cancelled due to an error.
+FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder, uint64_t error)
+{
+    FMT_ASSERT(remainder < divisor, "");     // divisor - remainder won't overflow.
+    FMT_ASSERT(error < divisor, "");         // divisor - error won't overflow.
+    FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
+    // Round down if (remainder + error) * 2 <= divisor.
+    if(remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
+        return round_direction::down;
+    // Round up if (remainder - error) * 2 >= divisor.
+    if(remainder >= error && remainder - error >= divisor - (remainder - error))
+    {
+        return round_direction::up;
+    }
+    return round_direction::unknown;
+}
+
+namespace digits
+{
+enum result
+{
+    more, // Generate more digits.
+    done, // Done generating digits.
+    error // Digit generation cancelled due to an error.
 };
 }
 
-struct gen_digits_handler {
-  char* buf;
-  int size;
-  int precision;
-  int exp10;
-  bool fixed;
-
-  FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor,
-                                        uint64_t remainder, uint64_t error,
-                                        bool integral) {
-    FMT_ASSERT(remainder < divisor, "");
-    buf[size++] = digit;
-    if (!integral && error >= remainder) return digits::error;
-    if (size < precision) return digits::more;
-    if (!integral) {
-      // Check if error * 2 < divisor with overflow prevention.
-      // The check is not needed for the integral part because error = 1
-      // and divisor > (1 << 32) there.
-      if (error >= divisor || error >= divisor - error) return digits::error;
-    } else {
-      FMT_ASSERT(error == 1 && divisor > 2, "");
-    }
-    auto dir = get_round_direction(divisor, remainder, error);
-    if (dir != round_direction::up)
-      return dir == round_direction::down ? digits::done : digits::error;
-    ++buf[size - 1];
-    for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
-      buf[i] = '0';
-      ++buf[i - 1];
-    }
-    if (buf[0] > '9') {
-      buf[0] = '1';
-      if (fixed)
-        buf[size++] = '0';
-      else
-        ++exp10;
-    }
-    return digits::done;
-  }
+struct gen_digits_handler
+{
+    char *buf;
+    int   size;
+    int   precision;
+    int   exp10;
+    bool  fixed;
+
+    FMT_CONSTEXPR digits::result
+                  on_digit(char digit, uint64_t divisor, uint64_t remainder, uint64_t error, bool integral)
+    {
+        FMT_ASSERT(remainder < divisor, "");
+        buf[size++] = digit;
+        if(!integral && error >= remainder)
+            return digits::error;
+        if(size < precision)
+            return digits::more;
+        if(!integral)
+        {
+            // Check if error * 2 < divisor with overflow prevention.
+            // The check is not needed for the integral part because error = 1
+            // and divisor > (1 << 32) there.
+            if(error >= divisor || error >= divisor - error)
+                return digits::error;
+        }
+        else
+        {
+            FMT_ASSERT(error == 1 && divisor > 2, "");
+        }
+        auto dir = get_round_direction(divisor, remainder, error);
+        if(dir != round_direction::up)
+            return dir == round_direction::down ? digits::done : digits::error;
+        ++buf[size - 1];
+        for(int i = size - 1; i > 0 && buf[i] > '9'; --i)
+        {
+            buf[i] = '0';
+            ++buf[i - 1];
+        }
+        if(buf[0] > '9')
+        {
+            buf[0] = '1';
+            if(fixed)
+                buf[size++] = '0';
+            else
+                ++exp10;
+        }
+        return digits::done;
+    }
 };
 
-inline FMT_CONSTEXPR20 void adjust_precision(int& precision, int exp10) {
-  // Adjust fixed precision by exponent because it is relative to decimal
-  // point.
-  if (exp10 > 0 && precision > max_value<int>() - exp10)
-    FMT_THROW(format_error("number is too big"));
-  precision += exp10;
+inline FMT_CONSTEXPR20 void adjust_precision(int &precision, int exp10)
+{
+    // Adjust fixed precision by exponent because it is relative to decimal
+    // point.
+    if(exp10 > 0 && precision > max_value<int>() - exp10)
+        FMT_THROW(format_error("number is too big"));
+    precision += exp10;
 }
 
 // Generates output using the Grisu digit-gen algorithm.
 // error: the size of the region (lower, upper) outside of which numbers
 // definitely do not round to value (Delta in Grisu3).
-FMT_INLINE FMT_CONSTEXPR20 auto grisu_gen_digits(fp value, uint64_t error,
-                                                 int& exp,
-                                                 gen_digits_handler& handler)
-    -> digits::result {
-  const fp one(1ULL << -value.e, value.e);
-  // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
-  // zero because it contains a product of two 64-bit numbers with MSB set (due
-  // to normalization) - 1, shifted right by at most 60 bits.
-  auto integral = static_cast<uint32_t>(value.f >> -one.e);
-  FMT_ASSERT(integral != 0, "");
-  FMT_ASSERT(integral == value.f >> -one.e, "");
-  // The fractional part of scaled value (p2 in Grisu) c = value % one.
-  uint64_t fractional = value.f & (one.f - 1);
-  exp = count_digits(integral);  // kappa in Grisu.
-  // Non-fixed formats require at least one digit and no precision adjustment.
-  if (handler.fixed) {
-    adjust_precision(handler.precision, exp + handler.exp10);
-    // Check if precision is satisfied just by leading zeros, e.g.
-    // format("{:.2f}", 0.001) gives "0.00" without generating any digits.
-    if (handler.precision <= 0) {
-      if (handler.precision < 0) return digits::done;
-      // Divide by 10 to prevent overflow.
-      uint64_t divisor = data::power_of_10_64[exp - 1] << -one.e;
-      auto dir = get_round_direction(divisor, value.f / 10, error * 10);
-      if (dir == round_direction::unknown) return digits::error;
-      handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0';
-      return digits::done;
-    }
-  }
-  // Generate digits for the integral part. This can produce up to 10 digits.
-  do {
-    uint32_t digit = 0;
-    auto divmod_integral = [&](uint32_t divisor) {
-      digit = integral / divisor;
-      integral %= divisor;
-    };
-    // This optimization by Milo Yip reduces the number of integer divisions by
-    // one per iteration.
-    switch (exp) {
-    case 10:
-      divmod_integral(1000000000);
-      break;
-    case 9:
-      divmod_integral(100000000);
-      break;
-    case 8:
-      divmod_integral(10000000);
-      break;
-    case 7:
-      divmod_integral(1000000);
-      break;
-    case 6:
-      divmod_integral(100000);
-      break;
-    case 5:
-      divmod_integral(10000);
-      break;
-    case 4:
-      divmod_integral(1000);
-      break;
-    case 3:
-      divmod_integral(100);
-      break;
-    case 2:
-      divmod_integral(10);
-      break;
-    case 1:
-      digit = integral;
-      integral = 0;
-      break;
-    default:
-      FMT_ASSERT(false, "invalid number of digits");
-    }
-    --exp;
-    auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
-    auto result = handler.on_digit(static_cast<char>('0' + digit),
-                                   data::power_of_10_64[exp] << -one.e,
-                                   remainder, error, true);
-    if (result != digits::more) return result;
-  } while (exp > 0);
-  // Generate digits for the fractional part.
-  for (;;) {
-    fractional *= 10;
-    error *= 10;
-    char digit = static_cast<char>('0' + (fractional >> -one.e));
-    fractional &= one.f - 1;
-    --exp;
-    auto result = handler.on_digit(digit, one.f, fractional, error, false);
-    if (result != digits::more) return result;
-  }
-}
-
-class bigint {
- private:
-  // A bigint is stored as an array of bigits (big digits), with bigit at index
-  // 0 being the least significant one.
-  using bigit = uint32_t;
-  using double_bigit = uint64_t;
-  enum { bigits_capacity = 32 };
-  basic_memory_buffer<bigit, bigits_capacity> bigits_;
-  int exp_;
-
-  FMT_CONSTEXPR20 bigit operator[](int index) const {
-    return bigits_[to_unsigned(index)];
-  }
-  FMT_CONSTEXPR20 bigit& operator[](int index) {
-    return bigits_[to_unsigned(index)];
-  }
-
-  static constexpr const int bigit_bits = num_bits<bigit>();
-
-  friend struct formatter<bigint>;
-
-  FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) {
-    auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
-    (*this)[index] = static_cast<bigit>(result);
-    borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
-  }
-
-  FMT_CONSTEXPR20 void remove_leading_zeros() {
-    int num_bigits = static_cast<int>(bigits_.size()) - 1;
-    while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
-    bigits_.resize(to_unsigned(num_bigits + 1));
-  }
-
-  // Computes *this -= other assuming aligned bigints and *this >= other.
-  FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) {
-    FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
-    FMT_ASSERT(compare(*this, other) >= 0, "");
-    bigit borrow = 0;
-    int i = other.exp_ - exp_;
-    for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j)
-      subtract_bigits(i, other.bigits_[j], borrow);
-    while (borrow > 0) subtract_bigits(i, 0, borrow);
-    remove_leading_zeros();
-  }
-
-  FMT_CONSTEXPR20 void multiply(uint32_t value) {
-    const double_bigit wide_value = value;
-    bigit carry = 0;
-    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
-      double_bigit result = bigits_[i] * wide_value + carry;
-      bigits_[i] = static_cast<bigit>(result);
-      carry = static_cast<bigit>(result >> bigit_bits);
-    }
-    if (carry != 0) bigits_.push_back(carry);
-  }
-
-  template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
-                                         std::is_same<UInt, uint128_t>::value)>
-  FMT_CONSTEXPR20 void multiply(UInt value) {
-    using half_uint =
-        conditional_t<std::is_same<UInt, uint128_t>::value, uint64_t, uint32_t>;
-    const int shift = num_bits<half_uint>() - bigit_bits;
-    const UInt lower = static_cast<half_uint>(value);
-    const UInt upper = value >> num_bits<half_uint>();
-    UInt carry = 0;
-    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
-      UInt result = lower * bigits_[i] + static_cast<bigit>(carry);
-      carry = (upper * bigits_[i] << shift) + (result >> bigit_bits) +
-              (carry >> bigit_bits);
-      bigits_[i] = static_cast<bigit>(result);
-    }
-    while (carry != 0) {
-      bigits_.push_back(static_cast<bigit>(carry));
-      carry >>= bigit_bits;
-    }
-  }
-
-  template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value ||
-                                         std::is_same<UInt, uint128_t>::value)>
-  FMT_CONSTEXPR20 void assign(UInt n) {
-    size_t num_bigits = 0;
-    do {
-      bigits_[num_bigits++] = static_cast<bigit>(n);
-      n >>= bigit_bits;
-    } while (n != 0);
-    bigits_.resize(num_bigits);
-    exp_ = 0;
-  }
-
- public:
-  FMT_CONSTEXPR20 bigint() : exp_(0) {}
-  explicit bigint(uint64_t n) { assign(n); }
-
-  bigint(const bigint&) = delete;
-  void operator=(const bigint&) = delete;
-
-  FMT_CONSTEXPR20 void assign(const bigint& other) {
-    auto size = other.bigits_.size();
-    bigits_.resize(size);
-    auto data = other.bigits_.data();
-    std::copy(data, data + size, make_checked(bigits_.data(), size));
-    exp_ = other.exp_;
-  }
-
-  template <typename Int> FMT_CONSTEXPR20 void operator=(Int n) {
-    FMT_ASSERT(n > 0, "");
-    assign(uint64_or_128_t<Int>(n));
-  }
-
-  FMT_CONSTEXPR20 int num_bigits() const {
-    return static_cast<int>(bigits_.size()) + exp_;
-  }
-
-  FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) {
-    FMT_ASSERT(shift >= 0, "");
-    exp_ += shift / bigit_bits;
-    shift %= bigit_bits;
-    if (shift == 0) return *this;
-    bigit carry = 0;
-    for (size_t i = 0, n = bigits_.size(); i < n; ++i) {
-      bigit c = bigits_[i] >> (bigit_bits - shift);
-      bigits_[i] = (bigits_[i] << shift) + carry;
-      carry = c;
-    }
-    if (carry != 0) bigits_.push_back(carry);
-    return *this;
-  }
-
-  template <typename Int> FMT_CONSTEXPR20 bigint& operator*=(Int value) {
-    FMT_ASSERT(value > 0, "");
-    multiply(uint32_or_64_or_128_t<Int>(value));
-    return *this;
-  }
-
-  friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) {
-    int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
-    if (num_lhs_bigits != num_rhs_bigits)
-      return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
-    int i = static_cast<int>(lhs.bigits_.size()) - 1;
-    int j = static_cast<int>(rhs.bigits_.size()) - 1;
-    int end = i - j;
-    if (end < 0) end = 0;
-    for (; i >= end; --i, --j) {
-      bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
-      if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
-    }
-    if (i != j) return i > j ? 1 : -1;
-    return 0;
-  }
-
-  // Returns compare(lhs1 + lhs2, rhs).
-  friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2,
-                                         const bigint& rhs) {
-    auto minimum = [](int a, int b) { return a < b ? a : b; };
-    auto maximum = [](int a, int b) { return a > b ? a : b; };
-    int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits());
-    int num_rhs_bigits = rhs.num_bigits();
-    if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
-    if (max_lhs_bigits > num_rhs_bigits) return 1;
-    auto get_bigit = [](const bigint& n, int i) -> bigit {
-      return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
+FMT_INLINE FMT_CONSTEXPR20 auto grisu_gen_digits(fp value, uint64_t error, int &exp, gen_digits_handler &handler)
+    -> digits::result
+{
+    const fp one(1ULL << -value.e, value.e);
+    // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
+    // zero because it contains a product of two 64-bit numbers with MSB set (due
+    // to normalization) - 1, shifted right by at most 60 bits.
+    auto integral = static_cast<uint32_t>(value.f >> -one.e);
+    FMT_ASSERT(integral != 0, "");
+    FMT_ASSERT(integral == value.f >> -one.e, "");
+    // The fractional part of scaled value (p2 in Grisu) c = value % one.
+    uint64_t fractional = value.f & (one.f - 1);
+    exp                 = count_digits(integral); // kappa in Grisu.
+    // Non-fixed formats require at least one digit and no precision adjustment.
+    if(handler.fixed)
+    {
+        adjust_precision(handler.precision, exp + handler.exp10);
+        // Check if precision is satisfied just by leading zeros, e.g.
+        // format("{:.2f}", 0.001) gives "0.00" without generating any digits.
+        if(handler.precision <= 0)
+        {
+            if(handler.precision < 0)
+                return digits::done;
+            // Divide by 10 to prevent overflow.
+            uint64_t divisor = data::power_of_10_64[exp - 1] << -one.e;
+            auto     dir     = get_round_direction(divisor, value.f / 10, error * 10);
+            if(dir == round_direction::unknown)
+                return digits::error;
+            handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0';
+            return digits::done;
+        }
+    }
+    // Generate digits for the integral part. This can produce up to 10 digits.
+    do
+    {
+        uint32_t digit           = 0;
+        auto     divmod_integral = [&](uint32_t divisor)
+        {
+            digit = integral / divisor;
+            integral %= divisor;
+        };
+        // This optimization by Milo Yip reduces the number of integer divisions by
+        // one per iteration.
+        switch(exp)
+        {
+            case 10:
+                divmod_integral(1000000000);
+                break;
+            case 9:
+                divmod_integral(100000000);
+                break;
+            case 8:
+                divmod_integral(10000000);
+                break;
+            case 7:
+                divmod_integral(1000000);
+                break;
+            case 6:
+                divmod_integral(100000);
+                break;
+            case 5:
+                divmod_integral(10000);
+                break;
+            case 4:
+                divmod_integral(1000);
+                break;
+            case 3:
+                divmod_integral(100);
+                break;
+            case 2:
+                divmod_integral(10);
+                break;
+            case 1:
+                digit    = integral;
+                integral = 0;
+                break;
+            default:
+                FMT_ASSERT(false, "invalid number of digits");
+        }
+        --exp;
+        auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
+        auto result    = handler.on_digit(
+            static_cast<char>('0' + digit), data::power_of_10_64[exp] << -one.e, remainder, error, true);
+        if(result != digits::more)
+            return result;
+    } while(exp > 0);
+    // Generate digits for the fractional part.
+    for(;;)
+    {
+        fractional *= 10;
+        error *= 10;
+        char digit = static_cast<char>('0' + (fractional >> -one.e));
+        fractional &= one.f - 1;
+        --exp;
+        auto result = handler.on_digit(digit, one.f, fractional, error, false);
+        if(result != digits::more)
+            return result;
+    }
+}
+
+class bigint
+{
+private:
+    // A bigint is stored as an array of bigits (big digits), with bigit at index
+    // 0 being the least significant one.
+    using bigit        = uint32_t;
+    using double_bigit = uint64_t;
+    enum
+    {
+        bigits_capacity = 32
     };
-    double_bigit borrow = 0;
-    int min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_);
-    for (int i = num_rhs_bigits - 1; i >= min_exp; --i) {
-      double_bigit sum =
-          static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
-      bigit rhs_bigit = get_bigit(rhs, i);
-      if (sum > rhs_bigit + borrow) return 1;
-      borrow = rhs_bigit + borrow - sum;
-      if (borrow > 1) return -1;
-      borrow <<= bigit_bits;
-    }
-    return borrow != 0 ? -1 : 0;
-  }
-
-  // Assigns pow(10, exp) to this bigint.
-  FMT_CONSTEXPR20 void assign_pow10(int exp) {
-    FMT_ASSERT(exp >= 0, "");
-    if (exp == 0) return *this = 1;
-    // Find the top bit.
-    int bitmask = 1;
-    while (exp >= bitmask) bitmask <<= 1;
-    bitmask >>= 1;
-    // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
-    // repeated squaring and multiplication.
-    *this = 5;
-    bitmask >>= 1;
-    while (bitmask != 0) {
-      square();
-      if ((exp & bitmask) != 0) *this *= 5;
-      bitmask >>= 1;
-    }
-    *this <<= exp;  // Multiply by pow(2, exp) by shifting.
-  }
-
-  FMT_CONSTEXPR20 void square() {
-    int num_bigits = static_cast<int>(bigits_.size());
-    int num_result_bigits = 2 * num_bigits;
-    basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
-    bigits_.resize(to_unsigned(num_result_bigits));
-    auto sum = uint128_t();
-    for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
-      // Compute bigit at position bigit_index of the result by adding
-      // cross-product terms n[i] * n[j] such that i + j == bigit_index.
-      for (int i = 0, j = bigit_index; j >= 0; ++i, --j) {
-        // Most terms are multiplied twice which can be optimized in the future.
-        sum += static_cast<double_bigit>(n[i]) * n[j];
-      }
-      (*this)[bigit_index] = static_cast<bigit>(sum);
-      sum >>= num_bits<bigit>();  // Compute the carry.
-    }
-    // Do the same for the top half.
-    for (int bigit_index = num_bigits; bigit_index < num_result_bigits;
-         ++bigit_index) {
-      for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
-        sum += static_cast<double_bigit>(n[i++]) * n[j--];
-      (*this)[bigit_index] = static_cast<bigit>(sum);
-      sum >>= num_bits<bigit>();
-    }
-    remove_leading_zeros();
-    exp_ *= 2;
-  }
-
-  // If this bigint has a bigger exponent than other, adds trailing zero to make
-  // exponents equal. This simplifies some operations such as subtraction.
-  FMT_CONSTEXPR20 void align(const bigint& other) {
-    int exp_difference = exp_ - other.exp_;
-    if (exp_difference <= 0) return;
-    int num_bigits = static_cast<int>(bigits_.size());
-    bigits_.resize(to_unsigned(num_bigits + exp_difference));
-    for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
-      bigits_[j] = bigits_[i];
-    std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
-    exp_ -= exp_difference;
-  }
-
-  // Divides this bignum by divisor, assigning the remainder to this and
-  // returning the quotient.
-  FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) {
-    FMT_ASSERT(this != &divisor, "");
-    if (compare(*this, divisor) < 0) return 0;
-    FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
-    align(divisor);
-    int quotient = 0;
-    do {
-      subtract_aligned(divisor);
-      ++quotient;
-    } while (compare(*this, divisor) >= 0);
-    return quotient;
-  }
+    basic_memory_buffer<bigit, bigits_capacity> bigits_;
+    int                                         exp_;
+
+    FMT_CONSTEXPR20 bigit  operator[](int index) const { return bigits_[to_unsigned(index)]; }
+    FMT_CONSTEXPR20 bigit &operator[](int index) { return bigits_[to_unsigned(index)]; }
+
+    static constexpr const int bigit_bits = num_bits<bigit>();
+
+    friend struct formatter<bigint>;
+
+    FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit &borrow)
+    {
+        auto result    = static_cast<double_bigit>((*this)[index]) - other - borrow;
+        (*this)[index] = static_cast<bigit>(result);
+        borrow         = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
+    }
+
+    FMT_CONSTEXPR20 void remove_leading_zeros()
+    {
+        int num_bigits = static_cast<int>(bigits_.size()) - 1;
+        while(num_bigits > 0 && (*this)[num_bigits] == 0)
+            --num_bigits;
+        bigits_.resize(to_unsigned(num_bigits + 1));
+    }
+
+    // Computes *this -= other assuming aligned bigints and *this >= other.
+    FMT_CONSTEXPR20 void subtract_aligned(const bigint &other)
+    {
+        FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
+        FMT_ASSERT(compare(*this, other) >= 0, "");
+        bigit borrow = 0;
+        int   i      = other.exp_ - exp_;
+        for(size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j)
+            subtract_bigits(i, other.bigits_[j], borrow);
+        while(borrow > 0)
+            subtract_bigits(i, 0, borrow);
+        remove_leading_zeros();
+    }
+
+    FMT_CONSTEXPR20 void multiply(uint32_t value)
+    {
+        const double_bigit wide_value = value;
+        bigit              carry      = 0;
+        for(size_t i = 0, n = bigits_.size(); i < n; ++i)
+        {
+            double_bigit result = bigits_[i] * wide_value + carry;
+            bigits_[i]          = static_cast<bigit>(result);
+            carry               = static_cast<bigit>(result >> bigit_bits);
+        }
+        if(carry != 0)
+            bigits_.push_back(carry);
+    }
+
+    template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value || std::is_same<UInt, uint128_t>::value)>
+    FMT_CONSTEXPR20 void multiply(UInt value)
+    {
+        using half_uint  = conditional_t<std::is_same<UInt, uint128_t>::value, uint64_t, uint32_t>;
+        const int  shift = num_bits<half_uint>() - bigit_bits;
+        const UInt lower = static_cast<half_uint>(value);
+        const UInt upper = value >> num_bits<half_uint>();
+        UInt       carry = 0;
+        for(size_t i = 0, n = bigits_.size(); i < n; ++i)
+        {
+            UInt result = lower * bigits_[i] + static_cast<bigit>(carry);
+            carry       = (upper * bigits_[i] << shift) + (result >> bigit_bits) + (carry >> bigit_bits);
+            bigits_[i]  = static_cast<bigit>(result);
+        }
+        while(carry != 0)
+        {
+            bigits_.push_back(static_cast<bigit>(carry));
+            carry >>= bigit_bits;
+        }
+    }
+
+    template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value || std::is_same<UInt, uint128_t>::value)>
+    FMT_CONSTEXPR20 void assign(UInt n)
+    {
+        size_t num_bigits = 0;
+        do
+        {
+            bigits_[num_bigits++] = static_cast<bigit>(n);
+            n >>= bigit_bits;
+        } while(n != 0);
+        bigits_.resize(num_bigits);
+        exp_ = 0;
+    }
+
+public:
+    FMT_CONSTEXPR20 bigint() : exp_(0) {}
+    explicit bigint(uint64_t n) { assign(n); }
+
+    bigint(const bigint &)         = delete;
+    void operator=(const bigint &) = delete;
+
+    FMT_CONSTEXPR20 void assign(const bigint &other)
+    {
+        auto size = other.bigits_.size();
+        bigits_.resize(size);
+        auto data = other.bigits_.data();
+        std::copy(data, data + size, make_checked(bigits_.data(), size));
+        exp_ = other.exp_;
+    }
+
+    template <typename Int>
+    FMT_CONSTEXPR20 void operator=(Int n)
+    {
+        FMT_ASSERT(n > 0, "");
+        assign(uint64_or_128_t<Int>(n));
+    }
+
+    FMT_CONSTEXPR20 int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
+
+    FMT_NOINLINE FMT_CONSTEXPR20 bigint &operator<<=(int shift)
+    {
+        FMT_ASSERT(shift >= 0, "");
+        exp_ += shift / bigit_bits;
+        shift %= bigit_bits;
+        if(shift == 0)
+            return *this;
+        bigit carry = 0;
+        for(size_t i = 0, n = bigits_.size(); i < n; ++i)
+        {
+            bigit c    = bigits_[i] >> (bigit_bits - shift);
+            bigits_[i] = (bigits_[i] << shift) + carry;
+            carry      = c;
+        }
+        if(carry != 0)
+            bigits_.push_back(carry);
+        return *this;
+    }
+
+    template <typename Int>
+    FMT_CONSTEXPR20 bigint &operator*=(Int value)
+    {
+        FMT_ASSERT(value > 0, "");
+        multiply(uint32_or_64_or_128_t<Int>(value));
+        return *this;
+    }
+
+    friend FMT_CONSTEXPR20 int compare(const bigint &lhs, const bigint &rhs)
+    {
+        int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
+        if(num_lhs_bigits != num_rhs_bigits)
+            return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
+        int i   = static_cast<int>(lhs.bigits_.size()) - 1;
+        int j   = static_cast<int>(rhs.bigits_.size()) - 1;
+        int end = i - j;
+        if(end < 0)
+            end = 0;
+        for(; i >= end; --i, --j)
+        {
+            bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
+            if(lhs_bigit != rhs_bigit)
+                return lhs_bigit > rhs_bigit ? 1 : -1;
+        }
+        if(i != j)
+            return i > j ? 1 : -1;
+        return 0;
+    }
+
+    // Returns compare(lhs1 + lhs2, rhs).
+    friend FMT_CONSTEXPR20 int add_compare(const bigint &lhs1, const bigint &lhs2, const bigint &rhs)
+    {
+        auto minimum        = [](int a, int b) { return a < b ? a : b; };
+        auto maximum        = [](int a, int b) { return a > b ? a : b; };
+        int  max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits());
+        int  num_rhs_bigits = rhs.num_bigits();
+        if(max_lhs_bigits + 1 < num_rhs_bigits)
+            return -1;
+        if(max_lhs_bigits > num_rhs_bigits)
+            return 1;
+        auto get_bigit = [](const bigint &n, int i) -> bigit
+        { return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; };
+        double_bigit borrow  = 0;
+        int          min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_);
+        for(int i = num_rhs_bigits - 1; i >= min_exp; --i)
+        {
+            double_bigit sum       = static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
+            bigit        rhs_bigit = get_bigit(rhs, i);
+            if(sum > rhs_bigit + borrow)
+                return 1;
+            borrow = rhs_bigit + borrow - sum;
+            if(borrow > 1)
+                return -1;
+            borrow <<= bigit_bits;
+        }
+        return borrow != 0 ? -1 : 0;
+    }
+
+    // Assigns pow(10, exp) to this bigint.
+    FMT_CONSTEXPR20 void assign_pow10(int exp)
+    {
+        FMT_ASSERT(exp >= 0, "");
+        if(exp == 0)
+            return *this = 1;
+        // Find the top bit.
+        int bitmask = 1;
+        while(exp >= bitmask)
+            bitmask <<= 1;
+        bitmask >>= 1;
+        // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
+        // repeated squaring and multiplication.
+        *this = 5;
+        bitmask >>= 1;
+        while(bitmask != 0)
+        {
+            square();
+            if((exp & bitmask) != 0)
+                *this *= 5;
+            bitmask >>= 1;
+        }
+        *this <<= exp; // Multiply by pow(2, exp) by shifting.
+    }
+
+    FMT_CONSTEXPR20 void square()
+    {
+        int                                         num_bigits        = static_cast<int>(bigits_.size());
+        int                                         num_result_bigits = 2 * num_bigits;
+        basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
+        bigits_.resize(to_unsigned(num_result_bigits));
+        auto sum = uint128_t();
+        for(int bigit_index = 0; bigit_index < num_bigits; ++bigit_index)
+        {
+            // Compute bigit at position bigit_index of the result by adding
+            // cross-product terms n[i] * n[j] such that i + j == bigit_index.
+            for(int i = 0, j = bigit_index; j >= 0; ++i, --j)
+            {
+                // Most terms are multiplied twice which can be optimized in the future.
+                sum += static_cast<double_bigit>(n[i]) * n[j];
+            }
+            (*this)[bigit_index] = static_cast<bigit>(sum);
+            sum >>= num_bits<bigit>(); // Compute the carry.
+        }
+        // Do the same for the top half.
+        for(int bigit_index = num_bigits; bigit_index < num_result_bigits; ++bigit_index)
+        {
+            for(int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
+                sum += static_cast<double_bigit>(n[i++]) * n[j--];
+            (*this)[bigit_index] = static_cast<bigit>(sum);
+            sum >>= num_bits<bigit>();
+        }
+        remove_leading_zeros();
+        exp_ *= 2;
+    }
+
+    // If this bigint has a bigger exponent than other, adds trailing zero to make
+    // exponents equal. This simplifies some operations such as subtraction.
+    FMT_CONSTEXPR20 void align(const bigint &other)
+    {
+        int exp_difference = exp_ - other.exp_;
+        if(exp_difference <= 0)
+            return;
+        int num_bigits = static_cast<int>(bigits_.size());
+        bigits_.resize(to_unsigned(num_bigits + exp_difference));
+        for(int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
+            bigits_[j] = bigits_[i];
+        std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
+        exp_ -= exp_difference;
+    }
+
+    // Divides this bignum by divisor, assigning the remainder to this and
+    // returning the quotient.
+    FMT_CONSTEXPR20 int divmod_assign(const bigint &divisor)
+    {
+        FMT_ASSERT(this != &divisor, "");
+        if(compare(*this, divisor) < 0)
+            return 0;
+        FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
+        align(divisor);
+        int quotient = 0;
+        do
+        {
+            subtract_aligned(divisor);
+            ++quotient;
+        } while(compare(*this, divisor) >= 0);
+        return quotient;
+    }
 };
 
 // format_dragon flags.
-enum dragon {
-  predecessor_closer = 1,
-  fixup = 2,  // Run fixup to correct exp10 which can be off by one.
-  fixed = 4,
+enum dragon
+{
+    predecessor_closer = 1,
+    fixup              = 2, // Run fixup to correct exp10 which can be off by one.
+    fixed              = 4,
 };
 
 // Formats a floating-point number using a variation of the Fixed-Precision
 // Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
 // https://fmt.dev/papers/p372-steele.pdf.
-FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
-                                          unsigned flags, int num_digits,
-                                          buffer<char>& buf, int& exp10) {
-  bigint numerator;    // 2 * R in (FPP)^2.
-  bigint denominator;  // 2 * S in (FPP)^2.
-  // lower and upper are differences between value and corresponding boundaries.
-  bigint lower;             // (M^- in (FPP)^2).
-  bigint upper_store;       // upper's value if different from lower.
-  bigint* upper = nullptr;  // (M^+ in (FPP)^2).
-  // Shift numerator and denominator by an extra bit or two (if lower boundary
-  // is closer) to make lower and upper integers. This eliminates multiplication
-  // by 2 during later computations.
-  bool is_predecessor_closer = (flags & dragon::predecessor_closer) != 0;
-  int shift = is_predecessor_closer ? 2 : 1;
-  if (value.e >= 0) {
-    numerator = value.f;
-    numerator <<= value.e + shift;
-    lower = 1;
-    lower <<= value.e;
-    if (is_predecessor_closer) {
-      upper_store = 1;
-      upper_store <<= value.e + 1;
-      upper = &upper_store;
-    }
-    denominator.assign_pow10(exp10);
-    denominator <<= shift;
-  } else if (exp10 < 0) {
-    numerator.assign_pow10(-exp10);
-    lower.assign(numerator);
-    if (is_predecessor_closer) {
-      upper_store.assign(numerator);
-      upper_store <<= 1;
-      upper = &upper_store;
-    }
-    numerator *= value.f;
-    numerator <<= shift;
-    denominator = 1;
-    denominator <<= shift - value.e;
-  } else {
-    numerator = value.f;
-    numerator <<= shift;
-    denominator.assign_pow10(exp10);
-    denominator <<= shift - value.e;
-    lower = 1;
-    if (is_predecessor_closer) {
-      upper_store = 1ULL << 1;
-      upper = &upper_store;
-    }
-  }
-  int even = static_cast<int>((value.f & 1) == 0);
-  if (!upper) upper = &lower;
-  if ((flags & dragon::fixup) != 0) {
-    if (add_compare(numerator, *upper, denominator) + even <= 0) {
-      --exp10;
-      numerator *= 10;
-      if (num_digits < 0) {
-        lower *= 10;
-        if (upper != &lower) *upper *= 10;
-      }
-    }
-    if ((flags & dragon::fixed) != 0) adjust_precision(num_digits, exp10 + 1);
-  }
-  // Invariant: value == (numerator / denominator) * pow(10, exp10).
-  if (num_digits < 0) {
-    // Generate the shortest representation.
-    num_digits = 0;
-    char* data = buf.data();
-    for (;;) {
-      int digit = numerator.divmod_assign(denominator);
-      bool low = compare(numerator, lower) - even < 0;  // numerator <[=] lower.
-      // numerator + upper >[=] pow10:
-      bool high = add_compare(numerator, *upper, denominator) + even > 0;
-      data[num_digits++] = static_cast<char>('0' + digit);
-      if (low || high) {
-        if (!low) {
-          ++data[num_digits - 1];
-        } else if (high) {
-          int result = add_compare(numerator, numerator, denominator);
-          // Round half to even.
-          if (result > 0 || (result == 0 && (digit % 2) != 0))
-            ++data[num_digits - 1];
+FMT_CONSTEXPR20 inline void
+format_dragon(basic_fp<uint128_t> value, unsigned flags, int num_digits, buffer<char> &buf, int &exp10)
+{
+    bigint numerator;   // 2 * R in (FPP)^2.
+    bigint denominator; // 2 * S in (FPP)^2.
+    // lower and upper are differences between value and corresponding boundaries.
+    bigint  lower;           // (M^- in (FPP)^2).
+    bigint  upper_store;     // upper's value if different from lower.
+    bigint *upper = nullptr; // (M^+ in (FPP)^2).
+    // Shift numerator and denominator by an extra bit or two (if lower boundary
+    // is closer) to make lower and upper integers. This eliminates multiplication
+    // by 2 during later computations.
+    bool is_predecessor_closer = (flags & dragon::predecessor_closer) != 0;
+    int  shift                 = is_predecessor_closer ? 2 : 1;
+    if(value.e >= 0)
+    {
+        numerator = value.f;
+        numerator <<= value.e + shift;
+        lower = 1;
+        lower <<= value.e;
+        if(is_predecessor_closer)
+        {
+            upper_store = 1;
+            upper_store <<= value.e + 1;
+            upper = &upper_store;
+        }
+        denominator.assign_pow10(exp10);
+        denominator <<= shift;
+    }
+    else if(exp10 < 0)
+    {
+        numerator.assign_pow10(-exp10);
+        lower.assign(numerator);
+        if(is_predecessor_closer)
+        {
+            upper_store.assign(numerator);
+            upper_store <<= 1;
+            upper = &upper_store;
+        }
+        numerator *= value.f;
+        numerator <<= shift;
+        denominator = 1;
+        denominator <<= shift - value.e;
+    }
+    else
+    {
+        numerator = value.f;
+        numerator <<= shift;
+        denominator.assign_pow10(exp10);
+        denominator <<= shift - value.e;
+        lower = 1;
+        if(is_predecessor_closer)
+        {
+            upper_store = 1ULL << 1;
+            upper       = &upper_store;
+        }
+    }
+    int even = static_cast<int>((value.f & 1) == 0);
+    if(!upper)
+        upper = &lower;
+    if((flags & dragon::fixup) != 0)
+    {
+        if(add_compare(numerator, *upper, denominator) + even <= 0)
+        {
+            --exp10;
+            numerator *= 10;
+            if(num_digits < 0)
+            {
+                lower *= 10;
+                if(upper != &lower)
+                    *upper *= 10;
+            }
         }
-        buf.try_resize(to_unsigned(num_digits));
-        exp10 -= num_digits - 1;
+        if((flags & dragon::fixed) != 0)
+            adjust_precision(num_digits, exp10 + 1);
+    }
+    // Invariant: value == (numerator / denominator) * pow(10, exp10).
+    if(num_digits < 0)
+    {
+        // Generate the shortest representation.
+        num_digits = 0;
+        char *data = buf.data();
+        for(;;)
+        {
+            int  digit = numerator.divmod_assign(denominator);
+            bool low   = compare(numerator, lower) - even < 0; // numerator <[=] lower.
+            // numerator + upper >[=] pow10:
+            bool high          = add_compare(numerator, *upper, denominator) + even > 0;
+            data[num_digits++] = static_cast<char>('0' + digit);
+            if(low || high)
+            {
+                if(!low)
+                {
+                    ++data[num_digits - 1];
+                }
+                else if(high)
+                {
+                    int result = add_compare(numerator, numerator, denominator);
+                    // Round half to even.
+                    if(result > 0 || (result == 0 && (digit % 2) != 0))
+                        ++data[num_digits - 1];
+                }
+                buf.try_resize(to_unsigned(num_digits));
+                exp10 -= num_digits - 1;
+                return;
+            }
+            numerator *= 10;
+            lower *= 10;
+            if(upper != &lower)
+                *upper *= 10;
+        }
+    }
+    // Generate the given number of digits.
+    exp10 -= num_digits - 1;
+    if(num_digits == 0)
+    {
+        denominator *= 10;
+        auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
+        buf.push_back(digit);
         return;
-      }
-      numerator *= 10;
-      lower *= 10;
-      if (upper != &lower) *upper *= 10;
-    }
-  }
-  // Generate the given number of digits.
-  exp10 -= num_digits - 1;
-  if (num_digits == 0) {
-    denominator *= 10;
-    auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
-    buf.push_back(digit);
-    return;
-  }
-  buf.try_resize(to_unsigned(num_digits));
-  for (int i = 0; i < num_digits - 1; ++i) {
-    int digit = numerator.divmod_assign(denominator);
-    buf[i] = static_cast<char>('0' + digit);
-    numerator *= 10;
-  }
-  int digit = numerator.divmod_assign(denominator);
-  auto result = add_compare(numerator, numerator, denominator);
-  if (result > 0 || (result == 0 && (digit % 2) != 0)) {
-    if (digit == 9) {
-      const auto overflow = '0' + 10;
-      buf[num_digits - 1] = overflow;
-      // Propagate the carry.
-      for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) {
-        buf[i] = '0';
-        ++buf[i - 1];
-      }
-      if (buf[0] == overflow) {
-        buf[0] = '1';
-        ++exp10;
-      }
-      return;
-    }
-    ++digit;
-  }
-  buf[num_digits - 1] = static_cast<char>('0' + digit);
+    }
+    buf.try_resize(to_unsigned(num_digits));
+    for(int i = 0; i < num_digits - 1; ++i)
+    {
+        int digit = numerator.divmod_assign(denominator);
+        buf[i]    = static_cast<char>('0' + digit);
+        numerator *= 10;
+    }
+    int  digit  = numerator.divmod_assign(denominator);
+    auto result = add_compare(numerator, numerator, denominator);
+    if(result > 0 || (result == 0 && (digit % 2) != 0))
+    {
+        if(digit == 9)
+        {
+            const auto overflow = '0' + 10;
+            buf[num_digits - 1] = overflow;
+            // Propagate the carry.
+            for(int i = num_digits - 1; i > 0 && buf[i] == overflow; --i)
+            {
+                buf[i] = '0';
+                ++buf[i - 1];
+            }
+            if(buf[0] == overflow)
+            {
+                buf[0] = '1';
+                ++exp10;
+            }
+            return;
+        }
+        ++digit;
+    }
+    buf[num_digits - 1] = static_cast<char>('0' + digit);
 }
 
 template <typename Float>
-FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
-                                  buffer<char>& buf) -> int {
-  // float is passed as double to reduce the number of instantiations.
-  static_assert(!std::is_same<Float, float>::value, "");
-  FMT_ASSERT(value >= 0, "value is negative");
-  auto converted_value = convert_float(value);
-
-  const bool fixed = specs.format == float_format::fixed;
-  if (value <= 0) {  // <= instead of == to silence a warning.
-    if (precision <= 0 || !fixed) {
-      buf.push_back('0');
-      return 0;
-    }
-    buf.try_resize(to_unsigned(precision));
-    fill_n(buf.data(), precision, '0');
-    return -precision;
-  }
-
-  int exp = 0;
-  bool use_dragon = true;
-  unsigned dragon_flags = 0;
-  if (!is_fast_float<Float>()) {
-    const auto inv_log2_10 = 0.3010299956639812;  // 1 / log2(10)
-    using info = dragonbox::float_info<decltype(converted_value)>;
-    const auto f = basic_fp<typename info::carrier_uint>(converted_value);
-    // Compute exp, an approximate power of 10, such that
-    //   10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1).
-    // This is based on log10(value) == log2(value) / log2(10) and approximation
-    // of log2(value) by e + num_fraction_bits idea from double-conversion.
-    exp = static_cast<int>(
-        std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10));
-    dragon_flags = dragon::fixup;
-  } else if (!is_constant_evaluated() && precision < 0) {
-    // Use Dragonbox for the shortest format.
-    if (specs.binary32) {
-      auto dec = dragonbox::to_decimal(static_cast<float>(value));
-      write<char>(buffer_appender<char>(buf), dec.significand);
-      return dec.exponent;
-    }
-    auto dec = dragonbox::to_decimal(static_cast<double>(value));
-    write<char>(buffer_appender<char>(buf), dec.significand);
-    return dec.exponent;
-  } else {
-    // Use Grisu + Dragon4 for the given precision:
-    // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
-    const int min_exp = -60;  // alpha in Grisu.
-    int cached_exp10 = 0;     // K in Grisu.
-    fp normalized = normalize(fp(converted_value));
-    const auto cached_pow = get_cached_power(
-        min_exp - (normalized.e + fp::num_significand_bits), cached_exp10);
-    normalized = normalized * cached_pow;
-    gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
-    if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error &&
-        !is_constant_evaluated()) {
-      exp += handler.exp10;
-      buf.try_resize(to_unsigned(handler.size));
-      use_dragon = false;
-    } else {
-      exp += handler.size - cached_exp10 - 1;
-      precision = handler.precision;
-    }
-  }
-  if (use_dragon) {
-    auto f = basic_fp<uint128_t>();
-    bool is_predecessor_closer = specs.binary32
-                                     ? f.assign(static_cast<float>(value))
-                                     : f.assign(converted_value);
-    if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer;
-    if (fixed) dragon_flags |= dragon::fixed;
-    // Limit precision to the maximum possible number of significant digits in
-    // an IEEE754 double because we don't need to generate zeros.
-    const int max_double_digits = 767;
-    if (precision > max_double_digits) precision = max_double_digits;
-    format_dragon(f, dragon_flags, precision, buf, exp);
-  }
-  if (!fixed && !specs.showpoint) {
-    // Remove trailing zeros.
-    auto num_digits = buf.size();
-    while (num_digits > 0 && buf[num_digits - 1] == '0') {
-      --num_digits;
-      ++exp;
-    }
-    buf.try_resize(num_digits);
-  }
-  return exp;
-}
-
-template <typename Char, typename OutputIt, typename T,
-          FMT_ENABLE_IF(is_floating_point<T>::value)>
-FMT_CONSTEXPR20 auto write(OutputIt out, T value,
-                           basic_format_specs<Char> specs, locale_ref loc = {})
-    -> OutputIt {
-  if (const_check(!is_supported_floating_point(value))) return out;
-  float_specs fspecs = parse_float_type_spec(specs);
-  fspecs.sign = specs.sign;
-  if (detail::signbit(value)) {  // value < 0 is false for NaN so use signbit.
-    fspecs.sign = sign::minus;
-    value = -value;
-  } else if (fspecs.sign == sign::minus) {
-    fspecs.sign = sign::none;
-  }
-
-  if (!detail::isfinite(value))
-    return write_nonfinite(out, detail::isnan(value), specs, fspecs);
-
-  if (specs.align == align::numeric && fspecs.sign) {
-    auto it = reserve(out, 1);
-    *it++ = detail::sign<Char>(fspecs.sign);
-    out = base_iterator(out, it);
-    fspecs.sign = sign::none;
-    if (specs.width != 0) --specs.width;
-  }
-
-  memory_buffer buffer;
-  if (fspecs.format == float_format::hex) {
-    if (fspecs.sign) buffer.push_back(detail::sign<char>(fspecs.sign));
-    snprintf_float(convert_float(value), specs.precision, fspecs, buffer);
-    return write_bytes<align::right>(out, {buffer.data(), buffer.size()},
-                                     specs);
-  }
-  int precision = specs.precision >= 0 || specs.type == presentation_type::none
-                      ? specs.precision
-                      : 6;
-  if (fspecs.format == float_format::exp) {
-    if (precision == max_value<int>())
-      throw_format_error("number is too big");
+FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, buffer<char> &buf) -> int
+{
+    // float is passed as double to reduce the number of instantiations.
+    static_assert(!std::is_same<Float, float>::value, "");
+    FMT_ASSERT(value >= 0, "value is negative");
+    auto converted_value = convert_float(value);
+
+    const bool fixed = specs.format == float_format::fixed;
+    if(value <= 0)
+    { // <= instead of == to silence a warning.
+        if(precision <= 0 || !fixed)
+        {
+            buf.push_back('0');
+            return 0;
+        }
+        buf.try_resize(to_unsigned(precision));
+        fill_n(buf.data(), precision, '0');
+        return -precision;
+    }
+
+    int      exp          = 0;
+    bool     use_dragon   = true;
+    unsigned dragon_flags = 0;
+    if(!is_fast_float<Float>())
+    {
+        const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10)
+        using info             = dragonbox::float_info<decltype(converted_value)>;
+        const auto f           = basic_fp<typename info::carrier_uint>(converted_value);
+        // Compute exp, an approximate power of 10, such that
+        //   10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1).
+        // This is based on log10(value) == log2(value) / log2(10) and approximation
+        // of log2(value) by e + num_fraction_bits idea from double-conversion.
+        exp          = static_cast<int>(std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10));
+        dragon_flags = dragon::fixup;
+    }
+    else if(!is_constant_evaluated() && precision < 0)
+    {
+        // Use Dragonbox for the shortest format.
+        if(specs.binary32)
+        {
+            auto dec = dragonbox::to_decimal(static_cast<float>(value));
+            write<char>(buffer_appender<char>(buf), dec.significand);
+            return dec.exponent;
+        }
+        auto dec = dragonbox::to_decimal(static_cast<double>(value));
+        write<char>(buffer_appender<char>(buf), dec.significand);
+        return dec.exponent;
+    }
     else
-      ++precision;
-  } else if (fspecs.format != float_format::fixed && precision == 0) {
-    precision = 1;
-  }
-  if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
-  int exp = format_float(convert_float(value), precision, fspecs, buffer);
-  fspecs.precision = precision;
-  auto f = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
-  return write_float(out, f, specs, fspecs, loc);
-}
-
-template <typename Char, typename OutputIt, typename T,
-          FMT_ENABLE_IF(is_fast_float<T>::value)>
-FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
-  if (is_constant_evaluated())
-    return write(out, value, basic_format_specs<Char>());
-  if (const_check(!is_supported_floating_point(value))) return out;
+    {
+        // Use Grisu + Dragon4 for the given precision:
+        // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
+        const int  min_exp      = -60; // alpha in Grisu.
+        int        cached_exp10 = 0;   // K in Grisu.
+        fp         normalized   = normalize(fp(converted_value));
+        const auto cached_pow   = get_cached_power(min_exp - (normalized.e + fp::num_significand_bits), cached_exp10);
+        normalized              = normalized * cached_pow;
+        gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
+        if(grisu_gen_digits(normalized, 1, exp, handler) != digits::error && !is_constant_evaluated())
+        {
+            exp += handler.exp10;
+            buf.try_resize(to_unsigned(handler.size));
+            use_dragon = false;
+        }
+        else
+        {
+            exp += handler.size - cached_exp10 - 1;
+            precision = handler.precision;
+        }
+    }
+    if(use_dragon)
+    {
+        auto f                     = basic_fp<uint128_t>();
+        bool is_predecessor_closer = specs.binary32 ? f.assign(static_cast<float>(value)) : f.assign(converted_value);
+        if(is_predecessor_closer)
+            dragon_flags |= dragon::predecessor_closer;
+        if(fixed)
+            dragon_flags |= dragon::fixed;
+        // Limit precision to the maximum possible number of significant digits in
+        // an IEEE754 double because we don't need to generate zeros.
+        const int max_double_digits = 767;
+        if(precision > max_double_digits)
+            precision = max_double_digits;
+        format_dragon(f, dragon_flags, precision, buf, exp);
+    }
+    if(!fixed && !specs.showpoint)
+    {
+        // Remove trailing zeros.
+        auto num_digits = buf.size();
+        while(num_digits > 0 && buf[num_digits - 1] == '0')
+        {
+            --num_digits;
+            ++exp;
+        }
+        buf.try_resize(num_digits);
+    }
+    return exp;
+}
+
+template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(is_floating_point<T>::value)>
+FMT_CONSTEXPR20 auto write(OutputIt out, T value, basic_format_specs<Char> specs, locale_ref loc = {}) -> OutputIt
+{
+    if(const_check(!is_supported_floating_point(value)))
+        return out;
+    float_specs fspecs = parse_float_type_spec(specs);
+    fspecs.sign        = specs.sign;
+    if(detail::signbit(value))
+    { // value < 0 is false for NaN so use signbit.
+        fspecs.sign = sign::minus;
+        value       = -value;
+    }
+    else if(fspecs.sign == sign::minus)
+    {
+        fspecs.sign = sign::none;
+    }
 
-  auto fspecs = float_specs();
-  if (detail::signbit(value)) {
-    fspecs.sign = sign::minus;
-    value = -value;
-  }
+    if(!detail::isfinite(value))
+        return write_nonfinite(out, detail::isnan(value), specs, fspecs);
+
+    if(specs.align == align::numeric && fspecs.sign)
+    {
+        auto it     = reserve(out, 1);
+        *it++       = detail::sign<Char>(fspecs.sign);
+        out         = base_iterator(out, it);
+        fspecs.sign = sign::none;
+        if(specs.width != 0)
+            --specs.width;
+    }
+
+    memory_buffer buffer;
+    if(fspecs.format == float_format::hex)
+    {
+        if(fspecs.sign)
+            buffer.push_back(detail::sign<char>(fspecs.sign));
+        snprintf_float(convert_float(value), specs.precision, fspecs, buffer);
+        return write_bytes<align::right>(out, {buffer.data(), buffer.size()}, specs);
+    }
+    int precision = specs.precision >= 0 || specs.type == presentation_type::none ? specs.precision : 6;
+    if(fspecs.format == float_format::exp)
+    {
+        if(precision == max_value<int>())
+            throw_format_error("number is too big");
+        else
+            ++precision;
+    }
+    else if(fspecs.format != float_format::fixed && precision == 0)
+    {
+        precision = 1;
+    }
+    if(const_check(std::is_same<T, float>()))
+        fspecs.binary32 = true;
+    int exp          = format_float(convert_float(value), precision, fspecs, buffer);
+    fspecs.precision = precision;
+    auto f           = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
+    return write_float(out, f, specs, fspecs, loc);
+}
+
+template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(is_fast_float<T>::value)>
+FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt
+{
+    if(is_constant_evaluated())
+        return write(out, value, basic_format_specs<Char>());
+    if(const_check(!is_supported_floating_point(value)))
+        return out;
+
+    auto fspecs = float_specs();
+    if(detail::signbit(value))
+    {
+        fspecs.sign = sign::minus;
+        value       = -value;
+    }
 
-  constexpr auto specs = basic_format_specs<Char>();
-  using floaty = conditional_t<std::is_same<T, long double>::value, double, T>;
-  using uint = typename dragonbox::float_info<floaty>::carrier_uint;
-  uint mask = exponent_mask<floaty>();
-  if ((bit_cast<uint>(value) & mask) == mask)
-    return write_nonfinite(out, std::isnan(value), specs, fspecs);
+    constexpr auto specs = basic_format_specs<Char>();
+    using floaty         = conditional_t<std::is_same<T, long double>::value, double, T>;
+    using uint           = typename dragonbox::float_info<floaty>::carrier_uint;
+    uint mask            = exponent_mask<floaty>();
+    if((bit_cast<uint>(value) & mask) == mask)
+        return write_nonfinite(out, std::isnan(value), specs, fspecs);
 
-  auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
-  return write_float(out, dec, specs, fspecs, {});
+    auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
+    return write_float(out, dec, specs, fspecs, {});
 }
 
-template <typename Char, typename OutputIt, typename T,
-          FMT_ENABLE_IF(is_floating_point<T>::value &&
-                        !is_fast_float<T>::value)>
-inline auto write(OutputIt out, T value) -> OutputIt {
-  return write(out, value, basic_format_specs<Char>());
+template <
+    typename Char,
+    typename OutputIt,
+    typename T,
+    FMT_ENABLE_IF(is_floating_point<T>::value && !is_fast_float<T>::value)>
+inline auto write(OutputIt out, T value) -> OutputIt
+{
+    return write(out, value, basic_format_specs<Char>());
 }
 
 template <typename Char, typename OutputIt>
-auto write(OutputIt out, monostate, basic_format_specs<Char> = {},
-           locale_ref = {}) -> OutputIt {
-  FMT_ASSERT(false, "");
-  return out;
+auto write(OutputIt out, monostate, basic_format_specs<Char> = {}, locale_ref = {}) -> OutputIt
+{
+    FMT_ASSERT(false, "");
+    return out;
 }
 
 template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> value)
-    -> OutputIt {
-  auto it = reserve(out, value.size());
-  it = copy_str_noinline<Char>(value.begin(), value.end(), it);
-  return base_iterator(out, it);
+FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> value) -> OutputIt
+{
+    auto it = reserve(out, value.size());
+    it      = copy_str_noinline<Char>(value.begin(), value.end(), it);
+    return base_iterator(out, it);
 }
 
-template <typename Char, typename OutputIt, typename T,
-          FMT_ENABLE_IF(is_string<T>::value)>
-constexpr auto write(OutputIt out, const T& value) -> OutputIt {
-  return write<Char>(out, to_string_view(value));
+template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(is_string<T>::value)>
+constexpr auto write(OutputIt out, const T &value) -> OutputIt
+{
+    return write<Char>(out, to_string_view(value));
 }
 
 // FMT_ENABLE_IF() condition separated to workaround an MSVC bug.
 template <
-    typename Char, typename OutputIt, typename T,
-    bool check =
-        std::is_enum<T>::value && !std::is_same<T, Char>::value &&
-        mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value !=
-            type::custom_type,
+    typename Char,
+    typename OutputIt,
+    typename T,
+    bool check = std::is_enum<T>::value && !std::is_same<T, Char>::value &&
+                 mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value != type::custom_type,
     FMT_ENABLE_IF(check)>
-FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
-  return write<Char>(out, static_cast<underlying_t<T>>(value));
+FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt
+{
+    return write<Char>(out, static_cast<underlying_t<T>>(value));
 }
 
-template <typename Char, typename OutputIt, typename T,
-          FMT_ENABLE_IF(std::is_same<T, bool>::value)>
-FMT_CONSTEXPR auto write(OutputIt out, T value,
-                         const basic_format_specs<Char>& specs = {},
-                         locale_ref = {}) -> OutputIt {
-  return specs.type != presentation_type::none &&
-                 specs.type != presentation_type::string
-             ? write(out, value ? 1 : 0, specs, {})
-             : write_bytes(out, value ? "true" : "false", specs);
+template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(std::is_same<T, bool>::value)>
+FMT_CONSTEXPR auto write(OutputIt out, T value, const basic_format_specs<Char> &specs = {}, locale_ref = {}) -> OutputIt
+{
+    return specs.type != presentation_type::none && specs.type != presentation_type::string ?
+               write(out, value ? 1 : 0, specs, {}) :
+               write_bytes(out, value ? "true" : "false", specs);
 }
 
 template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt {
-  auto it = reserve(out, 1);
-  *it++ = value;
-  return base_iterator(out, it);
+FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt
+{
+    auto it = reserve(out, 1);
+    *it++   = value;
+    return base_iterator(out, it);
 }
 
 template <typename Char, typename OutputIt>
-FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value)
-    -> OutputIt {
-  if (!value) {
-    throw_format_error("string pointer is null");
-  } else {
-    out = write(out, basic_string_view<Char>(value));
-  }
-  return out;
+FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char *value) -> OutputIt
+{
+    if(!value)
+    {
+        throw_format_error("string pointer is null");
+    }
+    else
+    {
+        out = write(out, basic_string_view<Char>(value));
+    }
+    return out;
 }
 
-template <typename Char, typename OutputIt, typename T,
-          FMT_ENABLE_IF(std::is_same<T, void>::value)>
-auto write(OutputIt out, const T* value,
-           const basic_format_specs<Char>& specs = {}, locale_ref = {})
-    -> OutputIt {
-  check_pointer_type_spec(specs.type, error_handler());
-  return write_ptr<Char>(out, bit_cast<uintptr_t>(value), &specs);
+template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(std::is_same<T, void>::value)>
+auto write(OutputIt out, const T *value, const basic_format_specs<Char> &specs = {}, locale_ref = {}) -> OutputIt
+{
+    check_pointer_type_spec(specs.type, error_handler());
+    return write_ptr<Char>(out, bit_cast<uintptr_t>(value), &specs);
 }
 
 // A write overload that handles implicit conversions.
-template <typename Char, typename OutputIt, typename T,
-          typename Context = basic_format_context<OutputIt, Char>>
-FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t<
-    std::is_class<T>::value && !is_string<T>::value &&
-        !is_floating_point<T>::value && !std::is_same<T, Char>::value &&
-        !std::is_same<const T&,
-                      decltype(arg_mapper<Context>().map(value))>::value,
-    OutputIt> {
-  return write<Char>(out, arg_mapper<Context>().map(value));
-}
-
-template <typename Char, typename OutputIt, typename T,
-          typename Context = basic_format_context<OutputIt, Char>>
-FMT_CONSTEXPR auto write(OutputIt out, const T& value)
-    -> enable_if_t<mapped_type_constant<T, Context>::value == type::custom_type,
-                   OutputIt> {
-  using formatter_type =
-      conditional_t<has_formatter<T, Context>::value,
-                    typename Context::template formatter_type<T>,
-                    fallback_formatter<T, Char>>;
-  auto ctx = Context(out, {}, {});
-  return formatter_type().format(value, ctx);
+template <typename Char, typename OutputIt, typename T, typename Context = basic_format_context<OutputIt, Char>>
+FMT_CONSTEXPR auto write(OutputIt out, const T &value) -> enable_if_t<
+    std::is_class<T>::value && !is_string<T>::value && !is_floating_point<T>::value && !std::is_same<T, Char>::value &&
+        !std::is_same<const T &, decltype(arg_mapper<Context>().map(value))>::value,
+    OutputIt>
+{
+    return write<Char>(out, arg_mapper<Context>().map(value));
+}
+
+template <typename Char, typename OutputIt, typename T, typename Context = basic_format_context<OutputIt, Char>>
+FMT_CONSTEXPR auto write(OutputIt out, const T &value)
+    -> enable_if_t<mapped_type_constant<T, Context>::value == type::custom_type, OutputIt>
+{
+    using formatter_type = conditional_t<
+        has_formatter<T, Context>::value,
+        typename Context::template formatter_type<T>,
+        fallback_formatter<T, Char>>;
+    auto ctx = Context(out, {}, {});
+    return formatter_type().format(value, ctx);
 }
 
 // An argument visitor that formats the argument and writes it via the output
 // iterator. It's a class and not a generic lambda for compatibility with C++11.
-template <typename Char> struct default_arg_formatter {
-  using iterator = buffer_appender<Char>;
-  using context = buffer_context<Char>;
-
-  iterator out;
-  basic_format_args<context> args;
-  locale_ref loc;
-
-  template <typename T> auto operator()(T value) -> iterator {
-    return write<Char>(out, value);
-  }
-  auto operator()(typename basic_format_arg<context>::handle h) -> iterator {
-    basic_format_parse_context<Char> parse_ctx({});
-    context format_ctx(out, args, loc);
-    h.format(parse_ctx, format_ctx);
-    return format_ctx.out();
-  }
+template <typename Char>
+struct default_arg_formatter
+{
+    using iterator = buffer_appender<Char>;
+    using context  = buffer_context<Char>;
+
+    iterator                   out;
+    basic_format_args<context> args;
+    locale_ref                 loc;
+
+    template <typename T>
+    auto operator()(T value) -> iterator
+    {
+        return write<Char>(out, value);
+    }
+    auto operator()(typename basic_format_arg<context>::handle h) -> iterator
+    {
+        basic_format_parse_context<Char> parse_ctx({});
+        context                          format_ctx(out, args, loc);
+        h.format(parse_ctx, format_ctx);
+        return format_ctx.out();
+    }
 };
 
-template <typename Char> struct arg_formatter {
-  using iterator = buffer_appender<Char>;
-  using context = buffer_context<Char>;
-
-  iterator out;
-  const basic_format_specs<Char>& specs;
-  locale_ref locale;
-
-  template <typename T>
-  FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator {
-    return detail::write(out, value, specs, locale);
-  }
-  auto operator()(typename basic_format_arg<context>::handle) -> iterator {
-    // User-defined types are handled separately because they require access
-    // to the parse context.
-    return out;
-  }
+template <typename Char>
+struct arg_formatter
+{
+    using iterator = buffer_appender<Char>;
+    using context  = buffer_context<Char>;
+
+    iterator                        out;
+    const basic_format_specs<Char> &specs;
+    locale_ref                      locale;
+
+    template <typename T>
+    FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator
+    {
+        return detail::write(out, value, specs, locale);
+    }
+    auto operator()(typename basic_format_arg<context>::handle) -> iterator
+    {
+        // User-defined types are handled separately because they require access
+        // to the parse context.
+        return out;
+    }
 };
 
-template <typename Char> struct custom_formatter {
-  basic_format_parse_context<Char>& parse_ctx;
-  buffer_context<Char>& ctx;
-
-  void operator()(
-      typename basic_format_arg<buffer_context<Char>>::handle h) const {
-    h.format(parse_ctx, ctx);
-  }
-  template <typename T> void operator()(T) const {}
+template <typename Char>
+struct custom_formatter
+{
+    basic_format_parse_context<Char> &parse_ctx;
+    buffer_context<Char>             &ctx;
+
+    void operator()(typename basic_format_arg<buffer_context<Char>>::handle h) const { h.format(parse_ctx, ctx); }
+    template <typename T>
+    void operator()(T) const
+    {
+    }
 };
 
 template <typename T>
-using is_integer =
-    bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
-                  !std::is_same<T, char>::value &&
-                  !std::is_same<T, wchar_t>::value>;
-
-template <typename ErrorHandler> class width_checker {
- public:
-  explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
-
-  template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
-  FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
-    if (is_negative(value)) handler_.on_error("negative width");
-    return static_cast<unsigned long long>(value);
-  }
-
-  template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
-  FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
-    handler_.on_error("width is not integer");
-    return 0;
-  }
-
- private:
-  ErrorHandler& handler_;
-};
+using is_integer = bool_constant<
+    is_integral<T>::value && !std::is_same<T, bool>::value && !std::is_same<T, char>::value &&
+    !std::is_same<T, wchar_t>::value>;
+
+template <typename ErrorHandler>
+class width_checker
+{
+public:
+    explicit FMT_CONSTEXPR width_checker(ErrorHandler &eh) : handler_(eh) {}
+
+    template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
+    FMT_CONSTEXPR auto operator()(T value) -> unsigned long long
+    {
+        if(is_negative(value))
+            handler_.on_error("negative width");
+        return static_cast<unsigned long long>(value);
+    }
+
+    template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
+    FMT_CONSTEXPR auto operator()(T) -> unsigned long long
+    {
+        handler_.on_error("width is not integer");
+        return 0;
+    }
 
-template <typename ErrorHandler> class precision_checker {
- public:
-  explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {}
+private:
+    ErrorHandler &handler_;
+};
 
-  template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
-  FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
-    if (is_negative(value)) handler_.on_error("negative precision");
-    return static_cast<unsigned long long>(value);
-  }
+template <typename ErrorHandler>
+class precision_checker
+{
+public:
+    explicit FMT_CONSTEXPR precision_checker(ErrorHandler &eh) : handler_(eh) {}
+
+    template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
+    FMT_CONSTEXPR auto operator()(T value) -> unsigned long long
+    {
+        if(is_negative(value))
+            handler_.on_error("negative precision");
+        return static_cast<unsigned long long>(value);
+    }
 
-  template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
-  FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
-    handler_.on_error("precision is not integer");
-    return 0;
-  }
+    template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
+    FMT_CONSTEXPR auto operator()(T) -> unsigned long long
+    {
+        handler_.on_error("precision is not integer");
+        return 0;
+    }
 
- private:
-  ErrorHandler& handler_;
+private:
+    ErrorHandler &handler_;
 };
 
-template <template <typename> class Handler, typename FormatArg,
-          typename ErrorHandler>
-FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg, ErrorHandler eh) -> int {
-  unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
-  if (value > to_unsigned(max_value<int>())) eh.on_error("number is too big");
-  return static_cast<int>(value);
+template <template <typename> class Handler, typename FormatArg, typename ErrorHandler>
+FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg, ErrorHandler eh) -> int
+{
+    unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
+    if(value > to_unsigned(max_value<int>()))
+        eh.on_error("number is too big");
+    return static_cast<int>(value);
 }
 
 template <typename Context, typename ID>
-FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) ->
-    typename Context::format_arg {
-  auto arg = ctx.arg(id);
-  if (!arg) ctx.on_error("argument not found");
-  return arg;
+FMT_CONSTEXPR auto get_arg(Context &ctx, ID id) -> typename Context::format_arg
+{
+    auto arg = ctx.arg(id);
+    if(!arg)
+        ctx.on_error("argument not found");
+    return arg;
 }
 
 // The standard format specifier handler with checking.
-template <typename Char> class specs_handler : public specs_setter<Char> {
- private:
-  basic_format_parse_context<Char>& parse_context_;
-  buffer_context<Char>& context_;
-
-  // This is only needed for compatibility with gcc 4.4.
-  using format_arg = basic_format_arg<buffer_context<Char>>;
-
-  FMT_CONSTEXPR auto get_arg(auto_id) -> format_arg {
-    return detail::get_arg(context_, parse_context_.next_arg_id());
-  }
-
-  FMT_CONSTEXPR auto get_arg(int arg_id) -> format_arg {
-    parse_context_.check_arg_id(arg_id);
-    return detail::get_arg(context_, arg_id);
-  }
-
-  FMT_CONSTEXPR auto get_arg(basic_string_view<Char> arg_id) -> format_arg {
-    parse_context_.check_arg_id(arg_id);
-    return detail::get_arg(context_, arg_id);
-  }
-
- public:
-  FMT_CONSTEXPR specs_handler(basic_format_specs<Char>& specs,
-                              basic_format_parse_context<Char>& parse_ctx,
-                              buffer_context<Char>& ctx)
-      : specs_setter<Char>(specs), parse_context_(parse_ctx), context_(ctx) {}
-
-  template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
-    this->specs_.width = get_dynamic_spec<width_checker>(
-        get_arg(arg_id), context_.error_handler());
-  }
-
-  template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
-    this->specs_.precision = get_dynamic_spec<precision_checker>(
-        get_arg(arg_id), context_.error_handler());
-  }
-
-  void on_error(const char* message) { context_.on_error(message); }
+template <typename Char>
+class specs_handler : public specs_setter<Char>
+{
+private:
+    basic_format_parse_context<Char> &parse_context_;
+    buffer_context<Char>             &context_;
+
+    // This is only needed for compatibility with gcc 4.4.
+    using format_arg = basic_format_arg<buffer_context<Char>>;
+
+    FMT_CONSTEXPR auto get_arg(auto_id) -> format_arg
+    {
+        return detail::get_arg(context_, parse_context_.next_arg_id());
+    }
+
+    FMT_CONSTEXPR auto get_arg(int arg_id) -> format_arg
+    {
+        parse_context_.check_arg_id(arg_id);
+        return detail::get_arg(context_, arg_id);
+    }
+
+    FMT_CONSTEXPR auto get_arg(basic_string_view<Char> arg_id) -> format_arg
+    {
+        parse_context_.check_arg_id(arg_id);
+        return detail::get_arg(context_, arg_id);
+    }
+
+public:
+    FMT_CONSTEXPR specs_handler(
+        basic_format_specs<Char>         &specs,
+        basic_format_parse_context<Char> &parse_ctx,
+        buffer_context<Char>             &ctx) :
+        specs_setter<Char>(specs), parse_context_(parse_ctx), context_(ctx)
+    {
+    }
+
+    template <typename Id>
+    FMT_CONSTEXPR void on_dynamic_width(Id arg_id)
+    {
+        this->specs_.width = get_dynamic_spec<width_checker>(get_arg(arg_id), context_.error_handler());
+    }
+
+    template <typename Id>
+    FMT_CONSTEXPR void on_dynamic_precision(Id arg_id)
+    {
+        this->specs_.precision = get_dynamic_spec<precision_checker>(get_arg(arg_id), context_.error_handler());
+    }
+
+    void on_error(const char *message) { context_.on_error(message); }
 };
 
 template <template <typename> class Handler, typename Context>
-FMT_CONSTEXPR void handle_dynamic_spec(int& value,
-                                       arg_ref<typename Context::char_type> ref,
-                                       Context& ctx) {
-  switch (ref.kind) {
-  case arg_id_kind::none:
-    break;
-  case arg_id_kind::index:
-    value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.index),
-                                              ctx.error_handler());
-    break;
-  case arg_id_kind::name:
-    value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.name),
-                                              ctx.error_handler());
-    break;
-  }
+FMT_CONSTEXPR void handle_dynamic_spec(int &value, arg_ref<typename Context::char_type> ref, Context &ctx)
+{
+    switch(ref.kind)
+    {
+        case arg_id_kind::none:
+            break;
+        case arg_id_kind::index:
+            value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.index), ctx.error_handler());
+            break;
+        case arg_id_kind::name:
+            value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.name), ctx.error_handler());
+            break;
+    }
 }
 
 #if FMT_USE_USER_DEFINED_LITERALS
-template <typename Char> struct udl_formatter {
-  basic_string_view<Char> str;
-
-  template <typename... T>
-  auto operator()(T&&... args) const -> std::basic_string<Char> {
-    return vformat(str, fmt::make_format_args<buffer_context<Char>>(args...));
-  }
+template <typename Char>
+struct udl_formatter
+{
+    basic_string_view<Char> str;
+
+    template <typename... T>
+    auto operator()(T &&...args) const -> std::basic_string<Char>
+    {
+        return vformat(str, fmt::make_format_args<buffer_context<Char>>(args...));
+    }
 };
 
-#  if FMT_USE_NONTYPE_TEMPLATE_ARGS
-template <typename T, typename Char, size_t N,
-          fmt::detail_exported::fixed_string<Char, N> Str>
-struct statically_named_arg : view {
-  static constexpr auto name = Str.data;
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+template <typename T, typename Char, size_t N, fmt::detail_exported::fixed_string<Char, N> Str>
+struct statically_named_arg : view
+{
+    static constexpr auto name = Str.data;
+
+    const T &value;
+    statically_named_arg(const T &v) : value(v) {}
+};
 
-  const T& value;
-  statically_named_arg(const T& v) : value(v) {}
+template <typename T, typename Char, size_t N, fmt::detail_exported::fixed_string<Char, N> Str>
+struct is_named_arg<statically_named_arg<T, Char, N, Str>> : std::true_type
+{
 };
 
-template <typename T, typename Char, size_t N,
-          fmt::detail_exported::fixed_string<Char, N> Str>
-struct is_named_arg<statically_named_arg<T, Char, N, Str>> : std::true_type {};
-
-template <typename T, typename Char, size_t N,
-          fmt::detail_exported::fixed_string<Char, N> Str>
-struct is_statically_named_arg<statically_named_arg<T, Char, N, Str>>
-    : std::true_type {};
-
-template <typename Char, size_t N,
-          fmt::detail_exported::fixed_string<Char, N> Str>
-struct udl_arg {
-  template <typename T> auto operator=(T&& value) const {
-    return statically_named_arg<T, Char, N, Str>(std::forward<T>(value));
-  }
+template <typename T, typename Char, size_t N, fmt::detail_exported::fixed_string<Char, N> Str>
+struct is_statically_named_arg<statically_named_arg<T, Char, N, Str>> : std::true_type
+{
 };
-#  else
-template <typename Char> struct udl_arg {
-  const Char* str;
 
-  template <typename T> auto operator=(T&& value) const -> named_arg<Char, T> {
-    return {str, std::forward<T>(value)};
-  }
+template <typename Char, size_t N, fmt::detail_exported::fixed_string<Char, N> Str>
+struct udl_arg
+{
+    template <typename T>
+    auto operator=(T &&value) const
+    {
+        return statically_named_arg<T, Char, N, Str>(std::forward<T>(value));
+    }
 };
-#  endif
-#endif  // FMT_USE_USER_DEFINED_LITERALS
+#else
+template <typename Char>
+struct udl_arg
+{
+    const Char *str;
+
+    template <typename T>
+    auto operator=(T &&value) const -> named_arg<Char, T>
+    {
+        return {str, std::forward<T>(value)};
+    }
+};
+#endif
+#endif // FMT_USE_USER_DEFINED_LITERALS
 
 template <typename Locale, typename Char>
-auto vformat(const Locale& loc, basic_string_view<Char> format_str,
-             basic_format_args<buffer_context<type_identity_t<Char>>> args)
-    -> std::basic_string<Char> {
-  basic_memory_buffer<Char> buffer;
-  detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
-  return {buffer.data(), buffer.size()};
+auto vformat(
+    const Locale                                            &loc,
+    basic_string_view<Char>                                  format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args) -> std::basic_string<Char>
+{
+    basic_memory_buffer<Char> buffer;
+    detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
+    return {buffer.data(), buffer.size()};
 }
 
-using format_func = void (*)(detail::buffer<char>&, int, const char*);
+using format_func = void (*)(detail::buffer<char> &, int, const char *);
 
-FMT_API void format_error_code(buffer<char>& out, int error_code,
-                               string_view message) noexcept;
+FMT_API void format_error_code(buffer<char> &out, int error_code, string_view message) noexcept;
 
-FMT_API void report_error(format_func func, int error_code,
-                          const char* message) noexcept;
+FMT_API void report_error(format_func func, int error_code, const char *message) noexcept;
 FMT_END_DETAIL_NAMESPACE
 
-FMT_API auto vsystem_error(int error_code, string_view format_str,
-                           format_args args) -> std::system_error;
+FMT_API auto vsystem_error(int error_code, string_view format_str, format_args args) -> std::system_error;
 
 /**
  \rst
@@ -3619,9 +4117,9 @@ FMT_API auto vsystem_error(int error_code, string_view format_str,
  \endrst
 */
 template <typename... T>
-auto system_error(int error_code, format_string<T...> fmt, T&&... args)
-    -> std::system_error {
-  return vsystem_error(error_code, fmt, fmt::make_format_args(args...));
+auto system_error(int error_code, format_string<T...> fmt, T &&...args) -> std::system_error
+{
+    return vsystem_error(error_code, fmt, fmt::make_format_args(args...));
 }
 
 /**
@@ -3640,108 +4138,115 @@ auto system_error(int error_code, format_string<T...> fmt, T&&... args)
   *error_code* is a system error code as given by ``errno``.
   \endrst
  */
-FMT_API void format_system_error(detail::buffer<char>& out, int error_code,
-                                 const char* message) noexcept;
+FMT_API void format_system_error(detail::buffer<char> &out, int error_code, const char *message) noexcept;
 
 // Reports a system error without throwing an exception.
 // Can be used to report errors from destructors.
-FMT_API void report_system_error(int error_code, const char* message) noexcept;
+FMT_API void report_system_error(int error_code, const char *message) noexcept;
 
 /** Fast integer formatter. */
-class format_int {
- private:
-  // Buffer should be large enough to hold all digits (digits10 + 1),
-  // a sign and a null character.
-  enum { buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3 };
-  mutable char buffer_[buffer_size];
-  char* str_;
-
-  template <typename UInt> auto format_unsigned(UInt value) -> char* {
-    auto n = static_cast<detail::uint32_or_64_or_128_t<UInt>>(value);
-    return detail::format_decimal(buffer_, n, buffer_size - 1).begin;
-  }
-
-  template <typename Int> auto format_signed(Int value) -> char* {
-    auto abs_value = static_cast<detail::uint32_or_64_or_128_t<Int>>(value);
-    bool negative = value < 0;
-    if (negative) abs_value = 0 - abs_value;
-    auto begin = format_unsigned(abs_value);
-    if (negative) *--begin = '-';
-    return begin;
-  }
-
- public:
-  explicit format_int(int value) : str_(format_signed(value)) {}
-  explicit format_int(long value) : str_(format_signed(value)) {}
-  explicit format_int(long long value) : str_(format_signed(value)) {}
-  explicit format_int(unsigned value) : str_(format_unsigned(value)) {}
-  explicit format_int(unsigned long value) : str_(format_unsigned(value)) {}
-  explicit format_int(unsigned long long value)
-      : str_(format_unsigned(value)) {}
-
-  /** Returns the number of characters written to the output buffer. */
-  auto size() const -> size_t {
-    return detail::to_unsigned(buffer_ - str_ + buffer_size - 1);
-  }
-
-  /**
-    Returns a pointer to the output buffer content. No terminating null
-    character is appended.
-   */
-  auto data() const -> const char* { return str_; }
-
-  /**
-    Returns a pointer to the output buffer content with terminating null
-    character appended.
-   */
-  auto c_str() const -> const char* {
-    buffer_[buffer_size - 1] = '\0';
-    return str_;
-  }
-
-  /**
-    \rst
-    Returns the content of the output buffer as an ``std::string``.
-    \endrst
-   */
-  auto str() const -> std::string { return std::string(str_, size()); }
+class format_int
+{
+private:
+    // Buffer should be large enough to hold all digits (digits10 + 1),
+    // a sign and a null character.
+    enum
+    {
+        buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3
+    };
+    mutable char buffer_[buffer_size];
+    char        *str_;
+
+    template <typename UInt>
+    auto format_unsigned(UInt value) -> char *
+    {
+        auto n = static_cast<detail::uint32_or_64_or_128_t<UInt>>(value);
+        return detail::format_decimal(buffer_, n, buffer_size - 1).begin;
+    }
+
+    template <typename Int>
+    auto format_signed(Int value) -> char *
+    {
+        auto abs_value = static_cast<detail::uint32_or_64_or_128_t<Int>>(value);
+        bool negative  = value < 0;
+        if(negative)
+            abs_value = 0 - abs_value;
+        auto begin = format_unsigned(abs_value);
+        if(negative)
+            *--begin = '-';
+        return begin;
+    }
+
+public:
+    explicit format_int(int value) : str_(format_signed(value)) {}
+    explicit format_int(long value) : str_(format_signed(value)) {}
+    explicit format_int(long long value) : str_(format_signed(value)) {}
+    explicit format_int(unsigned value) : str_(format_unsigned(value)) {}
+    explicit format_int(unsigned long value) : str_(format_unsigned(value)) {}
+    explicit format_int(unsigned long long value) : str_(format_unsigned(value)) {}
+
+    /** Returns the number of characters written to the output buffer. */
+    auto size() const -> size_t { return detail::to_unsigned(buffer_ - str_ + buffer_size - 1); }
+
+    /**
+      Returns a pointer to the output buffer content. No terminating null
+      character is appended.
+     */
+    auto data() const -> const char * { return str_; }
+
+    /**
+      Returns a pointer to the output buffer content with terminating null
+      character appended.
+     */
+    auto c_str() const -> const char *
+    {
+        buffer_[buffer_size - 1] = '\0';
+        return str_;
+    }
+
+    /**
+      \rst
+      Returns the content of the output buffer as an ``std::string``.
+      \endrst
+     */
+    auto str() const -> std::string { return std::string(str_, size()); }
 };
 
 template <typename T, typename Char>
 template <typename FormatContext>
 FMT_CONSTEXPR FMT_INLINE auto
-formatter<T, Char,
-          enable_if_t<detail::type_constant<T, Char>::value !=
-                      detail::type::custom_type>>::format(const T& val,
-                                                          FormatContext& ctx)
-    const -> decltype(ctx.out()) {
-  if (specs_.width_ref.kind != detail::arg_id_kind::none ||
-      specs_.precision_ref.kind != detail::arg_id_kind::none) {
-    auto specs = specs_;
-    detail::handle_dynamic_spec<detail::width_checker>(specs.width,
-                                                       specs.width_ref, ctx);
-    detail::handle_dynamic_spec<detail::precision_checker>(
-        specs.precision, specs.precision_ref, ctx);
-    return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
-  }
-  return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
+formatter<T, Char, enable_if_t<detail::type_constant<T, Char>::value != detail::type::custom_type>>::format(
+    const T       &val,
+    FormatContext &ctx) const -> decltype(ctx.out())
+{
+    if(specs_.width_ref.kind != detail::arg_id_kind::none || specs_.precision_ref.kind != detail::arg_id_kind::none)
+    {
+        auto specs = specs_;
+        detail::handle_dynamic_spec<detail::width_checker>(specs.width, specs.width_ref, ctx);
+        detail::handle_dynamic_spec<detail::precision_checker>(specs.precision, specs.precision_ref, ctx);
+        return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
+    }
+    return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
 }
 
 template <typename Char>
-struct formatter<void*, Char> : formatter<const void*, Char> {
-  template <typename FormatContext>
-  auto format(void* val, FormatContext& ctx) const -> decltype(ctx.out()) {
-    return formatter<const void*, Char>::format(val, ctx);
-  }
+struct formatter<void *, Char> : formatter<const void *, Char>
+{
+    template <typename FormatContext>
+    auto format(void *val, FormatContext &ctx) const -> decltype(ctx.out())
+    {
+        return formatter<const void *, Char>::format(val, ctx);
+    }
 };
 
 template <typename Char, size_t N>
-struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {
-  template <typename FormatContext>
-  FMT_CONSTEXPR auto format(const Char* val, FormatContext& ctx) const
-      -> decltype(ctx.out()) {
-    return formatter<basic_string_view<Char>, Char>::format(val, ctx);
-  }
+struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char>
+{
+    template <typename FormatContext>
+    FMT_CONSTEXPR auto format(const Char *val, FormatContext &ctx) const -> decltype(ctx.out())
+    {
+        return formatter<basic_string_view<Char>, Char>::format(val, ctx);
+    }
 };
 
 // A formatter for types known only at run time such as variant alternatives.
@@ -3756,44 +4261,52 @@ struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {
 //       }, v);
 //     }
 //   };
-template <typename Char = char> class dynamic_formatter {
- private:
-  detail::dynamic_format_specs<Char> specs_;
-  const Char* format_str_;
-
-  struct null_handler : detail::error_handler {
-    void on_align(align_t) {}
-    void on_sign(sign_t) {}
-    void on_hash() {}
-  };
-
-  template <typename Context> void handle_specs(Context& ctx) {
-    detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
-                                                       specs_.width_ref, ctx);
-    detail::handle_dynamic_spec<detail::precision_checker>(
-        specs_.precision, specs_.precision_ref, ctx);
-  }
-
- public:
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    format_str_ = ctx.begin();
-    // Checks are deferred to formatting time when the argument type is known.
-    detail::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
-    return detail::parse_format_specs(ctx.begin(), ctx.end(), handler);
-  }
-
-  template <typename T, typename FormatContext>
-  auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
-    handle_specs(ctx);
-    detail::specs_checker<null_handler> checker(
-        null_handler(), detail::mapped_type_constant<T, FormatContext>::value);
-    checker.on_align(specs_.align);
-    if (specs_.sign != sign::none) checker.on_sign(specs_.sign);
-    if (specs_.alt) checker.on_hash();
-    if (specs_.precision >= 0) checker.end_precision();
-    return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
-  }
+template <typename Char = char>
+class dynamic_formatter
+{
+private:
+    detail::dynamic_format_specs<Char> specs_;
+    const Char                        *format_str_;
+
+    struct null_handler : detail::error_handler
+    {
+        void on_align(align_t) {}
+        void on_sign(sign_t) {}
+        void on_hash() {}
+    };
+
+    template <typename Context>
+    void handle_specs(Context &ctx)
+    {
+        detail::handle_dynamic_spec<detail::width_checker>(specs_.width, specs_.width_ref, ctx);
+        detail::handle_dynamic_spec<detail::precision_checker>(specs_.precision, specs_.precision_ref, ctx);
+    }
+
+public:
+    template <typename ParseContext>
+    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
+    {
+        format_str_ = ctx.begin();
+        // Checks are deferred to formatting time when the argument type is known.
+        detail::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
+        return detail::parse_format_specs(ctx.begin(), ctx.end(), handler);
+    }
+
+    template <typename T, typename FormatContext>
+    auto format(const T &val, FormatContext &ctx) -> decltype(ctx.out())
+    {
+        handle_specs(ctx);
+        detail::specs_checker<null_handler> checker(
+            null_handler(), detail::mapped_type_constant<T, FormatContext>::value);
+        checker.on_align(specs_.align);
+        if(specs_.sign != sign::none)
+            checker.on_sign(specs_.sign);
+        if(specs_.alt)
+            checker.on_hash();
+        if(specs_.precision >= 0)
+            checker.end_precision();
+        return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
+    }
 };
 
 /**
@@ -3805,15 +4318,21 @@ template <typename Char = char> class dynamic_formatter {
     auto s = fmt::format("{}", fmt::ptr(p));
   \endrst
  */
-template <typename T> auto ptr(T p) -> const void* {
-  static_assert(std::is_pointer<T>::value, "");
-  return detail::bit_cast<const void*>(p);
+template <typename T>
+auto ptr(T p) -> const void *
+{
+    static_assert(std::is_pointer<T>::value, "");
+    return detail::bit_cast<const void *>(p);
 }
-template <typename T> auto ptr(const std::unique_ptr<T>& p) -> const void* {
-  return p.get();
+template <typename T>
+auto ptr(const std::unique_ptr<T> &p) -> const void *
+{
+    return p.get();
 }
-template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
-  return p.get();
+template <typename T>
+auto ptr(const std::shared_ptr<T> &p) -> const void *
+{
+    return p.get();
 }
 
 /**
@@ -3827,53 +4346,62 @@ template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
   \endrst
  */
 template <typename Enum>
-constexpr auto underlying(Enum e) noexcept -> underlying_t<Enum> {
-  return static_cast<underlying_t<Enum>>(e);
+constexpr auto underlying(Enum e) noexcept -> underlying_t<Enum>
+{
+    return static_cast<underlying_t<Enum>>(e);
 }
 
-namespace enums {
+namespace enums
+{
 template <typename Enum, FMT_ENABLE_IF(std::is_enum<Enum>::value)>
-constexpr auto format_as(Enum e) noexcept -> underlying_t<Enum> {
-  return static_cast<underlying_t<Enum>>(e);
+constexpr auto format_as(Enum e) noexcept -> underlying_t<Enum>
+{
+    return static_cast<underlying_t<Enum>>(e);
 }
-}  // namespace enums
+} // namespace enums
 
-class bytes {
- private:
-  string_view data_;
-  friend struct formatter<bytes>;
+class bytes
+{
+private:
+    string_view data_;
+    friend struct formatter<bytes>;
 
- public:
-  explicit bytes(string_view data) : data_(data) {}
+public:
+    explicit bytes(string_view data) : data_(data) {}
 };
 
-template <> struct formatter<bytes> {
- private:
-  detail::dynamic_format_specs<char> specs_;
-
- public:
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    using handler_type = detail::dynamic_specs_handler<ParseContext>;
-    detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
-                                                detail::type::string_type);
-    auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
-    detail::check_string_type_spec(specs_.type, ctx.error_handler());
-    return it;
-  }
-
-  template <typename FormatContext>
-  auto format(bytes b, FormatContext& ctx) -> decltype(ctx.out()) {
-    detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
-                                                       specs_.width_ref, ctx);
-    detail::handle_dynamic_spec<detail::precision_checker>(
-        specs_.precision, specs_.precision_ref, ctx);
-    return detail::write_bytes(ctx.out(), b.data_, specs_);
-  }
+template <>
+struct formatter<bytes>
+{
+private:
+    detail::dynamic_format_specs<char> specs_;
+
+public:
+    template <typename ParseContext>
+    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
+    {
+        using handler_type = detail::dynamic_specs_handler<ParseContext>;
+        detail::specs_checker<handler_type> handler(handler_type(specs_, ctx), detail::type::string_type);
+        auto                                it = parse_format_specs(ctx.begin(), ctx.end(), handler);
+        detail::check_string_type_spec(specs_.type, ctx.error_handler());
+        return it;
+    }
+
+    template <typename FormatContext>
+    auto format(bytes b, FormatContext &ctx) -> decltype(ctx.out())
+    {
+        detail::handle_dynamic_spec<detail::width_checker>(specs_.width, specs_.width_ref, ctx);
+        detail::handle_dynamic_spec<detail::precision_checker>(specs_.precision, specs_.precision_ref, ctx);
+        return detail::write_bytes(ctx.out(), b.data_, specs_);
+    }
 };
 
 // group_digits_view is not derived from view because it copies the argument.
-template <typename T> struct group_digits_view { T value; };
+template <typename T>
+struct group_digits_view
+{
+    T value;
+};
 
 /**
   \rst
@@ -3886,101 +4414,110 @@ template <typename T> struct group_digits_view { T value; };
     // Output: "12,345"
   \endrst
  */
-template <typename T> auto group_digits(T value) -> group_digits_view<T> {
-  return {value};
-}
-
-template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
- private:
-  detail::dynamic_format_specs<char> specs_;
-
- public:
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    using handler_type = detail::dynamic_specs_handler<ParseContext>;
-    detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
-                                                detail::type::int_type);
-    auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
-    detail::check_string_type_spec(specs_.type, ctx.error_handler());
-    return it;
-  }
-
-  template <typename FormatContext>
-  auto format(group_digits_view<T> t, FormatContext& ctx)
-      -> decltype(ctx.out()) {
-    detail::handle_dynamic_spec<detail::width_checker>(specs_.width,
-                                                       specs_.width_ref, ctx);
-    detail::handle_dynamic_spec<detail::precision_checker>(
-        specs_.precision, specs_.precision_ref, ctx);
-    return detail::write_int_localized(
-        ctx.out(), static_cast<detail::uint64_or_128_t<T>>(t.value), 0, specs_,
-        detail::digit_grouping<char>({"\3", ','}));
-  }
+template <typename T>
+auto group_digits(T value) -> group_digits_view<T>
+{
+    return {value};
+}
+
+template <typename T>
+struct formatter<group_digits_view<T>> : formatter<T>
+{
+private:
+    detail::dynamic_format_specs<char> specs_;
+
+public:
+    template <typename ParseContext>
+    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
+    {
+        using handler_type = detail::dynamic_specs_handler<ParseContext>;
+        detail::specs_checker<handler_type> handler(handler_type(specs_, ctx), detail::type::int_type);
+        auto                                it = parse_format_specs(ctx.begin(), ctx.end(), handler);
+        detail::check_string_type_spec(specs_.type, ctx.error_handler());
+        return it;
+    }
+
+    template <typename FormatContext>
+    auto format(group_digits_view<T> t, FormatContext &ctx) -> decltype(ctx.out())
+    {
+        detail::handle_dynamic_spec<detail::width_checker>(specs_.width, specs_.width_ref, ctx);
+        detail::handle_dynamic_spec<detail::precision_checker>(specs_.precision, specs_.precision_ref, ctx);
+        return detail::write_int_localized(
+            ctx.out(),
+            static_cast<detail::uint64_or_128_t<T>>(t.value),
+            0,
+            specs_,
+            detail::digit_grouping<char>({"\3", ','}));
+    }
 };
 
 template <typename It, typename Sentinel, typename Char = char>
-struct join_view : detail::view {
-  It begin;
-  Sentinel end;
-  basic_string_view<Char> sep;
+struct join_view : detail::view
+{
+    It                      begin;
+    Sentinel                end;
+    basic_string_view<Char> sep;
 
-  join_view(It b, Sentinel e, basic_string_view<Char> s)
-      : begin(b), end(e), sep(s) {}
+    join_view(It b, Sentinel e, basic_string_view<Char> s) : begin(b), end(e), sep(s) {}
 };
 
 template <typename It, typename Sentinel, typename Char>
-struct formatter<join_view<It, Sentinel, Char>, Char> {
- private:
-  using value_type =
+struct formatter<join_view<It, Sentinel, Char>, Char>
+{
+private:
+    using value_type =
 #ifdef __cpp_lib_ranges
-      std::iter_value_t<It>;
+        std::iter_value_t<It>;
 #else
-      typename std::iterator_traits<It>::value_type;
+        typename std::iterator_traits<It>::value_type;
 #endif
-  using context = buffer_context<Char>;
-  using mapper = detail::arg_mapper<context>;
+    using context = buffer_context<Char>;
+    using mapper  = detail::arg_mapper<context>;
 
-  template <typename T, FMT_ENABLE_IF(has_formatter<T, context>::value)>
-  static auto map(const T& value) -> const T& {
-    return value;
-  }
-  template <typename T, FMT_ENABLE_IF(!has_formatter<T, context>::value)>
-  static auto map(const T& value) -> decltype(mapper().map(value)) {
-    return mapper().map(value);
-  }
-
-  using formatter_type =
-      conditional_t<is_formattable<value_type, Char>::value,
-                    formatter<remove_cvref_t<decltype(map(
-                                  std::declval<const value_type&>()))>,
-                              Char>,
-                    detail::fallback_formatter<value_type, Char>>;
-
-  formatter_type value_formatter_;
-
- public:
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    return value_formatter_.parse(ctx);
-  }
-
-  template <typename FormatContext>
-  auto format(const join_view<It, Sentinel, Char>& value,
-              FormatContext& ctx) const -> decltype(ctx.out()) {
-    auto it = value.begin;
-    auto out = ctx.out();
-    if (it != value.end) {
-      out = value_formatter_.format(map(*it), ctx);
-      ++it;
-      while (it != value.end) {
-        out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
-        ctx.advance_to(out);
-        out = value_formatter_.format(map(*it), ctx);
-        ++it;
-      }
+    template <typename T, FMT_ENABLE_IF(has_formatter<T, context>::value)>
+    static auto map(const T &value) -> const T &
+    {
+        return value;
+    }
+    template <typename T, FMT_ENABLE_IF(!has_formatter<T, context>::value)>
+    static auto map(const T &value) -> decltype(mapper().map(value))
+    {
+        return mapper().map(value);
+    }
+
+    using formatter_type = conditional_t<
+        is_formattable<value_type, Char>::value,
+        formatter<remove_cvref_t<decltype(map(std::declval<const value_type &>()))>, Char>,
+        detail::fallback_formatter<value_type, Char>>;
+
+    formatter_type value_formatter_;
+
+public:
+    template <typename ParseContext>
+    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
+    {
+        return value_formatter_.parse(ctx);
+    }
+
+    template <typename FormatContext>
+    auto format(const join_view<It, Sentinel, Char> &value, FormatContext &ctx) const -> decltype(ctx.out())
+    {
+        auto it  = value.begin;
+        auto out = ctx.out();
+        if(it != value.end)
+        {
+            out = value_formatter_.format(map(*it), ctx);
+            ++it;
+            while(it != value.end)
+            {
+                out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
+                ctx.advance_to(out);
+                out = value_formatter_.format(map(*it), ctx);
+                ++it;
+            }
+        }
+        return out;
     }
-    return out;
-  }
 };
 
 /**
@@ -3988,8 +4525,9 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
   separated by `sep`.
  */
 template <typename It, typename Sentinel>
-auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
-  return {begin, end, sep};
+auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel>
+{
+    return {begin, end, sep};
 }
 
 /**
@@ -4009,9 +4547,9 @@ auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
   \endrst
  */
 template <typename Range>
-auto join(Range&& range, string_view sep)
-    -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>> {
-  return join(std::begin(range), std::end(range), sep);
+auto join(Range &&range, string_view sep) -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>>
+{
+    return join(std::begin(range), std::end(range), sep);
 }
 
 /**
@@ -4026,129 +4564,136 @@ auto join(Range&& range, string_view sep)
   \endrst
  */
 template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-inline auto to_string(const T& value) -> std::string {
-  auto result = std::string();
-  detail::write<char>(std::back_inserter(result), value);
-  return result;
+inline auto to_string(const T &value) -> std::string
+{
+    auto result = std::string();
+    detail::write<char>(std::back_inserter(result), value);
+    return result;
 }
 
 template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-FMT_NODISCARD inline auto to_string(T value) -> std::string {
-  // The buffer should be large enough to store the number including the sign
-  // or "false" for bool.
-  constexpr int max_size = detail::digits10<T>() + 2;
-  char buffer[max_size > 5 ? static_cast<unsigned>(max_size) : 5];
-  char* begin = buffer;
-  return std::string(begin, detail::write<char>(begin, value));
+FMT_NODISCARD inline auto to_string(T value) -> std::string
+{
+    // The buffer should be large enough to store the number including the sign
+    // or "false" for bool.
+    constexpr int max_size = detail::digits10<T>() + 2;
+    char          buffer[max_size > 5 ? static_cast<unsigned>(max_size) : 5];
+    char         *begin = buffer;
+    return std::string(begin, detail::write<char>(begin, value));
 }
 
 template <typename Char, size_t SIZE>
-FMT_NODISCARD auto to_string(const basic_memory_buffer<Char, SIZE>& buf)
-    -> std::basic_string<Char> {
-  auto size = buf.size();
-  detail::assume(size < std::basic_string<Char>().max_size());
-  return std::basic_string<Char>(buf.data(), size);
+FMT_NODISCARD auto to_string(const basic_memory_buffer<Char, SIZE> &buf) -> std::basic_string<Char>
+{
+    auto size = buf.size();
+    detail::assume(size < std::basic_string<Char>().max_size());
+    return std::basic_string<Char>(buf.data(), size);
 }
 
 FMT_BEGIN_DETAIL_NAMESPACE
 
 template <typename Char>
 void vformat_to(
-    buffer<Char>& buf, basic_string_view<Char> fmt,
+    buffer<Char>                                                &buf,
+    basic_string_view<Char>                                      fmt,
     basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
-    locale_ref loc) {
-  // workaround for msvc bug regarding name-lookup in module
-  // link names into function scope
-  using detail::arg_formatter;
-  using detail::buffer_appender;
-  using detail::custom_formatter;
-  using detail::default_arg_formatter;
-  using detail::get_arg;
-  using detail::locale_ref;
-  using detail::parse_format_specs;
-  using detail::specs_checker;
-  using detail::specs_handler;
-  using detail::to_unsigned;
-  using detail::type;
-  using detail::write;
-  auto out = buffer_appender<Char>(buf);
-  if (fmt.size() == 2 && equal2(fmt.data(), "{}")) {
-    auto arg = args.get(0);
-    if (!arg) error_handler().on_error("argument not found");
-    visit_format_arg(default_arg_formatter<Char>{out, args, loc}, arg);
-    return;
-  }
-
-  struct format_handler : error_handler {
-    basic_format_parse_context<Char> parse_context;
-    buffer_context<Char> context;
-
-    format_handler(buffer_appender<Char> p_out, basic_string_view<Char> str,
-                   basic_format_args<buffer_context<Char>> p_args,
-                   locale_ref p_loc)
-        : parse_context(str), context(p_out, p_args, p_loc) {}
-
-    void on_text(const Char* begin, const Char* end) {
-      auto text = basic_string_view<Char>(begin, to_unsigned(end - begin));
-      context.advance_to(write<Char>(context.out(), text));
-    }
-
-    FMT_CONSTEXPR auto on_arg_id() -> int {
-      return parse_context.next_arg_id();
-    }
-    FMT_CONSTEXPR auto on_arg_id(int id) -> int {
-      return parse_context.check_arg_id(id), id;
-    }
-    FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
-      int arg_id = context.arg_id(id);
-      if (arg_id < 0) on_error("argument not found");
-      return arg_id;
-    }
-
-    FMT_INLINE void on_replacement_field(int id, const Char*) {
-      auto arg = get_arg(context, id);
-      context.advance_to(visit_format_arg(
-          default_arg_formatter<Char>{context.out(), context.args(),
-                                      context.locale()},
-          arg));
-    }
-
-    auto on_format_specs(int id, const Char* begin, const Char* end)
-        -> const Char* {
-      auto arg = get_arg(context, id);
-      if (arg.type() == type::custom_type) {
-        parse_context.advance_to(parse_context.begin() +
-                                 (begin - &*parse_context.begin()));
-        visit_format_arg(custom_formatter<Char>{parse_context, context}, arg);
-        return parse_context.begin();
-      }
-      auto specs = basic_format_specs<Char>();
-      specs_checker<specs_handler<Char>> handler(
-          specs_handler<Char>(specs, parse_context, context), arg.type());
-      begin = parse_format_specs(begin, end, handler);
-      if (begin == end || *begin != '}')
-        on_error("missing '}' in format string");
-      auto f = arg_formatter<Char>{context.out(), specs, context.locale()};
-      context.advance_to(visit_format_arg(f, arg));
-      return begin;
-    }
-  };
-  detail::parse_format_string<false>(fmt, format_handler(out, fmt, args, loc));
+    locale_ref                                                   loc)
+{
+    // workaround for msvc bug regarding name-lookup in module
+    // link names into function scope
+    using detail::arg_formatter;
+    using detail::buffer_appender;
+    using detail::custom_formatter;
+    using detail::default_arg_formatter;
+    using detail::get_arg;
+    using detail::locale_ref;
+    using detail::parse_format_specs;
+    using detail::specs_checker;
+    using detail::specs_handler;
+    using detail::to_unsigned;
+    using detail::type;
+    using detail::write;
+    auto out = buffer_appender<Char>(buf);
+    if(fmt.size() == 2 && equal2(fmt.data(), "{}"))
+    {
+        auto arg = args.get(0);
+        if(!arg)
+            error_handler().on_error("argument not found");
+        visit_format_arg(default_arg_formatter<Char>{out, args, loc}, arg);
+        return;
+    }
+
+    struct format_handler : error_handler
+    {
+        basic_format_parse_context<Char> parse_context;
+        buffer_context<Char>             context;
+
+        format_handler(
+            buffer_appender<Char>                   p_out,
+            basic_string_view<Char>                 str,
+            basic_format_args<buffer_context<Char>> p_args,
+            locale_ref                              p_loc) :
+            parse_context(str), context(p_out, p_args, p_loc)
+        {
+        }
+
+        void on_text(const Char *begin, const Char *end)
+        {
+            auto text = basic_string_view<Char>(begin, to_unsigned(end - begin));
+            context.advance_to(write<Char>(context.out(), text));
+        }
+
+        FMT_CONSTEXPR auto on_arg_id() -> int { return parse_context.next_arg_id(); }
+        FMT_CONSTEXPR auto on_arg_id(int id) -> int { return parse_context.check_arg_id(id), id; }
+        FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int
+        {
+            int arg_id = context.arg_id(id);
+            if(arg_id < 0)
+                on_error("argument not found");
+            return arg_id;
+        }
+
+        FMT_INLINE void on_replacement_field(int id, const Char *)
+        {
+            auto arg = get_arg(context, id);
+            context.advance_to(
+                visit_format_arg(default_arg_formatter<Char>{context.out(), context.args(), context.locale()}, arg));
+        }
+
+        auto on_format_specs(int id, const Char *begin, const Char *end) -> const Char *
+        {
+            auto arg = get_arg(context, id);
+            if(arg.type() == type::custom_type)
+            {
+                parse_context.advance_to(parse_context.begin() + (begin - &*parse_context.begin()));
+                visit_format_arg(custom_formatter<Char>{parse_context, context}, arg);
+                return parse_context.begin();
+            }
+            auto                               specs = basic_format_specs<Char>();
+            specs_checker<specs_handler<Char>> handler(specs_handler<Char>(specs, parse_context, context), arg.type());
+            begin = parse_format_specs(begin, end, handler);
+            if(begin == end || *begin != '}')
+                on_error("missing '}' in format string");
+            auto f = arg_formatter<Char>{context.out(), specs, context.locale()};
+            context.advance_to(visit_format_arg(f, arg));
+            return begin;
+        }
+    };
+    detail::parse_format_string<false>(fmt, format_handler(out, fmt, args, loc));
 }
 
 #ifndef FMT_HEADER_ONLY
-extern template FMT_API auto thousands_sep_impl<char>(locale_ref)
-    -> thousands_sep_result<char>;
-extern template FMT_API auto thousands_sep_impl<wchar_t>(locale_ref)
-    -> thousands_sep_result<wchar_t>;
+extern template FMT_API auto thousands_sep_impl<char>(locale_ref) -> thousands_sep_result<char>;
+extern template FMT_API auto thousands_sep_impl<wchar_t>(locale_ref) -> thousands_sep_result<wchar_t>;
 extern template FMT_API auto decimal_point_impl(locale_ref) -> char;
 extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
-#endif  // FMT_HEADER_ONLY
+#endif // FMT_HEADER_ONLY
 
 FMT_END_DETAIL_NAMESPACE
 
 #if FMT_USE_USER_DEFINED_LITERALS
-inline namespace literals {
+inline namespace literals
+{
 /**
   \rst
   User-defined literal equivalent of :func:`fmt::arg`.
@@ -4159,59 +4704,64 @@ inline namespace literals {
     fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
   \endrst
  */
-#  if FMT_USE_NONTYPE_TEMPLATE_ARGS
-template <detail_exported::fixed_string Str> constexpr auto operator""_a() {
-  using char_t = remove_cvref_t<decltype(Str.data[0])>;
-  return detail::udl_arg<char_t, sizeof(Str.data) / sizeof(char_t), Str>();
+#if FMT_USE_NONTYPE_TEMPLATE_ARGS
+template <detail_exported::fixed_string Str>
+constexpr auto operator""_a()
+{
+    using char_t = remove_cvref_t<decltype(Str.data[0])>;
+    return detail::udl_arg<char_t, sizeof(Str.data) / sizeof(char_t), Str>();
 }
-#  else
-constexpr auto operator"" _a(const char* s, size_t) -> detail::udl_arg<char> {
-  return {s};
+#else
+constexpr auto operator"" _a(const char *s, size_t) -> detail::udl_arg<char>
+{
+    return {s};
 }
-#  endif
-}  // namespace literals
-#endif  // FMT_USE_USER_DEFINED_LITERALS
+#endif
+} // namespace literals
+#endif // FMT_USE_USER_DEFINED_LITERALS
 
 template <typename Locale, FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
-inline auto vformat(const Locale& loc, string_view fmt, format_args args)
-    -> std::string {
-  return detail::vformat(loc, fmt, args);
+inline auto vformat(const Locale &loc, string_view fmt, format_args args) -> std::string
+{
+    return detail::vformat(loc, fmt, args);
 }
 
-template <typename Locale, typename... T,
-          FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
-inline auto format(const Locale& loc, format_string<T...> fmt, T&&... args)
-    -> std::string {
-  return vformat(loc, string_view(fmt), fmt::make_format_args(args...));
+template <typename Locale, typename... T, FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
+inline auto format(const Locale &loc, format_string<T...> fmt, T &&...args) -> std::string
+{
+    return vformat(loc, string_view(fmt), fmt::make_format_args(args...));
 }
 
-template <typename OutputIt, typename Locale,
-          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&&
-                            detail::is_locale<Locale>::value)>
-auto vformat_to(OutputIt out, const Locale& loc, string_view fmt,
-                format_args args) -> OutputIt {
-  using detail::get_buffer;
-  auto&& buf = get_buffer<char>(out);
-  detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
-  return detail::get_iterator(buf);
+template <
+    typename OutputIt,
+    typename Locale,
+    FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value &&detail::is_locale<Locale>::value)>
+auto vformat_to(OutputIt out, const Locale &loc, string_view fmt, format_args args) -> OutputIt
+{
+    using detail::get_buffer;
+    auto &&buf = get_buffer<char>(out);
+    detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
+    return detail::get_iterator(buf);
 }
 
-template <typename OutputIt, typename Locale, typename... T,
-          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value&&
-                            detail::is_locale<Locale>::value)>
-FMT_INLINE auto format_to(OutputIt out, const Locale& loc,
-                          format_string<T...> fmt, T&&... args) -> OutputIt {
-  return vformat_to(out, loc, fmt, fmt::make_format_args(args...));
+template <
+    typename OutputIt,
+    typename Locale,
+    typename... T,
+    FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value &&detail::is_locale<Locale>::value)>
+FMT_INLINE auto format_to(OutputIt out, const Locale &loc, format_string<T...> fmt, T &&...args) -> OutputIt
+{
+    return vformat_to(out, loc, fmt, fmt::make_format_args(args...));
 }
 
 FMT_MODULE_EXPORT_END
 FMT_END_NAMESPACE
 
 #ifdef FMT_HEADER_ONLY
-#  define FMT_FUNC inline
-#  include "format-inl.h"
+#define FMT_FUNC inline
+#include "format-inl.h"
 #else
-#  define FMT_FUNC
+#define FMT_FUNC
 #endif
 
-#endif  // FMT_FORMAT_H_
+#endif // FMT_FORMAT_H_
diff --git a/include/spdlog/fmt/bundled/os.h b/include/spdlog/fmt/bundled/os.h
index d82be1125..417c09e35 100644
--- a/include/spdlog/fmt/bundled/os.h
+++ b/include/spdlog/fmt/bundled/os.h
@@ -11,61 +11,60 @@
 #include <cerrno>
 #include <cstddef>
 #include <cstdio>
-#include <system_error>  // std::system_error
+#include <system_error> // std::system_error
 
 #if defined __APPLE__ || defined(__FreeBSD__)
-#  include <xlocale.h>  // for LC_NUMERIC_MASK on OS X
+#include <xlocale.h> // for LC_NUMERIC_MASK on OS X
 #endif
 
 #include "format.h"
 
 #ifndef FMT_USE_FCNTL
 // UWP doesn't provide _pipe.
-#  if FMT_HAS_INCLUDE("winapifamily.h")
-#    include <winapifamily.h>
-#  endif
-#  if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
-       defined(__linux__)) &&                              \
-      (!defined(WINAPI_FAMILY) ||                          \
-       (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
-#    include <fcntl.h>  // for O_RDONLY
-#    define FMT_USE_FCNTL 1
-#  else
-#    define FMT_USE_FCNTL 0
-#  endif
+#if FMT_HAS_INCLUDE("winapifamily.h")
+#include <winapifamily.h>
+#endif
+#if(FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || defined(__linux__)) &&                                         \
+    (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
+#include <fcntl.h> // for O_RDONLY
+#define FMT_USE_FCNTL 1
+#else
+#define FMT_USE_FCNTL 0
+#endif
 #endif
 
 #ifndef FMT_POSIX
-#  if defined(_WIN32) && !defined(__MINGW32__)
+#if defined(_WIN32) && !defined(__MINGW32__)
 // Fix warnings about deprecated symbols.
-#    define FMT_POSIX(call) _##call
-#  else
-#    define FMT_POSIX(call) call
-#  endif
+#define FMT_POSIX(call) _##call
+#else
+#define FMT_POSIX(call) call
+#endif
 #endif
 
 // Calls to system functions are wrapped in FMT_SYSTEM for testability.
 #ifdef FMT_SYSTEM
-#  define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
+#define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
 #else
-#  define FMT_SYSTEM(call) ::call
-#  ifdef _WIN32
+#define FMT_SYSTEM(call) ::call
+#ifdef _WIN32
 // Fix warnings about deprecated symbols.
-#    define FMT_POSIX_CALL(call) ::_##call
-#  else
-#    define FMT_POSIX_CALL(call) ::call
-#  endif
+#define FMT_POSIX_CALL(call) ::_##call
+#else
+#define FMT_POSIX_CALL(call) ::call
+#endif
 #endif
 
 // Retries the expression while it evaluates to error_result and errno
 // equals to EINTR.
 #ifndef _WIN32
-#  define FMT_RETRY_VAL(result, expression, error_result) \
-    do {                                                  \
-      (result) = (expression);                            \
-    } while ((result) == (error_result) && errno == EINTR)
+#define FMT_RETRY_VAL(result, expression, error_result)                                                                \
+    do                                                                                                                 \
+    {                                                                                                                  \
+        (result) = (expression);                                                                                       \
+    } while((result) == (error_result) && errno == EINTR)
 #else
-#  define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
+#define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
 #endif
 
 #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
@@ -98,76 +97,79 @@ FMT_MODULE_EXPORT_BEGIN
     format(std::string("{}"), 42);
   \endrst
  */
-template <typename Char> class basic_cstring_view {
- private:
-  const Char* data_;
-
- public:
-  /** Constructs a string reference object from a C string. */
-  basic_cstring_view(const Char* s) : data_(s) {}
-
-  /**
-    \rst
-    Constructs a string reference from an ``std::string`` object.
-    \endrst
-   */
-  basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
-
-  /** Returns the pointer to a C string. */
-  const Char* c_str() const { return data_; }
+template <typename Char>
+class basic_cstring_view
+{
+private:
+    const Char *data_;
+
+public:
+    /** Constructs a string reference object from a C string. */
+    basic_cstring_view(const Char *s) : data_(s) {}
+
+    /**
+      \rst
+      Constructs a string reference from an ``std::string`` object.
+      \endrst
+     */
+    basic_cstring_view(const std::basic_string<Char> &s) : data_(s.c_str()) {}
+
+    /** Returns the pointer to a C string. */
+    const Char *c_str() const { return data_; }
 };
 
-using cstring_view = basic_cstring_view<char>;
+using cstring_view  = basic_cstring_view<char>;
 using wcstring_view = basic_cstring_view<wchar_t>;
 
-template <typename Char> struct formatter<std::error_code, Char> {
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    return ctx.begin();
-  }
-
-  template <typename FormatContext>
-  FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-      -> decltype(ctx.out()) {
-    auto out = ctx.out();
-    out = detail::write_bytes(out, ec.category().name(),
-                              basic_format_specs<Char>());
-    out = detail::write<Char>(out, Char(':'));
-    out = detail::write<Char>(out, ec.value());
-    return out;
-  }
+template <typename Char>
+struct formatter<std::error_code, Char>
+{
+    template <typename ParseContext>
+    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
+    {
+        return ctx.begin();
+    }
+
+    template <typename FormatContext>
+    FMT_CONSTEXPR auto format(const std::error_code &ec, FormatContext &ctx) const -> decltype(ctx.out())
+    {
+        auto out = ctx.out();
+        out      = detail::write_bytes(out, ec.category().name(), basic_format_specs<Char>());
+        out      = detail::write<Char>(out, Char(':'));
+        out      = detail::write<Char>(out, ec.value());
+        return out;
+    }
 };
 
 #ifdef _WIN32
-FMT_API const std::error_category& system_category() noexcept;
+FMT_API const std::error_category &system_category() noexcept;
 
 FMT_BEGIN_DETAIL_NAMESPACE
 // A converter from UTF-16 to UTF-8.
 // It is only provided for Windows since other systems support UTF-8 natively.
-class utf16_to_utf8 {
- private:
-  memory_buffer buffer_;
-
- public:
-  utf16_to_utf8() {}
-  FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
-  operator string_view() const { return string_view(&buffer_[0], size()); }
-  size_t size() const { return buffer_.size() - 1; }
-  const char* c_str() const { return &buffer_[0]; }
-  std::string str() const { return std::string(&buffer_[0], size()); }
-
-  // Performs conversion returning a system error code instead of
-  // throwing exception on conversion error. This method may still throw
-  // in case of memory allocation error.
-  FMT_API int convert(basic_string_view<wchar_t> s);
+class utf16_to_utf8
+{
+private:
+    memory_buffer buffer_;
+
+public:
+    utf16_to_utf8() {}
+    FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
+                operator string_view() const { return string_view(&buffer_[0], size()); }
+    size_t      size() const { return buffer_.size() - 1; }
+    const char *c_str() const { return &buffer_[0]; }
+    std::string str() const { return std::string(&buffer_[0], size()); }
+
+    // Performs conversion returning a system error code instead of
+    // throwing exception on conversion error. This method may still throw
+    // in case of memory allocation error.
+    FMT_API int convert(basic_string_view<wchar_t> s);
 };
 
-FMT_API void format_windows_error(buffer<char>& out, int error_code,
-                                  const char* message) noexcept;
+FMT_API void format_windows_error(buffer<char> &out, int error_code, const char *message) noexcept;
 FMT_END_DETAIL_NAMESPACE
 
-FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
-                                         format_args args);
+FMT_API std::system_error vwindows_error(int error_code, string_view format_str, format_args args);
 
 /**
  \rst
@@ -198,78 +200,79 @@ FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
  \endrst
 */
 template <typename... Args>
-std::system_error windows_error(int error_code, string_view message,
-                                const Args&... args) {
-  return vwindows_error(error_code, message, fmt::make_format_args(args...));
+std::system_error windows_error(int error_code, string_view message, const Args &...args)
+{
+    return vwindows_error(error_code, message, fmt::make_format_args(args...));
 }
 
 // Reports a Windows error without throwing an exception.
 // Can be used to report errors from destructors.
-FMT_API void report_windows_error(int error_code, const char* message) noexcept;
+FMT_API void report_windows_error(int error_code, const char *message) noexcept;
 #else
-inline const std::error_category& system_category() noexcept {
-  return std::system_category();
+inline const std::error_category &system_category() noexcept
+{
+    return std::system_category();
 }
-#endif  // _WIN32
+#endif // _WIN32
 
 // std::system is not available on some platforms such as iOS (#2248).
 #ifdef __OSX__
 template <typename S, typename... Args, typename Char = char_t<S>>
-void say(const S& format_str, Args&&... args) {
-  std::system(format("say \"{}\"", format(format_str, args...)).c_str());
+void say(const S &format_str, Args &&...args)
+{
+    std::system(format("say \"{}\"", format(format_str, args...)).c_str());
 }
 #endif
 
 // A buffered file.
-class buffered_file {
- private:
-  FILE* file_;
+class buffered_file
+{
+private:
+    FILE *file_;
 
-  friend class file;
+    friend class file;
 
-  explicit buffered_file(FILE* f) : file_(f) {}
+    explicit buffered_file(FILE *f) : file_(f) {}
 
- public:
-  buffered_file(const buffered_file&) = delete;
-  void operator=(const buffered_file&) = delete;
+public:
+    buffered_file(const buffered_file &)  = delete;
+    void operator=(const buffered_file &) = delete;
 
-  // Constructs a buffered_file object which doesn't represent any file.
-  buffered_file() noexcept : file_(nullptr) {}
+    // Constructs a buffered_file object which doesn't represent any file.
+    buffered_file() noexcept : file_(nullptr) {}
 
-  // Destroys the object closing the file it represents if any.
-  FMT_API ~buffered_file() noexcept;
+    // Destroys the object closing the file it represents if any.
+    FMT_API ~buffered_file() noexcept;
 
- public:
-  buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
-    other.file_ = nullptr;
-  }
+public:
+    buffered_file(buffered_file &&other) noexcept : file_(other.file_) { other.file_ = nullptr; }
 
-  buffered_file& operator=(buffered_file&& other) {
-    close();
-    file_ = other.file_;
-    other.file_ = nullptr;
-    return *this;
-  }
+    buffered_file &operator=(buffered_file &&other)
+    {
+        close();
+        file_       = other.file_;
+        other.file_ = nullptr;
+        return *this;
+    }
 
-  // Opens a file.
-  FMT_API buffered_file(cstring_view filename, cstring_view mode);
+    // Opens a file.
+    FMT_API buffered_file(cstring_view filename, cstring_view mode);
 
-  // Closes the file.
-  FMT_API void close();
+    // Closes the file.
+    FMT_API void close();
 
-  // Returns the pointer to a FILE object representing this file.
-  FILE* get() const noexcept { return file_; }
+    // Returns the pointer to a FILE object representing this file.
+    FILE *get() const noexcept { return file_; }
 
-  FMT_API int descriptor() const;
+    FMT_API int descriptor() const;
 
-  void vprint(string_view format_str, format_args args) {
-    fmt::vprint(file_, format_str, args);
-  }
+    void vprint(string_view format_str, format_args args) { fmt::vprint(file_, format_str, args); }
 
-  template <typename... Args>
-  inline void print(string_view format_str, const Args&... args) {
-    vprint(format_str, fmt::make_format_args(args...));
-  }
+    template <typename... Args>
+    inline void print(string_view format_str, const Args &...args)
+    {
+        vprint(format_str, fmt::make_format_args(args...));
+    }
 };
 
 #if FMT_USE_FCNTL
@@ -279,82 +282,85 @@ class buffered_file {
 // closing the file multiple times will cause a crash on Windows rather
 // than an exception. You can get standard behavior by overriding the
 // invalid parameter handler with _set_invalid_parameter_handler.
-class FMT_API file {
- private:
-  int fd_;  // File descriptor.
-
-  // Constructs a file object with a given descriptor.
-  explicit file(int fd) : fd_(fd) {}
-
- public:
-  // Possible values for the oflag argument to the constructor.
-  enum {
-    RDONLY = FMT_POSIX(O_RDONLY),  // Open for reading only.
-    WRONLY = FMT_POSIX(O_WRONLY),  // Open for writing only.
-    RDWR = FMT_POSIX(O_RDWR),      // Open for reading and writing.
-    CREATE = FMT_POSIX(O_CREAT),   // Create if the file doesn't exist.
-    APPEND = FMT_POSIX(O_APPEND),  // Open in append mode.
-    TRUNC = FMT_POSIX(O_TRUNC)     // Truncate the content of the file.
-  };
-
-  // Constructs a file object which doesn't represent any file.
-  file() noexcept : fd_(-1) {}
-
-  // Opens a file and constructs a file object representing this file.
-  file(cstring_view path, int oflag);
-
- public:
-  file(const file&) = delete;
-  void operator=(const file&) = delete;
-
-  file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
-
-  // Move assignment is not noexcept because close may throw.
-  file& operator=(file&& other) {
-    close();
-    fd_ = other.fd_;
-    other.fd_ = -1;
-    return *this;
-  }
-
-  // Destroys the object closing the file it represents if any.
-  ~file() noexcept;
-
-  // Returns the file descriptor.
-  int descriptor() const noexcept { return fd_; }
-
-  // Closes the file.
-  void close();
-
-  // Returns the file size. The size has signed type for consistency with
-  // stat::st_size.
-  long long size() const;
-
-  // Attempts to read count bytes from the file into the specified buffer.
-  size_t read(void* buffer, size_t count);
-
-  // Attempts to write count bytes from the specified buffer to the file.
-  size_t write(const void* buffer, size_t count);
-
-  // Duplicates a file descriptor with the dup function and returns
-  // the duplicate as a file object.
-  static file dup(int fd);
-
-  // Makes fd be the copy of this file descriptor, closing fd first if
-  // necessary.
-  void dup2(int fd);
-
-  // Makes fd be the copy of this file descriptor, closing fd first if
-  // necessary.
-  void dup2(int fd, std::error_code& ec) noexcept;
-
-  // Creates a pipe setting up read_end and write_end file objects for reading
-  // and writing respectively.
-  static void pipe(file& read_end, file& write_end);
-
-  // Creates a buffered_file object associated with this file and detaches
-  // this file object from the file.
-  buffered_file fdopen(const char* mode);
+class FMT_API file
+{
+private:
+    int fd_; // File descriptor.
+
+    // Constructs a file object with a given descriptor.
+    explicit file(int fd) : fd_(fd) {}
+
+public:
+    // Possible values for the oflag argument to the constructor.
+    enum
+    {
+        RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
+        WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
+        RDWR   = FMT_POSIX(O_RDWR),   // Open for reading and writing.
+        CREATE = FMT_POSIX(O_CREAT),  // Create if the file doesn't exist.
+        APPEND = FMT_POSIX(O_APPEND), // Open in append mode.
+        TRUNC  = FMT_POSIX(O_TRUNC)   // Truncate the content of the file.
+    };
+
+    // Constructs a file object which doesn't represent any file.
+    file() noexcept : fd_(-1) {}
+
+    // Opens a file and constructs a file object representing this file.
+    file(cstring_view path, int oflag);
+
+public:
+    file(const file &)           = delete;
+    void operator=(const file &) = delete;
+
+    file(file &&other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
+
+    // Move assignment is not noexcept because close may throw.
+    file &operator=(file &&other)
+    {
+        close();
+        fd_       = other.fd_;
+        other.fd_ = -1;
+        return *this;
+    }
+
+    // Destroys the object closing the file it represents if any.
+    ~file() noexcept;
+
+    // Returns the file descriptor.
+    int descriptor() const noexcept { return fd_; }
+
+    // Closes the file.
+    void close();
+
+    // Returns the file size. The size has signed type for consistency with
+    // stat::st_size.
+    long long size() const;
+
+    // Attempts to read count bytes from the file into the specified buffer.
+    size_t read(void *buffer, size_t count);
+
+    // Attempts to write count bytes from the specified buffer to the file.
+    size_t write(const void *buffer, size_t count);
+
+    // Duplicates a file descriptor with the dup function and returns
+    // the duplicate as a file object.
+    static file dup(int fd);
+
+    // Makes fd be the copy of this file descriptor, closing fd first if
+    // necessary.
+    void dup2(int fd);
+
+    // Makes fd be the copy of this file descriptor, closing fd first if
+    // necessary.
+    void dup2(int fd, std::error_code &ec) noexcept;
+
+    // Creates a pipe setting up read_end and write_end file objects for reading
+    // and writing respectively.
+    static void pipe(file &read_end, file &write_end);
+
+    // Creates a buffered_file object associated with this file and detaches
+    // this file object from the file.
+    buffered_file fdopen(const char *mode);
 };
 
 // Returns the memory page size.
@@ -362,39 +368,43 @@ long getpagesize();
 
 FMT_BEGIN_DETAIL_NAMESPACE
 
-struct buffer_size {
-  buffer_size() = default;
-  size_t value = 0;
-  buffer_size operator=(size_t val) const {
-    auto bs = buffer_size();
-    bs.value = val;
-    return bs;
-  }
+struct buffer_size
+{
+    buffer_size()     = default;
+    size_t      value = 0;
+    buffer_size operator=(size_t val) const
+    {
+        auto bs  = buffer_size();
+        bs.value = val;
+        return bs;
+    }
 };
 
-struct ostream_params {
-  int oflag = file::WRONLY | file::CREATE | file::TRUNC;
-  size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
+struct ostream_params
+{
+    int    oflag       = file::WRONLY | file::CREATE | file::TRUNC;
+    size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
 
-  ostream_params() {}
+    ostream_params() {}
 
-  template <typename... T>
-  ostream_params(T... params, int new_oflag) : ostream_params(params...) {
-    oflag = new_oflag;
-  }
+    template <typename... T>
+    ostream_params(T... params, int new_oflag) : ostream_params(params...)
+    {
+        oflag = new_oflag;
+    }
 
-  template <typename... T>
-  ostream_params(T... params, detail::buffer_size bs)
-      : ostream_params(params...) {
-    this->buffer_size = bs.value;
-  }
+    template <typename... T>
+    ostream_params(T... params, detail::buffer_size bs) : ostream_params(params...)
+    {
+        this->buffer_size = bs.value;
+    }
 
 // Intel has a bug that results in failure to deduce a constructor
 // for empty parameter packs.
-#  if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
-  ostream_params(int new_oflag) : oflag(new_oflag) {}
-  ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
-#  endif
+#if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
+    ostream_params(int new_oflag) : oflag(new_oflag) {}
+    ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
+#endif
 };
 
 FMT_END_DETAIL_NAMESPACE
@@ -404,51 +414,57 @@ FMT_END_DETAIL_NAMESPACE
 constexpr detail::buffer_size buffer_size{};
 
 /** A fast output stream which is not thread-safe. */
-class FMT_API ostream final : private detail::buffer<char> {
- private:
-  file file_;
-
-  void grow(size_t) override;
-
-  ostream(cstring_view path, const detail::ostream_params& params)
-      : file_(path, params.oflag) {
-    set(new char[params.buffer_size], params.buffer_size);
-  }
-
- public:
-  ostream(ostream&& other)
-      : detail::buffer<char>(other.data(), other.size(), other.capacity()),
-        file_(std::move(other.file_)) {
-    other.clear();
-    other.set(nullptr, 0);
-  }
-  ~ostream() {
-    flush();
-    delete[] data();
-  }
-
-  void flush() {
-    if (size() == 0) return;
-    file_.write(data(), size());
-    clear();
-  }
-
-  template <typename... T>
-  friend ostream output_file(cstring_view path, T... params);
-
-  void close() {
-    flush();
-    file_.close();
-  }
-
-  /**
-    Formats ``args`` according to specifications in ``fmt`` and writes the
-    output to the file.
-   */
-  template <typename... T> void print(format_string<T...> fmt, T&&... args) {
-    vformat_to(detail::buffer_appender<char>(*this), fmt,
-               fmt::make_format_args(args...));
-  }
+class FMT_API ostream final : private detail::buffer<char>
+{
+private:
+    file file_;
+
+    void grow(size_t) override;
+
+    ostream(cstring_view path, const detail::ostream_params &params) : file_(path, params.oflag)
+    {
+        set(new char[params.buffer_size], params.buffer_size);
+    }
+
+public:
+    ostream(ostream &&other) :
+        detail::buffer<char>(other.data(), other.size(), other.capacity()), file_(std::move(other.file_))
+    {
+        other.clear();
+        other.set(nullptr, 0);
+    }
+    ~ostream()
+    {
+        flush();
+        delete[] data();
+    }
+
+    void flush()
+    {
+        if(size() == 0)
+            return;
+        file_.write(data(), size());
+        clear();
+    }
+
+    template <typename... T>
+    friend ostream output_file(cstring_view path, T... params);
+
+    void close()
+    {
+        flush();
+        file_.close();
+    }
+
+    /**
+      Formats ``args`` according to specifications in ``fmt`` and writes the
+      output to the file.
+     */
+    template <typename... T>
+    void print(format_string<T...> fmt, T &&...args)
+    {
+        vformat_to(detail::buffer_appender<char>(*this), fmt, fmt::make_format_args(args...));
+    }
 };
 
 /**
@@ -467,12 +483,13 @@ class FMT_API ostream final : private detail::buffer<char> {
   \endrst
  */
 template <typename... T>
-inline ostream output_file(cstring_view path, T... params) {
-  return {path, detail::ostream_params(params...)};
+inline ostream output_file(cstring_view path, T... params)
+{
+    return {path, detail::ostream_params(params...)};
 }
-#endif  // FMT_USE_FCNTL
+#endif // FMT_USE_FCNTL
 
 FMT_MODULE_EXPORT_END
 FMT_END_NAMESPACE
 
-#endif  // FMT_OS_H_
+#endif // FMT_OS_H_
diff --git a/include/spdlog/fmt/bundled/ostream.h b/include/spdlog/fmt/bundled/ostream.h
index c3cdd4a61..64a3b6cd2 100644
--- a/include/spdlog/fmt/bundled/ostream.h
+++ b/include/spdlog/fmt/bundled/ostream.h
@@ -11,156 +11,173 @@
 #include <fstream>
 #include <ostream>
 #if defined(_WIN32) && defined(__GLIBCXX__)
-#  include <ext/stdio_filebuf.h>
-#  include <ext/stdio_sync_filebuf.h>
+#include <ext/stdio_filebuf.h>
+#include <ext/stdio_sync_filebuf.h>
 #elif defined(_WIN32) && defined(_LIBCPP_VERSION)
-#  include <__std_stream>
+#include <__std_stream>
 #endif
 
 #include "format.h"
 
 FMT_BEGIN_NAMESPACE
 
-template <typename OutputIt, typename Char> class basic_printf_context;
+template <typename OutputIt, typename Char>
+class basic_printf_context;
 
-namespace detail {
+namespace detail
+{
 
 // Checks if T has a user-defined operator<<.
 template <typename T, typename Char, typename Enable = void>
-class is_streamable {
- private:
-  template <typename U>
-  static auto test(int)
-      -> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
-                              << std::declval<U>()) != 0>;
+class is_streamable
+{
+private:
+    template <typename U>
+    static auto test(int)
+        -> bool_constant<sizeof(std::declval<std::basic_ostream<Char> &>() << std::declval<U>()) != 0>;
 
-  template <typename> static auto test(...) -> std::false_type;
+    template <typename>
+    static auto test(...) -> std::false_type;
 
-  using result = decltype(test<T>(0));
+    using result = decltype(test<T>(0));
 
- public:
-  is_streamable() = default;
+public:
+    is_streamable() = default;
 
-  static const bool value = result::value;
+    static const bool value = result::value;
 };
 
 // Formatting of built-in types and arrays is intentionally disabled because
 // it's handled by standard (non-ostream) formatters.
 template <typename T, typename Char>
 struct is_streamable<
-    T, Char,
+    T,
+    Char,
     enable_if_t<
-        std::is_arithmetic<T>::value || std::is_array<T>::value ||
-        std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
-        std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
+        std::is_arithmetic<T>::value || std::is_array<T>::value || std::is_pointer<T>::value ||
+        std::is_same<T, char8_type>::value || std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
         std::is_same<T, std_string_view<Char>>::value ||
-        (std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
-    : std::false_type {};
+        (std::is_convertible<T, int>::value && !std::is_enum<T>::value)>> : std::false_type
+{
+};
 
 // Generate a unique explicit instantion in every translation unit using a tag
 // type in an anonymous namespace.
-namespace {
-struct file_access_tag {};
-}  // namespace
-template <class Tag, class BufType, FILE* BufType::*FileMemberPtr>
-class file_access {
-  friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
+namespace
+{
+    struct file_access_tag
+    {
+    };
+} // namespace
+template <class Tag, class BufType, FILE *BufType::*FileMemberPtr>
+class file_access
+{
+    friend auto get_file(BufType &obj) -> FILE * { return obj.*FileMemberPtr; }
 };
 
 #if FMT_MSC_VERSION
-template class file_access<file_access_tag, std::filebuf,
-                           &std::filebuf::_Myfile>;
-auto get_file(std::filebuf&) -> FILE*;
+template class file_access<file_access_tag, std::filebuf, &std::filebuf::_Myfile>;
+auto get_file(std::filebuf &) -> FILE *;
 #elif defined(_WIN32) && defined(_LIBCPP_VERSION)
-template class file_access<file_access_tag, std::__stdoutbuf<char>,
-                           &std::__stdoutbuf<char>::__file_>;
-auto get_file(std::__stdoutbuf<char>&) -> FILE*;
+template class file_access<file_access_tag, std::__stdoutbuf<char>, &std::__stdoutbuf<char>::__file_>;
+auto get_file(std::__stdoutbuf<char> &) -> FILE *;
 #endif
 
-inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
+inline bool write_ostream_unicode(std::ostream &os, fmt::string_view data)
+{
 #if FMT_MSC_VERSION
-  if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
-    if (FILE* f = get_file(*buf)) return write_console(f, data);
+    if(auto *buf = dynamic_cast<std::filebuf *>(os.rdbuf()))
+        if(FILE *f = get_file(*buf))
+            return write_console(f, data);
 #elif defined(_WIN32) && defined(__GLIBCXX__)
-  auto* rdbuf = os.rdbuf();
-  FILE* c_file;
-  if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
-    c_file = fbuf->file();
-  else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
-    c_file = fbuf->file();
-  else
-    return false;
-  if (c_file) return write_console(c_file, data);
+    auto *rdbuf = os.rdbuf();
+    FILE *c_file;
+    if(auto *fbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char> *>(rdbuf))
+        c_file = fbuf->file();
+    else if(auto *fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char> *>(rdbuf))
+        c_file = fbuf->file();
+    else
+        return false;
+    if(c_file)
+        return write_console(c_file, data);
 #elif defined(_WIN32) && defined(_LIBCPP_VERSION)
-  if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf()))
-    if (FILE* f = get_file(*buf)) return write_console(f, data);
+    if(auto *buf = dynamic_cast<std::__stdoutbuf<char> *>(os.rdbuf()))
+        if(FILE *f = get_file(*buf))
+            return write_console(f, data);
 #else
-  ignore_unused(os, data);
+    ignore_unused(os, data);
 #endif
-  return false;
+    return false;
 }
-inline bool write_ostream_unicode(std::wostream&,
-                                  fmt::basic_string_view<wchar_t>) {
-  return false;
+inline bool write_ostream_unicode(std::wostream &, fmt::basic_string_view<wchar_t>)
+{
+    return false;
 }
 
 // Write the content of buf to os.
 // It is a separate function rather than a part of vprint to simplify testing.
 template <typename Char>
-void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
-  const Char* buf_data = buf.data();
-  using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
-  unsigned_streamsize size = buf.size();
-  unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
-  do {
-    unsigned_streamsize n = size <= max_size ? size : max_size;
-    os.write(buf_data, static_cast<std::streamsize>(n));
-    buf_data += n;
-    size -= n;
-  } while (size != 0);
+void write_buffer(std::basic_ostream<Char> &os, buffer<Char> &buf)
+{
+    const Char *buf_data         = buf.data();
+    using unsigned_streamsize    = std::make_unsigned<std::streamsize>::type;
+    unsigned_streamsize size     = buf.size();
+    unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
+    do
+    {
+        unsigned_streamsize n = size <= max_size ? size : max_size;
+        os.write(buf_data, static_cast<std::streamsize>(n));
+        buf_data += n;
+        size -= n;
+    } while(size != 0);
 }
 
 template <typename Char, typename T>
-void format_value(buffer<Char>& buf, const T& value,
-                  locale_ref loc = locale_ref()) {
-  auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
-  auto&& output = std::basic_ostream<Char>(&format_buf);
+void format_value(buffer<Char> &buf, const T &value, locale_ref loc = locale_ref())
+{
+    auto &&format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
+    auto &&output     = std::basic_ostream<Char>(&format_buf);
 #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
-  if (loc) output.imbue(loc.get<std::locale>());
+    if(loc)
+        output.imbue(loc.get<std::locale>());
 #endif
-  output << value;
-  output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
+    output << value;
+    output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
 }
 
-template <typename T> struct streamed_view { const T& value; };
+template <typename T>
+struct streamed_view
+{
+    const T &value;
+};
 
-}  // namespace detail
+} // namespace detail
 
 // Formats an object of type T that has an overloaded ostream operator<<.
 template <typename Char>
-struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
-  void set_debug_format() = delete;
-
-  template <typename T, typename OutputIt>
-  auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
-      -> OutputIt {
-    auto buffer = basic_memory_buffer<Char>();
-    format_value(buffer, value, ctx.locale());
-    return formatter<basic_string_view<Char>, Char>::format(
-        {buffer.data(), buffer.size()}, ctx);
-  }
+struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char>
+{
+    void set_debug_format() = delete;
+
+    template <typename T, typename OutputIt>
+    auto format(const T &value, basic_format_context<OutputIt, Char> &ctx) const -> OutputIt
+    {
+        auto buffer = basic_memory_buffer<Char>();
+        format_value(buffer, value, ctx.locale());
+        return formatter<basic_string_view<Char>, Char>::format({buffer.data(), buffer.size()}, ctx);
+    }
 };
 
 using ostream_formatter = basic_ostream_formatter<char>;
 
 template <typename T, typename Char>
-struct formatter<detail::streamed_view<T>, Char>
-    : basic_ostream_formatter<Char> {
-  template <typename OutputIt>
-  auto format(detail::streamed_view<T> view,
-              basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
-    return basic_ostream_formatter<Char>::format(view.value, ctx);
-  }
+struct formatter<detail::streamed_view<T>, Char> : basic_ostream_formatter<Char>
+{
+    template <typename OutputIt>
+    auto format(detail::streamed_view<T> view, basic_format_context<OutputIt, Char> &ctx) const -> OutputIt
+    {
+        return basic_ostream_formatter<Char>::format(view.value, ctx);
+    }
 };
 
 /**
@@ -174,36 +191,41 @@ struct formatter<detail::streamed_view<T>, Char>
   \endrst
  */
 template <typename T>
-auto streamed(const T& value) -> detail::streamed_view<T> {
-  return {value};
+auto streamed(const T &value) -> detail::streamed_view<T>
+{
+    return {value};
 }
 
-namespace detail {
+namespace detail
+{
 
 // Formats an object of type T that has an overloaded ostream operator<<.
 template <typename T, typename Char>
-struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
-    : basic_ostream_formatter<Char> {
-  using basic_ostream_formatter<Char>::format;
+struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> : basic_ostream_formatter<Char>
+{
+    using basic_ostream_formatter<Char>::format;
 };
 
-inline void vprint_directly(std::ostream& os, string_view format_str,
-                            format_args args) {
-  auto buffer = memory_buffer();
-  detail::vformat_to(buffer, format_str, args);
-  detail::write_buffer(os, buffer);
+inline void vprint_directly(std::ostream &os, string_view format_str, format_args args)
+{
+    auto buffer = memory_buffer();
+    detail::vformat_to(buffer, format_str, args);
+    detail::write_buffer(os, buffer);
 }
 
-}  // namespace detail
+} // namespace detail
 
 FMT_MODULE_EXPORT template <typename Char>
-void vprint(std::basic_ostream<Char>& os,
-            basic_string_view<type_identity_t<Char>> format_str,
-            basic_format_args<buffer_context<type_identity_t<Char>>> args) {
-  auto buffer = basic_memory_buffer<Char>();
-  detail::vformat_to(buffer, format_str, args);
-  if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
-  detail::write_buffer(os, buffer);
+void vprint(
+    std::basic_ostream<Char>                                &os,
+    basic_string_view<type_identity_t<Char>>                 format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args)
+{
+    auto buffer = basic_memory_buffer<Char>();
+    detail::vformat_to(buffer, format_str, args);
+    if(detail::write_ostream_unicode(os, {buffer.data(), buffer.size()}))
+        return;
+    detail::write_buffer(os, buffer);
 }
 
 /**
@@ -216,22 +238,22 @@ void vprint(std::basic_ostream<Char>& os,
   \endrst
  */
 FMT_MODULE_EXPORT template <typename... T>
-void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
-  const auto& vargs = fmt::make_format_args(args...);
-  if (detail::is_utf8())
-    vprint(os, fmt, vargs);
-  else
-    detail::vprint_directly(os, fmt, vargs);
+void print(std::ostream &os, format_string<T...> fmt, T &&...args)
+{
+    const auto &vargs = fmt::make_format_args(args...);
+    if(detail::is_utf8())
+        vprint(os, fmt, vargs);
+    else
+        detail::vprint_directly(os, fmt, vargs);
 }
 
 FMT_MODULE_EXPORT
 template <typename... Args>
-void print(std::wostream& os,
-           basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
-           Args&&... args) {
-  vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
+void print(std::wostream &os, basic_format_string<wchar_t, type_identity_t<Args>...> fmt, Args &&...args)
+{
+    vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
 }
 
 FMT_END_NAMESPACE
 
-#endif  // FMT_OSTREAM_H_
+#endif // FMT_OSTREAM_H_
diff --git a/include/spdlog/fmt/bundled/printf.h b/include/spdlog/fmt/bundled/printf.h
index 70a592dc2..3c2e6d16a 100644
--- a/include/spdlog/fmt/bundled/printf.h
+++ b/include/spdlog/fmt/bundled/printf.h
@@ -8,152 +8,188 @@
 #ifndef FMT_PRINTF_H_
 #define FMT_PRINTF_H_
 
-#include <algorithm>  // std::max
-#include <limits>     // std::numeric_limits
-
 #include "format.h"
 
+#include <algorithm> // std::max
+#include <limits>    // std::numeric_limits
+
 FMT_BEGIN_NAMESPACE
 FMT_MODULE_EXPORT_BEGIN
 
-template <typename T> struct printf_formatter { printf_formatter() = delete; };
+template <typename T>
+struct printf_formatter
+{
+    printf_formatter() = delete;
+};
 
 template <typename Char>
-class basic_printf_parse_context : public basic_format_parse_context<Char> {
-  using basic_format_parse_context<Char>::basic_format_parse_context;
+class basic_printf_parse_context : public basic_format_parse_context<Char>
+{
+    using basic_format_parse_context<Char>::basic_format_parse_context;
 };
 
-template <typename OutputIt, typename Char> class basic_printf_context {
- private:
-  OutputIt out_;
-  basic_format_args<basic_printf_context> args_;
-
- public:
-  using char_type = Char;
-  using format_arg = basic_format_arg<basic_printf_context>;
-  using parse_context_type = basic_printf_parse_context<Char>;
-  template <typename T> using formatter_type = printf_formatter<T>;
-
-  /**
-    \rst
-    Constructs a ``printf_context`` object. References to the arguments are
-    stored in the context object so make sure they have appropriate lifetimes.
-    \endrst
-   */
-  basic_printf_context(OutputIt out,
-                       basic_format_args<basic_printf_context> args)
-      : out_(out), args_(args) {}
-
-  OutputIt out() { return out_; }
-  void advance_to(OutputIt it) { out_ = it; }
-
-  detail::locale_ref locale() { return {}; }
-
-  format_arg arg(int id) const { return args_.get(id); }
-
-  FMT_CONSTEXPR void on_error(const char* message) {
-    detail::error_handler().on_error(message);
-  }
+template <typename OutputIt, typename Char>
+class basic_printf_context
+{
+private:
+    OutputIt                                out_;
+    basic_format_args<basic_printf_context> args_;
+
+public:
+    using char_type          = Char;
+    using format_arg         = basic_format_arg<basic_printf_context>;
+    using parse_context_type = basic_printf_parse_context<Char>;
+    template <typename T>
+    using formatter_type = printf_formatter<T>;
+
+    /**
+      \rst
+      Constructs a ``printf_context`` object. References to the arguments are
+      stored in the context object so make sure they have appropriate lifetimes.
+      \endrst
+     */
+    basic_printf_context(OutputIt out, basic_format_args<basic_printf_context> args) : out_(out), args_(args) {}
+
+    OutputIt out() { return out_; }
+    void     advance_to(OutputIt it) { out_ = it; }
+
+    detail::locale_ref locale() { return {}; }
+
+    format_arg arg(int id) const { return args_.get(id); }
+
+    FMT_CONSTEXPR void on_error(const char *message) { detail::error_handler().on_error(message); }
 };
 
 FMT_BEGIN_DETAIL_NAMESPACE
 
 // Checks if a value fits in int - used to avoid warnings about comparing
 // signed and unsigned integers.
-template <bool IsSigned> struct int_checker {
-  template <typename T> static bool fits_in_int(T value) {
-    unsigned max = max_value<int>();
-    return value <= max;
-  }
-  static bool fits_in_int(bool) { return true; }
+template <bool IsSigned>
+struct int_checker
+{
+    template <typename T>
+    static bool fits_in_int(T value)
+    {
+        unsigned max = max_value<int>();
+        return value <= max;
+    }
+    static bool fits_in_int(bool) { return true; }
 };
 
-template <> struct int_checker<true> {
-  template <typename T> static bool fits_in_int(T value) {
-    return value >= (std::numeric_limits<int>::min)() &&
-           value <= max_value<int>();
-  }
-  static bool fits_in_int(int) { return true; }
+template <>
+struct int_checker<true>
+{
+    template <typename T>
+    static bool fits_in_int(T value)
+    {
+        return value >= (std::numeric_limits<int>::min)() && value <= max_value<int>();
+    }
+    static bool fits_in_int(int) { return true; }
 };
 
-class printf_precision_handler {
- public:
-  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-  int operator()(T value) {
-    if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
-      FMT_THROW(format_error("number is too big"));
-    return (std::max)(static_cast<int>(value), 0);
-  }
-
-  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-  int operator()(T) {
-    FMT_THROW(format_error("precision is not integer"));
-    return 0;
-  }
+class printf_precision_handler
+{
+public:
+    template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+    int operator()(T value)
+    {
+        if(!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
+            FMT_THROW(format_error("number is too big"));
+        return (std::max)(static_cast<int>(value), 0);
+    }
+
+    template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+    int operator()(T)
+    {
+        FMT_THROW(format_error("precision is not integer"));
+        return 0;
+    }
 };
 
 // An argument visitor that returns true iff arg is a zero integer.
-class is_zero_int {
- public:
-  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-  bool operator()(T value) {
-    return value == 0;
-  }
-
-  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-  bool operator()(T) {
-    return false;
-  }
+class is_zero_int
+{
+public:
+    template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+    bool operator()(T value)
+    {
+        return value == 0;
+    }
+
+    template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+    bool operator()(T)
+    {
+        return false;
+    }
+};
+
+template <typename T>
+struct make_unsigned_or_bool : std::make_unsigned<T>
+{
+};
+
+template <>
+struct make_unsigned_or_bool<bool>
+{
+    using type = bool;
 };
 
-template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
-
-template <> struct make_unsigned_or_bool<bool> { using type = bool; };
-
-template <typename T, typename Context> class arg_converter {
- private:
-  using char_type = typename Context::char_type;
-
-  basic_format_arg<Context>& arg_;
-  char_type type_;
-
- public:
-  arg_converter(basic_format_arg<Context>& arg, char_type type)
-      : arg_(arg), type_(type) {}
-
-  void operator()(bool value) {
-    if (type_ != 's') operator()<bool>(value);
-  }
-
-  template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
-  void operator()(U value) {
-    bool is_signed = type_ == 'd' || type_ == 'i';
-    using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
-    if (const_check(sizeof(target_type) <= sizeof(int))) {
-      // Extra casts are used to silence warnings.
-      if (is_signed) {
-        arg_ = detail::make_arg<Context>(
-            static_cast<int>(static_cast<target_type>(value)));
-      } else {
-        using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
-        arg_ = detail::make_arg<Context>(
-            static_cast<unsigned>(static_cast<unsigned_type>(value)));
-      }
-    } else {
-      if (is_signed) {
-        // glibc's printf doesn't sign extend arguments of smaller types:
-        //   std::printf("%lld", -42);  // prints "4294967254"
-        // but we don't have to do the same because it's a UB.
-        arg_ = detail::make_arg<Context>(static_cast<long long>(value));
-      } else {
-        arg_ = detail::make_arg<Context>(
-            static_cast<typename make_unsigned_or_bool<U>::type>(value));
-      }
+template <typename T, typename Context>
+class arg_converter
+{
+private:
+    using char_type = typename Context::char_type;
+
+    basic_format_arg<Context> &arg_;
+    char_type                  type_;
+
+public:
+    arg_converter(basic_format_arg<Context> &arg, char_type type) : arg_(arg), type_(type) {}
+
+    void operator()(bool value)
+    {
+        if(type_ != 's')
+            operator()<bool>(value);
     }
-  }
 
-  template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
-  void operator()(U) {}  // No conversion needed for non-integral types.
+    template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
+    void operator()(U value)
+    {
+        bool is_signed    = type_ == 'd' || type_ == 'i';
+        using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
+        if(const_check(sizeof(target_type) <= sizeof(int)))
+        {
+            // Extra casts are used to silence warnings.
+            if(is_signed)
+            {
+                arg_ = detail::make_arg<Context>(static_cast<int>(static_cast<target_type>(value)));
+            }
+            else
+            {
+                using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
+                arg_ = detail::make_arg<Context>(static_cast<unsigned>(static_cast<unsigned_type>(value)));
+            }
+        }
+        else
+        {
+            if(is_signed)
+            {
+                // glibc's printf doesn't sign extend arguments of smaller types:
+                //   std::printf("%lld", -42);  // prints "4294967254"
+                // but we don't have to do the same because it's a UB.
+                arg_ = detail::make_arg<Context>(static_cast<long long>(value));
+            }
+            else
+            {
+                arg_ = detail::make_arg<Context>(static_cast<typename make_unsigned_or_bool<U>::type>(value));
+            }
+        }
+    }
+
+    template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
+    void operator()(U)
+    {
+    } // No conversion needed for non-integral types.
 };
 
 // Converts an integer argument to T for printf, if T is an integral type.
@@ -161,373 +197,423 @@ template <typename T, typename Context> class arg_converter {
 // type depending on the type specifier: 'd' and 'i' - signed, other -
 // unsigned).
 template <typename T, typename Context, typename Char>
-void convert_arg(basic_format_arg<Context>& arg, Char type) {
-  visit_format_arg(arg_converter<T, Context>(arg, type), arg);
+void convert_arg(basic_format_arg<Context> &arg, Char type)
+{
+    visit_format_arg(arg_converter<T, Context>(arg, type), arg);
 }
 
 // Converts an integer argument to char for printf.
-template <typename Context> class char_converter {
- private:
-  basic_format_arg<Context>& arg_;
-
- public:
-  explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
-
-  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-  void operator()(T value) {
-    arg_ = detail::make_arg<Context>(
-        static_cast<typename Context::char_type>(value));
-  }
+template <typename Context>
+class char_converter
+{
+private:
+    basic_format_arg<Context> &arg_;
+
+public:
+    explicit char_converter(basic_format_arg<Context> &arg) : arg_(arg) {}
+
+    template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+    void operator()(T value)
+    {
+        arg_ = detail::make_arg<Context>(static_cast<typename Context::char_type>(value));
+    }
 
-  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-  void operator()(T) {}  // No conversion needed for non-integral types.
+    template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+    void operator()(T)
+    {
+    } // No conversion needed for non-integral types.
 };
 
 // An argument visitor that return a pointer to a C string if argument is a
 // string or null otherwise.
-template <typename Char> struct get_cstring {
-  template <typename T> const Char* operator()(T) { return nullptr; }
-  const Char* operator()(const Char* s) { return s; }
+template <typename Char>
+struct get_cstring
+{
+    template <typename T>
+    const Char *operator()(T)
+    {
+        return nullptr;
+    }
+    const Char *operator()(const Char *s) { return s; }
 };
 
 // Checks if an argument is a valid printf width specifier and sets
 // left alignment if it is negative.
-template <typename Char> class printf_width_handler {
- private:
-  using format_specs = basic_format_specs<Char>;
-
-  format_specs& specs_;
-
- public:
-  explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
+template <typename Char>
+class printf_width_handler
+{
+private:
+    using format_specs = basic_format_specs<Char>;
+
+    format_specs &specs_;
+
+public:
+    explicit printf_width_handler(format_specs &specs) : specs_(specs) {}
+
+    template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
+    unsigned operator()(T value)
+    {
+        auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
+        if(detail::is_negative(value))
+        {
+            specs_.align = align::left;
+            width        = 0 - width;
+        }
+        unsigned int_max = max_value<int>();
+        if(width > int_max)
+            FMT_THROW(format_error("number is too big"));
+        return static_cast<unsigned>(width);
+    }
 
-  template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-  unsigned operator()(T value) {
-    auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
-    if (detail::is_negative(value)) {
-      specs_.align = align::left;
-      width = 0 - width;
+    template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
+    unsigned operator()(T)
+    {
+        FMT_THROW(format_error("width is not integer"));
+        return 0;
     }
-    unsigned int_max = max_value<int>();
-    if (width > int_max) FMT_THROW(format_error("number is too big"));
-    return static_cast<unsigned>(width);
-  }
-
-  template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-  unsigned operator()(T) {
-    FMT_THROW(format_error("width is not integer"));
-    return 0;
-  }
 };
 
 // The ``printf`` argument formatter.
 template <typename OutputIt, typename Char>
-class printf_arg_formatter : public arg_formatter<Char> {
- private:
-  using base = arg_formatter<Char>;
-  using context_type = basic_printf_context<OutputIt, Char>;
-  using format_specs = basic_format_specs<Char>;
-
-  context_type& context_;
-
-  OutputIt write_null_pointer(bool is_string = false) {
-    auto s = this->specs;
-    s.type = presentation_type::none;
-    return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
-  }
-
- public:
-  printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
-      : base{iter, s, locale_ref()}, context_(ctx) {}
-
-  OutputIt operator()(monostate value) { return base::operator()(value); }
-
-  template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
-  OutputIt operator()(T value) {
-    // MSVC2013 fails to compile separate overloads for bool and Char so use
-    // std::is_same instead.
-    if (std::is_same<T, Char>::value) {
-      format_specs fmt_specs = this->specs;
-      if (fmt_specs.type != presentation_type::none &&
-          fmt_specs.type != presentation_type::chr) {
-        return (*this)(static_cast<int>(value));
-      }
-      fmt_specs.sign = sign::none;
-      fmt_specs.alt = false;
-      fmt_specs.fill[0] = ' ';  // Ignore '0' flag for char types.
-      // align::numeric needs to be overwritten here since the '0' flag is
-      // ignored for non-numeric types
-      if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
-        fmt_specs.align = align::right;
-      return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
+class printf_arg_formatter : public arg_formatter<Char>
+{
+private:
+    using base         = arg_formatter<Char>;
+    using context_type = basic_printf_context<OutputIt, Char>;
+    using format_specs = basic_format_specs<Char>;
+
+    context_type &context_;
+
+    OutputIt write_null_pointer(bool is_string = false)
+    {
+        auto s = this->specs;
+        s.type = presentation_type::none;
+        return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
     }
-    return base::operator()(value);
-  }
-
-  template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
-  OutputIt operator()(T value) {
-    return base::operator()(value);
-  }
-
-  /** Formats a null-terminated C string. */
-  OutputIt operator()(const char* value) {
-    if (value) return base::operator()(value);
-    return write_null_pointer(this->specs.type != presentation_type::pointer);
-  }
-
-  /** Formats a null-terminated wide C string. */
-  OutputIt operator()(const wchar_t* value) {
-    if (value) return base::operator()(value);
-    return write_null_pointer(this->specs.type != presentation_type::pointer);
-  }
-
-  OutputIt operator()(basic_string_view<Char> value) {
-    return base::operator()(value);
-  }
-
-  /** Formats a pointer. */
-  OutputIt operator()(const void* value) {
-    return value ? base::operator()(value) : write_null_pointer();
-  }
-
-  /** Formats an argument of a custom (user-defined) type. */
-  OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
-    auto parse_ctx =
-        basic_printf_parse_context<Char>(basic_string_view<Char>());
-    handle.format(parse_ctx, context_);
-    return this->out;
-  }
-};
 
-template <typename Char>
-void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
-                 const Char* end) {
-  for (; it != end; ++it) {
-    switch (*it) {
-    case '-':
-      specs.align = align::left;
-      break;
-    case '+':
-      specs.sign = sign::plus;
-      break;
-    case '0':
-      specs.fill[0] = '0';
-      break;
-    case ' ':
-      if (specs.sign != sign::plus) {
-        specs.sign = sign::space;
-      }
-      break;
-    case '#':
-      specs.alt = true;
-      break;
-    default:
-      return;
+public:
+    printf_arg_formatter(OutputIt iter, format_specs &s, context_type &ctx) : base{iter, s, locale_ref()}, context_(ctx)
+    {
     }
-  }
-}
 
-template <typename Char, typename GetArg>
-int parse_header(const Char*& it, const Char* end,
-                 basic_format_specs<Char>& specs, GetArg get_arg) {
-  int arg_index = -1;
-  Char c = *it;
-  if (c >= '0' && c <= '9') {
-    // Parse an argument index (if followed by '$') or a width possibly
-    // preceded with '0' flag(s).
-    int value = parse_nonnegative_int(it, end, -1);
-    if (it != end && *it == '$') {  // value is an argument index
-      ++it;
-      arg_index = value != -1 ? value : max_value<int>();
-    } else {
-      if (c == '0') specs.fill[0] = '0';
-      if (value != 0) {
-        // Nonzero value means that we parsed width and don't need to
-        // parse it or flags again, so return now.
-        if (value == -1) FMT_THROW(format_error("number is too big"));
-        specs.width = value;
-        return arg_index;
-      }
-    }
-  }
-  parse_flags(specs, it, end);
-  // Parse width.
-  if (it != end) {
-    if (*it >= '0' && *it <= '9') {
-      specs.width = parse_nonnegative_int(it, end, -1);
-      if (specs.width == -1) FMT_THROW(format_error("number is too big"));
-    } else if (*it == '*') {
-      ++it;
-      specs.width = static_cast<int>(visit_format_arg(
-          detail::printf_width_handler<Char>(specs), get_arg(-1)));
+    OutputIt operator()(monostate value) { return base::operator()(value); }
+
+    template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
+    OutputIt operator()(T value)
+    {
+        // MSVC2013 fails to compile separate overloads for bool and Char so use
+        // std::is_same instead.
+        if(std::is_same<T, Char>::value)
+        {
+            format_specs fmt_specs = this->specs;
+            if(fmt_specs.type != presentation_type::none && fmt_specs.type != presentation_type::chr)
+            {
+                return (*this)(static_cast<int>(value));
+            }
+            fmt_specs.sign    = sign::none;
+            fmt_specs.alt     = false;
+            fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
+            // align::numeric needs to be overwritten here since the '0' flag is
+            // ignored for non-numeric types
+            if(fmt_specs.align == align::none || fmt_specs.align == align::numeric)
+                fmt_specs.align = align::right;
+            return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
+        }
+        return base::operator()(value);
     }
-  }
-  return arg_index;
-}
 
-template <typename Char, typename Context>
-void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
-             basic_format_args<Context> args) {
-  using OutputIt = buffer_appender<Char>;
-  auto out = OutputIt(buf);
-  auto context = basic_printf_context<OutputIt, Char>(out, args);
-  auto parse_ctx = basic_printf_parse_context<Char>(format);
-
-  // Returns the argument with specified index or, if arg_index is -1, the next
-  // argument.
-  auto get_arg = [&](int arg_index) {
-    if (arg_index < 0)
-      arg_index = parse_ctx.next_arg_id();
-    else
-      parse_ctx.check_arg_id(--arg_index);
-    return detail::get_arg(context, arg_index);
-  };
-
-  const Char* start = parse_ctx.begin();
-  const Char* end = parse_ctx.end();
-  auto it = start;
-  while (it != end) {
-    if (!detail::find<false, Char>(it, end, '%', it)) {
-      it = end;  // detail::find leaves it == nullptr if it doesn't find '%'
-      break;
-    }
-    Char c = *it++;
-    if (it != end && *it == c) {
-      out = detail::write(
-          out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
-      start = ++it;
-      continue;
-    }
-    out = detail::write(out, basic_string_view<Char>(
-                                 start, detail::to_unsigned(it - 1 - start)));
-
-    basic_format_specs<Char> specs;
-    specs.align = align::right;
-
-    // Parse argument index, flags and width.
-    int arg_index = parse_header(it, end, specs, get_arg);
-    if (arg_index == 0) parse_ctx.on_error("argument not found");
-
-    // Parse precision.
-    if (it != end && *it == '.') {
-      ++it;
-      c = it != end ? *it : 0;
-      if ('0' <= c && c <= '9') {
-        specs.precision = parse_nonnegative_int(it, end, 0);
-      } else if (c == '*') {
-        ++it;
-        specs.precision = static_cast<int>(
-            visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
-      } else {
-        specs.precision = 0;
-      }
+    template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
+    OutputIt operator()(T value)
+    {
+        return base::operator()(value);
     }
 
-    auto arg = get_arg(arg_index);
-    // For d, i, o, u, x, and X conversion specifiers, if a precision is
-    // specified, the '0' flag is ignored
-    if (specs.precision >= 0 && arg.is_integral())
-      specs.fill[0] =
-          ' ';  // Ignore '0' flag for non-numeric types or if '-' present.
-    if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
-      auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
-      auto str_end = str + specs.precision;
-      auto nul = std::find(str, str_end, Char());
-      arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
-          basic_string_view<Char>(
-              str, detail::to_unsigned(nul != str_end ? nul - str
-                                                      : specs.precision)));
+    /** Formats a null-terminated C string. */
+    OutputIt operator()(const char *value)
+    {
+        if(value)
+            return base::operator()(value);
+        return write_null_pointer(this->specs.type != presentation_type::pointer);
     }
-    if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
-      specs.alt = false;
-    if (specs.fill[0] == '0') {
-      if (arg.is_arithmetic() && specs.align != align::left)
-        specs.align = align::numeric;
-      else
-        specs.fill[0] = ' ';  // Ignore '0' flag for non-numeric types or if '-'
-                              // flag is also present.
+
+    /** Formats a null-terminated wide C string. */
+    OutputIt operator()(const wchar_t *value)
+    {
+        if(value)
+            return base::operator()(value);
+        return write_null_pointer(this->specs.type != presentation_type::pointer);
     }
 
-    // Parse length and convert the argument to the required type.
-    c = it != end ? *it++ : 0;
-    Char t = it != end ? *it : 0;
-    using detail::convert_arg;
-    switch (c) {
-    case 'h':
-      if (t == 'h') {
-        ++it;
-        t = it != end ? *it : 0;
-        convert_arg<signed char>(arg, t);
-      } else {
-        convert_arg<short>(arg, t);
-      }
-      break;
-    case 'l':
-      if (t == 'l') {
-        ++it;
-        t = it != end ? *it : 0;
-        convert_arg<long long>(arg, t);
-      } else {
-        convert_arg<long>(arg, t);
-      }
-      break;
-    case 'j':
-      convert_arg<intmax_t>(arg, t);
-      break;
-    case 'z':
-      convert_arg<size_t>(arg, t);
-      break;
-    case 't':
-      convert_arg<std::ptrdiff_t>(arg, t);
-      break;
-    case 'L':
-      // printf produces garbage when 'L' is omitted for long double, no
-      // need to do the same.
-      break;
-    default:
-      --it;
-      convert_arg<void>(arg, c);
+    OutputIt operator()(basic_string_view<Char> value) { return base::operator()(value); }
+
+    /** Formats a pointer. */
+    OutputIt operator()(const void *value) { return value ? base::operator()(value) : write_null_pointer(); }
+
+    /** Formats an argument of a custom (user-defined) type. */
+    OutputIt operator()(typename basic_format_arg<context_type>::handle handle)
+    {
+        auto parse_ctx = basic_printf_parse_context<Char>(basic_string_view<Char>());
+        handle.format(parse_ctx, context_);
+        return this->out;
     }
+};
 
-    // Parse type.
-    if (it == end) FMT_THROW(format_error("invalid format string"));
-    char type = static_cast<char>(*it++);
-    if (arg.is_integral()) {
-      // Normalize type.
-      switch (type) {
-      case 'i':
-      case 'u':
-        type = 'd';
-        break;
-      case 'c':
-        visit_format_arg(
-            detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
-            arg);
-        break;
-      }
+template <typename Char>
+void parse_flags(basic_format_specs<Char> &specs, const Char *&it, const Char *end)
+{
+    for(; it != end; ++it)
+    {
+        switch(*it)
+        {
+            case '-':
+                specs.align = align::left;
+                break;
+            case '+':
+                specs.sign = sign::plus;
+                break;
+            case '0':
+                specs.fill[0] = '0';
+                break;
+            case ' ':
+                if(specs.sign != sign::plus)
+                {
+                    specs.sign = sign::space;
+                }
+                break;
+            case '#':
+                specs.alt = true;
+                break;
+            default:
+                return;
+        }
     }
-    specs.type = parse_presentation_type(type);
-    if (specs.type == presentation_type::none)
-      parse_ctx.on_error("invalid type specifier");
+}
 
-    start = it;
+template <typename Char, typename GetArg>
+int parse_header(const Char *&it, const Char *end, basic_format_specs<Char> &specs, GetArg get_arg)
+{
+    int  arg_index = -1;
+    Char c         = *it;
+    if(c >= '0' && c <= '9')
+    {
+        // Parse an argument index (if followed by '$') or a width possibly
+        // preceded with '0' flag(s).
+        int value = parse_nonnegative_int(it, end, -1);
+        if(it != end && *it == '$')
+        { // value is an argument index
+            ++it;
+            arg_index = value != -1 ? value : max_value<int>();
+        }
+        else
+        {
+            if(c == '0')
+                specs.fill[0] = '0';
+            if(value != 0)
+            {
+                // Nonzero value means that we parsed width and don't need to
+                // parse it or flags again, so return now.
+                if(value == -1)
+                    FMT_THROW(format_error("number is too big"));
+                specs.width = value;
+                return arg_index;
+            }
+        }
+    }
+    parse_flags(specs, it, end);
+    // Parse width.
+    if(it != end)
+    {
+        if(*it >= '0' && *it <= '9')
+        {
+            specs.width = parse_nonnegative_int(it, end, -1);
+            if(specs.width == -1)
+                FMT_THROW(format_error("number is too big"));
+        }
+        else if(*it == '*')
+        {
+            ++it;
+            specs.width = static_cast<int>(visit_format_arg(detail::printf_width_handler<Char>(specs), get_arg(-1)));
+        }
+    }
+    return arg_index;
+}
 
-    // Format argument.
-    out = visit_format_arg(
-        detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
-  }
-  detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
+template <typename Char, typename Context>
+void vprintf(buffer<Char> &buf, basic_string_view<Char> format, basic_format_args<Context> args)
+{
+    using OutputIt = buffer_appender<Char>;
+    auto out       = OutputIt(buf);
+    auto context   = basic_printf_context<OutputIt, Char>(out, args);
+    auto parse_ctx = basic_printf_parse_context<Char>(format);
+
+    // Returns the argument with specified index or, if arg_index is -1, the next
+    // argument.
+    auto get_arg = [&](int arg_index)
+    {
+        if(arg_index < 0)
+            arg_index = parse_ctx.next_arg_id();
+        else
+            parse_ctx.check_arg_id(--arg_index);
+        return detail::get_arg(context, arg_index);
+    };
+
+    const Char *start = parse_ctx.begin();
+    const Char *end   = parse_ctx.end();
+    auto        it    = start;
+    while(it != end)
+    {
+        if(!detail::find<false, Char>(it, end, '%', it))
+        {
+            it = end; // detail::find leaves it == nullptr if it doesn't find '%'
+            break;
+        }
+        Char c = *it++;
+        if(it != end && *it == c)
+        {
+            out   = detail::write(out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
+            start = ++it;
+            continue;
+        }
+        out = detail::write(out, basic_string_view<Char>(start, detail::to_unsigned(it - 1 - start)));
+
+        basic_format_specs<Char> specs;
+        specs.align = align::right;
+
+        // Parse argument index, flags and width.
+        int arg_index = parse_header(it, end, specs, get_arg);
+        if(arg_index == 0)
+            parse_ctx.on_error("argument not found");
+
+        // Parse precision.
+        if(it != end && *it == '.')
+        {
+            ++it;
+            c = it != end ? *it : 0;
+            if('0' <= c && c <= '9')
+            {
+                specs.precision = parse_nonnegative_int(it, end, 0);
+            }
+            else if(c == '*')
+            {
+                ++it;
+                specs.precision = static_cast<int>(visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
+            }
+            else
+            {
+                specs.precision = 0;
+            }
+        }
+
+        auto arg = get_arg(arg_index);
+        // For d, i, o, u, x, and X conversion specifiers, if a precision is
+        // specified, the '0' flag is ignored
+        if(specs.precision >= 0 && arg.is_integral())
+            specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-' present.
+        if(specs.precision >= 0 && arg.type() == detail::type::cstring_type)
+        {
+            auto str     = visit_format_arg(detail::get_cstring<Char>(), arg);
+            auto str_end = str + specs.precision;
+            auto nul     = std::find(str, str_end, Char());
+            arg          = detail::make_arg<basic_printf_context<OutputIt, Char>>(
+                basic_string_view<Char>(str, detail::to_unsigned(nul != str_end ? nul - str : specs.precision)));
+        }
+        if(specs.alt && visit_format_arg(detail::is_zero_int(), arg))
+            specs.alt = false;
+        if(specs.fill[0] == '0')
+        {
+            if(arg.is_arithmetic() && specs.align != align::left)
+                specs.align = align::numeric;
+            else
+                specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
+                                     // flag is also present.
+        }
+
+        // Parse length and convert the argument to the required type.
+        c      = it != end ? *it++ : 0;
+        Char t = it != end ? *it : 0;
+        using detail::convert_arg;
+        switch(c)
+        {
+            case 'h':
+                if(t == 'h')
+                {
+                    ++it;
+                    t = it != end ? *it : 0;
+                    convert_arg<signed char>(arg, t);
+                }
+                else
+                {
+                    convert_arg<short>(arg, t);
+                }
+                break;
+            case 'l':
+                if(t == 'l')
+                {
+                    ++it;
+                    t = it != end ? *it : 0;
+                    convert_arg<long long>(arg, t);
+                }
+                else
+                {
+                    convert_arg<long>(arg, t);
+                }
+                break;
+            case 'j':
+                convert_arg<intmax_t>(arg, t);
+                break;
+            case 'z':
+                convert_arg<size_t>(arg, t);
+                break;
+            case 't':
+                convert_arg<std::ptrdiff_t>(arg, t);
+                break;
+            case 'L':
+                // printf produces garbage when 'L' is omitted for long double, no
+                // need to do the same.
+                break;
+            default:
+                --it;
+                convert_arg<void>(arg, c);
+        }
+
+        // Parse type.
+        if(it == end)
+            FMT_THROW(format_error("invalid format string"));
+        char type = static_cast<char>(*it++);
+        if(arg.is_integral())
+        {
+            // Normalize type.
+            switch(type)
+            {
+                case 'i':
+                case 'u':
+                    type = 'd';
+                    break;
+                case 'c':
+                    visit_format_arg(detail::char_converter<basic_printf_context<OutputIt, Char>>(arg), arg);
+                    break;
+            }
+        }
+        specs.type = parse_presentation_type(type);
+        if(specs.type == presentation_type::none)
+            parse_ctx.on_error("invalid type specifier");
+
+        start = it;
+
+        // Format argument.
+        out = visit_format_arg(detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
+    }
+    detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
 }
 FMT_END_DETAIL_NAMESPACE
 
 template <typename Char>
-using basic_printf_context_t =
-    basic_printf_context<detail::buffer_appender<Char>, Char>;
+using basic_printf_context_t = basic_printf_context<detail::buffer_appender<Char>, Char>;
 
-using printf_context = basic_printf_context_t<char>;
+using printf_context  = basic_printf_context_t<char>;
 using wprintf_context = basic_printf_context_t<wchar_t>;
 
-using printf_args = basic_format_args<printf_context>;
+using printf_args  = basic_format_args<printf_context>;
 using wprintf_args = basic_format_args<wprintf_context>;
 
 /**
@@ -537,9 +623,9 @@ using wprintf_args = basic_format_args<wprintf_context>;
   \endrst
  */
 template <typename... T>
-inline auto make_printf_args(const T&... args)
-    -> format_arg_store<printf_context, T...> {
-  return {args...};
+inline auto make_printf_args(const T &...args) -> format_arg_store<printf_context, T...>
+{
+    return {args...};
 }
 
 /**
@@ -549,19 +635,18 @@ inline auto make_printf_args(const T&... args)
   \endrst
  */
 template <typename... T>
-inline auto make_wprintf_args(const T&... args)
-    -> format_arg_store<wprintf_context, T...> {
-  return {args...};
+inline auto make_wprintf_args(const T &...args) -> format_arg_store<wprintf_context, T...>
+{
+    return {args...};
 }
 
 template <typename S, typename Char = char_t<S>>
-inline auto vsprintf(
-    const S& fmt,
-    basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-    -> std::basic_string<Char> {
-  basic_memory_buffer<Char> buffer;
-  vprintf(buffer, detail::to_string_view(fmt), args);
-  return to_string(buffer);
+inline auto vsprintf(const S &fmt, basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
+    -> std::basic_string<Char>
+{
+    basic_memory_buffer<Char> buffer;
+    vprintf(buffer, detail::to_string_view(fmt), args);
+    return to_string(buffer);
 }
 
 /**
@@ -573,25 +658,21 @@ inline auto vsprintf(
     std::string message = fmt::sprintf("The answer is %d", 42);
   \endrst
 */
-template <typename S, typename... T,
-          typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
-inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
-  using context = basic_printf_context_t<Char>;
-  return vsprintf(detail::to_string_view(fmt),
-                  fmt::make_format_args<context>(args...));
+template <typename S, typename... T, typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
+inline auto sprintf(const S &fmt, const T &...args) -> std::basic_string<Char>
+{
+    using context = basic_printf_context_t<Char>;
+    return vsprintf(detail::to_string_view(fmt), fmt::make_format_args<context>(args...));
 }
 
 template <typename S, typename Char = char_t<S>>
-inline auto vfprintf(
-    std::FILE* f, const S& fmt,
-    basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-    -> int {
-  basic_memory_buffer<Char> buffer;
-  vprintf(buffer, detail::to_string_view(fmt), args);
-  size_t size = buffer.size();
-  return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
-             ? -1
-             : static_cast<int>(size);
+inline auto vfprintf(std::FILE *f, const S &fmt, basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
+    -> int
+{
+    basic_memory_buffer<Char> buffer;
+    vprintf(buffer, detail::to_string_view(fmt), args);
+    size_t size = buffer.size();
+    return std::fwrite(buffer.data(), sizeof(Char), size, f) < size ? -1 : static_cast<int>(size);
 }
 
 /**
@@ -604,18 +685,16 @@ inline auto vfprintf(
   \endrst
  */
 template <typename S, typename... T, typename Char = char_t<S>>
-inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
-  using context = basic_printf_context_t<Char>;
-  return vfprintf(f, detail::to_string_view(fmt),
-                  fmt::make_format_args<context>(args...));
+inline auto fprintf(std::FILE *f, const S &fmt, const T &...args) -> int
+{
+    using context = basic_printf_context_t<Char>;
+    return vfprintf(f, detail::to_string_view(fmt), fmt::make_format_args<context>(args...));
 }
 
 template <typename S, typename Char = char_t<S>>
-inline auto vprintf(
-    const S& fmt,
-    basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-    -> int {
-  return vfprintf(stdout, detail::to_string_view(fmt), args);
+inline auto vprintf(const S &fmt, basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) -> int
+{
+    return vfprintf(stdout, detail::to_string_view(fmt), args);
 }
 
 /**
@@ -628,13 +707,12 @@ inline auto vprintf(
   \endrst
  */
 template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
-inline auto printf(const S& fmt, const T&... args) -> int {
-  return vprintf(
-      detail::to_string_view(fmt),
-      fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
+inline auto printf(const S &fmt, const T &...args) -> int
+{
+    return vprintf(detail::to_string_view(fmt), fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
 }
 
 FMT_MODULE_EXPORT_END
 FMT_END_NAMESPACE
 
-#endif  // FMT_PRINTF_H_
+#endif // FMT_PRINTF_H_
diff --git a/include/spdlog/fmt/bundled/ranges.h b/include/spdlog/fmt/bundled/ranges.h
index dea7d60dd..73cdbbbc7 100644
--- a/include/spdlog/fmt/bundled/ranges.h
+++ b/include/spdlog/fmt/bundled/ranges.h
@@ -12,191 +12,238 @@
 #ifndef FMT_RANGES_H_
 #define FMT_RANGES_H_
 
+#include "format.h"
+
 #include <initializer_list>
 #include <tuple>
 #include <type_traits>
 
-#include "format.h"
-
 FMT_BEGIN_NAMESPACE
 
-namespace detail {
+namespace detail
+{
 
 template <typename RangeT, typename OutputIterator>
-OutputIterator copy(const RangeT& range, OutputIterator out) {
-  for (auto it = range.begin(), end = range.end(); it != end; ++it)
-    *out++ = *it;
-  return out;
+OutputIterator copy(const RangeT &range, OutputIterator out)
+{
+    for(auto it = range.begin(), end = range.end(); it != end; ++it)
+        *out++ = *it;
+    return out;
 }
 
 template <typename OutputIterator>
-OutputIterator copy(const char* str, OutputIterator out) {
-  while (*str) *out++ = *str++;
-  return out;
+OutputIterator copy(const char *str, OutputIterator out)
+{
+    while(*str)
+        *out++ = *str++;
+    return out;
 }
 
 template <typename OutputIterator>
-OutputIterator copy(char ch, OutputIterator out) {
-  *out++ = ch;
-  return out;
+OutputIterator copy(char ch, OutputIterator out)
+{
+    *out++ = ch;
+    return out;
 }
 
 template <typename OutputIterator>
-OutputIterator copy(wchar_t ch, OutputIterator out) {
-  *out++ = ch;
-  return out;
+OutputIterator copy(wchar_t ch, OutputIterator out)
+{
+    *out++ = ch;
+    return out;
 }
 
 // Returns true if T has a std::string-like interface, like std::string_view.
-template <typename T> class is_std_string_like {
-  template <typename U>
-  static auto check(U* p)
-      -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
-  template <typename> static void check(...);
-
- public:
-  static constexpr const bool value =
-      is_string<T>::value ||
-      std::is_convertible<T, std_string_view<char>>::value ||
-      !std::is_void<decltype(check<T>(nullptr))>::value;
+template <typename T>
+class is_std_string_like
+{
+    template <typename U>
+    static auto check(U *p) -> decltype((void) p->find('a'), p->length(), (void) p->data(), int());
+    template <typename>
+    static void check(...);
+
+public:
+    static constexpr const bool value = is_string<T>::value || std::is_convertible<T, std_string_view<char>>::value ||
+                                        !std::is_void<decltype(check<T>(nullptr))>::value;
 };
 
 template <typename Char>
-struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
-
-template <typename T> class is_map {
-  template <typename U> static auto check(U*) -> typename U::mapped_type;
-  template <typename> static void check(...);
+struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type
+{
+};
 
- public:
+template <typename T>
+class is_map
+{
+    template <typename U>
+    static auto check(U *) -> typename U::mapped_type;
+    template <typename>
+    static void check(...);
+
+public:
 #ifdef FMT_FORMAT_MAP_AS_LIST
-  static constexpr const bool value = false;
+    static constexpr const bool value = false;
 #else
-  static constexpr const bool value =
-      !std::is_void<decltype(check<T>(nullptr))>::value;
+    static constexpr const bool value = !std::is_void<decltype(check<T>(nullptr))>::value;
 #endif
 };
 
-template <typename T> class is_set {
-  template <typename U> static auto check(U*) -> typename U::key_type;
-  template <typename> static void check(...);
-
- public:
+template <typename T>
+class is_set
+{
+    template <typename U>
+    static auto check(U *) -> typename U::key_type;
+    template <typename>
+    static void check(...);
+
+public:
 #ifdef FMT_FORMAT_SET_AS_LIST
-  static constexpr const bool value = false;
+    static constexpr const bool value = false;
 #else
-  static constexpr const bool value =
-      !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
+    static constexpr const bool value = !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
 #endif
 };
 
-template <typename... Ts> struct conditional_helper {};
+template <typename... Ts>
+struct conditional_helper
+{
+};
 
-template <typename T, typename _ = void> struct is_range_ : std::false_type {};
+template <typename T, typename _ = void>
+struct is_range_ : std::false_type
+{
+};
 
 #if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
 
-#  define FMT_DECLTYPE_RETURN(val)  \
-    ->decltype(val) { return val; } \
-    static_assert(                  \
-        true, "")  // This makes it so that a semicolon is required after the
-                   // macro, which helps clang-format handle the formatting.
+#define FMT_DECLTYPE_RETURN(val)                                                                                       \
+    ->decltype(val)                                                                                                    \
+    {                                                                                                                  \
+        return val;                                                                                                    \
+    }                                                                                                                  \
+    static_assert(true, "") // This makes it so that a semicolon is required after the
+                            // macro, which helps clang-format handle the formatting.
 
 // C array overload
 template <typename T, std::size_t N>
-auto range_begin(const T (&arr)[N]) -> const T* {
-  return arr;
+auto range_begin(const T (&arr)[N]) -> const T *
+{
+    return arr;
 }
 template <typename T, std::size_t N>
-auto range_end(const T (&arr)[N]) -> const T* {
-  return arr + N;
+auto range_end(const T (&arr)[N]) -> const T *
+{
+    return arr + N;
 }
 
 template <typename T, typename Enable = void>
-struct has_member_fn_begin_end_t : std::false_type {};
+struct has_member_fn_begin_end_t : std::false_type
+{
+};
 
 template <typename T>
-struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
-                                           decltype(std::declval<T>().end())>>
-    : std::true_type {};
+struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()), decltype(std::declval<T>().end())>>
+    : std::true_type
+{
+};
 
 // Member function overload
 template <typename T>
-auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
+auto range_begin(T &&rng) FMT_DECLTYPE_RETURN(static_cast<T &&>(rng).begin());
 template <typename T>
-auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
+auto range_end(T &&rng) FMT_DECLTYPE_RETURN(static_cast<T &&>(rng).end());
 
 // ADL overload. Only participates in overload resolution if member functions
 // are not found.
 template <typename T>
-auto range_begin(T&& rng)
-    -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
-                   decltype(begin(static_cast<T&&>(rng)))> {
-  return begin(static_cast<T&&>(rng));
+auto range_begin(T &&rng)
+    -> enable_if_t<!has_member_fn_begin_end_t<T &&>::value, decltype(begin(static_cast<T &&>(rng)))>
+{
+    return begin(static_cast<T &&>(rng));
 }
 template <typename T>
-auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
-                                       decltype(end(static_cast<T&&>(rng)))> {
-  return end(static_cast<T&&>(rng));
+auto range_end(T &&rng) -> enable_if_t<!has_member_fn_begin_end_t<T &&>::value, decltype(end(static_cast<T &&>(rng)))>
+{
+    return end(static_cast<T &&>(rng));
 }
 
 template <typename T, typename Enable = void>
-struct has_const_begin_end : std::false_type {};
+struct has_const_begin_end : std::false_type
+{
+};
 template <typename T, typename Enable = void>
-struct has_mutable_begin_end : std::false_type {};
+struct has_mutable_begin_end : std::false_type
+{
+};
 
 template <typename T>
 struct has_const_begin_end<
     T,
     void_t<
-        decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
-        decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
-    : std::true_type {};
+        decltype(detail::range_begin(std::declval<const remove_cvref_t<T> &>())),
+        decltype(detail::range_end(std::declval<const remove_cvref_t<T> &>()))>> : std::true_type
+{
+};
 
 template <typename T>
 struct has_mutable_begin_end<
-    T, void_t<decltype(detail::range_begin(std::declval<T>())),
-              decltype(detail::range_end(std::declval<T>())),
-              enable_if_t<std::is_copy_constructible<T>::value>>>
-    : std::true_type {};
+    T,
+    void_t<
+        decltype(detail::range_begin(std::declval<T>())),
+        decltype(detail::range_end(std::declval<T>())),
+        enable_if_t<std::is_copy_constructible<T>::value>>> : std::true_type
+{
+};
 
 template <typename T>
 struct is_range_<T, void>
-    : std::integral_constant<bool, (has_const_begin_end<T>::value ||
-                                    has_mutable_begin_end<T>::value)> {};
-#  undef FMT_DECLTYPE_RETURN
+    : std::integral_constant<bool, (has_const_begin_end<T>::value || has_mutable_begin_end<T>::value)>
+{
+};
+#undef FMT_DECLTYPE_RETURN
 #endif
 
 // tuple_size and tuple_element check.
-template <typename T> class is_tuple_like_ {
-  template <typename U>
-  static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
-  template <typename> static void check(...);
-
- public:
-  static constexpr const bool value =
-      !std::is_void<decltype(check<T>(nullptr))>::value;
+template <typename T>
+class is_tuple_like_
+{
+    template <typename U>
+    static auto check(U *p) -> decltype(std::tuple_size<U>::value, int());
+    template <typename>
+    static void check(...);
+
+public:
+    static constexpr const bool value = !std::is_void<decltype(check<T>(nullptr))>::value;
 };
 
 // Check for integer_sequence
 #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
 template <typename T, T... N>
 using integer_sequence = std::integer_sequence<T, N...>;
-template <size_t... N> using index_sequence = std::index_sequence<N...>;
-template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
+template <size_t... N>
+using index_sequence = std::index_sequence<N...>;
+template <size_t N>
+using make_index_sequence = std::make_index_sequence<N>;
 #else
-template <typename T, T... N> struct integer_sequence {
-  using value_type = T;
+template <typename T, T... N>
+struct integer_sequence
+{
+    using value_type = T;
 
-  static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
+    static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
 };
 
-template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
+template <size_t... N>
+using index_sequence = integer_sequence<size_t, N...>;
 
 template <typename T, size_t N, T... Ns>
-struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
+struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...>
+{
+};
 template <typename T, T... Ns>
-struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
+struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...>
+{
+};
 
 template <size_t N>
 using make_index_sequence = make_integer_sequence<size_t, N>;
@@ -206,62 +253,68 @@ template <typename T>
 using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
 
 template <typename T, typename C, bool = is_tuple_like_<T>::value>
-class is_tuple_formattable_ {
- public:
-  static constexpr const bool value = false;
-};
-template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
-  template <std::size_t... I>
-  static std::true_type check2(index_sequence<I...>,
-                               integer_sequence<bool, (I == I)...>);
-  static std::false_type check2(...);
-  template <std::size_t... I>
-  static decltype(check2(
-      index_sequence<I...>{},
-      integer_sequence<
-          bool, (is_formattable<typename std::tuple_element<I, T>::type,
-                                C>::value)...>{})) check(index_sequence<I...>);
-
- public:
-  static constexpr const bool value =
-      decltype(check(tuple_index_sequence<T>{}))::value;
+class is_tuple_formattable_
+{
+public:
+    static constexpr const bool value = false;
+};
+template <typename T, typename C>
+class is_tuple_formattable_<T, C, true>
+{
+    template <std::size_t... I>
+    static std::true_type  check2(index_sequence<I...>, integer_sequence<bool, (I == I)...>);
+    static std::false_type check2(...);
+    template <std::size_t... I>
+    static decltype(check2(
+        index_sequence<I...>{},
+        integer_sequence<bool, (is_formattable<typename std::tuple_element<I, T>::type, C>::value)...>{}))
+        check(index_sequence<I...>);
+
+public:
+    static constexpr const bool value = decltype(check(tuple_index_sequence<T>{}))::value;
 };
 
 template <class Tuple, class F, size_t... Is>
-void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) noexcept {
-  using std::get;
-  // using free function get<I>(T) now.
-  const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
-  (void)_;  // blocks warnings
+void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) noexcept
+{
+    using std::get;
+    // using free function get<I>(T) now.
+    const int _[] = {0, ((void) f(get<Is>(tup)), 0)...};
+    (void) _; // blocks warnings
 }
 
 template <class T>
-FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
-    T const&) {
-  return {};
+FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(T const &)
+{
+    return {};
 }
 
-template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
-  const auto indexes = get_indexes(tup);
-  for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
+template <class Tuple, class F>
+void for_each(Tuple &&tup, F &&f)
+{
+    const auto indexes = get_indexes(tup);
+    for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
 }
 
 #if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
 // Older MSVC doesn't get the reference type correctly for arrays.
-template <typename R> struct range_reference_type_impl {
-  using type = decltype(*detail::range_begin(std::declval<R&>()));
+template <typename R>
+struct range_reference_type_impl
+{
+    using type = decltype(*detail::range_begin(std::declval<R &>()));
 };
 
-template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
-  using type = T&;
+template <typename T, std::size_t N>
+struct range_reference_type_impl<T[N]>
+{
+    using type = T &;
 };
 
 template <typename T>
 using range_reference_type = typename range_reference_type_impl<T>::type;
 #else
 template <typename Range>
-using range_reference_type =
-    decltype(*detail::range_begin(std::declval<Range&>()));
+using range_reference_type = decltype(*detail::range_begin(std::declval<Range &>()));
 #endif
 
 // We don't use the Range's value_type for anything, but we do need the Range's
@@ -270,262 +323,288 @@ template <typename Range>
 using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
 
 template <typename Range>
-using uncvref_first_type =
-    remove_cvref_t<decltype(std::declval<range_reference_type<Range>>().first)>;
+using uncvref_first_type = remove_cvref_t<decltype(std::declval<range_reference_type<Range>>().first)>;
 
 template <typename Range>
-using uncvref_second_type = remove_cvref_t<
-    decltype(std::declval<range_reference_type<Range>>().second)>;
+using uncvref_second_type = remove_cvref_t<decltype(std::declval<range_reference_type<Range>>().second)>;
 
-template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
-  *out++ = ',';
-  *out++ = ' ';
-  return out;
+template <typename OutputIt>
+OutputIt write_delimiter(OutputIt out)
+{
+    *out++ = ',';
+    *out++ = ' ';
+    return out;
 }
 
 template <typename Char, typename OutputIt>
-auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
-  return write_escaped_string(out, str);
+auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt
+{
+    return write_escaped_string(out, str);
 }
 
-template <typename Char, typename OutputIt, typename T,
-          FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
-inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
-  auto sv = std_string_view<Char>(str);
-  return write_range_entry<Char>(out, basic_string_view<Char>(sv));
+template <
+    typename Char,
+    typename OutputIt,
+    typename T,
+    FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
+inline auto write_range_entry(OutputIt out, const T &str) -> OutputIt
+{
+    auto sv = std_string_view<Char>(str);
+    return write_range_entry<Char>(out, basic_string_view<Char>(sv));
 }
 
-template <typename Char, typename OutputIt, typename Arg,
-          FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
-OutputIt write_range_entry(OutputIt out, const Arg v) {
-  return write_escaped_char(out, v);
+template <typename Char, typename OutputIt, typename Arg, FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
+OutputIt write_range_entry(OutputIt out, const Arg v)
+{
+    return write_escaped_char(out, v);
 }
 
 template <
-    typename Char, typename OutputIt, typename Arg,
-    FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
-                  !std::is_same<Arg, Char>::value)>
-OutputIt write_range_entry(OutputIt out, const Arg& v) {
-  return write<Char>(out, v);
+    typename Char,
+    typename OutputIt,
+    typename Arg,
+    FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value && !std::is_same<Arg, Char>::value)>
+OutputIt write_range_entry(OutputIt out, const Arg &v)
+{
+    return write<Char>(out, v);
 }
 
-}  // namespace detail
+} // namespace detail
 
-template <typename T> struct is_tuple_like {
-  static constexpr const bool value =
-      detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
+template <typename T>
+struct is_tuple_like
+{
+    static constexpr const bool value = detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
 };
 
-template <typename T, typename C> struct is_tuple_formattable {
-  static constexpr const bool value =
-      detail::is_tuple_formattable_<T, C>::value;
+template <typename T, typename C>
+struct is_tuple_formattable
+{
+    static constexpr const bool value = detail::is_tuple_formattable_<T, C>::value;
 };
 
 template <typename TupleT, typename Char>
-struct formatter<TupleT, Char,
-                 enable_if_t<fmt::is_tuple_like<TupleT>::value &&
-                             fmt::is_tuple_formattable<TupleT, Char>::value>> {
- private:
-  basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
-  basic_string_view<Char> opening_bracket_ =
-      detail::string_literal<Char, '('>{};
-  basic_string_view<Char> closing_bracket_ =
-      detail::string_literal<Char, ')'>{};
-
-  // C++11 generic lambda for format().
-  template <typename FormatContext> struct format_each {
-    template <typename T> void operator()(const T& v) {
-      if (i > 0) out = detail::copy_str<Char>(separator, out);
-      out = detail::write_range_entry<Char>(out, v);
-      ++i;
+struct formatter<
+    TupleT,
+    Char,
+    enable_if_t<fmt::is_tuple_like<TupleT>::value && fmt::is_tuple_formattable<TupleT, Char>::value>>
+{
+private:
+    basic_string_view<Char> separator_       = detail::string_literal<Char, ',', ' '>{};
+    basic_string_view<Char> opening_bracket_ = detail::string_literal<Char, '('>{};
+    basic_string_view<Char> closing_bracket_ = detail::string_literal<Char, ')'>{};
+
+    // C++11 generic lambda for format().
+    template <typename FormatContext>
+    struct format_each
+    {
+        template <typename T>
+        void operator()(const T &v)
+        {
+            if(i > 0)
+                out = detail::copy_str<Char>(separator, out);
+            out = detail::write_range_entry<Char>(out, v);
+            ++i;
+        }
+        int                               i;
+        typename FormatContext::iterator &out;
+        basic_string_view<Char>           separator;
+    };
+
+public:
+    FMT_CONSTEXPR formatter() {}
+
+    FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { separator_ = sep; }
+
+    FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, basic_string_view<Char> close)
+    {
+        opening_bracket_ = open;
+        closing_bracket_ = close;
+    }
+
+    template <typename ParseContext>
+    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
+    {
+        return ctx.begin();
     }
-    int i;
-    typename FormatContext::iterator& out;
-    basic_string_view<Char> separator;
-  };
-
- public:
-  FMT_CONSTEXPR formatter() {}
-
-  FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
-    separator_ = sep;
-  }
-
-  FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
-                                  basic_string_view<Char> close) {
-    opening_bracket_ = open;
-    closing_bracket_ = close;
-  }
-
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    return ctx.begin();
-  }
-
-  template <typename FormatContext = format_context>
-  auto format(const TupleT& values, FormatContext& ctx) const
-      -> decltype(ctx.out()) {
-    auto out = ctx.out();
-    out = detail::copy_str<Char>(opening_bracket_, out);
-    detail::for_each(values, format_each<FormatContext>{0, out, separator_});
-    out = detail::copy_str<Char>(closing_bracket_, out);
-    return out;
-  }
-};
 
-template <typename T, typename Char> struct is_range {
-  static constexpr const bool value =
-      detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
-      !std::is_convertible<T, std::basic_string<Char>>::value &&
-      !std::is_convertible<T, detail::std_string_view<Char>>::value;
+    template <typename FormatContext = format_context>
+    auto format(const TupleT &values, FormatContext &ctx) const -> decltype(ctx.out())
+    {
+        auto out = ctx.out();
+        out      = detail::copy_str<Char>(opening_bracket_, out);
+        detail::for_each(values, format_each<FormatContext>{0, out, separator_});
+        out = detail::copy_str<Char>(closing_bracket_, out);
+        return out;
+    }
 };
 
-namespace detail {
-template <typename Context> struct range_mapper {
-  using mapper = arg_mapper<Context>;
+template <typename T, typename Char>
+struct is_range
+{
+    static constexpr const bool value = detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
+                                        !std::is_convertible<T, std::basic_string<Char>>::value &&
+                                        !std::is_convertible<T, detail::std_string_view<Char>>::value;
+};
 
-  template <typename T,
-            FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
-  static auto map(T&& value) -> T&& {
-    return static_cast<T&&>(value);
-  }
-  template <typename T,
-            FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
-  static auto map(T&& value)
-      -> decltype(mapper().map(static_cast<T&&>(value))) {
-    return mapper().map(static_cast<T&&>(value));
-  }
+namespace detail
+{
+template <typename Context>
+struct range_mapper
+{
+    using mapper = arg_mapper<Context>;
+
+    template <typename T, FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
+    static auto map(T &&value) -> T &&
+    {
+        return static_cast<T &&>(value);
+    }
+    template <typename T, FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
+    static auto map(T &&value) -> decltype(mapper().map(static_cast<T &&>(value)))
+    {
+        return mapper().map(static_cast<T &&>(value));
+    }
 };
 
 template <typename Char, typename Element>
 using range_formatter_type = conditional_t<
     is_formattable<Element, Char>::value,
-    formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
-                  std::declval<Element>()))>,
-              Char>,
+    formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(std::declval<Element>()))>, Char>,
     fallback_formatter<Element, Char>>;
 
 template <typename R>
-using maybe_const_range =
-    conditional_t<has_const_begin_end<R>::value, const R, R>;
+using maybe_const_range = conditional_t<has_const_begin_end<R>::value, const R, R>;
 
 // Workaround a bug in MSVC 2015 and earlier.
 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
 template <typename R, typename Char>
-struct is_formattable_delayed
-    : disjunction<
-          is_formattable<uncvref_type<maybe_const_range<R>>, Char>,
-          has_fallback_formatter<uncvref_type<maybe_const_range<R>>, Char>> {};
+struct is_formattable_delayed : disjunction<
+                                    is_formattable<uncvref_type<maybe_const_range<R>>, Char>,
+                                    has_fallback_formatter<uncvref_type<maybe_const_range<R>>, Char>>
+{
+};
 #endif
 
-}  // namespace detail
+} // namespace detail
 
 template <typename T, typename Char, typename Enable = void>
 struct range_formatter;
 
 template <typename T, typename Char>
 struct range_formatter<
-    T, Char,
+    T,
+    Char,
     enable_if_t<conjunction<
         std::is_same<T, remove_cvref_t<T>>,
-        disjunction<is_formattable<T, Char>,
-                    detail::has_fallback_formatter<T, Char>>>::value>> {
- private:
-  detail::range_formatter_type<Char, T> underlying_;
-  bool custom_specs_ = false;
-  basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
-  basic_string_view<Char> opening_bracket_ =
-      detail::string_literal<Char, '['>{};
-  basic_string_view<Char> closing_bracket_ =
-      detail::string_literal<Char, ']'>{};
-
-  template <class U>
-  FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, int)
-      -> decltype(u.set_debug_format()) {
-    u.set_debug_format();
-  }
-
-  template <class U>
-  FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
-
-  FMT_CONSTEXPR void maybe_set_debug_format() {
-    maybe_set_debug_format(underlying_, 0);
-  }
-
- public:
-  FMT_CONSTEXPR range_formatter() {}
-
-  FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
-    return underlying_;
-  }
-
-  FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
-    separator_ = sep;
-  }
-
-  FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
-                                  basic_string_view<Char> close) {
-    opening_bracket_ = open;
-    closing_bracket_ = close;
-  }
-
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    auto it = ctx.begin();
-    auto end = ctx.end();
-    if (it == end || *it == '}') {
-      maybe_set_debug_format();
-      return it;
+        disjunction<is_formattable<T, Char>, detail::has_fallback_formatter<T, Char>>>::value>>
+{
+private:
+    detail::range_formatter_type<Char, T> underlying_;
+    bool                                  custom_specs_    = false;
+    basic_string_view<Char>               separator_       = detail::string_literal<Char, ',', ' '>{};
+    basic_string_view<Char>               opening_bracket_ = detail::string_literal<Char, '['>{};
+    basic_string_view<Char>               closing_bracket_ = detail::string_literal<Char, ']'>{};
+
+    template <class U>
+    FMT_CONSTEXPR static auto maybe_set_debug_format(U &u, int) -> decltype(u.set_debug_format())
+    {
+        u.set_debug_format();
     }
 
-    if (*it == 'n') {
-      set_brackets({}, {});
-      ++it;
+    template <class U>
+    FMT_CONSTEXPR static void maybe_set_debug_format(U &, ...)
+    {
     }
 
-    if (*it == '}') {
-      maybe_set_debug_format();
-      return it;
+    FMT_CONSTEXPR void maybe_set_debug_format() { maybe_set_debug_format(underlying_, 0); }
+
+public:
+    FMT_CONSTEXPR range_formatter() {}
+
+    FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T> & { return underlying_; }
+
+    FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { separator_ = sep; }
+
+    FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, basic_string_view<Char> close)
+    {
+        opening_bracket_ = open;
+        closing_bracket_ = close;
     }
 
-    if (*it != ':')
-      FMT_THROW(format_error("no other top-level range formatters supported"));
-
-    custom_specs_ = true;
-    ++it;
-    ctx.advance_to(it);
-    return underlying_.parse(ctx);
-  }
-
-  template <typename R, class FormatContext>
-  auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
-    detail::range_mapper<buffer_context<Char>> mapper;
-    auto out = ctx.out();
-    out = detail::copy_str<Char>(opening_bracket_, out);
-    int i = 0;
-    auto it = detail::range_begin(range);
-    auto end = detail::range_end(range);
-    for (; it != end; ++it) {
-      if (i > 0) out = detail::copy_str<Char>(separator_, out);
-      ;
-      ctx.advance_to(out);
-      out = underlying_.format(mapper.map(*it), ctx);
-      ++i;
+    template <typename ParseContext>
+    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
+    {
+        auto it  = ctx.begin();
+        auto end = ctx.end();
+        if(it == end || *it == '}')
+        {
+            maybe_set_debug_format();
+            return it;
+        }
+
+        if(*it == 'n')
+        {
+            set_brackets({}, {});
+            ++it;
+        }
+
+        if(*it == '}')
+        {
+            maybe_set_debug_format();
+            return it;
+        }
+
+        if(*it != ':')
+            FMT_THROW(format_error("no other top-level range formatters supported"));
+
+        custom_specs_ = true;
+        ++it;
+        ctx.advance_to(it);
+        return underlying_.parse(ctx);
+    }
+
+    template <typename R, class FormatContext>
+    auto format(R &&range, FormatContext &ctx) const -> decltype(ctx.out())
+    {
+        detail::range_mapper<buffer_context<Char>> mapper;
+        auto                                       out = ctx.out();
+        out                                            = detail::copy_str<Char>(opening_bracket_, out);
+        int  i                                         = 0;
+        auto it                                        = detail::range_begin(range);
+        auto end                                       = detail::range_end(range);
+        for(; it != end; ++it)
+        {
+            if(i > 0)
+                out = detail::copy_str<Char>(separator_, out);
+            ;
+            ctx.advance_to(out);
+            out = underlying_.format(mapper.map(*it), ctx);
+            ++i;
+        }
+        out = detail::copy_str<Char>(closing_bracket_, out);
+        return out;
     }
-    out = detail::copy_str<Char>(closing_bracket_, out);
-    return out;
-  }
 };
 
-enum class range_format { disabled, map, set, sequence, string, debug_string };
+enum class range_format
+{
+    disabled,
+    map,
+    set,
+    sequence,
+    string,
+    debug_string
+};
 
-namespace detail {
-template <typename T> struct range_format_kind_ {
-  static constexpr auto value = std::is_same<range_reference_type<T>, T>::value
-                                    ? range_format::disabled
-                                : is_map<T>::value ? range_format::map
-                                : is_set<T>::value ? range_format::set
-                                                   : range_format::sequence;
+namespace detail
+{
+template <typename T>
+struct range_format_kind_
+{
+    static constexpr auto value = std::is_same<range_reference_type<T>, T>::value ? range_format::disabled :
+                                  is_map<T>::value                                ? range_format::map :
+                                  is_set<T>::value                                ? range_format::set :
+                                                                                    range_format::sequence;
 };
 
 template <range_format K, typename R, typename Char, typename Enable = void>
@@ -536,69 +615,74 @@ using range_format_constant = std::integral_constant<range_format, K>;
 
 template <range_format K, typename R, typename Char>
 struct range_default_formatter<
-    K, R, Char,
-    enable_if_t<(K == range_format::sequence || K == range_format::map ||
-                 K == range_format::set)>> {
-  using range_type = detail::maybe_const_range<R>;
-  range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
-
-  FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
-
-  FMT_CONSTEXPR void init(range_format_constant<range_format::set>) {
-    underlying_.set_brackets(detail::string_literal<Char, '{'>{},
-                             detail::string_literal<Char, '}'>{});
-  }
-
-  FMT_CONSTEXPR void init(range_format_constant<range_format::map>) {
-    underlying_.set_brackets(detail::string_literal<Char, '{'>{},
-                             detail::string_literal<Char, '}'>{});
-    underlying_.underlying().set_brackets({}, {});
-    underlying_.underlying().set_separator(
-        detail::string_literal<Char, ':', ' '>{});
-  }
-
-  FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
-
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    return underlying_.parse(ctx);
-  }
-
-  template <typename FormatContext>
-  auto format(range_type& range, FormatContext& ctx) const
-      -> decltype(ctx.out()) {
-    return underlying_.format(range, ctx);
-  }
-};
-}  // namespace detail
+    K,
+    R,
+    Char,
+    enable_if_t<(K == range_format::sequence || K == range_format::map || K == range_format::set)>>
+{
+    using range_type = detail::maybe_const_range<R>;
+    range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
+
+    FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
+
+    FMT_CONSTEXPR void init(range_format_constant<range_format::set>)
+    {
+        underlying_.set_brackets(detail::string_literal<Char, '{'>{}, detail::string_literal<Char, '}'>{});
+    }
+
+    FMT_CONSTEXPR void init(range_format_constant<range_format::map>)
+    {
+        underlying_.set_brackets(detail::string_literal<Char, '{'>{}, detail::string_literal<Char, '}'>{});
+        underlying_.underlying().set_brackets({}, {});
+        underlying_.underlying().set_separator(detail::string_literal<Char, ':', ' '>{});
+    }
+
+    FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
+
+    template <typename ParseContext>
+    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
+    {
+        return underlying_.parse(ctx);
+    }
+
+    template <typename FormatContext>
+    auto format(range_type &range, FormatContext &ctx) const -> decltype(ctx.out())
+    {
+        return underlying_.format(range, ctx);
+    }
+};
+} // namespace detail
 
 template <typename T, typename Char, typename Enable = void>
-struct range_format_kind
-    : conditional_t<
-          is_range<T, Char>::value, detail::range_format_kind_<T>,
-          std::integral_constant<range_format, range_format::disabled>> {};
+struct range_format_kind : conditional_t<
+                               is_range<T, Char>::value,
+                               detail::range_format_kind_<T>,
+                               std::integral_constant<range_format, range_format::disabled>>
+{
+};
 
 template <typename R, typename Char>
 struct formatter<
-    R, Char,
-    enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value !=
-                                          range_format::disabled>
+    R,
+    Char,
+    enable_if_t<conjunction<
+        bool_constant<range_format_kind<R, Char>::value != range_format::disabled>
 // Workaround a bug in MSVC 2015 and earlier.
 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
-                            ,
-                            detail::is_formattable_delayed<R, Char>
+        ,
+        detail::is_formattable_delayed<R, Char>
 #endif
-                            >::value>>
-    : detail::range_default_formatter<range_format_kind<R, Char>::value, R,
-                                      Char> {
+        >::value>> : detail::range_default_formatter<range_format_kind<R, Char>::value, R, Char>
+{
 };
 
-template <typename Char, typename... T> struct tuple_join_view : detail::view {
-  const std::tuple<T...>& tuple;
-  basic_string_view<Char> sep;
+template <typename Char, typename... T>
+struct tuple_join_view : detail::view
+{
+    const std::tuple<T...> &tuple;
+    basic_string_view<Char> sep;
 
-  tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
-      : tuple(t), sep{s} {}
+    tuple_join_view(const std::tuple<T...> &t, basic_string_view<Char> s) : tuple(t), sep{s} {}
 };
 
 template <typename Char, typename... T>
@@ -608,69 +692,70 @@ using tuple_arg_join = tuple_join_view<Char, T...>;
 // support in tuple_join. It is disabled by default because of issues with
 // the dynamic width and precision.
 #ifndef FMT_TUPLE_JOIN_SPECIFIERS
-#  define FMT_TUPLE_JOIN_SPECIFIERS 0
+#define FMT_TUPLE_JOIN_SPECIFIERS 0
 #endif
 
 template <typename Char, typename... T>
-struct formatter<tuple_join_view<Char, T...>, Char> {
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
-  }
-
-  template <typename FormatContext>
-  auto format(const tuple_join_view<Char, T...>& value,
-              FormatContext& ctx) const -> typename FormatContext::iterator {
-    return do_format(value, ctx,
-                     std::integral_constant<size_t, sizeof...(T)>());
-  }
-
- private:
-  std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
-
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
-                              std::integral_constant<size_t, 0>)
-      -> decltype(ctx.begin()) {
-    return ctx.begin();
-  }
-
-  template <typename ParseContext, size_t N>
-  FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
-                              std::integral_constant<size_t, N>)
-      -> decltype(ctx.begin()) {
-    auto end = ctx.begin();
-#if FMT_TUPLE_JOIN_SPECIFIERS
-    end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
-    if (N > 1) {
-      auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
-      if (end != end1)
-        FMT_THROW(format_error("incompatible format specs for tuple elements"));
+struct formatter<tuple_join_view<Char, T...>, Char>
+{
+    template <typename ParseContext>
+    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
+    {
+        return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
+    }
+
+    template <typename FormatContext>
+    auto format(const tuple_join_view<Char, T...> &value, FormatContext &ctx) const -> typename FormatContext::iterator
+    {
+        return do_format(value, ctx, std::integral_constant<size_t, sizeof...(T)>());
     }
+
+private:
+    std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
+
+    template <typename ParseContext>
+    FMT_CONSTEXPR auto do_parse(ParseContext &ctx, std::integral_constant<size_t, 0>) -> decltype(ctx.begin())
+    {
+        return ctx.begin();
+    }
+
+    template <typename ParseContext, size_t N>
+    FMT_CONSTEXPR auto do_parse(ParseContext &ctx, std::integral_constant<size_t, N>) -> decltype(ctx.begin())
+    {
+        auto end = ctx.begin();
+#if FMT_TUPLE_JOIN_SPECIFIERS
+        end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
+        if(N > 1)
+        {
+            auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
+            if(end != end1)
+                FMT_THROW(format_error("incompatible format specs for tuple elements"));
+        }
 #endif
-    return end;
-  }
-
-  template <typename FormatContext>
-  auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
-                 std::integral_constant<size_t, 0>) const ->
-      typename FormatContext::iterator {
-    return ctx.out();
-  }
-
-  template <typename FormatContext, size_t N>
-  auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
-                 std::integral_constant<size_t, N>) const ->
-      typename FormatContext::iterator {
-    auto out = std::get<sizeof...(T) - N>(formatters_)
-                   .format(std::get<sizeof...(T) - N>(value.tuple), ctx);
-    if (N > 1) {
-      out = std::copy(value.sep.begin(), value.sep.end(), out);
-      ctx.advance_to(out);
-      return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
+        return end;
+    }
+
+    template <typename FormatContext>
+    auto do_format(const tuple_join_view<Char, T...> &, FormatContext &ctx, std::integral_constant<size_t, 0>) const ->
+        typename FormatContext::iterator
+    {
+        return ctx.out();
+    }
+
+    template <typename FormatContext, size_t N>
+    auto
+    do_format(const tuple_join_view<Char, T...> &value, FormatContext &ctx, std::integral_constant<size_t, N>) const ->
+        typename FormatContext::iterator
+    {
+        auto out = std::get<sizeof...(T) - N>(formatters_).format(std::get<sizeof...(T) - N>(value.tuple), ctx);
+        if(N > 1)
+        {
+            out = std::copy(value.sep.begin(), value.sep.end(), out);
+            ctx.advance_to(out);
+            return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
+        }
+        return out;
     }
-    return out;
-  }
 };
 
 FMT_MODULE_EXPORT_BEGIN
@@ -687,16 +772,15 @@ FMT_MODULE_EXPORT_BEGIN
   \endrst
  */
 template <typename... T>
-FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
-    -> tuple_join_view<char, T...> {
-  return {tuple, sep};
+FMT_CONSTEXPR auto join(const std::tuple<T...> &tuple, string_view sep) -> tuple_join_view<char, T...>
+{
+    return {tuple, sep};
 }
 
 template <typename... T>
-FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
-                        basic_string_view<wchar_t> sep)
-    -> tuple_join_view<wchar_t, T...> {
-  return {tuple, sep};
+FMT_CONSTEXPR auto join(const std::tuple<T...> &tuple, basic_string_view<wchar_t> sep) -> tuple_join_view<wchar_t, T...>
+{
+    return {tuple, sep};
 }
 
 /**
@@ -711,12 +795,12 @@ FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
   \endrst
  */
 template <typename T>
-auto join(std::initializer_list<T> list, string_view sep)
-    -> join_view<const T*, const T*> {
-  return join(std::begin(list), std::end(list), sep);
+auto join(std::initializer_list<T> list, string_view sep) -> join_view<const T *, const T *>
+{
+    return join(std::begin(list), std::end(list), sep);
 }
 
 FMT_MODULE_EXPORT_END
 FMT_END_NAMESPACE
 
-#endif  // FMT_RANGES_H_
+#endif // FMT_RANGES_H_
diff --git a/include/spdlog/fmt/bundled/std.h b/include/spdlog/fmt/bundled/std.h
index 41d2b2838..436731489 100644
--- a/include/spdlog/fmt/bundled/std.h
+++ b/include/spdlog/fmt/bundled/std.h
@@ -8,164 +8,172 @@
 #ifndef FMT_STD_H_
 #define FMT_STD_H_
 
+#include "ostream.h"
+
 #include <thread>
 #include <type_traits>
 #include <utility>
 
-#include "ostream.h"
-
 #if FMT_HAS_INCLUDE(<version>)
-#  include <version>
+#include <version>
 #endif
 // Checking FMT_CPLUSPLUS for warning suppression in MSVC.
 #if FMT_CPLUSPLUS >= 201703L
-#  if FMT_HAS_INCLUDE(<filesystem>)
-#    include <filesystem>
-#  endif
-#  if FMT_HAS_INCLUDE(<variant>)
-#    include <variant>
-#  endif
+#if FMT_HAS_INCLUDE(<filesystem>)
+#include <filesystem>
+#endif
+#if FMT_HAS_INCLUDE(<variant>)
+#include <variant>
+#endif
 #endif
 
 #ifdef __cpp_lib_filesystem
 FMT_BEGIN_NAMESPACE
 
-namespace detail {
+namespace detail
+{
 
 template <typename Char>
-void write_escaped_path(basic_memory_buffer<Char>& quoted,
-                        const std::filesystem::path& p) {
-  write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
+void write_escaped_path(basic_memory_buffer<Char> &quoted, const std::filesystem::path &p)
+{
+    write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
 }
-#  ifdef _WIN32
+#ifdef _WIN32
 template <>
-inline void write_escaped_path<char>(basic_memory_buffer<char>& quoted,
-                                     const std::filesystem::path& p) {
-  auto s = p.u8string();
-  write_escaped_string<char>(
-      std::back_inserter(quoted),
-      string_view(reinterpret_cast<const char*>(s.c_str()), s.size()));
+inline void write_escaped_path<char>(basic_memory_buffer<char> &quoted, const std::filesystem::path &p)
+{
+    auto s = p.u8string();
+    write_escaped_string<char>(
+        std::back_inserter(quoted), string_view(reinterpret_cast<const char *>(s.c_str()), s.size()));
 }
-#  endif
+#endif
 template <>
 inline void write_escaped_path<std::filesystem::path::value_type>(
-    basic_memory_buffer<std::filesystem::path::value_type>& quoted,
-    const std::filesystem::path& p) {
-  write_escaped_string<std::filesystem::path::value_type>(
-      std::back_inserter(quoted), p.native());
+    basic_memory_buffer<std::filesystem::path::value_type> &quoted,
+    const std::filesystem::path                            &p)
+{
+    write_escaped_string<std::filesystem::path::value_type>(std::back_inserter(quoted), p.native());
 }
 
-}  // namespace detail
+} // namespace detail
 
 template <typename Char>
-struct formatter<std::filesystem::path, Char>
-    : formatter<basic_string_view<Char>> {
-  template <typename FormatContext>
-  auto format(const std::filesystem::path& p, FormatContext& ctx) const ->
-      typename FormatContext::iterator {
-    basic_memory_buffer<Char> quoted;
-    detail::write_escaped_path(quoted, p);
-    return formatter<basic_string_view<Char>>::format(
-        basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
-  }
+struct formatter<std::filesystem::path, Char> : formatter<basic_string_view<Char>>
+{
+    template <typename FormatContext>
+    auto format(const std::filesystem::path &p, FormatContext &ctx) const -> typename FormatContext::iterator
+    {
+        basic_memory_buffer<Char> quoted;
+        detail::write_escaped_path(quoted, p);
+        return formatter<basic_string_view<Char>>::format(basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
+    }
 };
 FMT_END_NAMESPACE
 #endif
 
 FMT_BEGIN_NAMESPACE
 template <typename Char>
-struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
+struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char>
+{
+};
 FMT_END_NAMESPACE
 
 #ifdef __cpp_lib_variant
 FMT_BEGIN_NAMESPACE
-template <typename Char> struct formatter<std::monostate, Char> {
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    return ctx.begin();
-  }
-
-  template <typename FormatContext>
-  auto format(const std::monostate&, FormatContext& ctx) const
-      -> decltype(ctx.out()) {
-    auto out = ctx.out();
-    out = detail::write<Char>(out, "monostate");
-    return out;
-  }
+template <typename Char>
+struct formatter<std::monostate, Char>
+{
+    template <typename ParseContext>
+    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
+    {
+        return ctx.begin();
+    }
+
+    template <typename FormatContext>
+    auto format(const std::monostate &, FormatContext &ctx) const -> decltype(ctx.out())
+    {
+        auto out = ctx.out();
+        out      = detail::write<Char>(out, "monostate");
+        return out;
+    }
 };
 
-namespace detail {
+namespace detail
+{
 
 template <typename T>
-using variant_index_sequence =
-    std::make_index_sequence<std::variant_size<T>::value>;
+using variant_index_sequence = std::make_index_sequence<std::variant_size<T>::value>;
 
 // variant_size and variant_alternative check.
 template <typename T, typename U = void>
-struct is_variant_like_ : std::false_type {};
+struct is_variant_like_ : std::false_type
+{
+};
 template <typename T>
-struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>>
-    : std::true_type {};
+struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>> : std::true_type
+{
+};
 
 // formattable element check
-template <typename T, typename C> class is_variant_formattable_ {
-  template <std::size_t... I>
-  static std::conjunction<
-      is_formattable<std::variant_alternative_t<I, T>, C>...>
-      check(std::index_sequence<I...>);
-
- public:
-  static constexpr const bool value =
-      decltype(check(variant_index_sequence<T>{}))::value;
+template <typename T, typename C>
+class is_variant_formattable_
+{
+    template <std::size_t... I>
+    static std::conjunction<is_formattable<std::variant_alternative_t<I, T>, C>...> check(std::index_sequence<I...>);
+
+public:
+    static constexpr const bool value = decltype(check(variant_index_sequence<T>{}))::value;
 };
 
 template <typename Char, typename OutputIt, typename T>
-auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
-  if constexpr (is_string<T>::value)
-    return write_escaped_string<Char>(out, detail::to_string_view(v));
-  else if constexpr (std::is_same_v<T, Char>)
-    return write_escaped_char(out, v);
-  else
-    return write<Char>(out, v);
+auto write_variant_alternative(OutputIt out, const T &v) -> OutputIt
+{
+    if constexpr(is_string<T>::value)
+        return write_escaped_string<Char>(out, detail::to_string_view(v));
+    else if constexpr(std::is_same_v<T, Char>)
+        return write_escaped_char(out, v);
+    else
+        return write<Char>(out, v);
 }
 
-}  // namespace detail
+} // namespace detail
 
-template <typename T> struct is_variant_like {
-  static constexpr const bool value = detail::is_variant_like_<T>::value;
+template <typename T>
+struct is_variant_like
+{
+    static constexpr const bool value = detail::is_variant_like_<T>::value;
 };
 
-template <typename T, typename C> struct is_variant_formattable {
-  static constexpr const bool value =
-      detail::is_variant_formattable_<T, C>::value;
+template <typename T, typename C>
+struct is_variant_formattable
+{
+    static constexpr const bool value = detail::is_variant_formattable_<T, C>::value;
 };
 
 template <typename Variant, typename Char>
 struct formatter<
-    Variant, Char,
-    std::enable_if_t<std::conjunction_v<
-        is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
-  template <typename ParseContext>
-  FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
-    return ctx.begin();
-  }
-
-  template <typename FormatContext>
-  auto format(const Variant& value, FormatContext& ctx) const
-      -> decltype(ctx.out()) {
-    auto out = ctx.out();
-
-    out = detail::write<Char>(out, "variant(");
-    std::visit(
-        [&](const auto& v) {
-          out = detail::write_variant_alternative<Char>(out, v);
-        },
-        value);
-    *out++ = ')';
-    return out;
-  }
+    Variant,
+    Char,
+    std::enable_if_t<std::conjunction_v<is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>>
+{
+    template <typename ParseContext>
+    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
+    {
+        return ctx.begin();
+    }
+
+    template <typename FormatContext>
+    auto format(const Variant &value, FormatContext &ctx) const -> decltype(ctx.out())
+    {
+        auto out = ctx.out();
+
+        out = detail::write<Char>(out, "variant(");
+        std::visit([&](const auto &v) { out = detail::write_variant_alternative<Char>(out, v); }, value);
+        *out++ = ')';
+        return out;
+    }
 };
 FMT_END_NAMESPACE
 #endif
 
-#endif  // FMT_STD_H_
+#endif // FMT_STD_H_
diff --git a/include/spdlog/fmt/bundled/xchar.h b/include/spdlog/fmt/bundled/xchar.h
index 3b5bc15ca..265ceffe9 100644
--- a/include/spdlog/fmt/bundled/xchar.h
+++ b/include/spdlog/fmt/bundled/xchar.h
@@ -8,222 +8,271 @@
 #ifndef FMT_XCHAR_H_
 #define FMT_XCHAR_H_
 
-#include <cwchar>
-
 #include "format.h"
 
+#include <cwchar>
+
 FMT_BEGIN_NAMESPACE
-namespace detail {
+namespace detail
+{
 template <typename T>
 using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
 }
 
 FMT_MODULE_EXPORT_BEGIN
 
-using wstring_view = basic_string_view<wchar_t>;
+using wstring_view          = basic_string_view<wchar_t>;
 using wformat_parse_context = basic_format_parse_context<wchar_t>;
-using wformat_context = buffer_context<wchar_t>;
-using wformat_args = basic_format_args<wformat_context>;
-using wmemory_buffer = basic_memory_buffer<wchar_t>;
+using wformat_context       = buffer_context<wchar_t>;
+using wformat_args          = basic_format_args<wformat_context>;
+using wmemory_buffer        = basic_memory_buffer<wchar_t>;
 
 #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
 // Workaround broken conversion on older gcc.
-template <typename... Args> using wformat_string = wstring_view;
-inline auto runtime(wstring_view s) -> wstring_view { return s; }
+template <typename... Args>
+using wformat_string = wstring_view;
+inline auto runtime(wstring_view s) -> wstring_view
+{
+    return s;
+}
 #else
 template <typename... Args>
 using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
-inline auto runtime(wstring_view s) -> basic_runtime<wchar_t> { return {{s}}; }
+inline auto runtime(wstring_view s) -> basic_runtime<wchar_t>
+{
+    return {{s}};
+}
 #endif
 
-template <> struct is_char<wchar_t> : std::true_type {};
-template <> struct is_char<detail::char8_type> : std::true_type {};
-template <> struct is_char<char16_t> : std::true_type {};
-template <> struct is_char<char32_t> : std::true_type {};
+template <>
+struct is_char<wchar_t> : std::true_type
+{
+};
+template <>
+struct is_char<detail::char8_type> : std::true_type
+{
+};
+template <>
+struct is_char<char16_t> : std::true_type
+{
+};
+template <>
+struct is_char<char32_t> : std::true_type
+{
+};
 
 template <typename... Args>
-constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
-    const Args&... args) {
-  return {args...};
+constexpr format_arg_store<wformat_context, Args...> make_wformat_args(const Args &...args)
+{
+    return {args...};
 }
 
-inline namespace literals {
+inline namespace literals
+{
 #if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
-constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
-  return {s};
+constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t *s, size_t)
+{
+    return {s};
 }
 #endif
-}  // namespace literals
+} // namespace literals
 
 template <typename It, typename Sentinel>
-auto join(It begin, Sentinel end, wstring_view sep)
-    -> join_view<It, Sentinel, wchar_t> {
-  return {begin, end, sep};
+auto join(It begin, Sentinel end, wstring_view sep) -> join_view<It, Sentinel, wchar_t>
+{
+    return {begin, end, sep};
 }
 
 template <typename Range>
-auto join(Range&& range, wstring_view sep)
-    -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
-                 wchar_t> {
-  return join(std::begin(range), std::end(range), sep);
+auto join(Range &&range, wstring_view sep) -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>, wchar_t>
+{
+    return join(std::begin(range), std::end(range), sep);
 }
 
 template <typename T>
-auto join(std::initializer_list<T> list, wstring_view sep)
-    -> join_view<const T*, const T*, wchar_t> {
-  return join(std::begin(list), std::end(list), sep);
+auto join(std::initializer_list<T> list, wstring_view sep) -> join_view<const T *, const T *, wchar_t>
+{
+    return join(std::begin(list), std::end(list), sep);
 }
 
 template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
-auto vformat(basic_string_view<Char> format_str,
-             basic_format_args<buffer_context<type_identity_t<Char>>> args)
-    -> std::basic_string<Char> {
-  basic_memory_buffer<Char> buffer;
-  detail::vformat_to(buffer, format_str, args);
-  return to_string(buffer);
+auto vformat(basic_string_view<Char> format_str, basic_format_args<buffer_context<type_identity_t<Char>>> args)
+    -> std::basic_string<Char>
+{
+    basic_memory_buffer<Char> buffer;
+    detail::vformat_to(buffer, format_str, args);
+    return to_string(buffer);
 }
 
 template <typename... T>
-auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
-  return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
+auto format(wformat_string<T...> fmt, T &&...args) -> std::wstring
+{
+    return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
 }
 
 // Pass char_t as a default template parameter instead of using
 // std::basic_string<char_t<S>> to reduce the symbol size.
-template <typename S, typename... Args, typename Char = char_t<S>,
-          FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
-                        !std::is_same<Char, wchar_t>::value)>
-auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
-  return vformat(detail::to_string_view(format_str),
-                 fmt::make_format_args<buffer_context<Char>>(args...));
-}
-
-template <typename Locale, typename S, typename Char = char_t<S>,
-          FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
-                            detail::is_exotic_char<Char>::value)>
-inline auto vformat(
-    const Locale& loc, const S& format_str,
-    basic_format_args<buffer_context<type_identity_t<Char>>> args)
-    -> std::basic_string<Char> {
-  return detail::vformat(loc, detail::to_string_view(format_str), args);
-}
-
-template <typename Locale, typename S, typename... Args,
-          typename Char = char_t<S>,
-          FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
-                            detail::is_exotic_char<Char>::value)>
-inline auto format(const Locale& loc, const S& format_str, Args&&... args)
-    -> std::basic_string<Char> {
-  return detail::vformat(loc, detail::to_string_view(format_str),
-                         fmt::make_format_args<buffer_context<Char>>(args...));
-}
-
-template <typename OutputIt, typename S, typename Char = char_t<S>,
-          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
-                            detail::is_exotic_char<Char>::value)>
-auto vformat_to(OutputIt out, const S& format_str,
-                basic_format_args<buffer_context<type_identity_t<Char>>> args)
-    -> OutputIt {
-  auto&& buf = detail::get_buffer<Char>(out);
-  detail::vformat_to(buf, detail::to_string_view(format_str), args);
-  return detail::get_iterator(buf);
-}
-
-template <typename OutputIt, typename S, typename... Args,
-          typename Char = char_t<S>,
-          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
-                            detail::is_exotic_char<Char>::value)>
-inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt {
-  return vformat_to(out, detail::to_string_view(fmt),
-                    fmt::make_format_args<buffer_context<Char>>(args...));
-}
-
-template <typename Locale, typename S, typename OutputIt, typename... Args,
-          typename Char = char_t<S>,
-          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
-                            detail::is_locale<Locale>::value&&
-                                detail::is_exotic_char<Char>::value)>
+template <
+    typename S,
+    typename... Args,
+    typename Char = char_t<S>,
+    FMT_ENABLE_IF(!std::is_same<Char, char>::value && !std::is_same<Char, wchar_t>::value)>
+auto format(const S &format_str, Args &&...args) -> std::basic_string<Char>
+{
+    return vformat(detail::to_string_view(format_str), fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+template <
+    typename Locale,
+    typename S,
+    typename Char = char_t<S>,
+    FMT_ENABLE_IF(detail::is_locale<Locale>::value &&detail::is_exotic_char<Char>::value)>
+inline auto
+vformat(const Locale &loc, const S &format_str, basic_format_args<buffer_context<type_identity_t<Char>>> args)
+    -> std::basic_string<Char>
+{
+    return detail::vformat(loc, detail::to_string_view(format_str), args);
+}
+
+template <
+    typename Locale,
+    typename S,
+    typename... Args,
+    typename Char = char_t<S>,
+    FMT_ENABLE_IF(detail::is_locale<Locale>::value &&detail::is_exotic_char<Char>::value)>
+inline auto format(const Locale &loc, const S &format_str, Args &&...args) -> std::basic_string<Char>
+{
+    return detail::vformat(
+        loc, detail::to_string_view(format_str), fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+template <
+    typename OutputIt,
+    typename S,
+    typename Char = char_t<S>,
+    FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&detail::is_exotic_char<Char>::value)>
+auto vformat_to(OutputIt out, const S &format_str, basic_format_args<buffer_context<type_identity_t<Char>>> args)
+    -> OutputIt
+{
+    auto &&buf = detail::get_buffer<Char>(out);
+    detail::vformat_to(buf, detail::to_string_view(format_str), args);
+    return detail::get_iterator(buf);
+}
+
+template <
+    typename OutputIt,
+    typename S,
+    typename... Args,
+    typename Char = char_t<S>,
+    FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&detail::is_exotic_char<Char>::value)>
+inline auto format_to(OutputIt out, const S &fmt, Args &&...args) -> OutputIt
+{
+    return vformat_to(out, detail::to_string_view(fmt), fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+template <
+    typename Locale,
+    typename S,
+    typename OutputIt,
+    typename... Args,
+    typename Char = char_t<S>,
+    FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&detail::is_locale<Locale>::value
+                                                                    &&detail::is_exotic_char<Char>::value)>
 inline auto vformat_to(
-    OutputIt out, const Locale& loc, const S& format_str,
-    basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
-  auto&& buf = detail::get_buffer<Char>(out);
-  vformat_to(buf, detail::to_string_view(format_str), args,
-             detail::locale_ref(loc));
-  return detail::get_iterator(buf);
+    OutputIt                                                 out,
+    const Locale                                            &loc,
+    const S                                                 &format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt
+{
+    auto &&buf = detail::get_buffer<Char>(out);
+    vformat_to(buf, detail::to_string_view(format_str), args, detail::locale_ref(loc));
+    return detail::get_iterator(buf);
 }
 
 template <
-    typename OutputIt, typename Locale, typename S, typename... Args,
+    typename OutputIt,
+    typename Locale,
+    typename S,
+    typename... Args,
     typename Char = char_t<S>,
-    bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
-        detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
-inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
-                      Args&&... args) ->
-    typename std::enable_if<enable, OutputIt>::type {
-  return vformat_to(out, loc, to_string_view(format_str),
-                    fmt::make_format_args<buffer_context<Char>>(args...));
-}
-
-template <typename OutputIt, typename Char, typename... Args,
-          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
-                            detail::is_exotic_char<Char>::value)>
+    bool enable   = detail::is_output_iterator<OutputIt, Char>::value &&detail::is_locale<Locale>::value
+                                                                    &&detail::is_exotic_char<Char>::value>
+inline auto format_to(OutputIt out, const Locale &loc, const S &format_str, Args &&...args) ->
+    typename std::enable_if<enable, OutputIt>::type
+{
+    return vformat_to(out, loc, to_string_view(format_str), fmt::make_format_args<buffer_context<Char>>(args...));
+}
+
+template <
+    typename OutputIt,
+    typename Char,
+    typename... Args,
+    FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&detail::is_exotic_char<Char>::value)>
 inline auto vformat_to_n(
-    OutputIt out, size_t n, basic_string_view<Char> format_str,
-    basic_format_args<buffer_context<type_identity_t<Char>>> args)
-    -> format_to_n_result<OutputIt> {
-  detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
-                                                                           n);
-  detail::vformat_to(buf, format_str, args);
-  return {buf.out(), buf.count()};
+    OutputIt                                                 out,
+    size_t                                                   n,
+    basic_string_view<Char>                                  format_str,
+    basic_format_args<buffer_context<type_identity_t<Char>>> args) -> format_to_n_result<OutputIt>
+{
+    detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out, n);
+    detail::vformat_to(buf, format_str, args);
+    return {buf.out(), buf.count()};
 }
 
-template <typename OutputIt, typename S, typename... Args,
-          typename Char = char_t<S>,
-          FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
-                            detail::is_exotic_char<Char>::value)>
-inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
-                        const Args&... args) -> format_to_n_result<OutputIt> {
-  return vformat_to_n(out, n, detail::to_string_view(fmt),
-                      fmt::make_format_args<buffer_context<Char>>(args...));
+template <
+    typename OutputIt,
+    typename S,
+    typename... Args,
+    typename Char = char_t<S>,
+    FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&detail::is_exotic_char<Char>::value)>
+inline auto format_to_n(OutputIt out, size_t n, const S &fmt, const Args &...args) -> format_to_n_result<OutputIt>
+{
+    return vformat_to_n(out, n, detail::to_string_view(fmt), fmt::make_format_args<buffer_context<Char>>(args...));
 }
 
-template <typename S, typename... Args, typename Char = char_t<S>,
-          FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
-inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
-  detail::counting_buffer<Char> buf;
-  detail::vformat_to(buf, detail::to_string_view(fmt),
-                     fmt::make_format_args<buffer_context<Char>>(args...));
-  return buf.count();
+template <typename S, typename... Args, typename Char = char_t<S>, FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
+inline auto formatted_size(const S &fmt, Args &&...args) -> size_t
+{
+    detail::counting_buffer<Char> buf;
+    detail::vformat_to(buf, detail::to_string_view(fmt), fmt::make_format_args<buffer_context<Char>>(args...));
+    return buf.count();
 }
 
-inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
-  wmemory_buffer buffer;
-  detail::vformat_to(buffer, fmt, args);
-  buffer.push_back(L'\0');
-  if (std::fputws(buffer.data(), f) == -1)
-    FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
+inline void vprint(std::FILE *f, wstring_view fmt, wformat_args args)
+{
+    wmemory_buffer buffer;
+    detail::vformat_to(buffer, fmt, args);
+    buffer.push_back(L'\0');
+    if(std::fputws(buffer.data(), f) == -1)
+        FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
 }
 
-inline void vprint(wstring_view fmt, wformat_args args) {
-  vprint(stdout, fmt, args);
+inline void vprint(wstring_view fmt, wformat_args args)
+{
+    vprint(stdout, fmt, args);
 }
 
 template <typename... T>
-void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
-  return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
+void print(std::FILE *f, wformat_string<T...> fmt, T &&...args)
+{
+    return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
 }
 
-template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
-  return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
+template <typename... T>
+void print(wformat_string<T...> fmt, T &&...args)
+{
+    return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
 }
 
 /**
   Converts *value* to ``std::wstring`` using the default format for type *T*.
  */
-template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
-  return format(FMT_STRING(L"{}"), value);
+template <typename T>
+inline auto to_wstring(const T &value) -> std::wstring
+{
+    return format(FMT_STRING(L"{}"), value);
 }
 FMT_MODULE_EXPORT_END
 FMT_END_NAMESPACE
 
-#endif  // FMT_XCHAR_H_
+#endif // FMT_XCHAR_H_
diff --git a/include/spdlog/fmt/chrono.h b/include/spdlog/fmt/chrono.h
index 83fad2ff9..a24c199b2 100644
--- a/include/spdlog/fmt/chrono.h
+++ b/include/spdlog/fmt/chrono.h
@@ -9,14 +9,14 @@
 //
 
 #if !defined(SPDLOG_USE_STD_FORMAT)
-#    if !defined(SPDLOG_FMT_EXTERNAL)
-#        ifdef SPDLOG_HEADER_ONLY
-#            ifndef FMT_HEADER_ONLY
-#                define FMT_HEADER_ONLY
-#            endif
-#        endif
-#        include <spdlog/fmt/bundled/chrono.h>
-#    else
-#        include <fmt/chrono.h>
-#    endif
+#if !defined(SPDLOG_FMT_EXTERNAL)
+#ifdef SPDLOG_HEADER_ONLY
+#ifndef FMT_HEADER_ONLY
+#define FMT_HEADER_ONLY
+#endif
+#endif
+#include <spdlog/fmt/bundled/chrono.h>
+#else
+#include <fmt/chrono.h>
+#endif
 #endif
diff --git a/include/spdlog/fmt/compile.h b/include/spdlog/fmt/compile.h
index 906e9f57a..498872f5e 100644
--- a/include/spdlog/fmt/compile.h
+++ b/include/spdlog/fmt/compile.h
@@ -9,14 +9,14 @@
 //
 
 #if !defined(SPDLOG_USE_STD_FORMAT)
-#    if !defined(SPDLOG_FMT_EXTERNAL)
-#        ifdef SPDLOG_HEADER_ONLY
-#            ifndef FMT_HEADER_ONLY
-#                define FMT_HEADER_ONLY
-#            endif
-#        endif
-#        include <spdlog/fmt/bundled/compile.h>
-#    else
-#        include <fmt/compile.h>
-#    endif
+#if !defined(SPDLOG_FMT_EXTERNAL)
+#ifdef SPDLOG_HEADER_ONLY
+#ifndef FMT_HEADER_ONLY
+#define FMT_HEADER_ONLY
+#endif
+#endif
+#include <spdlog/fmt/bundled/compile.h>
+#else
+#include <fmt/compile.h>
+#endif
 #endif
diff --git a/include/spdlog/fmt/fmt.h b/include/spdlog/fmt/fmt.h
index 90fcae0f0..5e7441d48 100644
--- a/include/spdlog/fmt/fmt.h
+++ b/include/spdlog/fmt/fmt.h
@@ -11,23 +11,23 @@
 //
 
 #if defined(SPDLOG_USE_STD_FORMAT) // SPDLOG_USE_STD_FORMAT is defined - use std::format
-#    include <format>
+#include <format>
 #elif !defined(SPDLOG_FMT_EXTERNAL)
-#    if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY)
-#        define FMT_HEADER_ONLY
-#    endif
-#    ifndef FMT_USE_WINDOWS_H
-#        define FMT_USE_WINDOWS_H 0
-#    endif
+#if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY)
+#define FMT_HEADER_ONLY
+#endif
+#ifndef FMT_USE_WINDOWS_H
+#define FMT_USE_WINDOWS_H 0
+#endif
 // enable the 'n' flag in for backward compatibility with fmt 6.x
-#    define FMT_DEPRECATED_N_SPECIFIER
+#define FMT_DEPRECATED_N_SPECIFIER
 // enable ostream formatting for backward compatibility with fmt 8.x
-#    define FMT_DEPRECATED_OSTREAM
+#define FMT_DEPRECATED_OSTREAM
 
-#    include <spdlog/fmt/bundled/core.h>
-#    include <spdlog/fmt/bundled/format.h>
+#include <spdlog/fmt/bundled/core.h>
+#include <spdlog/fmt/bundled/format.h>
 
 #else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
-#    include <fmt/core.h>
-#    include <fmt/format.h>
+#include <fmt/core.h>
+#include <fmt/format.h>
 #endif
diff --git a/include/spdlog/fmt/ostr.h b/include/spdlog/fmt/ostr.h
index 758803417..9e17d21b1 100644
--- a/include/spdlog/fmt/ostr.h
+++ b/include/spdlog/fmt/ostr.h
@@ -9,14 +9,14 @@
 //
 
 #if !defined(SPDLOG_USE_STD_FORMAT)
-#    if !defined(SPDLOG_FMT_EXTERNAL)
-#        ifdef SPDLOG_HEADER_ONLY
-#            ifndef FMT_HEADER_ONLY
-#                define FMT_HEADER_ONLY
-#            endif
-#        endif
-#        include <spdlog/fmt/bundled/ostream.h>
-#    else
-#        include <fmt/ostream.h>
-#    endif
+#if !defined(SPDLOG_FMT_EXTERNAL)
+#ifdef SPDLOG_HEADER_ONLY
+#ifndef FMT_HEADER_ONLY
+#define FMT_HEADER_ONLY
+#endif
+#endif
+#include <spdlog/fmt/bundled/ostream.h>
+#else
+#include <fmt/ostream.h>
+#endif
 #endif
diff --git a/include/spdlog/fmt/ranges.h b/include/spdlog/fmt/ranges.h
index 9103a5f6a..5b0c77ab5 100644
--- a/include/spdlog/fmt/ranges.h
+++ b/include/spdlog/fmt/ranges.h
@@ -9,14 +9,14 @@
 //
 
 #if !defined(SPDLOG_USE_STD_FORMAT)
-#    if !defined(SPDLOG_FMT_EXTERNAL)
-#        ifdef SPDLOG_HEADER_ONLY
-#            ifndef FMT_HEADER_ONLY
-#                define FMT_HEADER_ONLY
-#            endif
-#        endif
-#        include <spdlog/fmt/bundled/ranges.h>
-#    else
-#        include <fmt/ranges.h>
-#    endif
+#if !defined(SPDLOG_FMT_EXTERNAL)
+#ifdef SPDLOG_HEADER_ONLY
+#ifndef FMT_HEADER_ONLY
+#define FMT_HEADER_ONLY
+#endif
+#endif
+#include <spdlog/fmt/bundled/ranges.h>
+#else
+#include <fmt/ranges.h>
+#endif
 #endif
diff --git a/include/spdlog/fmt/std.h b/include/spdlog/fmt/std.h
index 0490cab0b..260c64ef3 100644
--- a/include/spdlog/fmt/std.h
+++ b/include/spdlog/fmt/std.h
@@ -5,19 +5,19 @@
 
 #pragma once
 //
-// include bundled or external copy of fmtlib's std support (for formatting e.g. std::filesystem::path, std::thread::id, std::monostate,
-// std::variant, ...)
+// include bundled or external copy of fmtlib's std support (for formatting e.g. std::filesystem::path, std::thread::id,
+// std::monostate, std::variant, ...)
 //
 
 #if !defined(SPDLOG_USE_STD_FORMAT)
-#    if !defined(SPDLOG_FMT_EXTERNAL)
-#        ifdef SPDLOG_HEADER_ONLY
-#            ifndef FMT_HEADER_ONLY
-#                define FMT_HEADER_ONLY
-#            endif
-#        endif
-#        include <spdlog/fmt/bundled/std.h>
-#    else
-#        include <fmt/std.h>
-#    endif
+#if !defined(SPDLOG_FMT_EXTERNAL)
+#ifdef SPDLOG_HEADER_ONLY
+#ifndef FMT_HEADER_ONLY
+#define FMT_HEADER_ONLY
+#endif
+#endif
+#include <spdlog/fmt/bundled/std.h>
+#else
+#include <fmt/std.h>
+#endif
 #endif
diff --git a/include/spdlog/fmt/xchar.h b/include/spdlog/fmt/xchar.h
index 9a766e5a0..6d5328469 100644
--- a/include/spdlog/fmt/xchar.h
+++ b/include/spdlog/fmt/xchar.h
@@ -9,14 +9,14 @@
 //
 
 #if !defined(SPDLOG_USE_STD_FORMAT)
-#    if !defined(SPDLOG_FMT_EXTERNAL)
-#        ifdef SPDLOG_HEADER_ONLY
-#            ifndef FMT_HEADER_ONLY
-#                define FMT_HEADER_ONLY
-#            endif
-#        endif
-#        include <spdlog/fmt/bundled/xchar.h>
-#    else
-#        include <fmt/xchar.h>
-#    endif
+#if !defined(SPDLOG_FMT_EXTERNAL)
+#ifdef SPDLOG_HEADER_ONLY
+#ifndef FMT_HEADER_ONLY
+#define FMT_HEADER_ONLY
+#endif
+#endif
+#include <spdlog/fmt/bundled/xchar.h>
+#else
+#include <fmt/xchar.h>
+#endif
 #endif
diff --git a/include/spdlog/formatter.h b/include/spdlog/formatter.h
index 5086fb217..51afa4fd5 100644
--- a/include/spdlog/formatter.h
+++ b/include/spdlog/formatter.h
@@ -3,16 +3,17 @@
 
 #pragma once
 
-#include <spdlog/fmt/fmt.h>
 #include <spdlog/details/log_msg.h>
+#include <spdlog/fmt/fmt.h>
 
-namespace spdlog {
+namespace spdlog
+{
 
 class formatter
 {
 public:
-    virtual ~formatter() = default;
-    virtual void format(const details::log_msg &msg, memory_buf_t &dest) = 0;
-    virtual std::unique_ptr<formatter> clone() const = 0;
+    virtual ~formatter()                                                                       = default;
+    virtual void                       format(const details::log_msg &msg, memory_buf_t &dest) = 0;
+    virtual std::unique_ptr<formatter> clone() const                                           = 0;
 };
 } // namespace spdlog
diff --git a/include/spdlog/fwd.h b/include/spdlog/fwd.h
index d25882572..bdefd39fe 100644
--- a/include/spdlog/fwd.h
+++ b/include/spdlog/fwd.h
@@ -3,16 +3,19 @@
 
 #pragma once
 
-namespace spdlog {
+namespace spdlog
+{
 class logger;
 class formatter;
 
-namespace sinks {
-class sink;
+namespace sinks
+{
+    class sink;
 }
 
-namespace level {
-enum level_enum : int;
+namespace level
+{
+    enum level_enum : int;
 }
 
 } // namespace spdlog
diff --git a/include/spdlog/logger-inl.h b/include/spdlog/logger-inl.h
index 411f2cb5a..5c4c058af 100644
--- a/include/spdlog/logger-inl.h
+++ b/include/spdlog/logger-inl.h
@@ -4,35 +4,38 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/logger.h>
+#include <spdlog/logger.h>
 #endif
 
-#include <spdlog/sinks/sink.h>
+#include <cstdio>
 #include <spdlog/details/backtracer.h>
 #include <spdlog/pattern_formatter.h>
+#include <spdlog/sinks/sink.h>
 
-#include <cstdio>
-
-namespace spdlog {
+namespace spdlog
+{
 
 // public methods
-SPDLOG_INLINE logger::logger(const logger &other)
-    : name_(other.name_)
-    , sinks_(other.sinks_)
-    , level_(other.level_.load(std::memory_order_relaxed))
-    , flush_level_(other.flush_level_.load(std::memory_order_relaxed))
-    , custom_err_handler_(other.custom_err_handler_)
-    , tracer_(other.tracer_)
-{}
-
-SPDLOG_INLINE logger::logger(logger &&other) SPDLOG_NOEXCEPT : name_(std::move(other.name_)),
-                                                               sinks_(std::move(other.sinks_)),
-                                                               level_(other.level_.load(std::memory_order_relaxed)),
-                                                               flush_level_(other.flush_level_.load(std::memory_order_relaxed)),
-                                                               custom_err_handler_(std::move(other.custom_err_handler_)),
-                                                               tracer_(std::move(other.tracer_))
-
-{}
+SPDLOG_INLINE logger::logger(const logger &other) :
+    name_(other.name_),
+    sinks_(other.sinks_),
+    level_(other.level_.load(std::memory_order_relaxed)),
+    flush_level_(other.flush_level_.load(std::memory_order_relaxed)),
+    custom_err_handler_(other.custom_err_handler_),
+    tracer_(other.tracer_)
+{
+}
+
+SPDLOG_INLINE logger::logger(logger &&other) SPDLOG_NOEXCEPT
+    : name_(std::move(other.name_)),
+      sinks_(std::move(other.sinks_)),
+      level_(other.level_.load(std::memory_order_relaxed)),
+      flush_level_(other.flush_level_.load(std::memory_order_relaxed)),
+      custom_err_handler_(std::move(other.custom_err_handler_)),
+      tracer_(std::move(other.tracer_))
+
+{
+}
 
 SPDLOG_INLINE logger &logger::operator=(logger other) SPDLOG_NOEXCEPT
 {
@@ -47,12 +50,12 @@ SPDLOG_INLINE void logger::swap(spdlog::logger &other) SPDLOG_NOEXCEPT
 
     // swap level_
     auto other_level = other.level_.load();
-    auto my_level = level_.exchange(other_level);
+    auto my_level    = level_.exchange(other_level);
     other.level_.store(my_level);
 
     // swap flush level_
     other_level = other.flush_level_.load();
-    my_level = flush_level_.exchange(other_level);
+    my_level    = flush_level_.exchange(other_level);
     other.flush_level_.store(my_level);
 
     custom_err_handler_.swap(other.custom_err_handler_);
@@ -83,9 +86,9 @@ SPDLOG_INLINE const std::string &logger::name() const
 // each sink will get a separate instance of the formatter object.
 SPDLOG_INLINE void logger::set_formatter(std::unique_ptr<formatter> f)
 {
-    for (auto it = sinks_.begin(); it != sinks_.end(); ++it)
+    for(auto it = sinks_.begin(); it != sinks_.end(); ++it)
     {
-        if (std::next(it) == sinks_.end())
+        if(std::next(it) == sinks_.end())
         {
             // last element - we can be move it.
             (*it)->set_formatter(std::move(f));
@@ -157,7 +160,7 @@ SPDLOG_INLINE void logger::set_error_handler(err_handler handler)
 // create new logger with same sinks and configuration.
 SPDLOG_INLINE std::shared_ptr<logger> logger::clone(std::string logger_name)
 {
-    auto cloned = std::make_shared<logger>(*this);
+    auto cloned   = std::make_shared<logger>(*this);
     cloned->name_ = std::move(logger_name);
     return cloned;
 }
@@ -165,11 +168,11 @@ SPDLOG_INLINE std::shared_ptr<logger> logger::clone(std::string logger_name)
 // protected methods
 SPDLOG_INLINE void logger::log_it_(const spdlog::details::log_msg &log_msg, bool log_enabled, bool traceback_enabled)
 {
-    if (log_enabled)
+    if(log_enabled)
     {
         sink_it_(log_msg);
     }
-    if (traceback_enabled)
+    if(traceback_enabled)
     {
         tracer_.push_back(log_msg);
     }
@@ -177,9 +180,9 @@ SPDLOG_INLINE void logger::log_it_(const spdlog::details::log_msg &log_msg, bool
 
 SPDLOG_INLINE void logger::sink_it_(const details::log_msg &msg)
 {
-    for (auto &sink : sinks_)
+    for(auto &sink : sinks_)
     {
-        if (sink->should_log(msg.level))
+        if(sink->should_log(msg.level))
         {
             SPDLOG_TRY
             {
@@ -189,7 +192,7 @@ SPDLOG_INLINE void logger::sink_it_(const details::log_msg &msg)
         }
     }
 
-    if (should_flush_(msg))
+    if(should_flush_(msg))
     {
         flush_();
     }
@@ -197,7 +200,7 @@ SPDLOG_INLINE void logger::sink_it_(const details::log_msg &msg)
 
 SPDLOG_INLINE void logger::flush_()
 {
-    for (auto &sink : sinks_)
+    for(auto &sink : sinks_)
     {
         SPDLOG_TRY
         {
@@ -210,7 +213,7 @@ SPDLOG_INLINE void logger::flush_()
 SPDLOG_INLINE void logger::dump_backtrace_()
 {
     using details::log_msg;
-    if (tracer_.enabled())
+    if(tracer_.enabled())
     {
         sink_it_(log_msg{name(), level::info, "****************** Backtrace Start ******************"});
         tracer_.foreach_pop([this](const log_msg &msg) { this->sink_it_(msg); });
@@ -226,31 +229,32 @@ SPDLOG_INLINE bool logger::should_flush_(const details::log_msg &msg)
 
 SPDLOG_INLINE void logger::err_handler_(const std::string &msg)
 {
-    if (custom_err_handler_)
+    if(custom_err_handler_)
     {
         custom_err_handler_(msg);
     }
     else
     {
         using std::chrono::system_clock;
-        static std::mutex mutex;
+        static std::mutex                            mutex;
         static std::chrono::system_clock::time_point last_report_time;
-        static size_t err_counter = 0;
-        std::lock_guard<std::mutex> lk{mutex};
-        auto now = system_clock::now();
+        static size_t                                err_counter = 0;
+        std::lock_guard<std::mutex>                  lk{mutex};
+        auto                                         now = system_clock::now();
         err_counter++;
-        if (now - last_report_time < std::chrono::seconds(1))
+        if(now - last_report_time < std::chrono::seconds(1))
         {
             return;
         }
         last_report_time = now;
-        auto tm_time = details::os::localtime(system_clock::to_time_t(now));
+        auto tm_time     = details::os::localtime(system_clock::to_time_t(now));
         char date_buf[64];
         std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
 #if defined(USING_R) && defined(R_R_H) // if in R environment
         REprintf("[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
 #else
-        std::fprintf(stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
+        std::fprintf(
+            stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
 #endif
     }
 }
diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h
index 1fafdabdb..f3bb2a85d 100644
--- a/include/spdlog/logger.h
+++ b/include/spdlog/logger.h
@@ -15,95 +15,89 @@
 // formatted data, and support for different format per sink.
 
 #include <spdlog/common.h>
-#include <spdlog/details/log_msg.h>
 #include <spdlog/details/backtracer.h>
+#include <spdlog/details/log_msg.h>
 
 #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
-#    ifndef _WIN32
-#        error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
-#    endif
-#    include <spdlog/details/os.h>
+#ifndef _WIN32
+#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
+#endif
+#include <spdlog/details/os.h>
 #endif
 
 #include <vector>
 
 #ifndef SPDLOG_NO_EXCEPTIONS
-#    define SPDLOG_LOGGER_CATCH(location)                                                                                                  \
-        catch (const std::exception &ex)                                                                                                   \
-        {                                                                                                                                  \
-            if (location.filename)                                                                                                         \
-            {                                                                                                                              \
-                err_handler_(fmt_lib::format(SPDLOG_FMT_STRING("{} [{}({})]"), ex.what(), location.filename, location.line));              \
-            }                                                                                                                              \
-            else                                                                                                                           \
-            {                                                                                                                              \
-                err_handler_(ex.what());                                                                                                   \
-            }                                                                                                                              \
-        }                                                                                                                                  \
-        catch (...)                                                                                                                        \
-        {                                                                                                                                  \
-            err_handler_("Rethrowing unknown exception in logger");                                                                        \
-            throw;                                                                                                                         \
-        }
+#define SPDLOG_LOGGER_CATCH(location)                                                                                  \
+    catch(const std::exception &ex)                                                                                    \
+    {                                                                                                                  \
+        if(location.filename)                                                                                          \
+        {                                                                                                              \
+            err_handler_(                                                                                              \
+                fmt_lib::format(SPDLOG_FMT_STRING("{} [{}({})]"), ex.what(), location.filename, location.line));       \
+        }                                                                                                              \
+        else                                                                                                           \
+        {                                                                                                              \
+            err_handler_(ex.what());                                                                                   \
+        }                                                                                                              \
+    }                                                                                                                  \
+    catch(...)                                                                                                         \
+    {                                                                                                                  \
+        err_handler_("Rethrowing unknown exception in logger");                                                        \
+        throw;                                                                                                         \
+    }
 #else
-#    define SPDLOG_LOGGER_CATCH(location)
+#define SPDLOG_LOGGER_CATCH(location)
 #endif
 
-namespace spdlog {
+namespace spdlog
+{
 
 class SPDLOG_API logger
 {
 public:
     // Empty logger
-    explicit logger(std::string name)
-        : name_(std::move(name))
-        , sinks_()
-    {}
+    explicit logger(std::string name) : name_(std::move(name)), sinks_() {}
 
     // Logger with range on sinks
-    template<typename It>
-    logger(std::string name, It begin, It end)
-        : name_(std::move(name))
-        , sinks_(begin, end)
-    {}
+    template <typename It>
+    logger(std::string name, It begin, It end) : name_(std::move(name)), sinks_(begin, end)
+    {
+    }
 
     // Logger with single sink
-    logger(std::string name, sink_ptr single_sink)
-        : logger(std::move(name), {std::move(single_sink)})
-    {}
+    logger(std::string name, sink_ptr single_sink) : logger(std::move(name), {std::move(single_sink)}) {}
 
     // Logger with sinks init list
-    logger(std::string name, sinks_init_list sinks)
-        : logger(std::move(name), sinks.begin(), sinks.end())
-    {}
+    logger(std::string name, sinks_init_list sinks) : logger(std::move(name), sinks.begin(), sinks.end()) {}
 
     virtual ~logger() = default;
 
     logger(const logger &other);
     logger(logger &&other) SPDLOG_NOEXCEPT;
     logger &operator=(logger other) SPDLOG_NOEXCEPT;
-    void swap(spdlog::logger &other) SPDLOG_NOEXCEPT;
+    void    swap(spdlog::logger &other) SPDLOG_NOEXCEPT;
 
-    template<typename... Args>
-    void log(source_loc loc, level::level_enum lvl, format_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void log(source_loc loc, level::level_enum lvl, format_string_t<Args...> fmt, Args &&...args)
     {
         log_(loc, lvl, details::to_string_view(fmt), std::forward<Args>(args)...);
     }
 
-    template<typename... Args>
-    void log(level::level_enum lvl, format_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void log(level::level_enum lvl, format_string_t<Args...> fmt, Args &&...args)
     {
         log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
     }
 
-    template<typename T>
+    template <typename T>
     void log(level::level_enum lvl, const T &msg)
     {
         log(source_loc{}, lvl, msg);
     }
 
     // T cannot be statically converted to format string (including string_view/wstring_view)
-    template<class T, typename std::enable_if<!is_convertible_to_any_format_string<const T &>::value, int>::type = 0>
+    template <class T, typename std::enable_if<!is_convertible_to_any_format_string<const T &>::value, int>::type = 0>
     void log(source_loc loc, level::level_enum lvl, const T &msg)
     {
         log(loc, lvl, "{}", msg);
@@ -111,9 +105,9 @@ public:
 
     void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, string_view_t msg)
     {
-        bool log_enabled = should_log(lvl);
+        bool log_enabled       = should_log(lvl);
         bool traceback_enabled = tracer_.enabled();
-        if (!log_enabled && !traceback_enabled)
+        if(!log_enabled && !traceback_enabled)
         {
             return;
         }
@@ -124,9 +118,9 @@ public:
 
     void log(source_loc loc, level::level_enum lvl, string_view_t msg)
     {
-        bool log_enabled = should_log(lvl);
+        bool log_enabled       = should_log(lvl);
         bool traceback_enabled = tracer_.enabled();
-        if (!log_enabled && !traceback_enabled)
+        if(!log_enabled && !traceback_enabled)
         {
             return;
         }
@@ -135,65 +129,62 @@ public:
         log_it_(log_msg, log_enabled, traceback_enabled);
     }
 
-    void log(level::level_enum lvl, string_view_t msg)
-    {
-        log(source_loc{}, lvl, msg);
-    }
+    void log(level::level_enum lvl, string_view_t msg) { log(source_loc{}, lvl, msg); }
 
-    template<typename... Args>
-    void trace(format_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void trace(format_string_t<Args...> fmt, Args &&...args)
     {
         log(level::trace, fmt, std::forward<Args>(args)...);
     }
 
-    template<typename... Args>
-    void debug(format_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void debug(format_string_t<Args...> fmt, Args &&...args)
     {
         log(level::debug, fmt, std::forward<Args>(args)...);
     }
 
-    template<typename... Args>
-    void info(format_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void info(format_string_t<Args...> fmt, Args &&...args)
     {
         log(level::info, fmt, std::forward<Args>(args)...);
     }
 
-    template<typename... Args>
-    void warn(format_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void warn(format_string_t<Args...> fmt, Args &&...args)
     {
         log(level::warn, fmt, std::forward<Args>(args)...);
     }
 
-    template<typename... Args>
-    void error(format_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void error(format_string_t<Args...> fmt, Args &&...args)
     {
         log(level::err, fmt, std::forward<Args>(args)...);
     }
 
-    template<typename... Args>
-    void critical(format_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void critical(format_string_t<Args...> fmt, Args &&...args)
     {
         log(level::critical, fmt, std::forward<Args>(args)...);
     }
 
 #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
-    template<typename... Args>
-    void log(source_loc loc, level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void log(source_loc loc, level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&...args)
     {
         log_(loc, lvl, details::to_string_view(fmt), std::forward<Args>(args)...);
     }
 
-    template<typename... Args>
-    void log(level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void log(level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&...args)
     {
         log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
     }
 
     void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, wstring_view_t msg)
     {
-        bool log_enabled = should_log(lvl);
+        bool log_enabled       = should_log(lvl);
         bool traceback_enabled = tracer_.enabled();
-        if (!log_enabled && !traceback_enabled)
+        if(!log_enabled && !traceback_enabled)
         {
             return;
         }
@@ -206,9 +197,9 @@ public:
 
     void log(source_loc loc, level::level_enum lvl, wstring_view_t msg)
     {
-        bool log_enabled = should_log(lvl);
+        bool log_enabled       = should_log(lvl);
         bool traceback_enabled = tracer_.enabled();
-        if (!log_enabled && !traceback_enabled)
+        if(!log_enabled && !traceback_enabled)
         {
             return;
         }
@@ -224,74 +215,74 @@ public:
         log(source_loc{}, lvl, msg);
     }
 
-    template<typename... Args>
-    void trace(wformat_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void trace(wformat_string_t<Args...> fmt, Args &&...args)
     {
         log(level::trace, fmt, std::forward<Args>(args)...);
     }
 
-    template<typename... Args>
-    void debug(wformat_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void debug(wformat_string_t<Args...> fmt, Args &&...args)
     {
         log(level::debug, fmt, std::forward<Args>(args)...);
     }
 
-    template<typename... Args>
-    void info(wformat_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void info(wformat_string_t<Args...> fmt, Args &&...args)
     {
         log(level::info, fmt, std::forward<Args>(args)...);
     }
 
-    template<typename... Args>
-    void warn(wformat_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void warn(wformat_string_t<Args...> fmt, Args &&...args)
     {
         log(level::warn, fmt, std::forward<Args>(args)...);
     }
 
-    template<typename... Args>
-    void error(wformat_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void error(wformat_string_t<Args...> fmt, Args &&...args)
     {
         log(level::err, fmt, std::forward<Args>(args)...);
     }
 
-    template<typename... Args>
-    void critical(wformat_string_t<Args...> fmt, Args &&... args)
+    template <typename... Args>
+    void critical(wformat_string_t<Args...> fmt, Args &&...args)
     {
         log(level::critical, fmt, std::forward<Args>(args)...);
     }
 #endif
 
-    template<typename T>
+    template <typename T>
     void trace(const T &msg)
     {
         log(level::trace, msg);
     }
 
-    template<typename T>
+    template <typename T>
     void debug(const T &msg)
     {
         log(level::debug, msg);
     }
 
-    template<typename T>
+    template <typename T>
     void info(const T &msg)
     {
         log(level::info, msg);
     }
 
-    template<typename T>
+    template <typename T>
     void warn(const T &msg)
     {
         log(level::warn, msg);
     }
 
-    template<typename T>
+    template <typename T>
     void error(const T &msg)
     {
         log(level::err, msg);
     }
 
-    template<typename T>
+    template <typename T>
     void critical(const T &msg)
     {
         log(level::critical, msg);
@@ -332,8 +323,8 @@ public:
     void dump_backtrace();
 
     // flush functions
-    void flush();
-    void flush_on(level::level_enum log_level);
+    void              flush();
+    void              flush_on(level::level_enum log_level);
     level::level_enum flush_level() const;
 
     // sinks
@@ -348,20 +339,20 @@ public:
     virtual std::shared_ptr<logger> clone(std::string logger_name);
 
 protected:
-    std::string name_;
+    std::string           name_;
     std::vector<sink_ptr> sinks_;
-    spdlog::level_t level_{level::info};
-    spdlog::level_t flush_level_{level::off};
-    err_handler custom_err_handler_{nullptr};
-    details::backtracer tracer_;
+    spdlog::level_t       level_{level::info};
+    spdlog::level_t       flush_level_{level::off};
+    err_handler           custom_err_handler_{nullptr};
+    details::backtracer   tracer_;
 
     // common implementation for after templated public api has been resolved
-    template<typename... Args>
-    void log_(source_loc loc, level::level_enum lvl, string_view_t fmt, Args &&... args)
+    template <typename... Args>
+    void log_(source_loc loc, level::level_enum lvl, string_view_t fmt, Args &&...args)
     {
-        bool log_enabled = should_log(lvl);
+        bool log_enabled       = should_log(lvl);
         bool traceback_enabled = tracer_.enabled();
-        if (!log_enabled && !traceback_enabled)
+        if(!log_enabled && !traceback_enabled)
         {
             return;
         }
@@ -381,12 +372,12 @@ protected:
     }
 
 #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
-    template<typename... Args>
-    void log_(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args &&... args)
+    template <typename... Args>
+    void log_(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args &&...args)
     {
-        bool log_enabled = should_log(lvl);
+        bool log_enabled       = should_log(lvl);
         bool traceback_enabled = tracer_.enabled();
-        if (!log_enabled && !traceback_enabled)
+        if(!log_enabled && !traceback_enabled)
         {
             return;
         }
@@ -395,7 +386,9 @@ protected:
             // format to wmemory_buffer and convert to utf8
             wmemory_buf_t wbuf;
             fmt_lib::vformat_to(
-                std::back_inserter(wbuf), fmt, fmt_lib::make_format_args<fmt_lib::wformat_context>(std::forward<Args>(args)...));
+                std::back_inserter(wbuf),
+                fmt,
+                fmt_lib::make_format_args<fmt_lib::wformat_context>(std::forward<Args>(args)...));
 
             memory_buf_t buf;
             details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);
@@ -408,11 +401,11 @@ protected:
 
     // log the given message (if the given log level is high enough),
     // and save backtrace (if backtrace is enabled).
-    void log_it_(const details::log_msg &log_msg, bool log_enabled, bool traceback_enabled);
+    void         log_it_(const details::log_msg &log_msg, bool log_enabled, bool traceback_enabled);
     virtual void sink_it_(const details::log_msg &msg);
     virtual void flush_();
-    void dump_backtrace_();
-    bool should_flush_(const details::log_msg &msg);
+    void         dump_backtrace_();
+    bool         should_flush_(const details::log_msg &msg);
 
     // handle errors during logging.
     // default handler prints the error to stderr at max rate of 1 message/sec.
@@ -424,5 +417,5 @@ void swap(logger &a, logger &b);
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "logger-inl.h"
+#include "logger-inl.h"
 #endif
diff --git a/include/spdlog/pattern_formatter-inl.h b/include/spdlog/pattern_formatter-inl.h
index 01afbe6f0..0462f9a82 100644
--- a/include/spdlog/pattern_formatter-inl.h
+++ b/include/spdlog/pattern_formatter-inl.h
@@ -4,1041 +4,973 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/pattern_formatter.h>
+#include <spdlog/pattern_formatter.h>
 #endif
 
-#include <spdlog/details/fmt_helper.h>
-#include <spdlog/details/log_msg.h>
-#include <spdlog/details/os.h>
-#include <spdlog/fmt/fmt.h>
-#include <spdlog/formatter.h>
-
 #include <algorithm>
 #include <array>
-#include <chrono>
-#include <ctime>
 #include <cctype>
+#include <chrono>
 #include <cstring>
+#include <ctime>
 #include <iterator>
 #include <memory>
 #include <mutex>
+#include <spdlog/details/fmt_helper.h>
+#include <spdlog/details/log_msg.h>
+#include <spdlog/details/os.h>
+#include <spdlog/fmt/fmt.h>
+#include <spdlog/formatter.h>
 #include <string>
 #include <thread>
 #include <utility>
 #include <vector>
 
-namespace spdlog {
-namespace details {
+namespace spdlog
+{
+namespace details
+{
 
-///////////////////////////////////////////////////////////////////////
-// name & level pattern appender
-///////////////////////////////////////////////////////////////////////
+    ///////////////////////////////////////////////////////////////////////
+    // name & level pattern appender
+    ///////////////////////////////////////////////////////////////////////
 
-class scoped_padder
-{
-public:
-    scoped_padder(size_t wrapped_size, const padding_info &padinfo, memory_buf_t &dest)
-        : padinfo_(padinfo)
-        , dest_(dest)
+    class scoped_padder
     {
-        remaining_pad_ = static_cast<long>(padinfo.width_) - static_cast<long>(wrapped_size);
-        if (remaining_pad_ <= 0)
+    public:
+        scoped_padder(size_t wrapped_size, const padding_info &padinfo, memory_buf_t &dest) :
+            padinfo_(padinfo), dest_(dest)
+        {
+            remaining_pad_ = static_cast<long>(padinfo.width_) - static_cast<long>(wrapped_size);
+            if(remaining_pad_ <= 0)
+            {
+                return;
+            }
+
+            if(padinfo_.side_ == padding_info::pad_side::left)
+            {
+                pad_it(remaining_pad_);
+                remaining_pad_ = 0;
+            }
+            else if(padinfo_.side_ == padding_info::pad_side::center)
+            {
+                auto half_pad = remaining_pad_ / 2;
+                auto reminder = remaining_pad_ & 1;
+                pad_it(half_pad);
+                remaining_pad_ = half_pad + reminder; // for the right side
+            }
+        }
+
+        template <typename T>
+        static unsigned int count_digits(T n)
         {
-            return;
+            return fmt_helper::count_digits(n);
         }
 
-        if (padinfo_.side_ == padding_info::pad_side::left)
+        ~scoped_padder()
         {
-            pad_it(remaining_pad_);
-            remaining_pad_ = 0;
+            if(remaining_pad_ >= 0)
+            {
+                pad_it(remaining_pad_);
+            }
+            else if(padinfo_.truncate_)
+            {
+                long new_size = static_cast<long>(dest_.size()) + remaining_pad_;
+                dest_.resize(static_cast<size_t>(new_size));
+            }
         }
-        else if (padinfo_.side_ == padding_info::pad_side::center)
+
+    private:
+        void pad_it(long count)
         {
-            auto half_pad = remaining_pad_ / 2;
-            auto reminder = remaining_pad_ & 1;
-            pad_it(half_pad);
-            remaining_pad_ = half_pad + reminder; // for the right side
+            fmt_helper::append_string_view(string_view_t(spaces_.data(), static_cast<size_t>(count)), dest_);
         }
-    }
 
-    template<typename T>
-    static unsigned int count_digits(T n)
-    {
-        return fmt_helper::count_digits(n);
-    }
+        const padding_info &padinfo_;
+        memory_buf_t       &dest_;
+        long                remaining_pad_;
+        string_view_t       spaces_{"                                                                ", 64};
+    };
 
-    ~scoped_padder()
+    struct null_scoped_padder
     {
-        if (remaining_pad_ >= 0)
-        {
-            pad_it(remaining_pad_);
-        }
-        else if (padinfo_.truncate_)
+        null_scoped_padder(size_t /*wrapped_size*/, const padding_info & /*padinfo*/, memory_buf_t & /*dest*/) {}
+
+        template <typename T>
+        static unsigned int count_digits(T /* number */)
         {
-            long new_size = static_cast<long>(dest_.size()) + remaining_pad_;
-            dest_.resize(static_cast<size_t>(new_size));
+            return 0;
         }
-    }
+    };
 
-private:
-    void pad_it(long count)
+    template <typename ScopedPadder>
+    class name_formatter final : public flag_formatter
     {
-        fmt_helper::append_string_view(string_view_t(spaces_.data(), static_cast<size_t>(count)), dest_);
-    }
-
-    const padding_info &padinfo_;
-    memory_buf_t &dest_;
-    long remaining_pad_;
-    string_view_t spaces_{"                                                                ", 64};
-};
+    public:
+        explicit name_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-struct null_scoped_padder
-{
-    null_scoped_padder(size_t /*wrapped_size*/, const padding_info & /*padinfo*/, memory_buf_t & /*dest*/) {}
+        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+        {
+            ScopedPadder p(msg.logger_name.size(), padinfo_, dest);
+            fmt_helper::append_string_view(msg.logger_name, dest);
+        }
+    };
 
-    template<typename T>
-    static unsigned int count_digits(T /* number */)
+    // log level appender
+    template <typename ScopedPadder>
+    class level_formatter final : public flag_formatter
     {
-        return 0;
-    }
-};
+    public:
+        explicit level_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-template<typename ScopedPadder>
-class name_formatter final : public flag_formatter
-{
-public:
-    explicit name_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+        {
+            const string_view_t &level_name = level::to_string_view(msg.level);
+            ScopedPadder         p(level_name.size(), padinfo_, dest);
+            fmt_helper::append_string_view(level_name, dest);
+        }
+    };
 
-    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    // short log level appender
+    template <typename ScopedPadder>
+    class short_level_formatter final : public flag_formatter
     {
-        ScopedPadder p(msg.logger_name.size(), padinfo_, dest);
-        fmt_helper::append_string_view(msg.logger_name, dest);
-    }
-};
+    public:
+        explicit short_level_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// log level appender
-template<typename ScopedPadder>
-class level_formatter final : public flag_formatter
-{
-public:
-    explicit level_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+        {
+            string_view_t level_name{level::to_short_c_str(msg.level)};
+            ScopedPadder  p(level_name.size(), padinfo_, dest);
+            fmt_helper::append_string_view(level_name, dest);
+        }
+    };
+
+    ///////////////////////////////////////////////////////////////////////
+    // Date time pattern appenders
+    ///////////////////////////////////////////////////////////////////////
 
-    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    static const char *ampm(const tm &t)
     {
-        const string_view_t &level_name = level::to_string_view(msg.level);
-        ScopedPadder p(level_name.size(), padinfo_, dest);
-        fmt_helper::append_string_view(level_name, dest);
+        return t.tm_hour >= 12 ? "PM" : "AM";
     }
-};
 
-// short log level appender
-template<typename ScopedPadder>
-class short_level_formatter final : public flag_formatter
-{
-public:
-    explicit short_level_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
-
-    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    static int to12h(const tm &t)
     {
-        string_view_t level_name{level::to_short_c_str(msg.level)};
-        ScopedPadder p(level_name.size(), padinfo_, dest);
-        fmt_helper::append_string_view(level_name, dest);
+        return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
     }
-};
-
-///////////////////////////////////////////////////////////////////////
-// Date time pattern appenders
-///////////////////////////////////////////////////////////////////////
 
-static const char *ampm(const tm &t)
-{
-    return t.tm_hour >= 12 ? "PM" : "AM";
-}
+    // Abbreviated weekday name
+    static std::array<const char *, 7> days{{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}};
 
-static int to12h(const tm &t)
-{
-    return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
-}
-
-// Abbreviated weekday name
-static std::array<const char *, 7> days{{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}};
-
-template<typename ScopedPadder>
-class a_formatter final : public flag_formatter
-{
-public:
-    explicit a_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
-
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    template <typename ScopedPadder>
+    class a_formatter final : public flag_formatter
     {
-        string_view_t field_value{days[static_cast<size_t>(tm_time.tm_wday)]};
-        ScopedPadder p(field_value.size(), padinfo_, dest);
-        fmt_helper::append_string_view(field_value, dest);
-    }
-};
+    public:
+        explicit a_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// Full weekday name
-static std::array<const char *, 7> full_days{{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}};
+        void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+        {
+            string_view_t field_value{days[static_cast<size_t>(tm_time.tm_wday)]};
+            ScopedPadder  p(field_value.size(), padinfo_, dest);
+            fmt_helper::append_string_view(field_value, dest);
+        }
+    };
 
-template<typename ScopedPadder>
-class A_formatter : public flag_formatter
-{
-public:
-    explicit A_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+    // Full weekday name
+    static std::array<const char *, 7> full_days{
+        {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}};
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    template <typename ScopedPadder>
+    class A_formatter : public flag_formatter
     {
-        string_view_t field_value{full_days[static_cast<size_t>(tm_time.tm_wday)]};
-        ScopedPadder p(field_value.size(), padinfo_, dest);
-        fmt_helper::append_string_view(field_value, dest);
-    }
-};
+    public:
+        explicit A_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// Abbreviated month
-static const std::array<const char *, 12> months{{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}};
+        void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+        {
+            string_view_t field_value{full_days[static_cast<size_t>(tm_time.tm_wday)]};
+            ScopedPadder  p(field_value.size(), padinfo_, dest);
+            fmt_helper::append_string_view(field_value, dest);
+        }
+    };
 
-template<typename ScopedPadder>
-class b_formatter final : public flag_formatter
-{
-public:
-    explicit b_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+    // Abbreviated month
+    static const std::array<const char *, 12> months{
+        {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}};
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    template <typename ScopedPadder>
+    class b_formatter final : public flag_formatter
     {
-        string_view_t field_value{months[static_cast<size_t>(tm_time.tm_mon)]};
-        ScopedPadder p(field_value.size(), padinfo_, dest);
-        fmt_helper::append_string_view(field_value, dest);
-    }
-};
+    public:
+        explicit b_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// Full month name
-static const std::array<const char *, 12> full_months{
-    {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}};
+        void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+        {
+            string_view_t field_value{months[static_cast<size_t>(tm_time.tm_mon)]};
+            ScopedPadder  p(field_value.size(), padinfo_, dest);
+            fmt_helper::append_string_view(field_value, dest);
+        }
+    };
+
+    // Full month name
+    static const std::array<const char *, 12> full_months{
+        {"January",
+         "February",
+         "March",
+         "April",
+         "May",
+         "June",
+         "July",
+         "August",
+         "September",
+         "October",
+         "November",
+         "December"}};
+
+    template <typename ScopedPadder>
+    class B_formatter final : public flag_formatter
+    {
+    public:
+        explicit B_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-template<typename ScopedPadder>
-class B_formatter final : public flag_formatter
-{
-public:
-    explicit B_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+        void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+        {
+            string_view_t field_value{full_months[static_cast<size_t>(tm_time.tm_mon)]};
+            ScopedPadder  p(field_value.size(), padinfo_, dest);
+            fmt_helper::append_string_view(field_value, dest);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    // Date and time representation (Thu Aug 23 15:35:46 2014)
+    template <typename ScopedPadder>
+    class c_formatter final : public flag_formatter
     {
-        string_view_t field_value{full_months[static_cast<size_t>(tm_time.tm_mon)]};
-        ScopedPadder p(field_value.size(), padinfo_, dest);
-        fmt_helper::append_string_view(field_value, dest);
-    }
-};
+    public:
+        explicit c_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// Date and time representation (Thu Aug 23 15:35:46 2014)
-template<typename ScopedPadder>
-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, memory_buf_t &dest) override
+        {
+            const size_t field_size = 24;
+            ScopedPadder p(field_size, padinfo_, dest);
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
-    {
-        const size_t field_size = 24;
-        ScopedPadder p(field_size, padinfo_, dest);
-
-        fmt_helper::append_string_view(days[static_cast<size_t>(tm_time.tm_wday)], dest);
-        dest.push_back(' ');
-        fmt_helper::append_string_view(months[static_cast<size_t>(tm_time.tm_mon)], dest);
-        dest.push_back(' ');
-        fmt_helper::append_int(tm_time.tm_mday, dest);
-        dest.push_back(' ');
-        // time
-
-        fmt_helper::pad2(tm_time.tm_hour, 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_int(tm_time.tm_year + 1900, dest);
-    }
-};
+            fmt_helper::append_string_view(days[static_cast<size_t>(tm_time.tm_wday)], dest);
+            dest.push_back(' ');
+            fmt_helper::append_string_view(months[static_cast<size_t>(tm_time.tm_mon)], dest);
+            dest.push_back(' ');
+            fmt_helper::append_int(tm_time.tm_mday, dest);
+            dest.push_back(' ');
+            // time
 
-// year - 2 digit
-template<typename ScopedPadder>
-class C_formatter final : public flag_formatter
-{
-public:
-    explicit C_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+            fmt_helper::pad2(tm_time.tm_hour, 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_int(tm_time.tm_year + 1900, dest);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    // year - 2 digit
+    template <typename ScopedPadder>
+    class C_formatter final : public flag_formatter
     {
-        const size_t field_size = 2;
-        ScopedPadder p(field_size, padinfo_, dest);
-        fmt_helper::pad2(tm_time.tm_year % 100, dest);
-    }
-};
+    public:
+        explicit C_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
-template<typename ScopedPadder>
-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, memory_buf_t &dest) override
+        {
+            const size_t field_size = 2;
+            ScopedPadder p(field_size, padinfo_, dest);
+            fmt_helper::pad2(tm_time.tm_year % 100, dest);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    // Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
+    template <typename ScopedPadder>
+    class D_formatter final : public flag_formatter
     {
-        const size_t field_size = 10;
-        ScopedPadder 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);
-        dest.push_back('/');
-        fmt_helper::pad2(tm_time.tm_year % 100, dest);
-    }
-};
+    public:
+        explicit D_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// year - 4 digit
-template<typename ScopedPadder>
-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, memory_buf_t &dest) override
+        {
+            const size_t field_size = 10;
+            ScopedPadder 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);
+            dest.push_back('/');
+            fmt_helper::pad2(tm_time.tm_year % 100, dest);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    // year - 4 digit
+    template <typename ScopedPadder>
+    class Y_formatter final : public flag_formatter
     {
-        const size_t field_size = 4;
-        ScopedPadder p(field_size, padinfo_, dest);
-        fmt_helper::append_int(tm_time.tm_year + 1900, dest);
-    }
-};
+    public:
+        explicit Y_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// month 1-12
-template<typename ScopedPadder>
-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, memory_buf_t &dest) override
+        {
+            const size_t field_size = 4;
+            ScopedPadder p(field_size, padinfo_, dest);
+            fmt_helper::append_int(tm_time.tm_year + 1900, dest);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    // month 1-12
+    template <typename ScopedPadder>
+    class m_formatter final : public flag_formatter
     {
-        const size_t field_size = 2;
-        ScopedPadder p(field_size, padinfo_, dest);
-        fmt_helper::pad2(tm_time.tm_mon + 1, dest);
-    }
-};
+    public:
+        explicit m_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// day of month 1-31
-template<typename ScopedPadder>
-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, memory_buf_t &dest) override
+        {
+            const size_t field_size = 2;
+            ScopedPadder p(field_size, padinfo_, dest);
+            fmt_helper::pad2(tm_time.tm_mon + 1, dest);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    // day of month 1-31
+    template <typename ScopedPadder>
+    class d_formatter final : public flag_formatter
     {
-        const size_t field_size = 2;
-        ScopedPadder p(field_size, padinfo_, dest);
-        fmt_helper::pad2(tm_time.tm_mday, dest);
-    }
-};
+    public:
+        explicit d_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// hours in 24 format 0-23
-template<typename ScopedPadder>
-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, memory_buf_t &dest) override
+        {
+            const size_t field_size = 2;
+            ScopedPadder p(field_size, padinfo_, dest);
+            fmt_helper::pad2(tm_time.tm_mday, dest);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    // hours in 24 format 0-23
+    template <typename ScopedPadder>
+    class H_formatter final : public flag_formatter
     {
-        const size_t field_size = 2;
-        ScopedPadder p(field_size, padinfo_, dest);
-        fmt_helper::pad2(tm_time.tm_hour, dest);
-    }
-};
+    public:
+        explicit H_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// hours in 12 format 1-12
-template<typename ScopedPadder>
-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, memory_buf_t &dest) override
+        {
+            const size_t field_size = 2;
+            ScopedPadder p(field_size, padinfo_, dest);
+            fmt_helper::pad2(tm_time.tm_hour, dest);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    // hours in 12 format 1-12
+    template <typename ScopedPadder>
+    class I_formatter final : public flag_formatter
     {
-        const size_t field_size = 2;
-        ScopedPadder p(field_size, padinfo_, dest);
-        fmt_helper::pad2(to12h(tm_time), dest);
-    }
-};
+    public:
+        explicit I_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// minutes 0-59
-template<typename ScopedPadder>
-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, memory_buf_t &dest) override
+        {
+            const size_t field_size = 2;
+            ScopedPadder p(field_size, padinfo_, dest);
+            fmt_helper::pad2(to12h(tm_time), dest);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    // minutes 0-59
+    template <typename ScopedPadder>
+    class M_formatter final : public flag_formatter
     {
-        const size_t field_size = 2;
-        ScopedPadder p(field_size, padinfo_, dest);
-        fmt_helper::pad2(tm_time.tm_min, dest);
-    }
-};
+    public:
+        explicit M_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// seconds 0-59
-template<typename ScopedPadder>
-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, memory_buf_t &dest) override
+        {
+            const size_t field_size = 2;
+            ScopedPadder p(field_size, padinfo_, dest);
+            fmt_helper::pad2(tm_time.tm_min, dest);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    // seconds 0-59
+    template <typename ScopedPadder>
+    class S_formatter final : public flag_formatter
     {
-        const size_t field_size = 2;
-        ScopedPadder p(field_size, padinfo_, dest);
-        fmt_helper::pad2(tm_time.tm_sec, dest);
-    }
-};
+    public:
+        explicit S_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// milliseconds
-template<typename ScopedPadder>
-class e_formatter final : public flag_formatter
-{
-public:
-    explicit e_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+        void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+        {
+            const size_t field_size = 2;
+            ScopedPadder p(field_size, padinfo_, dest);
+            fmt_helper::pad2(tm_time.tm_sec, dest);
+        }
+    };
 
-    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    // milliseconds
+    template <typename ScopedPadder>
+    class e_formatter final : public flag_formatter
     {
-        auto millis = fmt_helper::time_fraction<std::chrono::milliseconds>(msg.time);
-        const size_t field_size = 3;
-        ScopedPadder p(field_size, padinfo_, dest);
-        fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest);
-    }
-};
+    public:
+        explicit e_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// microseconds
-template<typename ScopedPadder>
-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 &, memory_buf_t &dest) override
+        {
+            auto         millis     = fmt_helper::time_fraction<std::chrono::milliseconds>(msg.time);
+            const size_t field_size = 3;
+            ScopedPadder p(field_size, padinfo_, dest);
+            fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest);
+        }
+    };
 
-    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    // microseconds
+    template <typename ScopedPadder>
+    class f_formatter final : public flag_formatter
     {
-        auto micros = fmt_helper::time_fraction<std::chrono::microseconds>(msg.time);
+    public:
+        explicit f_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-        const size_t field_size = 6;
-        ScopedPadder p(field_size, padinfo_, dest);
-        fmt_helper::pad6(static_cast<size_t>(micros.count()), dest);
-    }
-};
+        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+        {
+            auto micros = fmt_helper::time_fraction<std::chrono::microseconds>(msg.time);
 
-// nanoseconds
-template<typename ScopedPadder>
-class F_formatter final : public flag_formatter
-{
-public:
-    explicit F_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+            const size_t field_size = 6;
+            ScopedPadder p(field_size, padinfo_, dest);
+            fmt_helper::pad6(static_cast<size_t>(micros.count()), dest);
+        }
+    };
 
-    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    // nanoseconds
+    template <typename ScopedPadder>
+    class F_formatter final : public flag_formatter
     {
-        auto ns = fmt_helper::time_fraction<std::chrono::nanoseconds>(msg.time);
-        const size_t field_size = 9;
-        ScopedPadder p(field_size, padinfo_, dest);
-        fmt_helper::pad9(static_cast<size_t>(ns.count()), dest);
-    }
-};
+    public:
+        explicit F_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// seconds since epoch
-template<typename ScopedPadder>
-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 &, memory_buf_t &dest) override
+        {
+            auto         ns         = fmt_helper::time_fraction<std::chrono::nanoseconds>(msg.time);
+            const size_t field_size = 9;
+            ScopedPadder p(field_size, padinfo_, dest);
+            fmt_helper::pad9(static_cast<size_t>(ns.count()), dest);
+        }
+    };
 
-    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    // seconds since epoch
+    template <typename ScopedPadder>
+    class E_formatter final : public flag_formatter
     {
-        const size_t field_size = 10;
-        ScopedPadder 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);
-    }
-};
+    public:
+        explicit E_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// AM/PM
-template<typename ScopedPadder>
-class p_formatter final : public flag_formatter
-{
-public:
-    explicit p_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+        {
+            const size_t field_size = 10;
+            ScopedPadder 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);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    // AM/PM
+    template <typename ScopedPadder>
+    class p_formatter final : public flag_formatter
     {
-        const size_t field_size = 2;
-        ScopedPadder p(field_size, padinfo_, dest);
-        fmt_helper::append_string_view(ampm(tm_time), dest);
-    }
-};
+    public:
+        explicit p_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// 12 hour clock 02:55:02 pm
-template<typename ScopedPadder>
-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, memory_buf_t &dest) override
+        {
+            const size_t field_size = 2;
+            ScopedPadder p(field_size, padinfo_, dest);
+            fmt_helper::append_string_view(ampm(tm_time), dest);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    // 12 hour clock 02:55:02 pm
+    template <typename ScopedPadder>
+    class r_formatter final : public flag_formatter
     {
-        const size_t field_size = 11;
-        ScopedPadder 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_string_view(ampm(tm_time), dest);
-    }
-};
+    public:
+        explicit r_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// 24-hour HH:MM time, equivalent to %H:%M
-template<typename ScopedPadder>
-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, memory_buf_t &dest) override
+        {
+            const size_t field_size = 11;
+            ScopedPadder 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_string_view(ampm(tm_time), dest);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    // 24-hour HH:MM time, equivalent to %H:%M
+    template <typename ScopedPadder>
+    class R_formatter final : public flag_formatter
     {
-        const size_t field_size = 5;
-        ScopedPadder p(field_size, padinfo_, dest);
+    public:
+        explicit R_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-        fmt_helper::pad2(tm_time.tm_hour, dest);
-        dest.push_back(':');
-        fmt_helper::pad2(tm_time.tm_min, dest);
-    }
-};
+        void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+        {
+            const size_t field_size = 5;
+            ScopedPadder p(field_size, padinfo_, dest);
 
-// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
-template<typename ScopedPadder>
-class T_formatter final : public flag_formatter
-{
-public:
-    explicit T_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+            fmt_helper::pad2(tm_time.tm_hour, dest);
+            dest.push_back(':');
+            fmt_helper::pad2(tm_time.tm_min, dest);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+    // ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
+    template <typename ScopedPadder>
+    class T_formatter final : public flag_formatter
     {
-        const size_t field_size = 8;
-        ScopedPadder p(field_size, padinfo_, dest);
-
-        fmt_helper::pad2(tm_time.tm_hour, dest);
-        dest.push_back(':');
-        fmt_helper::pad2(tm_time.tm_min, dest);
-        dest.push_back(':');
-        fmt_helper::pad2(tm_time.tm_sec, dest);
-    }
-};
+    public:
+        explicit T_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// ISO 8601 offset from UTC in timezone (+-HH:MM)
-template<typename ScopedPadder>
-class z_formatter final : public flag_formatter
-{
-public:
-    explicit z_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+        void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
+        {
+            const size_t field_size = 8;
+            ScopedPadder p(field_size, padinfo_, dest);
 
-    z_formatter() = default;
-    z_formatter(const z_formatter &) = delete;
-    z_formatter &operator=(const z_formatter &) = delete;
+            fmt_helper::pad2(tm_time.tm_hour, dest);
+            dest.push_back(':');
+            fmt_helper::pad2(tm_time.tm_min, dest);
+            dest.push_back(':');
+            fmt_helper::pad2(tm_time.tm_sec, dest);
+        }
+    };
 
-    void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) override
+    // ISO 8601 offset from UTC in timezone (+-HH:MM)
+    template <typename ScopedPadder>
+    class z_formatter final : public flag_formatter
     {
-        const size_t field_size = 6;
-        ScopedPadder p(field_size, padinfo_, dest);
+    public:
+        explicit z_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-        auto total_minutes = get_cached_offset(msg, tm_time);
-        bool is_negative = total_minutes < 0;
-        if (is_negative)
-        {
-            total_minutes = -total_minutes;
-            dest.push_back('-');
-        }
-        else
+        z_formatter()                               = default;
+        z_formatter(const z_formatter &)            = delete;
+        z_formatter &operator=(const z_formatter &) = delete;
+
+        void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) override
         {
-            dest.push_back('+');
-        }
+            const size_t field_size = 6;
+            ScopedPadder p(field_size, padinfo_, dest);
 
-        fmt_helper::pad2(total_minutes / 60, dest); // hours
-        dest.push_back(':');
-        fmt_helper::pad2(total_minutes % 60, dest); // minutes
-    }
+            auto total_minutes = get_cached_offset(msg, tm_time);
+            bool is_negative   = total_minutes < 0;
+            if(is_negative)
+            {
+                total_minutes = -total_minutes;
+                dest.push_back('-');
+            }
+            else
+            {
+                dest.push_back('+');
+            }
 
-private:
-    log_clock::time_point last_update_{std::chrono::seconds(0)};
-    int offset_minutes_{0};
+            fmt_helper::pad2(total_minutes / 60, dest); // hours
+            dest.push_back(':');
+            fmt_helper::pad2(total_minutes % 60, dest); // minutes
+        }
 
-    int get_cached_offset(const log_msg &msg, const std::tm &tm_time)
-    {
-        // refresh every 10 seconds
-        if (msg.time - last_update_ >= std::chrono::seconds(10))
+    private:
+        log_clock::time_point last_update_{std::chrono::seconds(0)};
+        int                   offset_minutes_{0};
+
+        int get_cached_offset(const log_msg &msg, const std::tm &tm_time)
         {
-            offset_minutes_ = os::utc_minutes_offset(tm_time);
-            last_update_ = msg.time;
+            // refresh every 10 seconds
+            if(msg.time - last_update_ >= std::chrono::seconds(10))
+            {
+                offset_minutes_ = os::utc_minutes_offset(tm_time);
+                last_update_    = msg.time;
+            }
+            return offset_minutes_;
         }
-        return offset_minutes_;
-    }
-};
-
-// Thread id
-template<typename ScopedPadder>
-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 &, memory_buf_t &dest) override
+    // Thread id
+    template <typename ScopedPadder>
+    class t_formatter final : public flag_formatter
     {
-        const auto field_size = ScopedPadder::count_digits(msg.thread_id);
-        ScopedPadder p(field_size, padinfo_, dest);
-        fmt_helper::append_int(msg.thread_id, dest);
-    }
-};
+    public:
+        explicit t_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// Current pid
-template<typename ScopedPadder>
-class pid_formatter final : public flag_formatter
-{
-public:
-    explicit pid_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+        {
+            const auto   field_size = ScopedPadder::count_digits(msg.thread_id);
+            ScopedPadder p(field_size, padinfo_, dest);
+            fmt_helper::append_int(msg.thread_id, dest);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override
+    // Current pid
+    template <typename ScopedPadder>
+    class pid_formatter final : public flag_formatter
     {
-        const auto pid = static_cast<uint32_t>(details::os::pid());
-        auto field_size = ScopedPadder::count_digits(pid);
-        ScopedPadder p(field_size, padinfo_, dest);
-        fmt_helper::append_int(pid, dest);
-    }
-};
+    public:
+        explicit pid_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-template<typename ScopedPadder>
-class v_formatter final : public flag_formatter
-{
-public:
-    explicit v_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+        void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override
+        {
+            const auto   pid        = static_cast<uint32_t>(details::os::pid());
+            auto         field_size = ScopedPadder::count_digits(pid);
+            ScopedPadder p(field_size, padinfo_, dest);
+            fmt_helper::append_int(pid, dest);
+        }
+    };
 
-    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    template <typename ScopedPadder>
+    class v_formatter final : public flag_formatter
     {
-        ScopedPadder p(msg.payload.size(), padinfo_, dest);
-        fmt_helper::append_string_view(msg.payload, dest);
-    }
-};
+    public:
+        explicit v_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-class ch_formatter final : public flag_formatter
-{
-public:
-    explicit ch_formatter(char ch)
-        : ch_(ch)
-    {}
+        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+        {
+            ScopedPadder p(msg.payload.size(), padinfo_, dest);
+            fmt_helper::append_string_view(msg.payload, dest);
+        }
+    };
 
-    void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override
+    class ch_formatter final : public flag_formatter
     {
-        dest.push_back(ch_);
-    }
+    public:
+        explicit ch_formatter(char ch) : ch_(ch) {}
 
-private:
-    char ch_;
-};
+        void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override { dest.push_back(ch_); }
 
-// aggregate user chars to display as is
-class aggregate_formatter final : public flag_formatter
-{
-public:
-    aggregate_formatter() = default;
+    private:
+        char ch_;
+    };
 
-    void add_ch(char ch)
-    {
-        str_ += ch;
-    }
-    void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override
+    // aggregate user chars to display as is
+    class aggregate_formatter final : public flag_formatter
     {
-        fmt_helper::append_string_view(str_, dest);
-    }
+    public:
+        aggregate_formatter() = default;
 
-private:
-    std::string str_;
-};
+        void add_ch(char ch) { str_ += ch; }
+        void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override
+        {
+            fmt_helper::append_string_view(str_, dest);
+        }
 
-// 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)
-    {}
+    private:
+        std::string str_;
+    };
 
-    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    // mark the color range. expect it to be in the form of "%^colored text%$"
+    class color_start_formatter final : public flag_formatter
     {
-        msg.color_range_start = dest.size();
-    }
-};
+    public:
+        explicit color_start_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-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 &, memory_buf_t &dest) override
+        {
+            msg.color_range_start = dest.size();
+        }
+    };
 
-    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    class color_stop_formatter final : public flag_formatter
     {
-        msg.color_range_end = dest.size();
-    }
-};
+    public:
+        explicit color_stop_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-// print source location
-template<typename ScopedPadder>
-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 &, memory_buf_t &dest) override
-    {
-        if (msg.source.empty())
+        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
         {
-            ScopedPadder p(0, padinfo_, dest);
-            return;
+            msg.color_range_end = dest.size();
         }
+    };
 
-        size_t text_size;
-        if (padinfo_.enabled())
-        {
-            // calc text size for padding based on "filename:line"
-            text_size = std::char_traits<char>::length(msg.source.filename) + ScopedPadder::count_digits(msg.source.line) + 1;
-        }
-        else
+    // print source location
+    template <typename ScopedPadder>
+    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 &, memory_buf_t &dest) override
         {
-            text_size = 0;
-        }
+            if(msg.source.empty())
+            {
+                ScopedPadder p(0, padinfo_, dest);
+                return;
+            }
 
-        ScopedPadder 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);
-    }
-};
+            size_t text_size;
+            if(padinfo_.enabled())
+            {
+                // calc text size for padding based on "filename:line"
+                text_size = std::char_traits<char>::length(msg.source.filename) +
+                            ScopedPadder::count_digits(msg.source.line) + 1;
+            }
+            else
+            {
+                text_size = 0;
+            }
 
-// print source filename
-template<typename ScopedPadder>
-class source_filename_formatter final : public flag_formatter
-{
-public:
-    explicit source_filename_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+            ScopedPadder 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);
+        }
+    };
 
-    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    // print source filename
+    template <typename ScopedPadder>
+    class source_filename_formatter final : public flag_formatter
     {
-        if (msg.source.empty())
+    public:
+        explicit source_filename_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
+
+        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
         {
-            ScopedPadder p(0, padinfo_, dest);
-            return;
+            if(msg.source.empty())
+            {
+                ScopedPadder p(0, padinfo_, dest);
+                return;
+            }
+            size_t       text_size = padinfo_.enabled() ? std::char_traits<char>::length(msg.source.filename) : 0;
+            ScopedPadder p(text_size, padinfo_, dest);
+            fmt_helper::append_string_view(msg.source.filename, dest);
         }
-        size_t text_size = padinfo_.enabled() ? std::char_traits<char>::length(msg.source.filename) : 0;
-        ScopedPadder p(text_size, padinfo_, dest);
-        fmt_helper::append_string_view(msg.source.filename, dest);
-    }
-};
+    };
 
-template<typename ScopedPadder>
-class short_filename_formatter final : public flag_formatter
-{
-public:
-    explicit short_filename_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+    template <typename ScopedPadder>
+    class short_filename_formatter final : public flag_formatter
+    {
+    public:
+        explicit short_filename_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
 #ifdef _MSC_VER
-#    pragma warning(push)
-#    pragma warning(disable : 4127) // consider using 'if constexpr' instead
-#endif                              // _MSC_VER
-    static const char *basename(const char *filename)
-    {
-        // if the size is 2 (1 character + null terminator) we can use the more efficient strrchr
-        // the branch will be elided by optimizations
-        if (sizeof(os::folder_seps) == 2)
-        {
-            const char *rv = std::strrchr(filename, os::folder_seps[0]);
-            return rv != nullptr ? rv + 1 : filename;
-        }
-        else
+#pragma warning(push)
+#pragma warning(disable : 4127) // consider using 'if constexpr' instead
+#endif                          // _MSC_VER
+        static const char *basename(const char *filename)
         {
-            const std::reverse_iterator<const char *> begin(filename + std::strlen(filename));
-            const std::reverse_iterator<const char *> end(filename);
+            // if the size is 2 (1 character + null terminator) we can use the more efficient strrchr
+            // the branch will be elided by optimizations
+            if(sizeof(os::folder_seps) == 2)
+            {
+                const char *rv = std::strrchr(filename, os::folder_seps[0]);
+                return rv != nullptr ? rv + 1 : filename;
+            }
+            else
+            {
+                const std::reverse_iterator<const char *> begin(filename + std::strlen(filename));
+                const std::reverse_iterator<const char *> end(filename);
 
-            const auto it = std::find_first_of(begin, end, std::begin(os::folder_seps), std::end(os::folder_seps) - 1);
-            return it != end ? it.base() : filename;
+                const auto it =
+                    std::find_first_of(begin, end, std::begin(os::folder_seps), std::end(os::folder_seps) - 1);
+                return it != end ? it.base() : filename;
+            }
         }
-    }
 #ifdef _MSC_VER
-#    pragma warning(pop)
+#pragma warning(pop)
 #endif // _MSC_VER
 
-    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
-    {
-        if (msg.source.empty())
+        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
         {
-            ScopedPadder p(0, padinfo_, dest);
-            return;
+            if(msg.source.empty())
+            {
+                ScopedPadder p(0, padinfo_, dest);
+                return;
+            }
+            auto         filename  = basename(msg.source.filename);
+            size_t       text_size = padinfo_.enabled() ? std::char_traits<char>::length(filename) : 0;
+            ScopedPadder p(text_size, padinfo_, dest);
+            fmt_helper::append_string_view(filename, dest);
         }
-        auto filename = basename(msg.source.filename);
-        size_t text_size = padinfo_.enabled() ? std::char_traits<char>::length(filename) : 0;
-        ScopedPadder p(text_size, padinfo_, dest);
-        fmt_helper::append_string_view(filename, dest);
-    }
-};
-
-template<typename ScopedPadder>
-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 &, memory_buf_t &dest) override
+    template <typename ScopedPadder>
+    class source_linenum_formatter final : public flag_formatter
     {
-        if (msg.source.empty())
-        {
-            ScopedPadder p(0, padinfo_, dest);
-            return;
-        }
+    public:
+        explicit source_linenum_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-        auto field_size = ScopedPadder::count_digits(msg.source.line);
-        ScopedPadder p(field_size, padinfo_, dest);
-        fmt_helper::append_int(msg.source.line, dest);
-    }
-};
+        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+        {
+            if(msg.source.empty())
+            {
+                ScopedPadder p(0, padinfo_, dest);
+                return;
+            }
 
-// print source funcname
-template<typename ScopedPadder>
-class source_funcname_formatter final : public flag_formatter
-{
-public:
-    explicit source_funcname_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-    {}
+            auto         field_size = ScopedPadder::count_digits(msg.source.line);
+            ScopedPadder p(field_size, padinfo_, dest);
+            fmt_helper::append_int(msg.source.line, dest);
+        }
+    };
 
-    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    // print source funcname
+    template <typename ScopedPadder>
+    class source_funcname_formatter final : public flag_formatter
     {
-        if (msg.source.empty())
+    public:
+        explicit source_funcname_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
+
+        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
         {
-            ScopedPadder p(0, padinfo_, dest);
-            return;
+            if(msg.source.empty())
+            {
+                ScopedPadder p(0, padinfo_, dest);
+                return;
+            }
+            size_t       text_size = padinfo_.enabled() ? std::char_traits<char>::length(msg.source.funcname) : 0;
+            ScopedPadder p(text_size, padinfo_, dest);
+            fmt_helper::append_string_view(msg.source.funcname, dest);
         }
-        size_t text_size = padinfo_.enabled() ? std::char_traits<char>::length(msg.source.funcname) : 0;
-        ScopedPadder p(text_size, padinfo_, dest);
-        fmt_helper::append_string_view(msg.source.funcname, dest);
-    }
-};
-
-// print elapsed time since last message
-template<typename ScopedPadder, typename Units>
-class elapsed_formatter final : public flag_formatter
-{
-public:
-    using DurationUnits = Units;
+    };
 
-    explicit elapsed_formatter(padding_info padinfo)
-        : flag_formatter(padinfo)
-        , last_message_time_(log_clock::now())
-    {}
-
-    void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
+    // print elapsed time since last message
+    template <typename ScopedPadder, typename Units>
+    class elapsed_formatter final : public flag_formatter
     {
-        auto delta = (std::max)(msg.time - last_message_time_, log_clock::duration::zero());
-        auto delta_units = std::chrono::duration_cast<DurationUnits>(delta);
-        last_message_time_ = msg.time;
-        auto delta_count = static_cast<size_t>(delta_units.count());
-        auto n_digits = static_cast<size_t>(ScopedPadder::count_digits(delta_count));
-        ScopedPadder p(n_digits, padinfo_, dest);
-        fmt_helper::append_int(delta_count, dest);
-    }
+    public:
+        using DurationUnits = Units;
 
-private:
-    log_clock::time_point last_message_time_;
-};
+        explicit elapsed_formatter(padding_info padinfo) : flag_formatter(padinfo), last_message_time_(log_clock::now())
+        {
+        }
 
-// Full info formatter
-// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] [%s:%#] %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 &, memory_buf_t &dest) override
+        {
+            auto delta               = (std::max)(msg.time - last_message_time_, log_clock::duration::zero());
+            auto delta_units         = std::chrono::duration_cast<DurationUnits>(delta);
+            last_message_time_       = msg.time;
+            auto         delta_count = static_cast<size_t>(delta_units.count());
+            auto         n_digits    = static_cast<size_t>(ScopedPadder::count_digits(delta_count));
+            ScopedPadder p(n_digits, padinfo_, dest);
+            fmt_helper::append_int(delta_count, dest);
+        }
 
-    void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) override
-    {
-        using std::chrono::duration_cast;
-        using std::chrono::milliseconds;
-        using std::chrono::seconds;
+    private:
+        log_clock::time_point last_message_time_;
+    };
 
-        // cache the date/time part for the next second.
-        auto duration = msg.time.time_since_epoch();
-        auto secs = duration_cast<seconds>(duration);
+    // Full info formatter
+    // pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] [%s:%#] %v
+    class full_formatter final : public flag_formatter
+    {
+    public:
+        explicit full_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
 
-        if (cache_timestamp_ != secs || cached_datetime_.size() == 0)
+        void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) override
         {
-            cached_datetime_.clear();
-            cached_datetime_.push_back('[');
-            fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_);
-            cached_datetime_.push_back('-');
+            using std::chrono::duration_cast;
+            using std::chrono::milliseconds;
+            using std::chrono::seconds;
 
-            fmt_helper::pad2(tm_time.tm_mon + 1, cached_datetime_);
-            cached_datetime_.push_back('-');
+            // cache the date/time part for the next second.
+            auto duration = msg.time.time_since_epoch();
+            auto secs     = duration_cast<seconds>(duration);
 
-            fmt_helper::pad2(tm_time.tm_mday, cached_datetime_);
-            cached_datetime_.push_back(' ');
+            if(cache_timestamp_ != secs || cached_datetime_.size() == 0)
+            {
+                cached_datetime_.clear();
+                cached_datetime_.push_back('[');
+                fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_);
+                cached_datetime_.push_back('-');
 
-            fmt_helper::pad2(tm_time.tm_hour, cached_datetime_);
-            cached_datetime_.push_back(':');
+                fmt_helper::pad2(tm_time.tm_mon + 1, cached_datetime_);
+                cached_datetime_.push_back('-');
 
-            fmt_helper::pad2(tm_time.tm_min, cached_datetime_);
-            cached_datetime_.push_back(':');
+                fmt_helper::pad2(tm_time.tm_mday, cached_datetime_);
+                cached_datetime_.push_back(' ');
 
-            fmt_helper::pad2(tm_time.tm_sec, cached_datetime_);
-            cached_datetime_.push_back('.');
+                fmt_helper::pad2(tm_time.tm_hour, cached_datetime_);
+                cached_datetime_.push_back(':');
 
-            cache_timestamp_ = secs;
-        }
-        dest.append(cached_datetime_.begin(), cached_datetime_.end());
+                fmt_helper::pad2(tm_time.tm_min, cached_datetime_);
+                cached_datetime_.push_back(':');
 
-        auto millis = fmt_helper::time_fraction<milliseconds>(msg.time);
-        fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest);
-        dest.push_back(']');
-        dest.push_back(' ');
+                fmt_helper::pad2(tm_time.tm_sec, cached_datetime_);
+                cached_datetime_.push_back('.');
 
-        // append logger name if exists
-        if (msg.logger_name.size() > 0)
-        {
-            dest.push_back('[');
-            fmt_helper::append_string_view(msg.logger_name, dest);
+                cache_timestamp_ = secs;
+            }
+            dest.append(cached_datetime_.begin(), cached_datetime_.end());
+
+            auto millis = fmt_helper::time_fraction<milliseconds>(msg.time);
+            fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest);
             dest.push_back(']');
             dest.push_back(' ');
-        }
 
-        dest.push_back('[');
-        // wrap the level name with color
-        msg.color_range_start = dest.size();
-        // 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(' ');
-
-        // add source location if present
-        if (!msg.source.empty())
-        {
+            // append logger name if exists
+            if(msg.logger_name.size() > 0)
+            {
+                dest.push_back('[');
+                fmt_helper::append_string_view(msg.logger_name, dest);
+                dest.push_back(']');
+                dest.push_back(' ');
+            }
+
             dest.push_back('[');
-            const char *filename = details::short_filename_formatter<details::null_scoped_padder>::basename(msg.source.filename);
-            fmt_helper::append_string_view(filename, dest);
-            dest.push_back(':');
-            fmt_helper::append_int(msg.source.line, dest);
+            // wrap the level name with color
+            msg.color_range_start = dest.size();
+            // 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(' ');
+
+            // add source location if present
+            if(!msg.source.empty())
+            {
+                dest.push_back('[');
+                const char *filename =
+                    details::short_filename_formatter<details::null_scoped_padder>::basename(msg.source.filename);
+                fmt_helper::append_string_view(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);
         }
-        // fmt_helper::append_string_view(msg.msg(), dest);
-        fmt_helper::append_string_view(msg.payload, dest);
-    }
 
-private:
-    std::chrono::seconds cache_timestamp_{0};
-    memory_buf_t cached_datetime_;
-};
+    private:
+        std::chrono::seconds cache_timestamp_{0};
+        memory_buf_t         cached_datetime_;
+    };
 
 } // namespace details
 
 SPDLOG_INLINE pattern_formatter::pattern_formatter(
-    std::string pattern, pattern_time_type time_type, std::string eol, custom_flags custom_user_flags)
-    : pattern_(std::move(pattern))
-    , eol_(std::move(eol))
-    , pattern_time_type_(time_type)
-    , need_localtime_(false)
-    , last_log_secs_(0)
-    , custom_handlers_(std::move(custom_user_flags))
+    std::string       pattern,
+    pattern_time_type time_type,
+    std::string       eol,
+    custom_flags      custom_user_flags) :
+    pattern_(std::move(pattern)),
+    eol_(std::move(eol)),
+    pattern_time_type_(time_type),
+    need_localtime_(false),
+    last_log_secs_(0),
+    custom_handlers_(std::move(custom_user_flags))
 {
     std::memset(&cached_tm_, 0, sizeof(cached_tm_));
     compile_pattern_(pattern_);
 }
 
 // use by default full formatter for if pattern is not given
-SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type, std::string eol)
-    : pattern_("%+")
-    , eol_(std::move(eol))
-    , pattern_time_type_(time_type)
-    , need_localtime_(true)
-    , last_log_secs_(0)
+SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type, std::string eol) :
+    pattern_("%+"), eol_(std::move(eol)), pattern_time_type_(time_type), need_localtime_(true), last_log_secs_(0)
 {
     std::memset(&cached_tm_, 0, sizeof(cached_tm_));
     formatters_.push_back(details::make_unique<details::full_formatter>(details::padding_info{}));
@@ -1047,11 +979,12 @@ SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type,
 SPDLOG_INLINE std::unique_ptr<formatter> pattern_formatter::clone() const
 {
     custom_flags cloned_custom_formatters;
-    for (auto &it : custom_handlers_)
+    for(auto &it : custom_handlers_)
     {
         cloned_custom_formatters[it.first] = it.second->clone();
     }
-    auto cloned = details::make_unique<pattern_formatter>(pattern_, pattern_time_type_, eol_, std::move(cloned_custom_formatters));
+    auto cloned = details::make_unique<pattern_formatter>(
+        pattern_, pattern_time_type_, eol_, std::move(cloned_custom_formatters));
     cloned->need_localtime(need_localtime_);
 #if defined(__GNUC__) && __GNUC__ < 5
     return std::move(cloned);
@@ -1062,17 +995,17 @@ SPDLOG_INLINE std::unique_ptr<formatter> pattern_formatter::clone() const
 
 SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory_buf_t &dest)
 {
-    if (need_localtime_)
+    if(need_localtime_)
     {
         const auto secs = std::chrono::duration_cast<std::chrono::seconds>(msg.time.time_since_epoch());
-        if (secs != last_log_secs_)
+        if(secs != last_log_secs_)
         {
-            cached_tm_ = get_time_(msg);
+            cached_tm_     = get_time_(msg);
             last_log_secs_ = secs;
         }
     }
 
-    for (auto &f : formatters_)
+    for(auto &f : formatters_)
     {
         f->format(msg, cached_tm_, dest);
     }
@@ -1082,7 +1015,7 @@ SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory
 
 SPDLOG_INLINE void pattern_formatter::set_pattern(std::string pattern)
 {
-    pattern_ = std::move(pattern);
+    pattern_        = std::move(pattern);
     need_localtime_ = false;
     compile_pattern_(pattern_);
 }
@@ -1094,19 +1027,19 @@ SPDLOG_INLINE void pattern_formatter::need_localtime(bool need)
 
 SPDLOG_INLINE std::tm pattern_formatter::get_time_(const details::log_msg &msg)
 {
-    if (pattern_time_type_ == pattern_time_type::local)
+    if(pattern_time_type_ == pattern_time_type::local)
     {
         return details::os::localtime(log_clock::to_time_t(msg.time));
     }
     return details::os::gmtime(log_clock::to_time_t(msg.time));
 }
 
-template<typename Padder>
+template <typename Padder>
 SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_info padding)
 {
     // process custom flags
     auto it = custom_handlers_.find(flag);
-    if (it != custom_handlers_.end())
+    if(it != custom_handlers_.end())
     {
         auto custom_handler = it->second->clone();
         custom_handler->set_padding_info(padding);
@@ -1115,267 +1048,272 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i
     }
 
     // process built-in flags
-    switch (flag)
+    switch(flag)
     {
-    case ('+'): // default formatter
-        formatters_.push_back(details::make_unique<details::full_formatter>(padding));
-        need_localtime_ = true;
-        break;
-
-    case 'n': // logger name
-        formatters_.push_back(details::make_unique<details::name_formatter<Padder>>(padding));
-        break;
-
-    case 'l': // level
-        formatters_.push_back(details::make_unique<details::level_formatter<Padder>>(padding));
-        break;
-
-    case 'L': // short level
-        formatters_.push_back(details::make_unique<details::short_level_formatter<Padder>>(padding));
-        break;
-
-    case ('t'): // thread id
-        formatters_.push_back(details::make_unique<details::t_formatter<Padder>>(padding));
-        break;
-
-    case ('v'): // the message text
-        formatters_.push_back(details::make_unique<details::v_formatter<Padder>>(padding));
-        break;
-
-    case ('a'): // weekday
-        formatters_.push_back(details::make_unique<details::a_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('A'): // short weekday
-        formatters_.push_back(details::make_unique<details::A_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('b'):
-    case ('h'): // month
-        formatters_.push_back(details::make_unique<details::b_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('B'): // short month
-        formatters_.push_back(details::make_unique<details::B_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('c'): // datetime
-        formatters_.push_back(details::make_unique<details::c_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('C'): // year 2 digits
-        formatters_.push_back(details::make_unique<details::C_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('Y'): // year 4 digits
-        formatters_.push_back(details::make_unique<details::Y_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('D'):
-    case ('x'): // datetime MM/DD/YY
-        formatters_.push_back(details::make_unique<details::D_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('m'): // month 1-12
-        formatters_.push_back(details::make_unique<details::m_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('d'): // day of month 1-31
-        formatters_.push_back(details::make_unique<details::d_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('H'): // hours 24
-        formatters_.push_back(details::make_unique<details::H_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('I'): // hours 12
-        formatters_.push_back(details::make_unique<details::I_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('M'): // minutes
-        formatters_.push_back(details::make_unique<details::M_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('S'): // seconds
-        formatters_.push_back(details::make_unique<details::S_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('e'): // milliseconds
-        formatters_.push_back(details::make_unique<details::e_formatter<Padder>>(padding));
-        break;
-
-    case ('f'): // microseconds
-        formatters_.push_back(details::make_unique<details::f_formatter<Padder>>(padding));
-        break;
-
-    case ('F'): // nanoseconds
-        formatters_.push_back(details::make_unique<details::F_formatter<Padder>>(padding));
-        break;
-
-    case ('E'): // seconds since epoch
-        formatters_.push_back(details::make_unique<details::E_formatter<Padder>>(padding));
-        break;
-
-    case ('p'): // am/pm
-        formatters_.push_back(details::make_unique<details::p_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('r'): // 12 hour clock 02:55:02 pm
-        formatters_.push_back(details::make_unique<details::r_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('R'): // 24-hour HH:MM time
-        formatters_.push_back(details::make_unique<details::R_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('T'):
-    case ('X'): // ISO 8601 time format (HH:MM:SS)
-        formatters_.push_back(details::make_unique<details::T_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('z'): // timezone
-        formatters_.push_back(details::make_unique<details::z_formatter<Padder>>(padding));
-        need_localtime_ = true;
-        break;
-
-    case ('P'): // pid
-        formatters_.push_back(details::make_unique<details::pid_formatter<Padder>>(padding));
-        break;
-
-    case ('^'): // color range start
-        formatters_.push_back(details::make_unique<details::color_start_formatter>(padding));
-        break;
-
-    case ('$'): // color range end
-        formatters_.push_back(details::make_unique<details::color_stop_formatter>(padding));
-        break;
-
-    case ('@'): // source location (filename:filenumber)
-        formatters_.push_back(details::make_unique<details::source_location_formatter<Padder>>(padding));
-        break;
-
-    case ('s'): // short source filename - without directory name
-        formatters_.push_back(details::make_unique<details::short_filename_formatter<Padder>>(padding));
-        break;
-
-    case ('g'): // full source filename
-        formatters_.push_back(details::make_unique<details::source_filename_formatter<Padder>>(padding));
-        break;
-
-    case ('#'): // source line number
-        formatters_.push_back(details::make_unique<details::source_linenum_formatter<Padder>>(padding));
-        break;
-
-    case ('!'): // source funcname
-        formatters_.push_back(details::make_unique<details::source_funcname_formatter<Padder>>(padding));
-        break;
-
-    case ('%'): // % char
-        formatters_.push_back(details::make_unique<details::ch_formatter>('%'));
-        break;
-
-    case ('u'): // elapsed time since last log message in nanos
-        formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::nanoseconds>>(padding));
-        break;
-
-    case ('i'): // elapsed time since last log message in micros
-        formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::microseconds>>(padding));
-        break;
-
-    case ('o'): // elapsed time since last log message in millis
-        formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::milliseconds>>(padding));
-        break;
-
-    case ('O'): // elapsed time since last log message in seconds
-        formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::seconds>>(padding));
-        break;
-
-    default: // Unknown flag appears as is
-        auto unknown_flag = details::make_unique<details::aggregate_formatter>();
-
-        if (!padding.truncate_)
-        {
-            unknown_flag->add_ch('%');
-            unknown_flag->add_ch(flag);
-            formatters_.push_back((std::move(unknown_flag)));
-        }
-        // fix issue #1617 (prev char was '!' and should have been treated as funcname flag instead of truncating flag)
-        // spdlog::set_pattern("[%10!] %v") => "[      main] some message"
-        // spdlog::set_pattern("[%3!!] %v") => "[mai] some message"
-        else
-        {
-            padding.truncate_ = false;
+        case('+'): // default formatter
+            formatters_.push_back(details::make_unique<details::full_formatter>(padding));
+            need_localtime_ = true;
+            break;
+
+        case 'n': // logger name
+            formatters_.push_back(details::make_unique<details::name_formatter<Padder>>(padding));
+            break;
+
+        case 'l': // level
+            formatters_.push_back(details::make_unique<details::level_formatter<Padder>>(padding));
+            break;
+
+        case 'L': // short level
+            formatters_.push_back(details::make_unique<details::short_level_formatter<Padder>>(padding));
+            break;
+
+        case('t'): // thread id
+            formatters_.push_back(details::make_unique<details::t_formatter<Padder>>(padding));
+            break;
+
+        case('v'): // the message text
+            formatters_.push_back(details::make_unique<details::v_formatter<Padder>>(padding));
+            break;
+
+        case('a'): // weekday
+            formatters_.push_back(details::make_unique<details::a_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('A'): // short weekday
+            formatters_.push_back(details::make_unique<details::A_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('b'):
+        case('h'): // month
+            formatters_.push_back(details::make_unique<details::b_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('B'): // short month
+            formatters_.push_back(details::make_unique<details::B_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('c'): // datetime
+            formatters_.push_back(details::make_unique<details::c_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('C'): // year 2 digits
+            formatters_.push_back(details::make_unique<details::C_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('Y'): // year 4 digits
+            formatters_.push_back(details::make_unique<details::Y_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('D'):
+        case('x'): // datetime MM/DD/YY
+            formatters_.push_back(details::make_unique<details::D_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('m'): // month 1-12
+            formatters_.push_back(details::make_unique<details::m_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('d'): // day of month 1-31
+            formatters_.push_back(details::make_unique<details::d_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('H'): // hours 24
+            formatters_.push_back(details::make_unique<details::H_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('I'): // hours 12
+            formatters_.push_back(details::make_unique<details::I_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('M'): // minutes
+            formatters_.push_back(details::make_unique<details::M_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('S'): // seconds
+            formatters_.push_back(details::make_unique<details::S_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('e'): // milliseconds
+            formatters_.push_back(details::make_unique<details::e_formatter<Padder>>(padding));
+            break;
+
+        case('f'): // microseconds
+            formatters_.push_back(details::make_unique<details::f_formatter<Padder>>(padding));
+            break;
+
+        case('F'): // nanoseconds
+            formatters_.push_back(details::make_unique<details::F_formatter<Padder>>(padding));
+            break;
+
+        case('E'): // seconds since epoch
+            formatters_.push_back(details::make_unique<details::E_formatter<Padder>>(padding));
+            break;
+
+        case('p'): // am/pm
+            formatters_.push_back(details::make_unique<details::p_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('r'): // 12 hour clock 02:55:02 pm
+            formatters_.push_back(details::make_unique<details::r_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('R'): // 24-hour HH:MM time
+            formatters_.push_back(details::make_unique<details::R_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('T'):
+        case('X'): // ISO 8601 time format (HH:MM:SS)
+            formatters_.push_back(details::make_unique<details::T_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('z'): // timezone
+            formatters_.push_back(details::make_unique<details::z_formatter<Padder>>(padding));
+            need_localtime_ = true;
+            break;
+
+        case('P'): // pid
+            formatters_.push_back(details::make_unique<details::pid_formatter<Padder>>(padding));
+            break;
+
+        case('^'): // color range start
+            formatters_.push_back(details::make_unique<details::color_start_formatter>(padding));
+            break;
+
+        case('$'): // color range end
+            formatters_.push_back(details::make_unique<details::color_stop_formatter>(padding));
+            break;
+
+        case('@'): // source location (filename:filenumber)
+            formatters_.push_back(details::make_unique<details::source_location_formatter<Padder>>(padding));
+            break;
+
+        case('s'): // short source filename - without directory name
+            formatters_.push_back(details::make_unique<details::short_filename_formatter<Padder>>(padding));
+            break;
+
+        case('g'): // full source filename
+            formatters_.push_back(details::make_unique<details::source_filename_formatter<Padder>>(padding));
+            break;
+
+        case('#'): // source line number
+            formatters_.push_back(details::make_unique<details::source_linenum_formatter<Padder>>(padding));
+            break;
+
+        case('!'): // source funcname
             formatters_.push_back(details::make_unique<details::source_funcname_formatter<Padder>>(padding));
-            unknown_flag->add_ch(flag);
-            formatters_.push_back((std::move(unknown_flag)));
-        }
+            break;
+
+        case('%'): // % char
+            formatters_.push_back(details::make_unique<details::ch_formatter>('%'));
+            break;
+
+        case('u'): // elapsed time since last log message in nanos
+            formatters_.push_back(
+                details::make_unique<details::elapsed_formatter<Padder, std::chrono::nanoseconds>>(padding));
+            break;
+
+        case('i'): // elapsed time since last log message in micros
+            formatters_.push_back(
+                details::make_unique<details::elapsed_formatter<Padder, std::chrono::microseconds>>(padding));
+            break;
+
+        case('o'): // elapsed time since last log message in millis
+            formatters_.push_back(
+                details::make_unique<details::elapsed_formatter<Padder, std::chrono::milliseconds>>(padding));
+            break;
+
+        case('O'): // elapsed time since last log message in seconds
+            formatters_.push_back(
+                details::make_unique<details::elapsed_formatter<Padder, std::chrono::seconds>>(padding));
+            break;
 
-        break;
+        default: // Unknown flag appears as is
+            auto unknown_flag = details::make_unique<details::aggregate_formatter>();
+
+            if(!padding.truncate_)
+            {
+                unknown_flag->add_ch('%');
+                unknown_flag->add_ch(flag);
+                formatters_.push_back((std::move(unknown_flag)));
+            }
+            // fix issue #1617 (prev char was '!' and should have been treated as funcname flag instead of truncating
+            // flag) spdlog::set_pattern("[%10!] %v") => "[      main] some message" spdlog::set_pattern("[%3!!] %v") =>
+            // "[mai] some message"
+            else
+            {
+                padding.truncate_ = false;
+                formatters_.push_back(details::make_unique<details::source_funcname_formatter<Padder>>(padding));
+                unknown_flag->add_ch(flag);
+                formatters_.push_back((std::move(unknown_flag)));
+            }
+
+            break;
     }
 }
 
 // Extract given pad spec (e.g. %8X, %=8X, %-8!X, %8!X, %=8!X, %-8!X, %+8!X)
 // Advance the given it pass the end of the padding spec found (if any)
 // Return padding.
-SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end)
+SPDLOG_INLINE details::padding_info
+              pattern_formatter::handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end)
 {
     using details::padding_info;
     using details::scoped_padder;
     const size_t max_width = 64;
-    if (it == end)
+    if(it == end)
     {
         return padding_info{};
     }
 
     padding_info::pad_side side;
-    switch (*it)
+    switch(*it)
     {
-    case '-':
-        side = padding_info::pad_side::right;
-        ++it;
-        break;
-    case '=':
-        side = padding_info::pad_side::center;
-        ++it;
-        break;
-    default:
-        side = details::padding_info::pad_side::left;
-        break;
+        case '-':
+            side = padding_info::pad_side::right;
+            ++it;
+            break;
+        case '=':
+            side = padding_info::pad_side::center;
+            ++it;
+            break;
+        default:
+            side = details::padding_info::pad_side::left;
+            break;
     }
 
-    if (it == end || !std::isdigit(static_cast<unsigned char>(*it)))
+    if(it == end || !std::isdigit(static_cast<unsigned char>(*it)))
     {
         return padding_info{}; // no padding if no digit found here
     }
 
     auto width = static_cast<size_t>(*it) - '0';
-    for (++it; it != end && std::isdigit(static_cast<unsigned char>(*it)); ++it)
+    for(++it; it != end && std::isdigit(static_cast<unsigned char>(*it)); ++it)
     {
         auto digit = static_cast<size_t>(*it) - '0';
-        width = width * 10 + digit;
+        width      = width * 10 + digit;
     }
 
     // search for the optional truncate marker '!'
     bool truncate;
-    if (it != end && *it == '!')
+    if(it != end && *it == '!')
     {
         truncate = true;
         ++it;
@@ -1389,23 +1327,23 @@ SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(std::stri
 
 SPDLOG_INLINE void pattern_formatter::compile_pattern_(const std::string &pattern)
 {
-    auto end = pattern.end();
+    auto                                          end = pattern.end();
     std::unique_ptr<details::aggregate_formatter> user_chars;
     formatters_.clear();
-    for (auto it = pattern.begin(); it != end; ++it)
+    for(auto it = pattern.begin(); it != end; ++it)
     {
-        if (*it == '%')
+        if(*it == '%')
         {
-            if (user_chars) // append user chars found so far
+            if(user_chars) // append user chars found so far
             {
                 formatters_.push_back(std::move(user_chars));
             }
 
             auto padding = handle_padspec_(++it, end);
 
-            if (it != end)
+            if(it != end)
             {
-                if (padding.enabled())
+                if(padding.enabled())
                 {
                     handle_flag_<details::scoped_padder>(*it, padding);
                 }
@@ -1421,14 +1359,14 @@ SPDLOG_INLINE void pattern_formatter::compile_pattern_(const std::string &patter
         }
         else // chars not following the % sign should be displayed as is
         {
-            if (!user_chars)
+            if(!user_chars)
             {
                 user_chars = details::make_unique<details::aggregate_formatter>();
             }
             user_chars->add_ch(*it);
         }
     }
-    if (user_chars) // append raw chars found so far
+    if(user_chars) // append raw chars found so far
     {
         formatters_.push_back(std::move(user_chars));
     }
diff --git a/include/spdlog/pattern_formatter.h b/include/spdlog/pattern_formatter.h
index acf1c5365..4240fe31c 100644
--- a/include/spdlog/pattern_formatter.h
+++ b/include/spdlog/pattern_formatter.h
@@ -3,63 +3,56 @@
 
 #pragma once
 
+#include <chrono>
+#include <ctime>
+#include <memory>
 #include <spdlog/common.h>
 #include <spdlog/details/log_msg.h>
 #include <spdlog/details/os.h>
 #include <spdlog/formatter.h>
-
-#include <chrono>
-#include <ctime>
-#include <memory>
-
 #include <string>
-#include <vector>
 #include <unordered_map>
+#include <vector>
 
-namespace spdlog {
-namespace details {
-
-// padding information.
-struct padding_info
+namespace spdlog
+{
+namespace details
 {
-    enum class pad_side
+
+    // padding information.
+    struct padding_info
     {
-        left,
-        right,
-        center
+        enum class pad_side
+        {
+            left,
+            right,
+            center
+        };
+
+        padding_info() = default;
+        padding_info(size_t width, padding_info::pad_side side, bool truncate) :
+            width_(width), side_(side), truncate_(truncate), enabled_(true)
+        {
+        }
+
+        bool     enabled() const { return enabled_; }
+        size_t   width_    = 0;
+        pad_side side_     = pad_side::left;
+        bool     truncate_ = false;
+        bool     enabled_  = false;
     };
 
-    padding_info() = default;
-    padding_info(size_t width, padding_info::pad_side side, bool truncate)
-        : width_(width)
-        , side_(side)
-        , truncate_(truncate)
-        , enabled_(true)
-    {}
-
-    bool enabled() const
+    class SPDLOG_API flag_formatter
     {
-        return enabled_;
-    }
-    size_t width_ = 0;
-    pad_side side_ = pad_side::left;
-    bool truncate_ = false;
-    bool enabled_ = false;
-};
-
-class SPDLOG_API 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, memory_buf_t &dest) = 0;
-
-protected:
-    padding_info padinfo_;
-};
+    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, memory_buf_t &dest) = 0;
+
+    protected:
+        padding_info padinfo_;
+    };
 
 } // namespace details
 
@@ -68,10 +61,7 @@ class SPDLOG_API custom_flag_formatter : public details::flag_formatter
 public:
     virtual std::unique_ptr<custom_flag_formatter> clone() const = 0;
 
-    void set_padding_info(const details::padding_info &padding)
-    {
-        flag_formatter::padinfo_ = padding;
-    }
+    void set_padding_info(const details::padding_info &padding) { flag_formatter::padinfo_ = padding; }
 };
 
 class SPDLOG_API pattern_formatter final : public formatter
@@ -79,20 +69,25 @@ class SPDLOG_API pattern_formatter final : public formatter
 public:
     using custom_flags = std::unordered_map<char, std::unique_ptr<custom_flag_formatter>>;
 
-    explicit pattern_formatter(std::string pattern, pattern_time_type time_type = pattern_time_type::local,
-        std::string eol = spdlog::details::os::default_eol, custom_flags custom_user_flags = custom_flags());
+    explicit pattern_formatter(
+        std::string       pattern,
+        pattern_time_type time_type         = pattern_time_type::local,
+        std::string       eol               = spdlog::details::os::default_eol,
+        custom_flags      custom_user_flags = custom_flags());
 
     // use default pattern is not given
-    explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol);
+    explicit pattern_formatter(
+        pattern_time_type time_type = pattern_time_type::local,
+        std::string       eol       = spdlog::details::os::default_eol);
 
-    pattern_formatter(const pattern_formatter &other) = delete;
+    pattern_formatter(const pattern_formatter &other)            = delete;
     pattern_formatter &operator=(const pattern_formatter &other) = delete;
 
     std::unique_ptr<formatter> clone() const override;
-    void format(const details::log_msg &msg, memory_buf_t &dest) override;
+    void                       format(const details::log_msg &msg, memory_buf_t &dest) override;
 
-    template<typename T, typename... Args>
-    pattern_formatter &add_flag(char flag, Args &&... args)
+    template <typename T, typename... Args>
+    pattern_formatter &add_flag(char flag, Args &&...args)
     {
         custom_handlers_[flag] = details::make_unique<T>(std::forward<Args>(args)...);
         return *this;
@@ -101,17 +96,17 @@ public:
     void need_localtime(bool need = true);
 
 private:
-    std::string pattern_;
-    std::string eol_;
-    pattern_time_type pattern_time_type_;
-    bool need_localtime_;
-    std::tm cached_tm_;
-    std::chrono::seconds last_log_secs_;
+    std::string                                           pattern_;
+    std::string                                           eol_;
+    pattern_time_type                                     pattern_time_type_;
+    bool                                                  need_localtime_;
+    std::tm                                               cached_tm_;
+    std::chrono::seconds                                  last_log_secs_;
     std::vector<std::unique_ptr<details::flag_formatter>> formatters_;
-    custom_flags custom_handlers_;
+    custom_flags                                          custom_handlers_;
 
     std::tm get_time_(const details::log_msg &msg);
-    template<typename Padder>
+    template <typename Padder>
     void handle_flag_(char flag, details::padding_info padding);
 
     // Extract given pad spec (e.g. %8X)
@@ -124,5 +119,5 @@ private:
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "pattern_formatter-inl.h"
+#include "pattern_formatter-inl.h"
 #endif
diff --git a/include/spdlog/sinks/android_sink.h b/include/spdlog/sinks/android_sink.h
index 07dbeea8e..016ade4b7 100644
--- a/include/spdlog/sinks/android_sink.h
+++ b/include/spdlog/sinks/android_sink.h
@@ -5,133 +5,137 @@
 
 #ifdef __ANDROID__
 
-#    include <spdlog/details/fmt_helper.h>
-#    include <spdlog/details/null_mutex.h>
-#    include <spdlog/details/os.h>
-#    include <spdlog/sinks/base_sink.h>
-#    include <spdlog/details/synchronous_factory.h>
-
-#    include <android/log.h>
-#    include <chrono>
-#    include <mutex>
-#    include <string>
-#    include <thread>
-#    include <type_traits>
-
-#    if !defined(SPDLOG_ANDROID_RETRIES)
-#        define SPDLOG_ANDROID_RETRIES 2
-#    endif
-
-namespace spdlog {
-namespace sinks {
-
-/*
- * Android sink
- * (logging using __android_log_write or __android_log_buf_write depending on the specified BufferID)
- */
-template<typename Mutex, int BufferID = log_id::LOG_ID_MAIN>
-class android_sink final : public base_sink<Mutex>
+#include <android/log.h>
+#include <chrono>
+#include <mutex>
+#include <spdlog/details/fmt_helper.h>
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/details/os.h>
+#include <spdlog/details/synchronous_factory.h>
+#include <spdlog/sinks/base_sink.h>
+#include <string>
+#include <thread>
+#include <type_traits>
+
+#if !defined(SPDLOG_ANDROID_RETRIES)
+#define SPDLOG_ANDROID_RETRIES 2
+#endif
+
+namespace spdlog
 {
-public:
-    explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false)
-        : tag_(std::move(tag))
-        , use_raw_msg_(use_raw_msg)
-    {}
-
-protected:
-    void sink_it_(const details::log_msg &msg) override
+namespace sinks
+{
+
+    /*
+     * Android sink
+     * (logging using __android_log_write or __android_log_buf_write depending on the specified BufferID)
+     */
+    template <typename Mutex, int BufferID = log_id::LOG_ID_MAIN>
+    class android_sink final : public base_sink<Mutex>
     {
-        const android_LogPriority priority = convert_to_android_(msg.level);
-        memory_buf_t formatted;
-        if (use_raw_msg_)
+    public:
+        explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false) :
+            tag_(std::move(tag)), use_raw_msg_(use_raw_msg)
         {
-            details::fmt_helper::append_string_view(msg.payload, formatted);
         }
-        else
+
+    protected:
+        void sink_it_(const details::log_msg &msg) override
         {
-            base_sink<Mutex>::formatter_->format(msg, formatted);
+            const android_LogPriority priority = convert_to_android_(msg.level);
+            memory_buf_t              formatted;
+            if(use_raw_msg_)
+            {
+                details::fmt_helper::append_string_view(msg.payload, formatted);
+            }
+            else
+            {
+                base_sink<Mutex>::formatter_->format(msg, formatted);
+            }
+            formatted.push_back('\0');
+            const char *msg_output = formatted.data();
+
+            // See system/core/liblog/logger_write.c for explanation of return value
+            int ret         = android_log(priority, tag_.c_str(), msg_output);
+            int retry_count = 0;
+            while((ret == -11 /*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES))
+            {
+                details::os::sleep_for_millis(5);
+                ret = android_log(priority, tag_.c_str(), msg_output);
+                retry_count++;
+            }
+
+            if(ret < 0)
+            {
+                throw_spdlog_ex("logging to Android failed", ret);
+            }
         }
-        formatted.push_back('\0');
-        const char *msg_output = formatted.data();
 
-        // See system/core/liblog/logger_write.c for explanation of return value
-        int ret = android_log(priority, tag_.c_str(), msg_output);
-        int retry_count = 0;
-        while ((ret == -11 /*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES))
+        void flush_() override {}
+
+    private:
+        // There might be liblog versions used, that do not support __android_log_buf_write. So we only compile and link
+        // against
+        // __android_log_buf_write, if user explicitely provides a non-default log buffer. Otherwise, when using the
+        // default log buffer, always log via __android_log_write.
+        template <int ID = BufferID>
+        typename std::enable_if<ID == static_cast<int>(log_id::LOG_ID_MAIN), int>::type
+        android_log(int prio, const char *tag, const char *text)
         {
-            details::os::sleep_for_millis(5);
-            ret = android_log(priority, tag_.c_str(), msg_output);
-            retry_count++;
+            return __android_log_write(prio, tag, text);
         }
 
-        if (ret < 0)
+        template <int ID = BufferID>
+        typename std::enable_if<ID != static_cast<int>(log_id::LOG_ID_MAIN), int>::type
+        android_log(int prio, const char *tag, const char *text)
         {
-            throw_spdlog_ex("logging to Android failed", ret);
+            return __android_log_buf_write(ID, prio, tag, text);
         }
-    }
-
-    void flush_() override {}
 
-private:
-    // There might be liblog versions used, that do not support __android_log_buf_write. So we only compile and link against
-    // __android_log_buf_write, if user explicitely provides a non-default log buffer. Otherwise, when using the default log buffer, always
-    // log via __android_log_write.
-    template<int ID = BufferID>
-    typename std::enable_if<ID == static_cast<int>(log_id::LOG_ID_MAIN), int>::type android_log(int prio, const char *tag, const char *text)
-    {
-        return __android_log_write(prio, tag, text);
-    }
-
-    template<int ID = BufferID>
-    typename std::enable_if<ID != static_cast<int>(log_id::LOG_ID_MAIN), int>::type android_log(int prio, const char *tag, const char *text)
-    {
-        return __android_log_buf_write(ID, prio, tag, text);
-    }
-
-    static android_LogPriority convert_to_android_(spdlog::level::level_enum level)
-    {
-        switch (level)
+        static android_LogPriority convert_to_android_(spdlog::level::level_enum level)
         {
-        case spdlog::level::trace:
-            return ANDROID_LOG_VERBOSE;
-        case spdlog::level::debug:
-            return ANDROID_LOG_DEBUG;
-        case spdlog::level::info:
-            return ANDROID_LOG_INFO;
-        case spdlog::level::warn:
-            return ANDROID_LOG_WARN;
-        case spdlog::level::err:
-            return ANDROID_LOG_ERROR;
-        case spdlog::level::critical:
-            return ANDROID_LOG_FATAL;
-        default:
-            return ANDROID_LOG_DEFAULT;
+            switch(level)
+            {
+                case spdlog::level::trace:
+                    return ANDROID_LOG_VERBOSE;
+                case spdlog::level::debug:
+                    return ANDROID_LOG_DEBUG;
+                case spdlog::level::info:
+                    return ANDROID_LOG_INFO;
+                case spdlog::level::warn:
+                    return ANDROID_LOG_WARN;
+                case spdlog::level::err:
+                    return ANDROID_LOG_ERROR;
+                case spdlog::level::critical:
+                    return ANDROID_LOG_FATAL;
+                default:
+                    return ANDROID_LOG_DEFAULT;
+            }
         }
-    }
 
-    std::string tag_;
-    bool use_raw_msg_;
-};
+        std::string tag_;
+        bool        use_raw_msg_;
+    };
 
-using android_sink_mt = android_sink<std::mutex>;
-using android_sink_st = android_sink<details::null_mutex>;
+    using android_sink_mt = android_sink<std::mutex>;
+    using android_sink_st = android_sink<details::null_mutex>;
 
-template<int BufferId = log_id::LOG_ID_MAIN>
-using android_sink_buf_mt = android_sink<std::mutex, BufferId>;
-template<int BufferId = log_id::LOG_ID_MAIN>
-using android_sink_buf_st = android_sink<details::null_mutex, BufferId>;
+    template <int BufferId = log_id::LOG_ID_MAIN>
+    using android_sink_buf_mt = android_sink<std::mutex, BufferId>;
+    template <int BufferId = log_id::LOG_ID_MAIN>
+    using android_sink_buf_st = android_sink<details::null_mutex, BufferId>;
 
 } // namespace sinks
 
 // Create and register android syslog logger
 
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_factory>
 inline std::shared_ptr<logger> android_logger_mt(const std::string &logger_name, const std::string &tag = "spdlog")
 {
     return Factory::template create<sinks::android_sink_mt>(logger_name, tag);
 }
 
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_factory>
 inline std::shared_ptr<logger> android_logger_st(const std::string &logger_name, const std::string &tag = "spdlog")
 {
     return Factory::template create<sinks::android_sink_st>(logger_name, tag);
diff --git a/include/spdlog/sinks/ansicolor_sink-inl.h b/include/spdlog/sinks/ansicolor_sink-inl.h
index b5848f2db..d554f6bcc 100644
--- a/include/spdlog/sinks/ansicolor_sink-inl.h
+++ b/include/spdlog/sinks/ansicolor_sink-inl.h
@@ -4,142 +4,147 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/sinks/ansicolor_sink.h>
+#include <spdlog/sinks/ansicolor_sink.h>
 #endif
 
-#include <spdlog/pattern_formatter.h>
 #include <spdlog/details/os.h>
+#include <spdlog/pattern_formatter.h>
 
-namespace spdlog {
-namespace sinks {
-
-template<typename ConsoleMutex>
-SPDLOG_INLINE ansicolor_sink<ConsoleMutex>::ansicolor_sink(FILE *target_file, color_mode mode)
-    : target_file_(target_file)
-    , mutex_(ConsoleMutex::mutex())
-    , formatter_(details::make_unique<spdlog::pattern_formatter>())
-
+namespace spdlog
 {
-    set_color_mode(mode);
-    colors_[level::trace] = to_string_(white);
-    colors_[level::debug] = to_string_(cyan);
-    colors_[level::info] = to_string_(green);
-    colors_[level::warn] = to_string_(yellow_bold);
-    colors_[level::err] = to_string_(red_bold);
-    colors_[level::critical] = to_string_(bold_on_red);
-    colors_[level::off] = to_string_(reset);
-}
-
-template<typename ConsoleMutex>
-SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color(level::level_enum color_level, string_view_t color)
+namespace sinks
 {
-    std::lock_guard<mutex_t> lock(mutex_);
-    colors_[static_cast<size_t>(color_level)] = to_string_(color);
-}
 
-template<typename ConsoleMutex>
-SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
-{
-    // Wrap the originally formatted message in color codes.
-    // If color is not supported in the terminal, log as is instead.
-    std::lock_guard<mutex_t> lock(mutex_);
-    msg.color_range_start = 0;
-    msg.color_range_end = 0;
-    memory_buf_t formatted;
-    formatter_->format(msg, formatted);
-    if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE ansicolor_sink<ConsoleMutex>::ansicolor_sink(FILE *target_file, color_mode mode) :
+        target_file_(target_file),
+        mutex_(ConsoleMutex::mutex()),
+        formatter_(details::make_unique<spdlog::pattern_formatter>())
+
     {
-        // before color range
-        print_range_(formatted, 0, msg.color_range_start);
-        // in color range
-        print_ccode_(colors_[static_cast<size_t>(msg.level)]);
-        print_range_(formatted, msg.color_range_start, msg.color_range_end);
-        print_ccode_(reset);
-        // after color range
-        print_range_(formatted, msg.color_range_end, formatted.size());
+        set_color_mode(mode);
+        colors_[level::trace]    = to_string_(white);
+        colors_[level::debug]    = to_string_(cyan);
+        colors_[level::info]     = to_string_(green);
+        colors_[level::warn]     = to_string_(yellow_bold);
+        colors_[level::err]      = to_string_(red_bold);
+        colors_[level::critical] = to_string_(bold_on_red);
+        colors_[level::off]      = to_string_(reset);
     }
-    else // no color
+
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color(level::level_enum color_level, string_view_t color)
     {
-        print_range_(formatted, 0, formatted.size());
+        std::lock_guard<mutex_t> lock(mutex_);
+        colors_[static_cast<size_t>(color_level)] = to_string_(color);
     }
-    fflush(target_file_);
-}
 
-template<typename ConsoleMutex>
-SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::flush()
-{
-    std::lock_guard<mutex_t> lock(mutex_);
-    fflush(target_file_);
-}
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
+    {
+        // Wrap the originally formatted message in color codes.
+        // If color is not supported in the terminal, log as is instead.
+        std::lock_guard<mutex_t> lock(mutex_);
+        msg.color_range_start = 0;
+        msg.color_range_end   = 0;
+        memory_buf_t formatted;
+        formatter_->format(msg, formatted);
+        if(should_do_colors_ && msg.color_range_end > msg.color_range_start)
+        {
+            // before color range
+            print_range_(formatted, 0, msg.color_range_start);
+            // in color range
+            print_ccode_(colors_[static_cast<size_t>(msg.level)]);
+            print_range_(formatted, msg.color_range_start, msg.color_range_end);
+            print_ccode_(reset);
+            // after color range
+            print_range_(formatted, msg.color_range_end, formatted.size());
+        }
+        else // no color
+        {
+            print_range_(formatted, 0, formatted.size());
+        }
+        fflush(target_file_);
+    }
 
-template<typename ConsoleMutex>
-SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_pattern(const std::string &pattern)
-{
-    std::lock_guard<mutex_t> lock(mutex_);
-    formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
-}
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::flush()
+    {
+        std::lock_guard<mutex_t> lock(mutex_);
+        fflush(target_file_);
+    }
 
-template<typename ConsoleMutex>
-SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
-{
-    std::lock_guard<mutex_t> lock(mutex_);
-    formatter_ = std::move(sink_formatter);
-}
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_pattern(const std::string &pattern)
+    {
+        std::lock_guard<mutex_t> lock(mutex_);
+        formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
+    }
 
-template<typename ConsoleMutex>
-SPDLOG_INLINE bool ansicolor_sink<ConsoleMutex>::should_color()
-{
-    return should_do_colors_;
-}
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
+    {
+        std::lock_guard<mutex_t> lock(mutex_);
+        formatter_ = std::move(sink_formatter);
+    }
 
-template<typename ConsoleMutex>
-SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
-{
-    switch (mode)
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE bool ansicolor_sink<ConsoleMutex>::should_color()
     {
-    case color_mode::always:
-        should_do_colors_ = true;
-        return;
-    case color_mode::automatic:
-        should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal();
-        return;
-    case color_mode::never:
-        should_do_colors_ = false;
-        return;
-    default:
-        should_do_colors_ = false;
+        return should_do_colors_;
     }
-}
 
-template<typename ConsoleMutex>
-SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_ccode_(const string_view_t &color_code)
-{
-    fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_);
-}
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
+    {
+        switch(mode)
+        {
+            case color_mode::always:
+                should_do_colors_ = true;
+                return;
+            case color_mode::automatic:
+                should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal();
+                return;
+            case color_mode::never:
+                should_do_colors_ = false;
+                return;
+            default:
+                should_do_colors_ = false;
+        }
+    }
 
-template<typename ConsoleMutex>
-SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
-{
-    fwrite(formatted.data() + start, sizeof(char), end - start, target_file_);
-}
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_ccode_(const string_view_t &color_code)
+    {
+        fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_);
+    }
 
-template<typename ConsoleMutex>
-SPDLOG_INLINE std::string ansicolor_sink<ConsoleMutex>::to_string_(const string_view_t &sv)
-{
-    return std::string(sv.data(), sv.size());
-}
-
-// ansicolor_stdout_sink
-template<typename ConsoleMutex>
-SPDLOG_INLINE ansicolor_stdout_sink<ConsoleMutex>::ansicolor_stdout_sink(color_mode mode)
-    : ansicolor_sink<ConsoleMutex>(stdout, mode)
-{}
-
-// ansicolor_stderr_sink
-template<typename ConsoleMutex>
-SPDLOG_INLINE ansicolor_stderr_sink<ConsoleMutex>::ansicolor_stderr_sink(color_mode mode)
-    : ansicolor_sink<ConsoleMutex>(stderr, mode)
-{}
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE void
+    ansicolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
+    {
+        fwrite(formatted.data() + start, sizeof(char), end - start, target_file_);
+    }
+
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE std::string ansicolor_sink<ConsoleMutex>::to_string_(const string_view_t &sv)
+    {
+        return std::string(sv.data(), sv.size());
+    }
+
+    // ansicolor_stdout_sink
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE ansicolor_stdout_sink<ConsoleMutex>::ansicolor_stdout_sink(color_mode mode) :
+        ansicolor_sink<ConsoleMutex>(stdout, mode)
+    {
+    }
+
+    // ansicolor_stderr_sink
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE ansicolor_stderr_sink<ConsoleMutex>::ansicolor_stderr_sink(color_mode mode) :
+        ansicolor_sink<ConsoleMutex>(stderr, mode)
+    {
+    }
 
 } // namespace sinks
 } // namespace spdlog
diff --git a/include/spdlog/sinks/ansicolor_sink.h b/include/spdlog/sinks/ansicolor_sink.h
index 39d966bc9..bd1973a11 100644
--- a/include/spdlog/sinks/ansicolor_sink.h
+++ b/include/spdlog/sinks/ansicolor_sink.h
@@ -3,116 +3,118 @@
 
 #pragma once
 
+#include <array>
+#include <memory>
+#include <mutex>
 #include <spdlog/details/console_globals.h>
 #include <spdlog/details/null_mutex.h>
 #include <spdlog/sinks/sink.h>
-#include <memory>
-#include <mutex>
 #include <string>
-#include <array>
 
-namespace spdlog {
-namespace sinks {
-
-/**
- * This sink prefixes the output with an ANSI escape sequence color code
- * depending on the severity
- * of the message.
- * If no color terminal detected, omit the escape codes.
- */
-
-template<typename ConsoleMutex>
-class ansicolor_sink : public sink
+namespace spdlog
 {
-public:
-    using mutex_t = typename ConsoleMutex::mutex_t;
-    ansicolor_sink(FILE *target_file, color_mode mode);
-    ~ansicolor_sink() override = default;
-
-    ansicolor_sink(const ansicolor_sink &other) = delete;
-    ansicolor_sink(ansicolor_sink &&other) = delete;
-
-    ansicolor_sink &operator=(const ansicolor_sink &other) = delete;
-    ansicolor_sink &operator=(ansicolor_sink &&other) = delete;
-
-    void set_color(level::level_enum color_level, string_view_t color);
-    void set_color_mode(color_mode mode);
-    bool should_color();
-
-    void log(const details::log_msg &msg) override;
-    void flush() override;
-    void set_pattern(const std::string &pattern) final;
-    void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
-
-    // Formatting codes
-    const string_view_t reset = "\033[m";
-    const string_view_t bold = "\033[1m";
-    const string_view_t dark = "\033[2m";
-    const string_view_t underline = "\033[4m";
-    const string_view_t blink = "\033[5m";
-    const string_view_t reverse = "\033[7m";
-    const string_view_t concealed = "\033[8m";
-    const string_view_t clear_line = "\033[K";
-
-    // Foreground colors
-    const string_view_t black = "\033[30m";
-    const string_view_t red = "\033[31m";
-    const string_view_t green = "\033[32m";
-    const string_view_t yellow = "\033[33m";
-    const string_view_t blue = "\033[34m";
-    const string_view_t magenta = "\033[35m";
-    const string_view_t cyan = "\033[36m";
-    const string_view_t white = "\033[37m";
-
-    /// Background colors
-    const string_view_t on_black = "\033[40m";
-    const string_view_t on_red = "\033[41m";
-    const string_view_t on_green = "\033[42m";
-    const string_view_t on_yellow = "\033[43m";
-    const string_view_t on_blue = "\033[44m";
-    const string_view_t on_magenta = "\033[45m";
-    const string_view_t on_cyan = "\033[46m";
-    const string_view_t on_white = "\033[47m";
-
-    /// Bold colors
-    const string_view_t yellow_bold = "\033[33m\033[1m";
-    const string_view_t red_bold = "\033[31m\033[1m";
-    const string_view_t bold_on_red = "\033[1m\033[41m";
-
-private:
-    FILE *target_file_;
-    mutex_t &mutex_;
-    bool should_do_colors_;
-    std::unique_ptr<spdlog::formatter> formatter_;
-    std::array<std::string, level::n_levels> colors_;
-    void print_ccode_(const string_view_t &color_code);
-    void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
-    static std::string to_string_(const string_view_t &sv);
-};
-
-template<typename ConsoleMutex>
-class ansicolor_stdout_sink : public ansicolor_sink<ConsoleMutex>
+namespace sinks
 {
-public:
-    explicit ansicolor_stdout_sink(color_mode mode = color_mode::automatic);
-};
-
-template<typename ConsoleMutex>
-class ansicolor_stderr_sink : public ansicolor_sink<ConsoleMutex>
-{
-public:
-    explicit ansicolor_stderr_sink(color_mode mode = color_mode::automatic);
-};
-
-using ansicolor_stdout_sink_mt = ansicolor_stdout_sink<details::console_mutex>;
-using ansicolor_stdout_sink_st = ansicolor_stdout_sink<details::console_nullmutex>;
 
-using ansicolor_stderr_sink_mt = ansicolor_stderr_sink<details::console_mutex>;
-using ansicolor_stderr_sink_st = ansicolor_stderr_sink<details::console_nullmutex>;
+    /**
+     * This sink prefixes the output with an ANSI escape sequence color code
+     * depending on the severity
+     * of the message.
+     * If no color terminal detected, omit the escape codes.
+     */
+
+    template <typename ConsoleMutex>
+    class ansicolor_sink : public sink
+    {
+    public:
+        using mutex_t = typename ConsoleMutex::mutex_t;
+        ansicolor_sink(FILE *target_file, color_mode mode);
+        ~ansicolor_sink() override = default;
+
+        ansicolor_sink(const ansicolor_sink &other) = delete;
+        ansicolor_sink(ansicolor_sink &&other)      = delete;
+
+        ansicolor_sink &operator=(const ansicolor_sink &other) = delete;
+        ansicolor_sink &operator=(ansicolor_sink &&other)      = delete;
+
+        void set_color(level::level_enum color_level, string_view_t color);
+        void set_color_mode(color_mode mode);
+        bool should_color();
+
+        void log(const details::log_msg &msg) override;
+        void flush() override;
+        void set_pattern(const std::string &pattern) final;
+        void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
+
+        // Formatting codes
+        const string_view_t reset      = "\033[m";
+        const string_view_t bold       = "\033[1m";
+        const string_view_t dark       = "\033[2m";
+        const string_view_t underline  = "\033[4m";
+        const string_view_t blink      = "\033[5m";
+        const string_view_t reverse    = "\033[7m";
+        const string_view_t concealed  = "\033[8m";
+        const string_view_t clear_line = "\033[K";
+
+        // Foreground colors
+        const string_view_t black   = "\033[30m";
+        const string_view_t red     = "\033[31m";
+        const string_view_t green   = "\033[32m";
+        const string_view_t yellow  = "\033[33m";
+        const string_view_t blue    = "\033[34m";
+        const string_view_t magenta = "\033[35m";
+        const string_view_t cyan    = "\033[36m";
+        const string_view_t white   = "\033[37m";
+
+        /// Background colors
+        const string_view_t on_black   = "\033[40m";
+        const string_view_t on_red     = "\033[41m";
+        const string_view_t on_green   = "\033[42m";
+        const string_view_t on_yellow  = "\033[43m";
+        const string_view_t on_blue    = "\033[44m";
+        const string_view_t on_magenta = "\033[45m";
+        const string_view_t on_cyan    = "\033[46m";
+        const string_view_t on_white   = "\033[47m";
+
+        /// Bold colors
+        const string_view_t yellow_bold = "\033[33m\033[1m";
+        const string_view_t red_bold    = "\033[31m\033[1m";
+        const string_view_t bold_on_red = "\033[1m\033[41m";
+
+    private:
+        FILE                                    *target_file_;
+        mutex_t                                 &mutex_;
+        bool                                     should_do_colors_;
+        std::unique_ptr<spdlog::formatter>       formatter_;
+        std::array<std::string, level::n_levels> colors_;
+        void                                     print_ccode_(const string_view_t &color_code);
+        void                                     print_range_(const memory_buf_t &formatted, size_t start, size_t end);
+        static std::string                       to_string_(const string_view_t &sv);
+    };
+
+    template <typename ConsoleMutex>
+    class ansicolor_stdout_sink : public ansicolor_sink<ConsoleMutex>
+    {
+    public:
+        explicit ansicolor_stdout_sink(color_mode mode = color_mode::automatic);
+    };
+
+    template <typename ConsoleMutex>
+    class ansicolor_stderr_sink : public ansicolor_sink<ConsoleMutex>
+    {
+    public:
+        explicit ansicolor_stderr_sink(color_mode mode = color_mode::automatic);
+    };
+
+    using ansicolor_stdout_sink_mt = ansicolor_stdout_sink<details::console_mutex>;
+    using ansicolor_stdout_sink_st = ansicolor_stdout_sink<details::console_nullmutex>;
+
+    using ansicolor_stderr_sink_mt = ansicolor_stderr_sink<details::console_mutex>;
+    using ansicolor_stderr_sink_st = ansicolor_stderr_sink<details::console_nullmutex>;
 
 } // namespace sinks
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "ansicolor_sink-inl.h"
+#include "ansicolor_sink-inl.h"
 #endif
diff --git a/include/spdlog/sinks/base_sink-inl.h b/include/spdlog/sinks/base_sink-inl.h
index 421fdf9dd..2b477e56f 100644
--- a/include/spdlog/sinks/base_sink-inl.h
+++ b/include/spdlog/sinks/base_sink-inl.h
@@ -4,59 +4,60 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/sinks/base_sink.h>
+#include <spdlog/sinks/base_sink.h>
 #endif
 
+#include <memory>
 #include <spdlog/common.h>
 #include <spdlog/pattern_formatter.h>
 
-#include <memory>
-
-template<typename Mutex>
-SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::base_sink()
-    : formatter_{details::make_unique<spdlog::pattern_formatter>()}
-{}
+template <typename Mutex>
+SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::base_sink() :
+    formatter_{details::make_unique<spdlog::pattern_formatter>()}
+{
+}
 
-template<typename Mutex>
-SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::base_sink(std::unique_ptr<spdlog::formatter> formatter)
-    : formatter_{std::move(formatter)}
-{}
+template <typename Mutex>
+SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::base_sink(std::unique_ptr<spdlog::formatter> formatter) :
+    formatter_{std::move(formatter)}
+{
+}
 
-template<typename Mutex>
+template <typename Mutex>
 void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::log(const details::log_msg &msg)
 {
     std::lock_guard<Mutex> lock(mutex_);
     sink_it_(msg);
 }
 
-template<typename Mutex>
+template <typename Mutex>
 void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::flush()
 {
     std::lock_guard<Mutex> lock(mutex_);
     flush_();
 }
 
-template<typename Mutex>
+template <typename Mutex>
 void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_pattern(const std::string &pattern)
 {
     std::lock_guard<Mutex> lock(mutex_);
     set_pattern_(pattern);
 }
 
-template<typename Mutex>
+template <typename Mutex>
 void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
 {
     std::lock_guard<Mutex> lock(mutex_);
     set_formatter_(std::move(sink_formatter));
 }
 
-template<typename Mutex>
+template <typename Mutex>
 void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_pattern_(const std::string &pattern)
 {
     set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
 }
 
-template<typename Mutex>
+template <typename Mutex>
 void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter)
 {
     formatter_ = std::move(sink_formatter);
diff --git a/include/spdlog/sinks/base_sink.h b/include/spdlog/sinks/base_sink.h
index 2e795f592..5eea770a3 100644
--- a/include/spdlog/sinks/base_sink.h
+++ b/include/spdlog/sinks/base_sink.h
@@ -13,40 +13,42 @@
 #include <spdlog/details/log_msg.h>
 #include <spdlog/sinks/sink.h>
 
-namespace spdlog {
-namespace sinks {
-template<typename Mutex>
-class SPDLOG_API base_sink : public sink
+namespace spdlog
 {
-public:
-    base_sink();
-    explicit base_sink(std::unique_ptr<spdlog::formatter> formatter);
-    ~base_sink() override = default;
-
-    base_sink(const base_sink &) = delete;
-    base_sink(base_sink &&) = delete;
-
-    base_sink &operator=(const base_sink &) = delete;
-    base_sink &operator=(base_sink &&) = delete;
-
-    void log(const details::log_msg &msg) final;
-    void flush() final;
-    void set_pattern(const std::string &pattern) final;
-    void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) final;
-
-protected:
-    // sink formatter
-    std::unique_ptr<spdlog::formatter> formatter_;
-    Mutex mutex_;
-
-    virtual void sink_it_(const details::log_msg &msg) = 0;
-    virtual void flush_() = 0;
-    virtual void set_pattern_(const std::string &pattern);
-    virtual void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter);
-};
+namespace sinks
+{
+    template <typename Mutex>
+    class SPDLOG_API base_sink : public sink
+    {
+    public:
+        base_sink();
+        explicit base_sink(std::unique_ptr<spdlog::formatter> formatter);
+        ~base_sink() override = default;
+
+        base_sink(const base_sink &) = delete;
+        base_sink(base_sink &&)      = delete;
+
+        base_sink &operator=(const base_sink &) = delete;
+        base_sink &operator=(base_sink &&)      = delete;
+
+        void log(const details::log_msg &msg) final;
+        void flush() final;
+        void set_pattern(const std::string &pattern) final;
+        void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) final;
+
+    protected:
+        // sink formatter
+        std::unique_ptr<spdlog::formatter> formatter_;
+        Mutex                              mutex_;
+
+        virtual void sink_it_(const details::log_msg &msg) = 0;
+        virtual void flush_()                              = 0;
+        virtual void set_pattern_(const std::string &pattern);
+        virtual void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter);
+    };
 } // namespace sinks
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "base_sink-inl.h"
+#include "base_sink-inl.h"
 #endif
diff --git a/include/spdlog/sinks/basic_file_sink-inl.h b/include/spdlog/sinks/basic_file_sink-inl.h
index 8d23f96df..03309eaea 100644
--- a/include/spdlog/sinks/basic_file_sink-inl.h
+++ b/include/spdlog/sinks/basic_file_sink-inl.h
@@ -4,41 +4,46 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/sinks/basic_file_sink.h>
+#include <spdlog/sinks/basic_file_sink.h>
 #endif
 
 #include <spdlog/common.h>
 #include <spdlog/details/os.h>
 
-namespace spdlog {
-namespace sinks {
-
-template<typename Mutex>
-SPDLOG_INLINE basic_file_sink<Mutex>::basic_file_sink(const filename_t &filename, bool truncate, const file_event_handlers &event_handlers)
-    : file_helper_{event_handlers}
+namespace spdlog
 {
-    file_helper_.open(filename, truncate);
-}
-
-template<typename Mutex>
-SPDLOG_INLINE const filename_t &basic_file_sink<Mutex>::filename() const
+namespace sinks
 {
-    return file_helper_.filename();
-}
 
-template<typename Mutex>
-SPDLOG_INLINE void basic_file_sink<Mutex>::sink_it_(const details::log_msg &msg)
-{
-    memory_buf_t formatted;
-    base_sink<Mutex>::formatter_->format(msg, formatted);
-    file_helper_.write(formatted);
-}
-
-template<typename Mutex>
-SPDLOG_INLINE void basic_file_sink<Mutex>::flush_()
-{
-    file_helper_.flush();
-}
+    template <typename Mutex>
+    SPDLOG_INLINE basic_file_sink<Mutex>::basic_file_sink(
+        const filename_t          &filename,
+        bool                       truncate,
+        const file_event_handlers &event_handlers) :
+        file_helper_{event_handlers}
+    {
+        file_helper_.open(filename, truncate);
+    }
+
+    template <typename Mutex>
+    SPDLOG_INLINE const filename_t &basic_file_sink<Mutex>::filename() const
+    {
+        return file_helper_.filename();
+    }
+
+    template <typename Mutex>
+    SPDLOG_INLINE void basic_file_sink<Mutex>::sink_it_(const details::log_msg &msg)
+    {
+        memory_buf_t formatted;
+        base_sink<Mutex>::formatter_->format(msg, formatted);
+        file_helper_.write(formatted);
+    }
+
+    template <typename Mutex>
+    SPDLOG_INLINE void basic_file_sink<Mutex>::flush_()
+    {
+        file_helper_.flush();
+    }
 
 } // namespace sinks
 } // namespace spdlog
diff --git a/include/spdlog/sinks/basic_file_sink.h b/include/spdlog/sinks/basic_file_sink.h
index aacc993bf..84ba75e44 100644
--- a/include/spdlog/sinks/basic_file_sink.h
+++ b/include/spdlog/sinks/basic_file_sink.h
@@ -3,52 +3,62 @@
 
 #pragma once
 
+#include <mutex>
 #include <spdlog/details/file_helper.h>
 #include <spdlog/details/null_mutex.h>
-#include <spdlog/sinks/base_sink.h>
 #include <spdlog/details/synchronous_factory.h>
-
-#include <mutex>
+#include <spdlog/sinks/base_sink.h>
 #include <string>
 
-namespace spdlog {
-namespace sinks {
-/*
- * Trivial file sink with single file as target
- */
-template<typename Mutex>
-class basic_file_sink final : public base_sink<Mutex>
+namespace spdlog
+{
+namespace sinks
 {
-public:
-    explicit basic_file_sink(const filename_t &filename, bool truncate = false, const file_event_handlers &event_handlers = {});
-    const filename_t &filename() const;
+    /*
+     * Trivial file sink with single file as target
+     */
+    template <typename Mutex>
+    class basic_file_sink final : public base_sink<Mutex>
+    {
+    public:
+        explicit basic_file_sink(
+            const filename_t          &filename,
+            bool                       truncate       = false,
+            const file_event_handlers &event_handlers = {});
+        const filename_t &filename() const;
 
-protected:
-    void sink_it_(const details::log_msg &msg) override;
-    void flush_() override;
+    protected:
+        void sink_it_(const details::log_msg &msg) override;
+        void flush_() override;
 
-private:
-    details::file_helper file_helper_;
-};
+    private:
+        details::file_helper file_helper_;
+    };
 
-using basic_file_sink_mt = basic_file_sink<std::mutex>;
-using basic_file_sink_st = basic_file_sink<details::null_mutex>;
+    using basic_file_sink_mt = basic_file_sink<std::mutex>;
+    using basic_file_sink_st = basic_file_sink<details::null_mutex>;
 
 } // namespace sinks
 
 //
 // factory functions
 //
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_factory>
 inline std::shared_ptr<logger> basic_logger_mt(
-    const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers &event_handlers = {})
+    const std::string         &logger_name,
+    const filename_t          &filename,
+    bool                       truncate       = false,
+    const file_event_handlers &event_handlers = {})
 {
     return Factory::template create<sinks::basic_file_sink_mt>(logger_name, filename, truncate, event_handlers);
 }
 
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_factory>
 inline std::shared_ptr<logger> basic_logger_st(
-    const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers &event_handlers = {})
+    const std::string         &logger_name,
+    const filename_t          &filename,
+    bool                       truncate       = false,
+    const file_event_handlers &event_handlers = {})
 {
     return Factory::template create<sinks::basic_file_sink_st>(logger_name, filename, truncate, event_handlers);
 }
@@ -56,5 +66,5 @@ inline std::shared_ptr<logger> basic_logger_st(
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "basic_file_sink-inl.h"
+#include "basic_file_sink-inl.h"
 #endif
diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h
index f6f1bb1d7..b2f201a30 100644
--- a/include/spdlog/sinks/daily_file_sink.h
+++ b/include/spdlog/sinks/daily_file_sink.h
@@ -3,293 +3,327 @@
 
 #pragma once
 
+#include <chrono>
+#include <cstdio>
+#include <ctime>
+#include <mutex>
 #include <spdlog/common.h>
+#include <spdlog/details/circular_q.h>
 #include <spdlog/details/file_helper.h>
 #include <spdlog/details/null_mutex.h>
-#include <spdlog/fmt/fmt.h>
-#include <spdlog/fmt/chrono.h>
-#include <spdlog/sinks/base_sink.h>
 #include <spdlog/details/os.h>
-#include <spdlog/details/circular_q.h>
 #include <spdlog/details/synchronous_factory.h>
-
-#include <chrono>
-#include <cstdio>
-#include <ctime>
-#include <mutex>
+#include <spdlog/fmt/chrono.h>
+#include <spdlog/fmt/fmt.h>
+#include <spdlog/sinks/base_sink.h>
 #include <string>
 
-namespace spdlog {
-namespace sinks {
-
-/*
- * Generator of daily log file names in format basename.YYYY-MM-DD.ext
- */
-struct daily_filename_calculator
+namespace spdlog
 {
-    // Create filename for the form basename.YYYY-MM-DD
-    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_extension(filename);
-        return fmt_lib::format(SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}")), basename, now_tm.tm_year + 1900,
-            now_tm.tm_mon + 1, now_tm.tm_mday, ext);
-    }
-};
-
-/*
- * Generator of daily log file names with strftime format.
- * Usages:
- *    auto sink =  std::make_shared<spdlog::sinks::daily_file_format_sink_mt>("myapp-%Y-%m-%d:%H:%M:%S.log", hour, minute);"
- *    auto logger = spdlog::daily_logger_format_mt("loggername, "myapp-%Y-%m-%d:%X.log", hour,  minute)"
- *
- */
-struct daily_filename_format_calculator
+namespace sinks
 {
-    static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
+
+    /*
+     * Generator of daily log file names in format basename.YYYY-MM-DD.ext
+     */
+    struct daily_filename_calculator
     {
-#ifdef SPDLOG_USE_STD_FORMAT
-        // adapted from fmtlib: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/chrono.h#L522-L546
-
-        filename_t tm_format;
-        tm_format.append(filename);
-        // By appending an extra space we can distinguish an empty result that
-        // indicates insufficient buffer size from a guaranteed non-empty result
-        // https://github.com/fmtlib/fmt/issues/2238
-        tm_format.push_back(' ');
-
-        const size_t MIN_SIZE = 10;
-        filename_t buf;
-        buf.resize(MIN_SIZE);
-        for (;;)
+        // Create filename for the form basename.YYYY-MM-DD
+        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_extension(filename);
+            return fmt_lib::format(
+                SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}")),
+                basename,
+                now_tm.tm_year + 1900,
+                now_tm.tm_mon + 1,
+                now_tm.tm_mday,
+                ext);
+        }
+    };
+
+    /*
+     * Generator of daily log file names with strftime format.
+     * Usages:
+     *    auto sink =  std::make_shared<spdlog::sinks::daily_file_format_sink_mt>("myapp-%Y-%m-%d:%H:%M:%S.log", hour,
+     * minute);" auto logger = spdlog::daily_logger_format_mt("loggername, "myapp-%Y-%m-%d:%X.log", hour,  minute)"
+     *
+     */
+    struct daily_filename_format_calculator
+    {
+        static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
         {
-            size_t count = strftime(buf.data(), buf.size(), tm_format.c_str(), &now_tm);
-            if (count != 0)
+#ifdef SPDLOG_USE_STD_FORMAT
+            // adapted from fmtlib: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/chrono.h#L522-L546
+
+            filename_t tm_format;
+            tm_format.append(filename);
+            // By appending an extra space we can distinguish an empty result that
+            // indicates insufficient buffer size from a guaranteed non-empty result
+            // https://github.com/fmtlib/fmt/issues/2238
+            tm_format.push_back(' ');
+
+            const size_t MIN_SIZE = 10;
+            filename_t   buf;
+            buf.resize(MIN_SIZE);
+            for(;;)
             {
-                // Remove the extra space.
-                buf.resize(count - 1);
-                break;
+                size_t count = strftime(buf.data(), buf.size(), tm_format.c_str(), &now_tm);
+                if(count != 0)
+                {
+                    // Remove the extra space.
+                    buf.resize(count - 1);
+                    break;
+                }
+                buf.resize(buf.size() * 2);
             }
-            buf.resize(buf.size() * 2);
-        }
 
-        return buf;
+            return buf;
 #else
-        // generate fmt datetime format string, e.g. {:%Y-%m-%d}.
-        filename_t fmt_filename = fmt::format(SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{{:{}}}")), filename);
-
-        // MSVC doesn't allow fmt::runtime(..) with wchar, with fmtlib versions < 9.1.x
-#    if defined(_MSC_VER) && defined(SPDLOG_WCHAR_FILENAMES) && FMT_VERSION < 90101
-        return fmt::format(fmt_filename, now_tm);
-#    else
-        return fmt::format(SPDLOG_FMT_RUNTIME(fmt_filename), now_tm);
-#    endif
+            // generate fmt datetime format string, e.g. {:%Y-%m-%d}.
+            filename_t fmt_filename = fmt::format(SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{{:{}}}")), filename);
 
+            // MSVC doesn't allow fmt::runtime(..) with wchar, with fmtlib versions < 9.1.x
+#if defined(_MSC_VER) && defined(SPDLOG_WCHAR_FILENAMES) && FMT_VERSION < 90101
+            return fmt::format(fmt_filename, now_tm);
+#else
+            return fmt::format(SPDLOG_FMT_RUNTIME(fmt_filename), now_tm);
 #endif
-    }
 
-private:
-#if defined __GNUC__
-#    pragma GCC diagnostic push
-#    pragma GCC diagnostic ignored "-Wformat-nonliteral"
 #endif
+        }
 
-    static size_t strftime(char *str, size_t count, const char *format, const std::tm *time)
-    {
-        return std::strftime(str, count, format, time);
-    }
-
-    static size_t strftime(wchar_t *str, size_t count, const wchar_t *format, const std::tm *time)
-    {
-        return std::wcsftime(str, count, format, time);
-    }
-
-#if defined(__GNUC__)
-#    pragma GCC diagnostic pop
+    private:
+#if defined            __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
 #endif
-};
-
-/*
- * Rotating file sink based on date.
- * If truncate != false , the created file will be truncated.
- * If max_files > 0, retain only the last max_files and delete previous.
- */
-template<typename Mutex, typename FileNameCalc = daily_filename_calculator>
-class daily_file_sink final : public base_sink<Mutex>
-{
-public:
-    // create daily file sink which rotates on given time
-    daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0,
-        const file_event_handlers &event_handlers = {})
-        : base_filename_(std::move(base_filename))
-        , rotation_h_(rotation_hour)
-        , rotation_m_(rotation_minute)
-        , file_helper_{event_handlers}
-        , truncate_(truncate)
-        , max_files_(max_files)
-        , filenames_q_()
-    {
-        if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
+
+        static size_t strftime(char *str, size_t count, const char *format, const std::tm *time)
         {
-            throw_spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
+            return std::strftime(str, count, format, time);
         }
 
-        auto now = log_clock::now();
-        auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
-        file_helper_.open(filename, truncate_);
-        rotation_tp_ = next_rotation_tp_();
-
-        if (max_files_ > 0)
+        static size_t strftime(wchar_t *str, size_t count, const wchar_t *format, const std::tm *time)
         {
-            init_filenames_q_();
+            return std::wcsftime(str, count, format, time);
         }
-    }
-
-    filename_t filename()
-    {
-        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
-        return file_helper_.filename();
-    }
 
-protected:
-    void sink_it_(const details::log_msg &msg) override
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+    };
+
+    /*
+     * Rotating file sink based on date.
+     * If truncate != false , the created file will be truncated.
+     * If max_files > 0, retain only the last max_files and delete previous.
+     */
+    template <typename Mutex, typename FileNameCalc = daily_filename_calculator>
+    class daily_file_sink final : public base_sink<Mutex>
     {
-        auto time = msg.time;
-        bool should_rotate = time >= rotation_tp_;
-        if (should_rotate)
+    public:
+        // create daily file sink which rotates on given time
+        daily_file_sink(
+            filename_t                 base_filename,
+            int                        rotation_hour,
+            int                        rotation_minute,
+            bool                       truncate       = false,
+            uint16_t                   max_files      = 0,
+            const file_event_handlers &event_handlers = {}) :
+            base_filename_(std::move(base_filename)),
+            rotation_h_(rotation_hour),
+            rotation_m_(rotation_minute),
+            file_helper_{event_handlers},
+            truncate_(truncate),
+            max_files_(max_files),
+            filenames_q_()
         {
-            auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
+            if(rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
+            {
+                throw_spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
+            }
+
+            auto now      = log_clock::now();
+            auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
             file_helper_.open(filename, truncate_);
             rotation_tp_ = next_rotation_tp_();
+
+            if(max_files_ > 0)
+            {
+                init_filenames_q_();
+            }
         }
-        memory_buf_t formatted;
-        base_sink<Mutex>::formatter_->format(msg, formatted);
-        file_helper_.write(formatted);
 
-        // Do the cleaning only at the end because it might throw on failure.
-        if (should_rotate && max_files_ > 0)
+        filename_t filename()
         {
-            delete_old_();
+            std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+            return file_helper_.filename();
         }
-    }
 
-    void flush_() override
-    {
-        file_helper_.flush();
-    }
+    protected:
+        void sink_it_(const details::log_msg &msg) override
+        {
+            auto time          = msg.time;
+            bool should_rotate = time >= rotation_tp_;
+            if(should_rotate)
+            {
+                auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
+                file_helper_.open(filename, truncate_);
+                rotation_tp_ = next_rotation_tp_();
+            }
+            memory_buf_t formatted;
+            base_sink<Mutex>::formatter_->format(msg, formatted);
+            file_helper_.write(formatted);
 
-private:
-    void init_filenames_q_()
-    {
-        using details::os::path_exists;
+            // Do the cleaning only at the end because it might throw on failure.
+            if(should_rotate && max_files_ > 0)
+            {
+                delete_old_();
+            }
+        }
+
+        void flush_() override { file_helper_.flush(); }
 
-        filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
-        std::vector<filename_t> filenames;
-        auto now = log_clock::now();
-        while (filenames.size() < max_files_)
+    private:
+        void init_filenames_q_()
         {
-            auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
-            if (!path_exists(filename))
+            using details::os::path_exists;
+
+            filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
+            std::vector<filename_t> filenames;
+            auto                    now = log_clock::now();
+            while(filenames.size() < max_files_)
             {
-                break;
+                auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
+                if(!path_exists(filename))
+                {
+                    break;
+                }
+                filenames.emplace_back(filename);
+                now -= std::chrono::hours(24);
+            }
+            for(auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
+            {
+                filenames_q_.push_back(std::move(*iter));
             }
-            filenames.emplace_back(filename);
-            now -= std::chrono::hours(24);
         }
-        for (auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
+
+        tm now_tm(log_clock::time_point tp)
         {
-            filenames_q_.push_back(std::move(*iter));
+            time_t tnow = log_clock::to_time_t(tp);
+            return spdlog::details::os::localtime(tnow);
         }
-    }
-
-    tm now_tm(log_clock::time_point tp)
-    {
-        time_t tnow = log_clock::to_time_t(tp);
-        return spdlog::details::os::localtime(tnow);
-    }
 
-    log_clock::time_point next_rotation_tp_()
-    {
-        auto now = log_clock::now();
-        tm date = now_tm(now);
-        date.tm_hour = rotation_h_;
-        date.tm_min = rotation_m_;
-        date.tm_sec = 0;
-        auto rotation_time = log_clock::from_time_t(std::mktime(&date));
-        if (rotation_time > now)
+        log_clock::time_point next_rotation_tp_()
         {
-            return rotation_time;
+            auto now           = log_clock::now();
+            tm   date          = now_tm(now);
+            date.tm_hour       = rotation_h_;
+            date.tm_min        = rotation_m_;
+            date.tm_sec        = 0;
+            auto rotation_time = log_clock::from_time_t(std::mktime(&date));
+            if(rotation_time > now)
+            {
+                return rotation_time;
+            }
+            return {rotation_time + std::chrono::hours(24)};
         }
-        return {rotation_time + std::chrono::hours(24)};
-    }
-
-    // Delete the file N rotations ago.
-    // Throw spdlog_ex on failure to delete the old file.
-    void delete_old_()
-    {
-        using details::os::filename_to_str;
-        using details::os::remove_if_exists;
 
-        filename_t current_file = file_helper_.filename();
-        if (filenames_q_.full())
+        // Delete the file N rotations ago.
+        // Throw spdlog_ex on failure to delete the old file.
+        void delete_old_()
         {
-            auto old_filename = std::move(filenames_q_.front());
-            filenames_q_.pop_front();
-            bool ok = remove_if_exists(old_filename) == 0;
-            if (!ok)
+            using details::os::filename_to_str;
+            using details::os::remove_if_exists;
+
+            filename_t current_file = file_helper_.filename();
+            if(filenames_q_.full())
             {
-                filenames_q_.push_back(std::move(current_file));
-                throw_spdlog_ex("Failed removing daily file " + filename_to_str(old_filename), errno);
+                auto old_filename = std::move(filenames_q_.front());
+                filenames_q_.pop_front();
+                bool ok = remove_if_exists(old_filename) == 0;
+                if(!ok)
+                {
+                    filenames_q_.push_back(std::move(current_file));
+                    throw_spdlog_ex("Failed removing daily file " + filename_to_str(old_filename), errno);
+                }
             }
+            filenames_q_.push_back(std::move(current_file));
         }
-        filenames_q_.push_back(std::move(current_file));
-    }
-
-    filename_t base_filename_;
-    int rotation_h_;
-    int rotation_m_;
-    log_clock::time_point rotation_tp_;
-    details::file_helper file_helper_;
-    bool truncate_;
-    uint16_t max_files_;
-    details::circular_q<filename_t> filenames_q_;
-};
-
-using daily_file_sink_mt = daily_file_sink<std::mutex>;
-using daily_file_sink_st = daily_file_sink<details::null_mutex>;
-using daily_file_format_sink_mt = daily_file_sink<std::mutex, daily_filename_format_calculator>;
-using daily_file_format_sink_st = daily_file_sink<details::null_mutex, daily_filename_format_calculator>;
+
+        filename_t                      base_filename_;
+        int                             rotation_h_;
+        int                             rotation_m_;
+        log_clock::time_point           rotation_tp_;
+        details::file_helper            file_helper_;
+        bool                            truncate_;
+        uint16_t                        max_files_;
+        details::circular_q<filename_t> filenames_q_;
+    };
+
+    using daily_file_sink_mt        = daily_file_sink<std::mutex>;
+    using daily_file_sink_st        = daily_file_sink<details::null_mutex>;
+    using daily_file_format_sink_mt = daily_file_sink<std::mutex, daily_filename_format_calculator>;
+    using daily_file_format_sink_st = daily_file_sink<details::null_mutex, daily_filename_format_calculator>;
 
 } // namespace sinks
 
 //
 // factory functions
 //
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> daily_logger_mt(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0,
-    bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> daily_logger_mt(
+    const std::string         &logger_name,
+    const filename_t          &filename,
+    int                        hour           = 0,
+    int                        minute         = 0,
+    bool                       truncate       = false,
+    uint16_t                   max_files      = 0,
+    const file_event_handlers &event_handlers = {})
 {
-    return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate, max_files, event_handlers);
+    return Factory::template create<sinks::daily_file_sink_mt>(
+        logger_name, filename, hour, minute, truncate, max_files, event_handlers);
 }
 
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> daily_logger_format_mt(const std::string &logger_name, const filename_t &filename, int hour = 0,
-    int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> daily_logger_format_mt(
+    const std::string         &logger_name,
+    const filename_t          &filename,
+    int                        hour           = 0,
+    int                        minute         = 0,
+    bool                       truncate       = false,
+    uint16_t                   max_files      = 0,
+    const file_event_handlers &event_handlers = {})
 {
     return Factory::template create<sinks::daily_file_format_sink_mt>(
         logger_name, filename, hour, minute, truncate, max_files, event_handlers);
 }
 
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> daily_logger_st(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0,
-    bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> daily_logger_st(
+    const std::string         &logger_name,
+    const filename_t          &filename,
+    int                        hour           = 0,
+    int                        minute         = 0,
+    bool                       truncate       = false,
+    uint16_t                   max_files      = 0,
+    const file_event_handlers &event_handlers = {})
 {
-    return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files, event_handlers);
+    return Factory::template create<sinks::daily_file_sink_st>(
+        logger_name, filename, hour, minute, truncate, max_files, event_handlers);
 }
 
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> daily_logger_format_st(const std::string &logger_name, const filename_t &filename, int hour = 0,
-    int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> daily_logger_format_st(
+    const std::string         &logger_name,
+    const filename_t          &filename,
+    int                        hour           = 0,
+    int                        minute         = 0,
+    bool                       truncate       = false,
+    uint16_t                   max_files      = 0,
+    const file_event_handlers &event_handlers = {})
 {
     return Factory::template create<sinks::daily_file_format_sink_st>(
         logger_name, filename, hour, minute, truncate, max_files, event_handlers);
diff --git a/include/spdlog/sinks/dist_sink.h b/include/spdlog/sinks/dist_sink.h
index 065048ad4..01ff83e7f 100644
--- a/include/spdlog/sinks/dist_sink.h
+++ b/include/spdlog/sinks/dist_sink.h
@@ -4,94 +4,91 @@
 #pragma once
 
 #include "base_sink.h"
-#include <spdlog/details/log_msg.h>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/pattern_formatter.h>
 
 #include <algorithm>
 #include <memory>
 #include <mutex>
+#include <spdlog/details/log_msg.h>
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/pattern_formatter.h>
 #include <vector>
 
 // Distribution sink (mux). Stores a vector of sinks which get called when log
 // is called
 
-namespace spdlog {
-namespace sinks {
-
-template<typename Mutex>
-class dist_sink : public base_sink<Mutex>
+namespace spdlog
+{
+namespace sinks
 {
-public:
-    dist_sink() = default;
-    explicit dist_sink(std::vector<std::shared_ptr<sink>> sinks)
-        : sinks_(sinks)
-    {}
-
-    dist_sink(const dist_sink &) = delete;
-    dist_sink &operator=(const dist_sink &) = delete;
 
-    void add_sink(std::shared_ptr<sink> sink)
+    template <typename Mutex>
+    class dist_sink : public base_sink<Mutex>
     {
-        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
-        sinks_.push_back(sink);
-    }
+    public:
+        dist_sink() = default;
+        explicit dist_sink(std::vector<std::shared_ptr<sink>> sinks) : sinks_(sinks) {}
 
-    void remove_sink(std::shared_ptr<sink> sink)
-    {
-        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
-        sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
-    }
+        dist_sink(const dist_sink &)            = delete;
+        dist_sink &operator=(const dist_sink &) = delete;
 
-    void set_sinks(std::vector<std::shared_ptr<sink>> sinks)
-    {
-        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
-        sinks_ = std::move(sinks);
-    }
+        void add_sink(std::shared_ptr<sink> sink)
+        {
+            std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+            sinks_.push_back(sink);
+        }
 
-    std::vector<std::shared_ptr<sink>> &sinks()
-    {
-        return sinks_;
-    }
+        void remove_sink(std::shared_ptr<sink> sink)
+        {
+            std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+            sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
+        }
 
-protected:
-    void sink_it_(const details::log_msg &msg) override
-    {
-        for (auto &sub_sink : sinks_)
+        void set_sinks(std::vector<std::shared_ptr<sink>> sinks)
+        {
+            std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+            sinks_ = std::move(sinks);
+        }
+
+        std::vector<std::shared_ptr<sink>> &sinks() { return sinks_; }
+
+    protected:
+        void sink_it_(const details::log_msg &msg) override
         {
-            if (sub_sink->should_log(msg.level))
+            for(auto &sub_sink : sinks_)
             {
-                sub_sink->log(msg);
+                if(sub_sink->should_log(msg.level))
+                {
+                    sub_sink->log(msg);
+                }
             }
         }
-    }
 
-    void flush_() override
-    {
-        for (auto &sub_sink : sinks_)
+        void flush_() override
         {
-            sub_sink->flush();
+            for(auto &sub_sink : sinks_)
+            {
+                sub_sink->flush();
+            }
         }
-    }
 
-    void set_pattern_(const std::string &pattern) override
-    {
-        set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
-    }
+        void set_pattern_(const std::string &pattern) override
+        {
+            set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
+        }
 
-    void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter) override
-    {
-        base_sink<Mutex>::formatter_ = std::move(sink_formatter);
-        for (auto &sub_sink : sinks_)
+        void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter) override
         {
-            sub_sink->set_formatter(base_sink<Mutex>::formatter_->clone());
+            base_sink<Mutex>::formatter_ = std::move(sink_formatter);
+            for(auto &sub_sink : sinks_)
+            {
+                sub_sink->set_formatter(base_sink<Mutex>::formatter_->clone());
+            }
         }
-    }
-    std::vector<std::shared_ptr<sink>> sinks_;
-};
+        std::vector<std::shared_ptr<sink>> sinks_;
+    };
 
-using dist_sink_mt = dist_sink<std::mutex>;
-using dist_sink_st = dist_sink<details::null_mutex>;
+    using dist_sink_mt = dist_sink<std::mutex>;
+    using dist_sink_st = dist_sink<details::null_mutex>;
 
 } // namespace sinks
 } // namespace spdlog
diff --git a/include/spdlog/sinks/dup_filter_sink.h b/include/spdlog/sinks/dup_filter_sink.h
index 1a4fb3485..858c8eb90 100644
--- a/include/spdlog/sinks/dup_filter_sink.h
+++ b/include/spdlog/sinks/dup_filter_sink.h
@@ -4,13 +4,13 @@
 #pragma once
 
 #include "dist_sink.h"
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/details/log_msg.h>
 
+#include <chrono>
 #include <cstdio>
 #include <mutex>
+#include <spdlog/details/log_msg.h>
+#include <spdlog/details/null_mutex.h>
 #include <string>
-#include <chrono>
 
 // Duplicate message removal sink.
 // Skip the message if previous one is identical and less than "max_skip_duration" have passed
@@ -34,61 +34,66 @@
 //       [2019-06-25 17:50:56.512] [logger] [info] Skipped 3 duplicate messages..
 //       [2019-06-25 17:50:56.512] [logger] [info] Different Hello
 
-namespace spdlog {
-namespace sinks {
-template<typename Mutex>
-class dup_filter_sink : public dist_sink<Mutex>
+namespace spdlog
 {
-public:
-    template<class Rep, class Period>
-    explicit dup_filter_sink(std::chrono::duration<Rep, Period> max_skip_duration)
-        : max_skip_duration_{max_skip_duration}
-    {}
-
-protected:
-    std::chrono::microseconds max_skip_duration_;
-    log_clock::time_point last_msg_time_;
-    std::string last_msg_payload_;
-    size_t skip_counter_ = 0;
-
-    void sink_it_(const details::log_msg &msg) override
+namespace sinks
+{
+    template <typename Mutex>
+    class dup_filter_sink : public dist_sink<Mutex>
     {
-        bool filtered = filter_(msg);
-        if (!filtered)
+    public:
+        template <class Rep, class Period>
+        explicit dup_filter_sink(std::chrono::duration<Rep, Period> max_skip_duration) :
+            max_skip_duration_{max_skip_duration}
         {
-            skip_counter_ += 1;
-            return;
         }
 
-        // log the "skipped.." message
-        if (skip_counter_ > 0)
+    protected:
+        std::chrono::microseconds max_skip_duration_;
+        log_clock::time_point     last_msg_time_;
+        std::string               last_msg_payload_;
+        size_t                    skip_counter_ = 0;
+
+        void sink_it_(const details::log_msg &msg) override
         {
-            char buf[64];
-            auto msg_size = ::snprintf(buf, sizeof(buf), "Skipped %u duplicate messages..", static_cast<unsigned>(skip_counter_));
-            if (msg_size > 0 && static_cast<size_t>(msg_size) < sizeof(buf))
+            bool filtered = filter_(msg);
+            if(!filtered)
             {
-                details::log_msg skipped_msg{msg.source, msg.logger_name, level::info, string_view_t{buf, static_cast<size_t>(msg_size)}};
-                dist_sink<Mutex>::sink_it_(skipped_msg);
+                skip_counter_ += 1;
+                return;
             }
-        }
 
-        // log current message
-        dist_sink<Mutex>::sink_it_(msg);
-        last_msg_time_ = msg.time;
-        skip_counter_ = 0;
-        last_msg_payload_.assign(msg.payload.data(), msg.payload.data() + msg.payload.size());
-    }
+            // log the "skipped.." message
+            if(skip_counter_ > 0)
+            {
+                char buf[64];
+                auto msg_size = ::snprintf(
+                    buf, sizeof(buf), "Skipped %u duplicate messages..", static_cast<unsigned>(skip_counter_));
+                if(msg_size > 0 && static_cast<size_t>(msg_size) < sizeof(buf))
+                {
+                    details::log_msg skipped_msg{
+                        msg.source, msg.logger_name, level::info, string_view_t{buf, static_cast<size_t>(msg_size)}};
+                    dist_sink<Mutex>::sink_it_(skipped_msg);
+                }
+            }
 
-    // return whether the log msg should be displayed (true) or skipped (false)
-    bool filter_(const details::log_msg &msg)
-    {
-        auto filter_duration = msg.time - last_msg_time_;
-        return (filter_duration > max_skip_duration_) || (msg.payload != last_msg_payload_);
-    }
-};
+            // log current message
+            dist_sink<Mutex>::sink_it_(msg);
+            last_msg_time_ = msg.time;
+            skip_counter_  = 0;
+            last_msg_payload_.assign(msg.payload.data(), msg.payload.data() + msg.payload.size());
+        }
+
+        // return whether the log msg should be displayed (true) or skipped (false)
+        bool filter_(const details::log_msg &msg)
+        {
+            auto filter_duration = msg.time - last_msg_time_;
+            return (filter_duration > max_skip_duration_) || (msg.payload != last_msg_payload_);
+        }
+    };
 
-using dup_filter_sink_mt = dup_filter_sink<std::mutex>;
-using dup_filter_sink_st = dup_filter_sink<details::null_mutex>;
+    using dup_filter_sink_mt = dup_filter_sink<std::mutex>;
+    using dup_filter_sink_st = dup_filter_sink<details::null_mutex>;
 
 } // namespace sinks
 } // namespace spdlog
diff --git a/include/spdlog/sinks/hourly_file_sink.h b/include/spdlog/sinks/hourly_file_sink.h
index 33dd89483..b8afa3483 100644
--- a/include/spdlog/sinks/hourly_file_sink.h
+++ b/include/spdlog/sinks/hourly_file_sink.h
@@ -3,202 +3,219 @@
 
 #pragma once
 
+#include <chrono>
+#include <cstdio>
+#include <ctime>
+#include <mutex>
 #include <spdlog/common.h>
+#include <spdlog/details/circular_q.h>
 #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/details/os.h>
-#include <spdlog/details/circular_q.h>
 #include <spdlog/details/synchronous_factory.h>
-
-#include <chrono>
-#include <cstdio>
-#include <ctime>
-#include <mutex>
+#include <spdlog/fmt/fmt.h>
+#include <spdlog/sinks/base_sink.h>
 #include <string>
 
-namespace spdlog {
-namespace sinks {
-
-/*
- * Generator of Hourly log file names in format basename.YYYY-MM-DD-HH.ext
- */
-struct hourly_filename_calculator
+namespace spdlog
 {
-    // Create filename for the form basename.YYYY-MM-DD-H
-    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_extension(filename);
-        return fmt_lib::format(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1,
-            now_tm.tm_mday, now_tm.tm_hour, ext);
-    }
-};
-
-/*
- * Rotating file sink based on time.
- * If truncate != false , the created file will be truncated.
- * If max_files > 0, retain only the last max_files and delete previous.
- */
-template<typename Mutex, typename FileNameCalc = hourly_filename_calculator>
-class hourly_file_sink final : public base_sink<Mutex>
+namespace sinks
 {
-public:
-    // create hourly file sink which rotates on given time
-    hourly_file_sink(
-        filename_t base_filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
-        : base_filename_(std::move(base_filename))
-        , file_helper_{event_handlers}
-        , truncate_(truncate)
-        , max_files_(max_files)
-        , filenames_q_()
-    {
-        auto now = log_clock::now();
-        auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
-        file_helper_.open(filename, truncate_);
-        remove_init_file_ = file_helper_.size() == 0;
-        rotation_tp_ = next_rotation_tp_();
 
-        if (max_files_ > 0)
+    /*
+     * Generator of Hourly log file names in format basename.YYYY-MM-DD-HH.ext
+     */
+    struct hourly_filename_calculator
+    {
+        // Create filename for the form basename.YYYY-MM-DD-H
+        static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
         {
-            init_filenames_q_();
+            filename_t basename, ext;
+            std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
+            return fmt_lib::format(
+                SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}{}"),
+                basename,
+                now_tm.tm_year + 1900,
+                now_tm.tm_mon + 1,
+                now_tm.tm_mday,
+                now_tm.tm_hour,
+                ext);
         }
-    }
-
-    filename_t filename()
-    {
-        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
-        return file_helper_.filename();
-    }
-
-protected:
-    void sink_it_(const details::log_msg &msg) override
+    };
+
+    /*
+     * Rotating file sink based on time.
+     * If truncate != false , the created file will be truncated.
+     * If max_files > 0, retain only the last max_files and delete previous.
+     */
+    template <typename Mutex, typename FileNameCalc = hourly_filename_calculator>
+    class hourly_file_sink final : public base_sink<Mutex>
     {
-        auto time = msg.time;
-        bool should_rotate = time >= rotation_tp_;
-        if (should_rotate)
+    public:
+        // create hourly file sink which rotates on given time
+        hourly_file_sink(
+            filename_t                 base_filename,
+            bool                       truncate       = false,
+            uint16_t                   max_files      = 0,
+            const file_event_handlers &event_handlers = {}) :
+            base_filename_(std::move(base_filename)),
+            file_helper_{event_handlers},
+            truncate_(truncate),
+            max_files_(max_files),
+            filenames_q_()
         {
-            if (remove_init_file_)
+            auto now      = log_clock::now();
+            auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
+            file_helper_.open(filename, truncate_);
+            remove_init_file_ = file_helper_.size() == 0;
+            rotation_tp_      = next_rotation_tp_();
+
+            if(max_files_ > 0)
             {
-                file_helper_.close();
-                details::os::remove(file_helper_.filename());
+                init_filenames_q_();
             }
-            auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
-            file_helper_.open(filename, truncate_);
-            rotation_tp_ = next_rotation_tp_();
         }
-        remove_init_file_ = false;
-        memory_buf_t formatted;
-        base_sink<Mutex>::formatter_->format(msg, formatted);
-        file_helper_.write(formatted);
 
-        // Do the cleaning only at the end because it might throw on failure.
-        if (should_rotate && max_files_ > 0)
+        filename_t filename()
         {
-            delete_old_();
+            std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+            return file_helper_.filename();
         }
-    }
 
-    void flush_() override
-    {
-        file_helper_.flush();
-    }
+    protected:
+        void sink_it_(const details::log_msg &msg) override
+        {
+            auto time          = msg.time;
+            bool should_rotate = time >= rotation_tp_;
+            if(should_rotate)
+            {
+                if(remove_init_file_)
+                {
+                    file_helper_.close();
+                    details::os::remove(file_helper_.filename());
+                }
+                auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
+                file_helper_.open(filename, truncate_);
+                rotation_tp_ = next_rotation_tp_();
+            }
+            remove_init_file_ = false;
+            memory_buf_t formatted;
+            base_sink<Mutex>::formatter_->format(msg, formatted);
+            file_helper_.write(formatted);
 
-private:
-    void init_filenames_q_()
-    {
-        using details::os::path_exists;
+            // Do the cleaning only at the end because it might throw on failure.
+            if(should_rotate && max_files_ > 0)
+            {
+                delete_old_();
+            }
+        }
+
+        void flush_() override { file_helper_.flush(); }
 
-        filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
-        std::vector<filename_t> filenames;
-        auto now = log_clock::now();
-        while (filenames.size() < max_files_)
+    private:
+        void init_filenames_q_()
         {
-            auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
-            if (!path_exists(filename))
+            using details::os::path_exists;
+
+            filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
+            std::vector<filename_t> filenames;
+            auto                    now = log_clock::now();
+            while(filenames.size() < max_files_)
+            {
+                auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
+                if(!path_exists(filename))
+                {
+                    break;
+                }
+                filenames.emplace_back(filename);
+                now -= std::chrono::hours(1);
+            }
+            for(auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
             {
-                break;
+                filenames_q_.push_back(std::move(*iter));
             }
-            filenames.emplace_back(filename);
-            now -= std::chrono::hours(1);
         }
-        for (auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
+
+        tm now_tm(log_clock::time_point tp)
         {
-            filenames_q_.push_back(std::move(*iter));
+            time_t tnow = log_clock::to_time_t(tp);
+            return spdlog::details::os::localtime(tnow);
         }
-    }
-
-    tm now_tm(log_clock::time_point tp)
-    {
-        time_t tnow = log_clock::to_time_t(tp);
-        return spdlog::details::os::localtime(tnow);
-    }
 
-    log_clock::time_point next_rotation_tp_()
-    {
-        auto now = log_clock::now();
-        tm date = now_tm(now);
-        date.tm_min = 0;
-        date.tm_sec = 0;
-        auto rotation_time = log_clock::from_time_t(std::mktime(&date));
-        if (rotation_time > now)
+        log_clock::time_point next_rotation_tp_()
         {
-            return rotation_time;
+            auto now           = log_clock::now();
+            tm   date          = now_tm(now);
+            date.tm_min        = 0;
+            date.tm_sec        = 0;
+            auto rotation_time = log_clock::from_time_t(std::mktime(&date));
+            if(rotation_time > now)
+            {
+                return rotation_time;
+            }
+            return {rotation_time + std::chrono::hours(1)};
         }
-        return {rotation_time + std::chrono::hours(1)};
-    }
 
-    // Delete the file N rotations ago.
-    // Throw spdlog_ex on failure to delete the old file.
-    void delete_old_()
-    {
-        using details::os::filename_to_str;
-        using details::os::remove_if_exists;
-
-        filename_t current_file = file_helper_.filename();
-        if (filenames_q_.full())
+        // Delete the file N rotations ago.
+        // Throw spdlog_ex on failure to delete the old file.
+        void delete_old_()
         {
-            auto old_filename = std::move(filenames_q_.front());
-            filenames_q_.pop_front();
-            bool ok = remove_if_exists(old_filename) == 0;
-            if (!ok)
+            using details::os::filename_to_str;
+            using details::os::remove_if_exists;
+
+            filename_t current_file = file_helper_.filename();
+            if(filenames_q_.full())
             {
-                filenames_q_.push_back(std::move(current_file));
-                SPDLOG_THROW(spdlog_ex("Failed removing hourly file " + filename_to_str(old_filename), errno));
+                auto old_filename = std::move(filenames_q_.front());
+                filenames_q_.pop_front();
+                bool ok = remove_if_exists(old_filename) == 0;
+                if(!ok)
+                {
+                    filenames_q_.push_back(std::move(current_file));
+                    SPDLOG_THROW(spdlog_ex("Failed removing hourly file " + filename_to_str(old_filename), errno));
+                }
             }
+            filenames_q_.push_back(std::move(current_file));
         }
-        filenames_q_.push_back(std::move(current_file));
-    }
 
-    filename_t base_filename_;
-    log_clock::time_point rotation_tp_;
-    details::file_helper file_helper_;
-    bool truncate_;
-    uint16_t max_files_;
-    details::circular_q<filename_t> filenames_q_;
-    bool remove_init_file_;
-};
+        filename_t                      base_filename_;
+        log_clock::time_point           rotation_tp_;
+        details::file_helper            file_helper_;
+        bool                            truncate_;
+        uint16_t                        max_files_;
+        details::circular_q<filename_t> filenames_q_;
+        bool                            remove_init_file_;
+    };
 
-using hourly_file_sink_mt = hourly_file_sink<std::mutex>;
-using hourly_file_sink_st = hourly_file_sink<details::null_mutex>;
+    using hourly_file_sink_mt = hourly_file_sink<std::mutex>;
+    using hourly_file_sink_st = hourly_file_sink<details::null_mutex>;
 
 } // namespace sinks
 
 //
 // factory functions
 //
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> hourly_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false,
-    uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> hourly_logger_mt(
+    const std::string         &logger_name,
+    const filename_t          &filename,
+    bool                       truncate       = false,
+    uint16_t                   max_files      = 0,
+    const file_event_handlers &event_handlers = {})
 {
-    return Factory::template create<sinks::hourly_file_sink_mt>(logger_name, filename, truncate, max_files, event_handlers);
+    return Factory::template create<sinks::hourly_file_sink_mt>(
+        logger_name, filename, truncate, max_files, event_handlers);
 }
 
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> hourly_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false,
-    uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> hourly_logger_st(
+    const std::string         &logger_name,
+    const filename_t          &filename,
+    bool                       truncate       = false,
+    uint16_t                   max_files      = 0,
+    const file_event_handlers &event_handlers = {})
 {
-    return Factory::template create<sinks::hourly_file_sink_st>(logger_name, filename, truncate, max_files, event_handlers);
+    return Factory::template create<sinks::hourly_file_sink_st>(
+        logger_name, filename, truncate, max_files, event_handlers);
 }
 } // namespace spdlog
diff --git a/include/spdlog/sinks/mongo_sink.h b/include/spdlog/sinks/mongo_sink.h
index 6a8927f54..2c48a53b0 100644
--- a/include/spdlog/sinks/mongo_sink.h
+++ b/include/spdlog/sinks/mongo_sink.h
@@ -13,93 +13,104 @@
 #include "spdlog/common.h"
 #include "spdlog/details/log_msg.h"
 #include "spdlog/sinks/base_sink.h"
-#include <spdlog/details/synchronous_factory.h>
 
 #include <bsoncxx/builder/stream/document.hpp>
 #include <bsoncxx/types.hpp>
 #include <bsoncxx/view_or_value.hpp>
-
 #include <mongocxx/client.hpp>
 #include <mongocxx/instance.hpp>
 #include <mongocxx/uri.hpp>
+#include <spdlog/details/synchronous_factory.h>
 
-namespace spdlog {
-namespace sinks {
-template<typename Mutex>
-class mongo_sink : public base_sink<Mutex>
+namespace spdlog
 {
-public:
-    mongo_sink(const std::string &db_name, const std::string &collection_name, const std::string &uri = "mongodb://localhost:27017")
-    try : mongo_sink(std::make_shared<mongocxx::instance>(), db_name, collection_name, uri)
-    {}
-    catch (const std::exception &e)
-    {
-        throw_spdlog_ex(fmt_lib::format("Error opening database: {}", e.what()));
-    }
-
-    mongo_sink(std::shared_ptr<mongocxx::instance> instance, const std::string &db_name, const std::string &collection_name,
-        const std::string &uri = "mongodb://localhost:27017")
-        : instance_(std::move(instance))
-        , db_name_(db_name)
-        , coll_name_(collection_name)
+namespace sinks
+{
+    template <typename Mutex>
+    class mongo_sink : public base_sink<Mutex>
     {
-        try
+    public:
+        mongo_sink(
+            const std::string &db_name,
+            const std::string &collection_name,
+            const std::string &uri = "mongodb://localhost:27017")
+        try : mongo_sink(std::make_shared<mongocxx::instance>(), db_name, collection_name, uri)
         {
-            client_ = spdlog::details::make_unique<mongocxx::client>(mongocxx::uri{uri});
         }
-        catch (const std::exception &e)
+        catch(const std::exception &e)
         {
             throw_spdlog_ex(fmt_lib::format("Error opening database: {}", e.what()));
         }
-    }
 
-    ~mongo_sink()
-    {
-        flush_();
-    }
+        mongo_sink(
+            std::shared_ptr<mongocxx::instance> instance,
+            const std::string                  &db_name,
+            const std::string                  &collection_name,
+            const std::string                  &uri = "mongodb://localhost:27017") :
+            instance_(std::move(instance)), db_name_(db_name), coll_name_(collection_name)
+        {
+            try
+            {
+                client_ = spdlog::details::make_unique<mongocxx::client>(mongocxx::uri{uri});
+            }
+            catch(const std::exception &e)
+            {
+                throw_spdlog_ex(fmt_lib::format("Error opening database: {}", e.what()));
+            }
+        }
 
-protected:
-    void sink_it_(const details::log_msg &msg) override
-    {
-        using bsoncxx::builder::stream::document;
-        using bsoncxx::builder::stream::finalize;
+        ~mongo_sink() { flush_(); }
 
-        if (client_ != nullptr)
+    protected:
+        void sink_it_(const details::log_msg &msg) override
         {
-            auto doc = document{} << "timestamp" << bsoncxx::types::b_date(msg.time) << "level" << level::to_string_view(msg.level).data()
-                                  << "level_num" << msg.level << "message" << std::string(msg.payload.begin(), msg.payload.end())
-                                  << "logger_name" << std::string(msg.logger_name.begin(), msg.logger_name.end()) << "thread_id"
-                                  << static_cast<int>(msg.thread_id) << finalize;
-            client_->database(db_name_).collection(coll_name_).insert_one(doc.view());
+            using bsoncxx::builder::stream::document;
+            using bsoncxx::builder::stream::finalize;
+
+            if(client_ != nullptr)
+            {
+                auto doc = document{} << "timestamp" << bsoncxx::types::b_date(msg.time) << "level"
+                                      << level::to_string_view(msg.level).data() << "level_num" << msg.level
+                                      << "message" << std::string(msg.payload.begin(), msg.payload.end())
+                                      << "logger_name" << std::string(msg.logger_name.begin(), msg.logger_name.end())
+                                      << "thread_id" << static_cast<int>(msg.thread_id) << finalize;
+                client_->database(db_name_).collection(coll_name_).insert_one(doc.view());
+            }
         }
-    }
 
-    void flush_() override {}
+        void flush_() override {}
 
-private:
-    std::shared_ptr<mongocxx::instance> instance_;
-    std::string db_name_;
-    std::string coll_name_;
-    std::unique_ptr<mongocxx::client> client_ = nullptr;
-};
+    private:
+        std::shared_ptr<mongocxx::instance> instance_;
+        std::string                         db_name_;
+        std::string                         coll_name_;
+        std::unique_ptr<mongocxx::client>   client_ = nullptr;
+    };
 
 #include "spdlog/details/null_mutex.h"
+
 #include <mutex>
-using mongo_sink_mt = mongo_sink<std::mutex>;
-using mongo_sink_st = mongo_sink<spdlog::details::null_mutex>;
+    using mongo_sink_mt = mongo_sink<std::mutex>;
+    using mongo_sink_st = mongo_sink<spdlog::details::null_mutex>;
 
 } // namespace sinks
 
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> mongo_logger_mt(const std::string &logger_name, const std::string &db_name,
-    const std::string &collection_name, const std::string &uri = "mongodb://localhost:27017")
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> mongo_logger_mt(
+    const std::string &logger_name,
+    const std::string &db_name,
+    const std::string &collection_name,
+    const std::string &uri = "mongodb://localhost:27017")
 {
     return Factory::template create<sinks::mongo_sink_mt>(logger_name, db_name, collection_name, uri);
 }
 
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> mongo_logger_st(const std::string &logger_name, const std::string &db_name,
-    const std::string &collection_name, const std::string &uri = "mongodb://localhost:27017")
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> mongo_logger_st(
+    const std::string &logger_name,
+    const std::string &db_name,
+    const std::string &collection_name,
+    const std::string &uri = "mongodb://localhost:27017")
 {
     return Factory::template create<sinks::mongo_sink_st>(logger_name, db_name, collection_name, uri);
 }
diff --git a/include/spdlog/sinks/msvc_sink.h b/include/spdlog/sinks/msvc_sink.h
index 09008b784..f89afb37f 100644
--- a/include/spdlog/sinks/msvc_sink.h
+++ b/include/spdlog/sinks/msvc_sink.h
@@ -6,52 +6,52 @@
 
 #if defined(_WIN32)
 
-#    include <spdlog/details/null_mutex.h>
-#    include <spdlog/sinks/base_sink.h>
-
-#    include <mutex>
-#    include <string>
+#include <mutex>
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/sinks/base_sink.h>
+#include <string>
 
 // Avoid including windows.h (https://stackoverflow.com/a/30741042)
 extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString);
 extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
 
-namespace spdlog {
-namespace sinks {
-/*
- * MSVC sink (logging using OutputDebugStringA)
- */
-template<typename Mutex>
-class msvc_sink : public base_sink<Mutex>
+namespace spdlog
 {
-public:
-    msvc_sink() = default;
-    msvc_sink(bool check_ebugger_present)
-        : check_debbugger_present_{check_ebugger_present} {};
-
-protected:
-    void sink_it_(const details::log_msg &msg) override
+namespace sinks
+{
+    /*
+     * MSVC sink (logging using OutputDebugStringA)
+     */
+    template <typename Mutex>
+    class msvc_sink : public base_sink<Mutex>
     {
-        if (check_debbugger_present_ && !IsDebuggerPresent())
+    public:
+        msvc_sink() = default;
+        msvc_sink(bool check_ebugger_present) : check_debbugger_present_{check_ebugger_present} {};
+
+    protected:
+        void sink_it_(const details::log_msg &msg) override
         {
-            return;
+            if(check_debbugger_present_ && !IsDebuggerPresent())
+            {
+                return;
+            }
+            memory_buf_t formatted;
+            base_sink<Mutex>::formatter_->format(msg, formatted);
+            formatted.push_back('\0'); // add a null terminator for OutputDebugStringA
+            OutputDebugStringA(formatted.data());
         }
-        memory_buf_t formatted;
-        base_sink<Mutex>::formatter_->format(msg, formatted);
-        formatted.push_back('\0'); // add a null terminator for OutputDebugStringA
-        OutputDebugStringA(formatted.data());
-    }
 
-    void flush_() override {}
+        void flush_() override {}
 
-    bool check_debbugger_present_ = true;
-};
+        bool check_debbugger_present_ = true;
+    };
 
-using msvc_sink_mt = msvc_sink<std::mutex>;
-using msvc_sink_st = msvc_sink<details::null_mutex>;
+    using msvc_sink_mt = msvc_sink<std::mutex>;
+    using msvc_sink_st = msvc_sink<details::null_mutex>;
 
-using windebug_sink_mt = msvc_sink_mt;
-using windebug_sink_st = msvc_sink_st;
+    using windebug_sink_mt = msvc_sink_mt;
+    using windebug_sink_st = msvc_sink_st;
 
 } // namespace sinks
 } // namespace spdlog
diff --git a/include/spdlog/sinks/null_sink.h b/include/spdlog/sinks/null_sink.h
index eb8328014..3f47bad84 100644
--- a/include/spdlog/sinks/null_sink.h
+++ b/include/spdlog/sinks/null_sink.h
@@ -3,29 +3,30 @@
 
 #pragma once
 
+#include <mutex>
 #include <spdlog/details/null_mutex.h>
-#include <spdlog/sinks/base_sink.h>
 #include <spdlog/details/synchronous_factory.h>
+#include <spdlog/sinks/base_sink.h>
 
-#include <mutex>
-
-namespace spdlog {
-namespace sinks {
-
-template<typename Mutex>
-class null_sink : public base_sink<Mutex>
+namespace spdlog
 {
-protected:
-    void sink_it_(const details::log_msg &) override {}
-    void flush_() override {}
-};
+namespace sinks
+{
+
+    template <typename Mutex>
+    class null_sink : public base_sink<Mutex>
+    {
+    protected:
+        void sink_it_(const details::log_msg &) override {}
+        void flush_() override {}
+    };
 
-using null_sink_mt = null_sink<details::null_mutex>;
-using null_sink_st = null_sink<details::null_mutex>;
+    using null_sink_mt = null_sink<details::null_mutex>;
+    using null_sink_st = null_sink<details::null_mutex>;
 
 } // namespace sinks
 
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_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);
@@ -33,7 +34,7 @@ inline std::shared_ptr<logger> null_logger_mt(const std::string &logger_name)
     return null_logger;
 }
 
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_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);
diff --git a/include/spdlog/sinks/ostream_sink.h b/include/spdlog/sinks/ostream_sink.h
index 95c1e9620..499b6d276 100644
--- a/include/spdlog/sinks/ostream_sink.h
+++ b/include/spdlog/sinks/ostream_sink.h
@@ -3,48 +3,43 @@
 
 #pragma once
 
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/sinks/base_sink.h>
-
 #include <mutex>
 #include <ostream>
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/sinks/base_sink.h>
 
-namespace spdlog {
-namespace sinks {
-template<typename Mutex>
-class ostream_sink final : public base_sink<Mutex>
+namespace spdlog
+{
+namespace sinks
 {
-public:
-    explicit ostream_sink(std::ostream &os, bool force_flush = false)
-        : ostream_(os)
-        , force_flush_(force_flush)
-    {}
-    ostream_sink(const ostream_sink &) = delete;
-    ostream_sink &operator=(const ostream_sink &) = delete;
-
-protected:
-    void sink_it_(const details::log_msg &msg) override
+    template <typename Mutex>
+    class ostream_sink final : public base_sink<Mutex>
     {
-        memory_buf_t formatted;
-        base_sink<Mutex>::formatter_->format(msg, formatted);
-        ostream_.write(formatted.data(), static_cast<std::streamsize>(formatted.size()));
-        if (force_flush_)
+    public:
+        explicit ostream_sink(std::ostream &os, bool force_flush = false) : ostream_(os), force_flush_(force_flush) {}
+        ostream_sink(const ostream_sink &)            = delete;
+        ostream_sink &operator=(const ostream_sink &) = delete;
+
+    protected:
+        void sink_it_(const details::log_msg &msg) override
         {
-            ostream_.flush();
+            memory_buf_t formatted;
+            base_sink<Mutex>::formatter_->format(msg, formatted);
+            ostream_.write(formatted.data(), static_cast<std::streamsize>(formatted.size()));
+            if(force_flush_)
+            {
+                ostream_.flush();
+            }
         }
-    }
 
-    void flush_() override
-    {
-        ostream_.flush();
-    }
+        void flush_() override { ostream_.flush(); }
 
-    std::ostream &ostream_;
-    bool force_flush_;
-};
+        std::ostream &ostream_;
+        bool          force_flush_;
+    };
 
-using ostream_sink_mt = ostream_sink<std::mutex>;
-using ostream_sink_st = ostream_sink<details::null_mutex>;
+    using ostream_sink_mt = ostream_sink<std::mutex>;
+    using ostream_sink_st = ostream_sink<details::null_mutex>;
 
 } // namespace sinks
 } // namespace spdlog
diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h
index 31b49c609..87d70c0d2 100644
--- a/include/spdlog/sinks/qt_sinks.h
+++ b/include/spdlog/sinks/qt_sinks.h
@@ -13,89 +13,100 @@
 #include "spdlog/details/synchronous_factory.h"
 #include "spdlog/sinks/base_sink.h"
 
-#include <QTextEdit>
 #include <QPlainTextEdit>
+#include <QTextEdit>
 
 //
 // qt_sink class
 //
-namespace spdlog {
-namespace sinks {
-template<typename Mutex>
-class qt_sink : public base_sink<Mutex>
+namespace spdlog
 {
-public:
-    qt_sink(QObject *qt_object, const std::string &meta_method)
-    {
-        qt_object_ = qt_object;
-        meta_method_ = meta_method;
-    }
-
-    ~qt_sink()
-    {
-        flush_();
-    }
-
-protected:
-    void sink_it_(const details::log_msg &msg) override
+namespace sinks
+{
+    template <typename Mutex>
+    class qt_sink : public base_sink<Mutex>
     {
-        memory_buf_t formatted;
-        base_sink<Mutex>::formatter_->format(msg, formatted);
-        string_view_t str = string_view_t(formatted.data(), formatted.size());
-        QMetaObject::invokeMethod(qt_object_, meta_method_.c_str(), Qt::AutoConnection,
-            Q_ARG(QString, QString::fromUtf8(str.data(), static_cast<int>(str.size())).trimmed()));
-    }
-
-    void flush_() override {}
-
-private:
-    QObject *qt_object_ = nullptr;
-    std::string meta_method_;
-};
+    public:
+        qt_sink(QObject *qt_object, const std::string &meta_method)
+        {
+            qt_object_   = qt_object;
+            meta_method_ = meta_method;
+        }
+
+        ~qt_sink() { flush_(); }
+
+    protected:
+        void sink_it_(const details::log_msg &msg) override
+        {
+            memory_buf_t formatted;
+            base_sink<Mutex>::formatter_->format(msg, formatted);
+            string_view_t str = string_view_t(formatted.data(), formatted.size());
+            QMetaObject::invokeMethod(
+                qt_object_,
+                meta_method_.c_str(),
+                Qt::AutoConnection,
+                Q_ARG(QString, QString::fromUtf8(str.data(), static_cast<int>(str.size())).trimmed()));
+        }
+
+        void flush_() override {}
+
+    private:
+        QObject    *qt_object_ = nullptr;
+        std::string meta_method_;
+    };
 
 #include "spdlog/details/null_mutex.h"
+
 #include <mutex>
-using qt_sink_mt = qt_sink<std::mutex>;
-using qt_sink_st = qt_sink<spdlog::details::null_mutex>;
+    using qt_sink_mt = qt_sink<std::mutex>;
+    using qt_sink_st = qt_sink<spdlog::details::null_mutex>;
 } // namespace sinks
 
 //
 // Factory functions
 //
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> qt_logger_mt(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append")
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger>
+qt_logger_mt(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append")
 {
     return Factory::template create<sinks::qt_sink_mt>(logger_name, qt_object, meta_method);
 }
 
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> qt_logger_st(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append")
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger>
+qt_logger_st(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append")
 {
     return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
 }
 
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_factory>
 inline std::shared_ptr<logger> qt_logger_mt(
-    const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText")
+    const std::string &logger_name,
+    QPlainTextEdit    *qt_object,
+    const std::string &meta_method = "appendPlainText")
 {
     return Factory::template create<sinks::qt_sink_mt>(logger_name, qt_object, meta_method);
 }
 
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_factory>
 inline std::shared_ptr<logger> qt_logger_st(
-    const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText")
+    const std::string &logger_name,
+    QPlainTextEdit    *qt_object,
+    const std::string &meta_method = "appendPlainText")
 {
     return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
 }
 
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> qt_logger_mt(const std::string &logger_name, QObject *qt_object, const std::string &meta_method)
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger>
+qt_logger_mt(const std::string &logger_name, QObject *qt_object, const std::string &meta_method)
 {
     return Factory::template create<sinks::qt_sink_mt>(logger_name, qt_object, meta_method);
 }
 
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> qt_logger_st(const std::string &logger_name, QObject *qt_object, const std::string &meta_method)
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger>
+qt_logger_st(const std::string &logger_name, QObject *qt_object, const std::string &meta_method)
 {
     return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
 }
diff --git a/include/spdlog/sinks/ringbuffer_sink.h b/include/spdlog/sinks/ringbuffer_sink.h
index 65e227aa5..a32db8cec 100644
--- a/include/spdlog/sinks/ringbuffer_sink.h
+++ b/include/spdlog/sinks/ringbuffer_sink.h
@@ -3,71 +3,68 @@
 
 #pragma once
 
-#include "spdlog/sinks/base_sink.h"
 #include "spdlog/details/circular_q.h"
 #include "spdlog/details/log_msg_buffer.h"
 #include "spdlog/details/null_mutex.h"
+#include "spdlog/sinks/base_sink.h"
 
 #include <mutex>
 #include <string>
 #include <vector>
 
-namespace spdlog {
-namespace sinks {
-/*
- * Ring buffer sink
- */
-template<typename Mutex>
-class ringbuffer_sink final : public base_sink<Mutex>
+namespace spdlog
 {
-public:
-    explicit ringbuffer_sink(size_t n_items)
-        : q_{n_items}
-    {}
-
-    std::vector<details::log_msg_buffer> last_raw(size_t lim = 0)
+namespace sinks
+{
+    /*
+     * Ring buffer sink
+     */
+    template <typename Mutex>
+    class ringbuffer_sink final : public base_sink<Mutex>
     {
-        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
-        auto items_available = q_.size();
-        auto n_items = lim > 0 ? (std::min)(lim, items_available) : items_available;
-        std::vector<details::log_msg_buffer> ret;
-        ret.reserve(n_items);
-        for (size_t i = (items_available - n_items); i < items_available; i++)
+    public:
+        explicit ringbuffer_sink(size_t n_items) : q_{n_items} {}
+
+        std::vector<details::log_msg_buffer> last_raw(size_t lim = 0)
         {
-            ret.push_back(q_.at(i));
+            std::lock_guard<Mutex>               lock(base_sink<Mutex>::mutex_);
+            auto                                 items_available = q_.size();
+            auto                                 n_items = lim > 0 ? (std::min)(lim, items_available) : items_available;
+            std::vector<details::log_msg_buffer> ret;
+            ret.reserve(n_items);
+            for(size_t i = (items_available - n_items); i < items_available; i++)
+            {
+                ret.push_back(q_.at(i));
+            }
+            return ret;
         }
-        return ret;
-    }
 
-    std::vector<std::string> last_formatted(size_t lim = 0)
-    {
-        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
-        auto items_available = q_.size();
-        auto n_items = lim > 0 ? (std::min)(lim, items_available) : items_available;
-        std::vector<std::string> ret;
-        ret.reserve(n_items);
-        for (size_t i = (items_available - n_items); i < items_available; i++)
+        std::vector<std::string> last_formatted(size_t lim = 0)
         {
-            memory_buf_t formatted;
-            base_sink<Mutex>::formatter_->format(q_.at(i), formatted);
-            ret.push_back(std::move(SPDLOG_BUF_TO_STRING(formatted)));
+            std::lock_guard<Mutex>   lock(base_sink<Mutex>::mutex_);
+            auto                     items_available = q_.size();
+            auto                     n_items         = lim > 0 ? (std::min)(lim, items_available) : items_available;
+            std::vector<std::string> ret;
+            ret.reserve(n_items);
+            for(size_t i = (items_available - n_items); i < items_available; i++)
+            {
+                memory_buf_t formatted;
+                base_sink<Mutex>::formatter_->format(q_.at(i), formatted);
+                ret.push_back(std::move(SPDLOG_BUF_TO_STRING(formatted)));
+            }
+            return ret;
         }
-        return ret;
-    }
 
-protected:
-    void sink_it_(const details::log_msg &msg) override
-    {
-        q_.push_back(details::log_msg_buffer{msg});
-    }
-    void flush_() override {}
+    protected:
+        void sink_it_(const details::log_msg &msg) override { q_.push_back(details::log_msg_buffer{msg}); }
+        void flush_() override {}
 
-private:
-    details::circular_q<details::log_msg_buffer> q_;
-};
+    private:
+        details::circular_q<details::log_msg_buffer> q_;
+    };
 
-using ringbuffer_sink_mt = ringbuffer_sink<std::mutex>;
-using ringbuffer_sink_st = ringbuffer_sink<details::null_mutex>;
+    using ringbuffer_sink_mt = ringbuffer_sink<std::mutex>;
+    using ringbuffer_sink_st = ringbuffer_sink<details::null_mutex>;
 
 } // namespace sinks
 
diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h
index cf8b9d5c6..cb341d4ed 100644
--- a/include/spdlog/sinks/rotating_file_sink-inl.h
+++ b/include/spdlog/sinks/rotating_file_sink-inl.h
@@ -4,149 +4,157 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/sinks/rotating_file_sink.h>
+#include <spdlog/sinks/rotating_file_sink.h>
 #endif
 
-#include <spdlog/common.h>
-
-#include <spdlog/details/file_helper.h>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/fmt/fmt.h>
-
 #include <cerrno>
 #include <chrono>
 #include <ctime>
 #include <mutex>
+#include <spdlog/common.h>
+#include <spdlog/details/file_helper.h>
+#include <spdlog/details/null_mutex.h>
+#include <spdlog/fmt/fmt.h>
 #include <string>
 #include <tuple>
 
-namespace spdlog {
-namespace sinks {
-
-template<typename Mutex>
-SPDLOG_INLINE rotating_file_sink<Mutex>::rotating_file_sink(
-    filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open, const file_event_handlers &event_handlers)
-    : base_filename_(std::move(base_filename))
-    , max_size_(max_size)
-    , max_files_(max_files)
-    , file_helper_{event_handlers}
+namespace spdlog
+{
+namespace sinks
 {
-    if (max_size == 0)
-    {
-        throw_spdlog_ex("rotating sink constructor: max_size arg cannot be zero");
-    }
 
-    if (max_files > 200000)
+    template <typename Mutex>
+    SPDLOG_INLINE rotating_file_sink<Mutex>::rotating_file_sink(
+        filename_t                 base_filename,
+        std::size_t                max_size,
+        std::size_t                max_files,
+        bool                       rotate_on_open,
+        const file_event_handlers &event_handlers) :
+        base_filename_(std::move(base_filename)),
+        max_size_(max_size),
+        max_files_(max_files),
+        file_helper_{event_handlers}
     {
-        throw_spdlog_ex("rotating sink constructor: max_files arg cannot exceed 200000");
+        if(max_size == 0)
+        {
+            throw_spdlog_ex("rotating sink constructor: max_size arg cannot be zero");
+        }
+
+        if(max_files > 200000)
+        {
+            throw_spdlog_ex("rotating sink constructor: max_files arg cannot exceed 200000");
+        }
+        file_helper_.open(calc_filename(base_filename_, 0));
+        current_size_ = file_helper_.size(); // expensive. called only once
+        if(rotate_on_open && current_size_ > 0)
+        {
+            rotate_();
+            current_size_ = 0;
+        }
     }
-    file_helper_.open(calc_filename(base_filename_, 0));
-    current_size_ = file_helper_.size(); // expensive. called only once
-    if (rotate_on_open && current_size_ > 0)
+
+    // calc filename according to index and file extension if exists.
+    // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
+    template <typename Mutex>
+    SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::calc_filename(const filename_t &filename, std::size_t index)
     {
-        rotate_();
-        current_size_ = 0;
+        if(index == 0u)
+        {
+            return filename;
+        }
+
+        filename_t basename, ext;
+        std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
+        return fmt_lib::format(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
     }
-}
 
-// calc filename according to index and file extension if exists.
-// e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
-template<typename Mutex>
-SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::calc_filename(const filename_t &filename, std::size_t index)
-{
-    if (index == 0u)
+    template <typename Mutex>
+    SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::filename()
     {
-        return filename;
+        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
+        return file_helper_.filename();
     }
 
-    filename_t basename, ext;
-    std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
-    return fmt_lib::format(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
-}
-
-template<typename Mutex>
-SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::filename()
-{
-    std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
-    return file_helper_.filename();
-}
-
-template<typename Mutex>
-SPDLOG_INLINE void rotating_file_sink<Mutex>::sink_it_(const details::log_msg &msg)
-{
-    memory_buf_t formatted;
-    base_sink<Mutex>::formatter_->format(msg, formatted);
-    auto new_size = current_size_ + formatted.size();
-
-    // rotate if the new estimated file size exceeds max size.
-    // rotate only if the real size > 0 to better deal with full disk (see issue #2261).
-    // we only check the real size when new_size > max_size_ because it is relatively expensive.
-    if (new_size > max_size_)
+    template <typename Mutex>
+    SPDLOG_INLINE void rotating_file_sink<Mutex>::sink_it_(const details::log_msg &msg)
     {
-        file_helper_.flush();
-        if (file_helper_.size() > 0)
+        memory_buf_t formatted;
+        base_sink<Mutex>::formatter_->format(msg, formatted);
+        auto new_size = current_size_ + formatted.size();
+
+        // rotate if the new estimated file size exceeds max size.
+        // rotate only if the real size > 0 to better deal with full disk (see issue #2261).
+        // we only check the real size when new_size > max_size_ because it is relatively expensive.
+        if(new_size > max_size_)
         {
-            rotate_();
-            new_size = formatted.size();
+            file_helper_.flush();
+            if(file_helper_.size() > 0)
+            {
+                rotate_();
+                new_size = formatted.size();
+            }
         }
+        file_helper_.write(formatted);
+        current_size_ = new_size;
     }
-    file_helper_.write(formatted);
-    current_size_ = new_size;
-}
 
-template<typename Mutex>
-SPDLOG_INLINE void rotating_file_sink<Mutex>::flush_()
-{
-    file_helper_.flush();
-}
-
-// Rotate files:
-// log.txt -> log.1.txt
-// log.1.txt -> log.2.txt
-// log.2.txt -> log.3.txt
-// log.3.txt -> delete
-template<typename Mutex>
-SPDLOG_INLINE void rotating_file_sink<Mutex>::rotate_()
-{
-    using details::os::filename_to_str;
-    using details::os::path_exists;
+    template <typename Mutex>
+    SPDLOG_INLINE void rotating_file_sink<Mutex>::flush_()
+    {
+        file_helper_.flush();
+    }
 
-    file_helper_.close();
-    for (auto i = max_files_; i > 0; --i)
+    // Rotate files:
+    // log.txt -> log.1.txt
+    // log.1.txt -> log.2.txt
+    // log.2.txt -> log.3.txt
+    // log.3.txt -> delete
+    template <typename Mutex>
+    SPDLOG_INLINE void rotating_file_sink<Mutex>::rotate_()
     {
-        filename_t src = calc_filename(base_filename_, i - 1);
-        if (!path_exists(src))
-        {
-            continue;
-        }
-        filename_t target = calc_filename(base_filename_, i);
+        using details::os::filename_to_str;
+        using details::os::path_exists;
 
-        if (!rename_file_(src, target))
+        file_helper_.close();
+        for(auto i = max_files_; i > 0; --i)
         {
-            // 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))
+            filename_t src = calc_filename(base_filename_, i - 1);
+            if(!path_exists(src))
             {
-                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);
+                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);
     }
-    file_helper_.reopen(true);
-}
 
-// delete the target if exists, and rename the src file  to target
-// return true on success, false otherwise.
-template<typename Mutex>
-SPDLOG_INLINE bool rotating_file_sink<Mutex>::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;
-}
+    // delete the target if exists, and rename the src file  to target
+    // return true on success, false otherwise.
+    template <typename Mutex>
+    SPDLOG_INLINE bool
+    rotating_file_sink<Mutex>::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;
+    }
 
 } // namespace sinks
 } // namespace spdlog
diff --git a/include/spdlog/sinks/rotating_file_sink.h b/include/spdlog/sinks/rotating_file_sink.h
index ce0d7b1ef..82e0f526f 100644
--- a/include/spdlog/sinks/rotating_file_sink.h
+++ b/include/spdlog/sinks/rotating_file_sink.h
@@ -3,55 +3,60 @@
 
 #pragma once
 
-#include <spdlog/sinks/base_sink.h>
+#include <chrono>
+#include <mutex>
 #include <spdlog/details/file_helper.h>
 #include <spdlog/details/null_mutex.h>
 #include <spdlog/details/synchronous_factory.h>
-
-#include <chrono>
-#include <mutex>
+#include <spdlog/sinks/base_sink.h>
 #include <string>
 
-namespace spdlog {
-namespace sinks {
-
-//
-// Rotating file sink based on size
-//
-template<typename Mutex>
-class rotating_file_sink final : public base_sink<Mutex>
+namespace spdlog
+{
+namespace sinks
 {
-public:
-    rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false,
-        const file_event_handlers &event_handlers = {});
-    static filename_t calc_filename(const filename_t &filename, std::size_t index);
-    filename_t filename();
 
-protected:
-    void sink_it_(const details::log_msg &msg) override;
-    void flush_() override;
+    //
+    // 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,
+            bool                       rotate_on_open = false,
+            const file_event_handlers &event_handlers = {});
+        static filename_t calc_filename(const filename_t &filename, std::size_t index);
+        filename_t        filename();
+
+    protected:
+        void sink_it_(const details::log_msg &msg) override;
+        void flush_() override;
 
-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_();
+    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_();
 
-    // 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);
+        // 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);
 
-    filename_t base_filename_;
-    std::size_t max_size_;
-    std::size_t max_files_;
-    std::size_t current_size_;
-    details::file_helper file_helper_;
-};
+        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>;
+    using rotating_file_sink_mt = rotating_file_sink<std::mutex>;
+    using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
 
 } // namespace sinks
 
@@ -59,17 +64,27 @@ using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
 // factory functions
 //
 
-template<typename Factory = spdlog::synchronous_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, bool rotate_on_open = false, const file_event_handlers &event_handlers = {})
+template <typename Factory = spdlog::synchronous_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,
+    bool                       rotate_on_open = false,
+    const file_event_handlers &event_handlers = {})
 {
     return Factory::template create<sinks::rotating_file_sink_mt>(
         logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers);
 }
 
-template<typename Factory = spdlog::synchronous_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, bool rotate_on_open = false, const file_event_handlers &event_handlers = {})
+template <typename Factory = spdlog::synchronous_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,
+    bool                       rotate_on_open = false,
+    const file_event_handlers &event_handlers = {})
 {
     return Factory::template create<sinks::rotating_file_sink_st>(
         logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers);
@@ -77,5 +92,5 @@ inline std::shared_ptr<logger> rotating_logger_st(const std::string &logger_name
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "rotating_file_sink-inl.h"
+#include "rotating_file_sink-inl.h"
 #endif
diff --git a/include/spdlog/sinks/sink-inl.h b/include/spdlog/sinks/sink-inl.h
index df07adda4..a8dd6a6c3 100644
--- a/include/spdlog/sinks/sink-inl.h
+++ b/include/spdlog/sinks/sink-inl.h
@@ -4,7 +4,7 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/sinks/sink.h>
+#include <spdlog/sinks/sink.h>
 #endif
 
 #include <spdlog/common.h>
diff --git a/include/spdlog/sinks/sink.h b/include/spdlog/sinks/sink.h
index 0a28cccc7..40850cbb1 100644
--- a/include/spdlog/sinks/sink.h
+++ b/include/spdlog/sinks/sink.h
@@ -6,30 +6,32 @@
 #include <spdlog/details/log_msg.h>
 #include <spdlog/formatter.h>
 
-namespace spdlog {
+namespace spdlog
+{
 
-namespace sinks {
-class SPDLOG_API sink
+namespace sinks
 {
-public:
-    virtual ~sink() = default;
-    virtual void log(const details::log_msg &msg) = 0;
-    virtual void flush() = 0;
-    virtual void set_pattern(const std::string &pattern) = 0;
-    virtual void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) = 0;
-
-    void set_level(level::level_enum log_level);
-    level::level_enum level() const;
-    bool should_log(level::level_enum msg_level) const;
-
-protected:
-    // sink log level - default is all
-    level_t level_{level::trace};
-};
+    class SPDLOG_API sink
+    {
+    public:
+        virtual ~sink()                                                               = default;
+        virtual void log(const details::log_msg &msg)                                 = 0;
+        virtual void flush()                                                          = 0;
+        virtual void set_pattern(const std::string &pattern)                          = 0;
+        virtual void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) = 0;
+
+        void              set_level(level::level_enum log_level);
+        level::level_enum level() const;
+        bool              should_log(level::level_enum msg_level) const;
+
+    protected:
+        // sink log level - default is all
+        level_t level_{level::trace};
+    };
 
 } // namespace sinks
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "sink-inl.h"
+#include "sink-inl.h"
 #endif
diff --git a/include/spdlog/sinks/stdout_color_sinks-inl.h b/include/spdlog/sinks/stdout_color_sinks-inl.h
index 066df1826..9deda50f1 100644
--- a/include/spdlog/sinks/stdout_color_sinks-inl.h
+++ b/include/spdlog/sinks/stdout_color_sinks-inl.h
@@ -4,33 +4,34 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/sinks/stdout_color_sinks.h>
+#include <spdlog/sinks/stdout_color_sinks.h>
 #endif
 
-#include <spdlog/logger.h>
 #include <spdlog/common.h>
+#include <spdlog/logger.h>
 
-namespace spdlog {
+namespace spdlog
+{
 
-template<typename Factory>
+template <typename Factory>
 SPDLOG_INLINE std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name, color_mode mode)
 {
     return Factory::template create<sinks::stdout_color_sink_mt>(logger_name, mode);
 }
 
-template<typename Factory>
+template <typename Factory>
 SPDLOG_INLINE std::shared_ptr<logger> stdout_color_st(const std::string &logger_name, color_mode mode)
 {
     return Factory::template create<sinks::stdout_color_sink_st>(logger_name, mode);
 }
 
-template<typename Factory>
+template <typename Factory>
 SPDLOG_INLINE std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name, color_mode mode)
 {
     return Factory::template create<sinks::stderr_color_sink_mt>(logger_name, mode);
 }
 
-template<typename Factory>
+template <typename Factory>
 SPDLOG_INLINE std::shared_ptr<logger> stderr_color_st(const std::string &logger_name, color_mode mode)
 {
     return Factory::template create<sinks::stderr_color_sink_st>(logger_name, mode);
diff --git a/include/spdlog/sinks/stdout_color_sinks.h b/include/spdlog/sinks/stdout_color_sinks.h
index 420b13ab4..227dea8ea 100644
--- a/include/spdlog/sinks/stdout_color_sinks.h
+++ b/include/spdlog/sinks/stdout_color_sinks.h
@@ -4,42 +4,44 @@
 #pragma once
 
 #ifdef _WIN32
-#    include <spdlog/sinks/wincolor_sink.h>
+#include <spdlog/sinks/wincolor_sink.h>
 #else
-#    include <spdlog/sinks/ansicolor_sink.h>
+#include <spdlog/sinks/ansicolor_sink.h>
 #endif
 
 #include <spdlog/details/synchronous_factory.h>
 
-namespace spdlog {
-namespace sinks {
+namespace spdlog
+{
+namespace sinks
+{
 #ifdef _WIN32
-using stdout_color_sink_mt = wincolor_stdout_sink_mt;
-using stdout_color_sink_st = wincolor_stdout_sink_st;
-using stderr_color_sink_mt = wincolor_stderr_sink_mt;
-using stderr_color_sink_st = wincolor_stderr_sink_st;
+    using stdout_color_sink_mt = wincolor_stdout_sink_mt;
+    using stdout_color_sink_st = wincolor_stdout_sink_st;
+    using stderr_color_sink_mt = wincolor_stderr_sink_mt;
+    using stderr_color_sink_st = wincolor_stderr_sink_st;
 #else
-using stdout_color_sink_mt = ansicolor_stdout_sink_mt;
-using stdout_color_sink_st = ansicolor_stdout_sink_st;
-using stderr_color_sink_mt = ansicolor_stderr_sink_mt;
-using stderr_color_sink_st = ansicolor_stderr_sink_st;
+    using stdout_color_sink_mt = ansicolor_stdout_sink_mt;
+    using stdout_color_sink_st = ansicolor_stdout_sink_st;
+    using stderr_color_sink_mt = ansicolor_stderr_sink_mt;
+    using stderr_color_sink_st = ansicolor_stderr_sink_st;
 #endif
 } // namespace sinks
 
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_factory>
 std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic);
 
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_factory>
 std::shared_ptr<logger> stdout_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic);
 
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_factory>
 std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic);
 
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_factory>
 std::shared_ptr<logger> stderr_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic);
 
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "stdout_color_sinks-inl.h"
+#include "stdout_color_sinks-inl.h"
 #endif
diff --git a/include/spdlog/sinks/stdout_sinks-inl.h b/include/spdlog/sinks/stdout_sinks-inl.h
index 756734bf6..06546bf7e 100644
--- a/include/spdlog/sinks/stdout_sinks-inl.h
+++ b/include/spdlog/sinks/stdout_sinks-inl.h
@@ -4,134 +4,135 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/sinks/stdout_sinks.h>
+#include <spdlog/sinks/stdout_sinks.h>
 #endif
 
+#include <memory>
 #include <spdlog/details/console_globals.h>
 #include <spdlog/pattern_formatter.h>
-#include <memory>
 
 #ifdef _WIN32
 // under windows using fwrite to non-binary stream results in \r\r\n (see issue #1675)
 // so instead we use ::FileWrite
-#    include <spdlog/details/windows_include.h>
-
-#    ifndef _USING_V110_SDK71_ // fileapi.h doesn't exist in winxp
-#        include <fileapi.h>   // WriteFile (..)
-#    endif
+#include <spdlog/details/windows_include.h>
 
-#    include <io.h>    // _get_osfhandle(..)
-#    include <stdio.h> // _fileno(..)
-#endif                 // WIN32
+#ifndef _USING_V110_SDK71_ // fileapi.h doesn't exist in winxp
+#include <fileapi.h>       // WriteFile (..)
+#endif
 
-namespace spdlog {
+#include <io.h>    // _get_osfhandle(..)
+#include <stdio.h> // _fileno(..)
+#endif             // WIN32
 
-namespace sinks {
+namespace spdlog
+{
 
-template<typename ConsoleMutex>
-SPDLOG_INLINE stdout_sink_base<ConsoleMutex>::stdout_sink_base(FILE *file)
-    : mutex_(ConsoleMutex::mutex())
-    , file_(file)
-    , formatter_(details::make_unique<spdlog::pattern_formatter>())
+namespace sinks
 {
+
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE stdout_sink_base<ConsoleMutex>::stdout_sink_base(FILE *file) :
+        mutex_(ConsoleMutex::mutex()), file_(file), formatter_(details::make_unique<spdlog::pattern_formatter>())
+    {
 #ifdef _WIN32
-    // get windows handle from the FILE* object
+        // get windows handle from the FILE* object
 
-    handle_ = reinterpret_cast<HANDLE>(::_get_osfhandle(::_fileno(file_)));
+        handle_ = reinterpret_cast<HANDLE>(::_get_osfhandle(::_fileno(file_)));
 
-    // don't throw to support cases where no console is attached,
-    // and let the log method to do nothing if (handle_ == INVALID_HANDLE_VALUE).
-    // throw only if non stdout/stderr target is requested (probably regular file and not console).
-    if (handle_ == INVALID_HANDLE_VALUE && file != stdout && file != stderr)
-    {
-        throw_spdlog_ex("spdlog::stdout_sink_base: _get_osfhandle() failed", errno);
-    }
+        // don't throw to support cases where no console is attached,
+        // and let the log method to do nothing if (handle_ == INVALID_HANDLE_VALUE).
+        // throw only if non stdout/stderr target is requested (probably regular file and not console).
+        if(handle_ == INVALID_HANDLE_VALUE && file != stdout && file != stderr)
+        {
+            throw_spdlog_ex("spdlog::stdout_sink_base: _get_osfhandle() failed", errno);
+        }
 #endif // WIN32
-}
-
-template<typename ConsoleMutex>
-SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &msg)
-{
-#ifdef _WIN32
-    if (handle_ == INVALID_HANDLE_VALUE)
-    {
-        return;
     }
-    std::lock_guard<mutex_t> lock(mutex_);
-    memory_buf_t formatted;
-    formatter_->format(msg, formatted);
-    ::fflush(file_); // flush in case there is something in this file_ already
-    auto size = static_cast<DWORD>(formatted.size());
-    DWORD bytes_written = 0;
-    bool ok = ::WriteFile(handle_, formatted.data(), size, &bytes_written, nullptr) != 0;
-    if (!ok)
+
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &msg)
     {
-        throw_spdlog_ex("stdout_sink_base: WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError()));
-    }
+#ifdef _WIN32
+        if(handle_ == INVALID_HANDLE_VALUE)
+        {
+            return;
+        }
+        std::lock_guard<mutex_t> lock(mutex_);
+        memory_buf_t             formatted;
+        formatter_->format(msg, formatted);
+        ::fflush(file_); // flush in case there is something in this file_ already
+        auto  size          = static_cast<DWORD>(formatted.size());
+        DWORD bytes_written = 0;
+        bool  ok            = ::WriteFile(handle_, formatted.data(), size, &bytes_written, nullptr) != 0;
+        if(!ok)
+        {
+            throw_spdlog_ex(
+                "stdout_sink_base: WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError()));
+        }
 #else
-    std::lock_guard<mutex_t> lock(mutex_);
-    memory_buf_t formatted;
-    formatter_->format(msg, formatted);
-    ::fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
-    ::fflush(file_); // flush every line to terminal
+        std::lock_guard<mutex_t> lock(mutex_);
+        memory_buf_t             formatted;
+        formatter_->format(msg, formatted);
+        ::fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
+        ::fflush(file_); // flush every line to terminal
 #endif // WIN32
-}
+    }
 
-template<typename ConsoleMutex>
-SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::flush()
-{
-    std::lock_guard<mutex_t> lock(mutex_);
-    fflush(file_);
-}
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::flush()
+    {
+        std::lock_guard<mutex_t> lock(mutex_);
+        fflush(file_);
+    }
 
-template<typename ConsoleMutex>
-SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::set_pattern(const std::string &pattern)
-{
-    std::lock_guard<mutex_t> lock(mutex_);
-    formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
-}
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::set_pattern(const std::string &pattern)
+    {
+        std::lock_guard<mutex_t> lock(mutex_);
+        formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
+    }
 
-template<typename ConsoleMutex>
-SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
-{
-    std::lock_guard<mutex_t> lock(mutex_);
-    formatter_ = std::move(sink_formatter);
-}
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
+    {
+        std::lock_guard<mutex_t> lock(mutex_);
+        formatter_ = std::move(sink_formatter);
+    }
 
-// stdout sink
-template<typename ConsoleMutex>
-SPDLOG_INLINE stdout_sink<ConsoleMutex>::stdout_sink()
-    : stdout_sink_base<ConsoleMutex>(stdout)
-{}
+    // stdout sink
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE stdout_sink<ConsoleMutex>::stdout_sink() : stdout_sink_base<ConsoleMutex>(stdout)
+    {
+    }
 
-// stderr sink
-template<typename ConsoleMutex>
-SPDLOG_INLINE stderr_sink<ConsoleMutex>::stderr_sink()
-    : stdout_sink_base<ConsoleMutex>(stderr)
-{}
+    // stderr sink
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE stderr_sink<ConsoleMutex>::stderr_sink() : stdout_sink_base<ConsoleMutex>(stderr)
+    {
+    }
 
 } // namespace sinks
 
 // factory methods
-template<typename Factory>
+template <typename Factory>
 SPDLOG_INLINE std::shared_ptr<logger> stdout_logger_mt(const std::string &logger_name)
 {
     return Factory::template create<sinks::stdout_sink_mt>(logger_name);
 }
 
-template<typename Factory>
+template <typename Factory>
 SPDLOG_INLINE std::shared_ptr<logger> stdout_logger_st(const std::string &logger_name)
 {
     return Factory::template create<sinks::stdout_sink_st>(logger_name);
 }
 
-template<typename Factory>
+template <typename Factory>
 SPDLOG_INLINE std::shared_ptr<logger> stderr_logger_mt(const std::string &logger_name)
 {
     return Factory::template create<sinks::stderr_sink_mt>(logger_name);
 }
 
-template<typename Factory>
+template <typename Factory>
 SPDLOG_INLINE std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name)
 {
     return Factory::template create<sinks::stderr_sink_st>(logger_name);
diff --git a/include/spdlog/sinks/stdout_sinks.h b/include/spdlog/sinks/stdout_sinks.h
index 6fdc0de34..d12be28c3 100644
--- a/include/spdlog/sinks/stdout_sinks.h
+++ b/include/spdlog/sinks/stdout_sinks.h
@@ -3,85 +3,87 @@
 
 #pragma once
 
+#include <cstdio>
 #include <spdlog/details/console_globals.h>
 #include <spdlog/details/synchronous_factory.h>
 #include <spdlog/sinks/sink.h>
-#include <cstdio>
 
 #ifdef _WIN32
-#    include <spdlog/details/windows_include.h>
+#include <spdlog/details/windows_include.h>
 #endif
 
-namespace spdlog {
-
-namespace sinks {
+namespace spdlog
+{
 
-template<typename ConsoleMutex>
-class stdout_sink_base : public sink
+namespace sinks
 {
-public:
-    using mutex_t = typename ConsoleMutex::mutex_t;
-    explicit stdout_sink_base(FILE *file);
-    ~stdout_sink_base() override = default;
 
-    stdout_sink_base(const stdout_sink_base &other) = delete;
-    stdout_sink_base(stdout_sink_base &&other) = delete;
+    template <typename ConsoleMutex>
+    class stdout_sink_base : public sink
+    {
+    public:
+        using mutex_t = typename ConsoleMutex::mutex_t;
+        explicit stdout_sink_base(FILE *file);
+        ~stdout_sink_base() override = default;
 
-    stdout_sink_base &operator=(const stdout_sink_base &other) = delete;
-    stdout_sink_base &operator=(stdout_sink_base &&other) = delete;
+        stdout_sink_base(const stdout_sink_base &other) = delete;
+        stdout_sink_base(stdout_sink_base &&other)      = delete;
 
-    void log(const details::log_msg &msg) override;
-    void flush() override;
-    void set_pattern(const std::string &pattern) override;
+        stdout_sink_base &operator=(const stdout_sink_base &other) = delete;
+        stdout_sink_base &operator=(stdout_sink_base &&other)      = delete;
 
-    void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
+        void log(const details::log_msg &msg) override;
+        void flush() override;
+        void set_pattern(const std::string &pattern) override;
 
-protected:
-    mutex_t &mutex_;
-    FILE *file_;
-    std::unique_ptr<spdlog::formatter> formatter_;
+        void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
+
+    protected:
+        mutex_t                           &mutex_;
+        FILE                              *file_;
+        std::unique_ptr<spdlog::formatter> formatter_;
 #ifdef _WIN32
-    HANDLE handle_;
+        HANDLE handle_;
 #endif // WIN32
-};
+    };
 
-template<typename ConsoleMutex>
-class stdout_sink : public stdout_sink_base<ConsoleMutex>
-{
-public:
-    stdout_sink();
-};
+    template <typename ConsoleMutex>
+    class stdout_sink : public stdout_sink_base<ConsoleMutex>
+    {
+    public:
+        stdout_sink();
+    };
 
-template<typename ConsoleMutex>
-class stderr_sink : public stdout_sink_base<ConsoleMutex>
-{
-public:
-    stderr_sink();
-};
+    template <typename ConsoleMutex>
+    class stderr_sink : public stdout_sink_base<ConsoleMutex>
+    {
+    public:
+        stderr_sink();
+    };
 
-using stdout_sink_mt = stdout_sink<details::console_mutex>;
-using stdout_sink_st = stdout_sink<details::console_nullmutex>;
+    using stdout_sink_mt = stdout_sink<details::console_mutex>;
+    using stdout_sink_st = stdout_sink<details::console_nullmutex>;
 
-using stderr_sink_mt = stderr_sink<details::console_mutex>;
-using stderr_sink_st = stderr_sink<details::console_nullmutex>;
+    using stderr_sink_mt = stderr_sink<details::console_mutex>;
+    using stderr_sink_st = stderr_sink<details::console_nullmutex>;
 
 } // namespace sinks
 
 // factory methods
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_factory>
 std::shared_ptr<logger> stdout_logger_mt(const std::string &logger_name);
 
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_factory>
 std::shared_ptr<logger> stdout_logger_st(const std::string &logger_name);
 
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_factory>
 std::shared_ptr<logger> stderr_logger_mt(const std::string &logger_name);
 
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_factory>
 std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name);
 
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "stdout_sinks-inl.h"
+#include "stdout_sinks-inl.h"
 #endif
diff --git a/include/spdlog/sinks/syslog_sink.h b/include/spdlog/sinks/syslog_sink.h
index 7c38fcb58..578a22573 100644
--- a/include/spdlog/sinks/syslog_sink.h
+++ b/include/spdlog/sinks/syslog_sink.h
@@ -3,107 +3,115 @@
 
 #pragma once
 
-#include <spdlog/sinks/base_sink.h>
+#include <array>
 #include <spdlog/details/null_mutex.h>
 #include <spdlog/details/synchronous_factory.h>
-
-#include <array>
+#include <spdlog/sinks/base_sink.h>
 #include <string>
 #include <syslog.h>
 
-namespace spdlog {
-namespace sinks {
-/**
- * Sink that write to syslog using the `syscall()` library call.
- */
-template<typename Mutex>
-class syslog_sink : public base_sink<Mutex>
+namespace spdlog
 {
-
-public:
-    syslog_sink(std::string ident, int syslog_option, int syslog_facility, bool enable_formatting)
-        : enable_formatting_{enable_formatting}
-        , syslog_levels_{{/* spdlog::level::trace      */ LOG_DEBUG,
-              /* spdlog::level::debug      */ LOG_DEBUG,
-              /* spdlog::level::info       */ LOG_INFO,
-              /* spdlog::level::warn       */ LOG_WARNING,
-              /* spdlog::level::err        */ LOG_ERR,
-              /* spdlog::level::critical   */ LOG_CRIT,
-              /* spdlog::level::off        */ LOG_INFO}}
-        , ident_{std::move(ident)}
-    {
-        // set ident to be program name if empty
-        ::openlog(ident_.empty() ? nullptr : ident_.c_str(), syslog_option, syslog_facility);
-    }
-
-    ~syslog_sink() override
-    {
-        ::closelog();
-    }
-
-    syslog_sink(const syslog_sink &) = delete;
-    syslog_sink &operator=(const syslog_sink &) = delete;
-
-protected:
-    void sink_it_(const details::log_msg &msg) override
+namespace sinks
+{
+    /**
+     * Sink that write to syslog using the `syscall()` library call.
+     */
+    template <typename Mutex>
+    class syslog_sink : public base_sink<Mutex>
     {
-        string_view_t payload;
-        memory_buf_t formatted;
-        if (enable_formatting_)
+    public:
+        syslog_sink(std::string ident, int syslog_option, int syslog_facility, bool enable_formatting) :
+            enable_formatting_{enable_formatting},
+            syslog_levels_{
+                {/* spdlog::level::trace      */ LOG_DEBUG,
+                 /* spdlog::level::debug      */ LOG_DEBUG,
+                 /* spdlog::level::info       */ LOG_INFO,
+                 /* spdlog::level::warn       */ LOG_WARNING,
+                 /* spdlog::level::err        */ LOG_ERR,
+                 /* spdlog::level::critical   */ LOG_CRIT,
+                 /* spdlog::level::off        */ LOG_INFO}},
+            ident_{std::move(ident)}
         {
-            base_sink<Mutex>::formatter_->format(msg, formatted);
-            payload = string_view_t(formatted.data(), formatted.size());
-        }
-        else
-        {
-            payload = msg.payload;
+            // set ident to be program name if empty
+            ::openlog(ident_.empty() ? nullptr : ident_.c_str(), syslog_option, syslog_facility);
         }
 
-        size_t length = payload.size();
-        // limit to max int
-        if (length > static_cast<size_t>(std::numeric_limits<int>::max()))
+        ~syslog_sink() override { ::closelog(); }
+
+        syslog_sink(const syslog_sink &)            = delete;
+        syslog_sink &operator=(const syslog_sink &) = delete;
+
+    protected:
+        void sink_it_(const details::log_msg &msg) override
         {
-            length = static_cast<size_t>(std::numeric_limits<int>::max());
+            string_view_t payload;
+            memory_buf_t  formatted;
+            if(enable_formatting_)
+            {
+                base_sink<Mutex>::formatter_->format(msg, formatted);
+                payload = string_view_t(formatted.data(), formatted.size());
+            }
+            else
+            {
+                payload = msg.payload;
+            }
+
+            size_t length = payload.size();
+            // limit to max int
+            if(length > static_cast<size_t>(std::numeric_limits<int>::max()))
+            {
+                length = static_cast<size_t>(std::numeric_limits<int>::max());
+            }
+
+            ::syslog(syslog_prio_from_level(msg), "%.*s", static_cast<int>(length), payload.data());
         }
 
-        ::syslog(syslog_prio_from_level(msg), "%.*s", static_cast<int>(length), payload.data());
-    }
-
-    void flush_() override {}
-    bool enable_formatting_ = false;
+        void flush_() override {}
+        bool enable_formatting_ = false;
 
-private:
-    using levels_array = std::array<int, 7>;
-    levels_array syslog_levels_;
-    // must store the ident because the man says openlog might use the pointer as
-    // is and not a string copy
-    const std::string ident_;
+    private:
+        using levels_array = std::array<int, 7>;
+        levels_array syslog_levels_;
+        // must store the ident because the man says openlog might use the pointer as
+        // is and not a string copy
+        const std::string ident_;
 
-    //
-    // Simply maps spdlog's log level to syslog priority level.
-    //
-    int syslog_prio_from_level(const details::log_msg &msg) const
-    {
-        return syslog_levels_.at(static_cast<levels_array::size_type>(msg.level));
-    }
-};
+        //
+        // Simply maps spdlog's log level to syslog priority level.
+        //
+        int syslog_prio_from_level(const details::log_msg &msg) const
+        {
+            return syslog_levels_.at(static_cast<levels_array::size_type>(msg.level));
+        }
+    };
 
-using syslog_sink_mt = syslog_sink<std::mutex>;
-using syslog_sink_st = syslog_sink<details::null_mutex>;
+    using syslog_sink_mt = syslog_sink<std::mutex>;
+    using syslog_sink_st = syslog_sink<details::null_mutex>;
 } // namespace sinks
 
 // Create and register a syslog logger
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> syslog_logger_mt(const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0,
-    int syslog_facility = LOG_USER, bool enable_formatting = false)
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> syslog_logger_mt(
+    const std::string &logger_name,
+    const std::string &syslog_ident      = "",
+    int                syslog_option     = 0,
+    int                syslog_facility   = LOG_USER,
+    bool               enable_formatting = false)
 {
-    return Factory::template create<sinks::syslog_sink_mt>(logger_name, syslog_ident, syslog_option, syslog_facility, enable_formatting);
+    return Factory::template create<sinks::syslog_sink_mt>(
+        logger_name, syslog_ident, syslog_option, syslog_facility, enable_formatting);
 }
 
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> syslog_logger_st(const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0,
-    int syslog_facility = LOG_USER, bool enable_formatting = false)
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger> syslog_logger_st(
+    const std::string &logger_name,
+    const std::string &syslog_ident      = "",
+    int                syslog_option     = 0,
+    int                syslog_facility   = LOG_USER,
+    bool               enable_formatting = false)
 {
-    return Factory::template create<sinks::syslog_sink_st>(logger_name, syslog_ident, syslog_option, syslog_facility, enable_formatting);
+    return Factory::template create<sinks::syslog_sink_st>(
+        logger_name, syslog_ident, syslog_option, syslog_facility, enable_formatting);
 }
 } // namespace spdlog
diff --git a/include/spdlog/sinks/systemd_sink.h b/include/spdlog/sinks/systemd_sink.h
index e1e97bffe..afed5f986 100644
--- a/include/spdlog/sinks/systemd_sink.h
+++ b/include/spdlog/sinks/systemd_sink.h
@@ -3,116 +3,116 @@
 
 #pragma once
 
-#include <spdlog/sinks/base_sink.h>
+#include <array>
 #include <spdlog/details/null_mutex.h>
 #include <spdlog/details/synchronous_factory.h>
-
-#include <array>
+#include <spdlog/sinks/base_sink.h>
 #ifndef SD_JOURNAL_SUPPRESS_LOCATION
-#    define SD_JOURNAL_SUPPRESS_LOCATION
+#define SD_JOURNAL_SUPPRESS_LOCATION
 #endif
 #include <systemd/sd-journal.h>
 
-namespace spdlog {
-namespace sinks {
-
-/**
- * Sink that write to systemd journal using the `sd_journal_send()` library call.
- */
-template<typename Mutex>
-class systemd_sink : public base_sink<Mutex>
+namespace spdlog
+{
+namespace sinks
 {
-public:
-    systemd_sink(std::string ident = "", bool enable_formatting = false)
-        : ident_{std::move(ident)}
-        , enable_formatting_{enable_formatting}
-        , syslog_levels_{{/* spdlog::level::trace      */ LOG_DEBUG,
-              /* spdlog::level::debug      */ LOG_DEBUG,
-              /* spdlog::level::info       */ LOG_INFO,
-              /* spdlog::level::warn       */ LOG_WARNING,
-              /* spdlog::level::err        */ LOG_ERR,
-              /* spdlog::level::critical   */ LOG_CRIT,
-              /* spdlog::level::off        */ LOG_INFO}}
-    {}
-
-    ~systemd_sink() override {}
-
-    systemd_sink(const systemd_sink &) = delete;
-    systemd_sink &operator=(const systemd_sink &) = delete;
-
-protected:
-    const std::string ident_;
-    bool enable_formatting_ = false;
-    using levels_array = std::array<int, 7>;
-    levels_array syslog_levels_;
-
-    void sink_it_(const details::log_msg &msg) override
+
+    /**
+     * Sink that write to systemd journal using the `sd_journal_send()` library call.
+     */
+    template <typename Mutex>
+    class systemd_sink : public base_sink<Mutex>
     {
-        int err;
-        string_view_t payload;
-        memory_buf_t formatted;
-        if (enable_formatting_)
-        {
-            base_sink<Mutex>::formatter_->format(msg, formatted);
-            payload = string_view_t(formatted.data(), formatted.size());
-        }
-        else
+    public:
+        systemd_sink(std::string ident = "", bool enable_formatting = false) :
+            ident_{std::move(ident)},
+            enable_formatting_{enable_formatting},
+            syslog_levels_{
+                {/* spdlog::level::trace      */ LOG_DEBUG,
+                 /* spdlog::level::debug      */ LOG_DEBUG,
+                 /* spdlog::level::info       */ LOG_INFO,
+                 /* spdlog::level::warn       */ LOG_WARNING,
+                 /* spdlog::level::err        */ LOG_ERR,
+                 /* spdlog::level::critical   */ LOG_CRIT,
+                 /* spdlog::level::off        */ LOG_INFO}}
         {
-            payload = msg.payload;
         }
 
-        size_t length = payload.size();
-        // limit to max int
-        if (length > static_cast<size_t>(std::numeric_limits<int>::max()))
-        {
-            length = static_cast<size_t>(std::numeric_limits<int>::max());
-        }
+        ~systemd_sink() override {}
 
-        const string_view_t syslog_identifier = ident_.empty() ? msg.logger_name : ident_;
+        systemd_sink(const systemd_sink &)            = delete;
+        systemd_sink &operator=(const systemd_sink &) = delete;
 
-        // Do not send source location if not available
-        if (msg.source.empty())
+    protected:
+        const std::string ident_;
+        bool              enable_formatting_ = false;
+        using levels_array                   = std::array<int, 7>;
+        levels_array syslog_levels_;
+
+        void sink_it_(const details::log_msg &msg) override
         {
-            // Note: function call inside '()' to avoid macro expansion
-            err = (sd_journal_send)("MESSAGE=%.*s", static_cast<int>(length), payload.data(), "PRIORITY=%d", syslog_level(msg.level),
+            int           err;
+            string_view_t payload;
+            memory_buf_t  formatted;
+            if(enable_formatting_)
+            {
+                base_sink<Mutex>::formatter_->format(msg, formatted);
+                payload = string_view_t(formatted.data(), formatted.size());
+            }
+            else
+            {
+                payload = msg.payload;
+            }
+
+            size_t length = payload.size();
+            // limit to max int
+            if(length > static_cast<size_t>(std::numeric_limits<int>::max()))
+            {
+                length = static_cast<size_t>(std::numeric_limits<int>::max());
+            }
+
+            const string_view_t syslog_identifier = ident_.empty() ? msg.logger_name : ident_;
+
+            // Do not send source location if not available
+            if(msg.source.empty())
+            {
+                // Note: function call inside '()' to avoid macro expansion
+                err = (sd_journal_send)("MESSAGE=%.*s", static_cast<int>(length), payload.data(), "PRIORITY=%d", syslog_level(msg.level),
                 "SYSLOG_IDENTIFIER=%.*s", static_cast<int>(syslog_identifier.size()), syslog_identifier.data(), nullptr);
-        }
-        else
-        {
-            err = (sd_journal_send)("MESSAGE=%.*s", static_cast<int>(length), payload.data(), "PRIORITY=%d", syslog_level(msg.level),
+            }
+            else
+            {
+                err = (sd_journal_send)("MESSAGE=%.*s", static_cast<int>(length), payload.data(), "PRIORITY=%d", syslog_level(msg.level),
                 "SYSLOG_IDENTIFIER=%.*s", static_cast<int>(syslog_identifier.size()), syslog_identifier.data(), "CODE_FILE=%s",
                 msg.source.filename, "CODE_LINE=%d", msg.source.line, "CODE_FUNC=%s", msg.source.funcname, nullptr);
-        }
+            }
 
-        if (err)
-        {
-            throw_spdlog_ex("Failed writing to systemd", errno);
+            if(err)
+            {
+                throw_spdlog_ex("Failed writing to systemd", errno);
+            }
         }
-    }
 
-    int syslog_level(level::level_enum l)
-    {
-        return syslog_levels_.at(static_cast<levels_array::size_type>(l));
-    }
+        int syslog_level(level::level_enum l) { return syslog_levels_.at(static_cast<levels_array::size_type>(l)); }
 
-    void flush_() override {}
-};
+        void flush_() override {}
+    };
 
-using systemd_sink_mt = systemd_sink<std::mutex>;
-using systemd_sink_st = systemd_sink<details::null_mutex>;
+    using systemd_sink_mt = systemd_sink<std::mutex>;
+    using systemd_sink_st = systemd_sink<details::null_mutex>;
 } // namespace sinks
 
 // Create and register a syslog logger
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> systemd_logger_mt(
-    const std::string &logger_name, const std::string &ident = "", bool enable_formatting = false)
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger>
+systemd_logger_mt(const std::string &logger_name, const std::string &ident = "", bool enable_formatting = false)
 {
     return Factory::template create<sinks::systemd_sink_mt>(logger_name, ident, enable_formatting);
 }
 
-template<typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> systemd_logger_st(
-    const std::string &logger_name, const std::string &ident = "", bool enable_formatting = false)
+template <typename Factory = spdlog::synchronous_factory>
+inline std::shared_ptr<logger>
+systemd_logger_st(const std::string &logger_name, const std::string &ident = "", bool enable_formatting = false)
 {
     return Factory::template create<sinks::systemd_sink_st>(logger_name, ident, enable_formatting);
 }
diff --git a/include/spdlog/sinks/tcp_sink.h b/include/spdlog/sinks/tcp_sink.h
index e0efb31d9..3c4cd6959 100644
--- a/include/spdlog/sinks/tcp_sink.h
+++ b/include/spdlog/sinks/tcp_sink.h
@@ -4,18 +4,18 @@
 #pragma once
 
 #include <spdlog/common.h>
-#include <spdlog/sinks/base_sink.h>
 #include <spdlog/details/null_mutex.h>
+#include <spdlog/sinks/base_sink.h>
 #ifdef _WIN32
-#    include <spdlog/details/tcp_client-windows.h>
+#include <spdlog/details/tcp_client-windows.h>
 #else
-#    include <spdlog/details/tcp_client.h>
+#include <spdlog/details/tcp_client.h>
 #endif
 
-#include <mutex>
-#include <string>
 #include <chrono>
 #include <functional>
+#include <mutex>
+#include <string>
 
 #pragma once
 
@@ -24,58 +24,56 @@
 // Will attempt to reconnect if connection drops.
 // If more complicated behaviour is needed (i.e get responses), you can inherit it and override the sink_it_ method.
 
-namespace spdlog {
-namespace sinks {
-
-struct tcp_sink_config
+namespace spdlog
+{
+namespace sinks
 {
-    std::string server_host;
-    int server_port;
-    bool lazy_connect = false; // if true connect on first log call instead of on construction
 
-    tcp_sink_config(std::string host, int port)
-        : server_host{std::move(host)}
-        , server_port{port}
-    {}
-};
+    struct tcp_sink_config
+    {
+        std::string server_host;
+        int         server_port;
+        bool        lazy_connect = false; // if true connect on first log call instead of on construction
 
-template<typename Mutex>
-class tcp_sink : public spdlog::sinks::base_sink<Mutex>
-{
-public:
-    // connect to tcp host/port or throw if failed
-    // host can be hostname or ip address
+        tcp_sink_config(std::string host, int port) : server_host{std::move(host)}, server_port{port} {}
+    };
 
-    explicit tcp_sink(tcp_sink_config sink_config)
-        : config_{std::move(sink_config)}
+    template <typename Mutex>
+    class tcp_sink : public spdlog::sinks::base_sink<Mutex>
     {
-        if (!config_.lazy_connect)
+    public:
+        // connect to tcp host/port or throw if failed
+        // host can be hostname or ip address
+
+        explicit tcp_sink(tcp_sink_config sink_config) : config_{std::move(sink_config)}
         {
-            this->client_.connect(config_.server_host, config_.server_port);
+            if(!config_.lazy_connect)
+            {
+                this->client_.connect(config_.server_host, config_.server_port);
+            }
         }
-    }
 
-    ~tcp_sink() override = default;
+        ~tcp_sink() override = default;
 
-protected:
-    void sink_it_(const spdlog::details::log_msg &msg) override
-    {
-        spdlog::memory_buf_t formatted;
-        spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
-        if (!client_.is_connected())
+    protected:
+        void sink_it_(const spdlog::details::log_msg &msg) override
         {
-            client_.connect(config_.server_host, config_.server_port);
+            spdlog::memory_buf_t formatted;
+            spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
+            if(!client_.is_connected())
+            {
+                client_.connect(config_.server_host, config_.server_port);
+            }
+            client_.send(formatted.data(), formatted.size());
         }
-        client_.send(formatted.data(), formatted.size());
-    }
 
-    void flush_() override {}
-    tcp_sink_config config_;
-    details::tcp_client client_;
-};
+        void                flush_() override {}
+        tcp_sink_config     config_;
+        details::tcp_client client_;
+    };
 
-using tcp_sink_mt = tcp_sink<std::mutex>;
-using tcp_sink_st = tcp_sink<spdlog::details::null_mutex>;
+    using tcp_sink_mt = tcp_sink<std::mutex>;
+    using tcp_sink_st = tcp_sink<spdlog::details::null_mutex>;
 
 } // namespace sinks
 } // namespace spdlog
diff --git a/include/spdlog/sinks/udp_sink.h b/include/spdlog/sinks/udp_sink.h
index ccbce2be3..ed5b2ab64 100644
--- a/include/spdlog/sinks/udp_sink.h
+++ b/include/spdlog/sinks/udp_sink.h
@@ -4,68 +4,65 @@
 #pragma once
 
 #include <spdlog/common.h>
-#include <spdlog/sinks/base_sink.h>
 #include <spdlog/details/null_mutex.h>
+#include <spdlog/sinks/base_sink.h>
 #ifdef _WIN32
-#    include <spdlog/details/udp_client-windows.h>
+#include <spdlog/details/udp_client-windows.h>
 #else
-#    include <spdlog/details/udp_client.h>
+#include <spdlog/details/udp_client.h>
 #endif
 
-#include <mutex>
-#include <string>
 #include <chrono>
 #include <functional>
+#include <mutex>
+#include <string>
 
 // Simple udp client sink
 // Sends formatted log via udp
 
-namespace spdlog {
-namespace sinks {
-
-struct udp_sink_config
+namespace spdlog
 {
-    std::string server_host;
-    uint16_t server_port;
-
-    udp_sink_config(std::string host, uint16_t port)
-        : server_host{std::move(host)}
-        , server_port{port}
-    {}
-};
-
-template<typename Mutex>
-class udp_sink : public spdlog::sinks::base_sink<Mutex>
+namespace sinks
 {
-public:
-    // host can be hostname or ip address
-    explicit udp_sink(udp_sink_config sink_config)
-        : client_{sink_config.server_host, sink_config.server_port}
-    {}
 
-    ~udp_sink() override = default;
+    struct udp_sink_config
+    {
+        std::string server_host;
+        uint16_t    server_port;
+
+        udp_sink_config(std::string host, uint16_t port) : server_host{std::move(host)}, server_port{port} {}
+    };
 
-protected:
-    void sink_it_(const spdlog::details::log_msg &msg) override
+    template <typename Mutex>
+    class udp_sink : public spdlog::sinks::base_sink<Mutex>
     {
-        spdlog::memory_buf_t formatted;
-        spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
-        client_.send(formatted.data(), formatted.size());
-    }
+    public:
+        // host can be hostname or ip address
+        explicit udp_sink(udp_sink_config sink_config) : client_{sink_config.server_host, sink_config.server_port} {}
+
+        ~udp_sink() override = default;
+
+    protected:
+        void sink_it_(const spdlog::details::log_msg &msg) override
+        {
+            spdlog::memory_buf_t formatted;
+            spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
+            client_.send(formatted.data(), formatted.size());
+        }
 
-    void flush_() override {}
-    details::udp_client client_;
-};
+        void                flush_() override {}
+        details::udp_client client_;
+    };
 
-using udp_sink_mt = udp_sink<std::mutex>;
-using udp_sink_st = udp_sink<spdlog::details::null_mutex>;
+    using udp_sink_mt = udp_sink<std::mutex>;
+    using udp_sink_st = udp_sink<spdlog::details::null_mutex>;
 
 } // namespace sinks
 
 //
 // factory functions
 //
-template<typename Factory = spdlog::synchronous_factory>
+template <typename Factory = spdlog::synchronous_factory>
 inline std::shared_ptr<logger> udp_logger_mt(const std::string &logger_name, sinks::udp_sink_config skin_config)
 {
     return Factory::template create<sinks::udp_sink_mt>(logger_name, skin_config);
diff --git a/include/spdlog/sinks/win_eventlog_sink.h b/include/spdlog/sinks/win_eventlog_sink.h
index 2f2aacb5c..cee2df781 100644
--- a/include/spdlog/sinks/win_eventlog_sink.h
+++ b/include/spdlog/sinks/win_eventlog_sink.h
@@ -30,260 +30,277 @@ Windows Registry Editor Version 5.00
 
 #pragma once
 
+#include <mutex>
 #include <spdlog/details/null_mutex.h>
-#include <spdlog/sinks/base_sink.h>
-
 #include <spdlog/details/windows_include.h>
-#include <winbase.h>
-
-#include <mutex>
+#include <spdlog/sinks/base_sink.h>
 #include <string>
 #include <vector>
+#include <winbase.h>
 
-namespace spdlog {
-namespace sinks {
-
-namespace win_eventlog {
-
-namespace internal {
-
-struct local_alloc_t
-{
-    HLOCAL hlocal_;
-
-    SPDLOG_CONSTEXPR local_alloc_t() SPDLOG_NOEXCEPT : hlocal_(nullptr) {}
-
-    local_alloc_t(local_alloc_t const &) = delete;
-    local_alloc_t &operator=(local_alloc_t const &) = delete;
-
-    ~local_alloc_t() SPDLOG_NOEXCEPT
-    {
-        if (hlocal_)
-        {
-            LocalFree(hlocal_);
-        }
-    }
-};
-
-/** Windows error */
-struct win32_error : public spdlog_ex
+namespace spdlog
 {
-    /** Formats an error report line: "user-message: error-code (system message)" */
-    static std::string format(std::string const &user_message, DWORD error_code = GetLastError())
-    {
-        std::string system_message;
-
-        local_alloc_t format_message_result{};
-        auto format_message_succeeded =
-            ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
-                error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&format_message_result.hlocal_, 0, nullptr);
-
-        if (format_message_succeeded && format_message_result.hlocal_)
-        {
-            system_message = fmt_lib::format(" ({})", (LPSTR)format_message_result.hlocal_);
-        }
-
-        return fmt_lib::format("{}: {}{}", user_message, error_code, system_message);
-    }
-
-    explicit win32_error(std::string const &func_name, DWORD error = GetLastError())
-        : spdlog_ex(format(func_name, error))
-    {}
-};
-
-/** Wrapper for security identifiers (SID) on Windows */
-struct sid_t
+namespace sinks
 {
-    std::vector<char> buffer_;
 
-public:
-    sid_t() {}
-
-    /** creates a wrapped SID copy */
-    static sid_t duplicate_sid(PSID psid)
+    namespace win_eventlog
     {
-        if (!::IsValidSid(psid))
-        {
-            throw_spdlog_ex("sid_t::sid_t(): invalid SID received");
-        }
-
-        auto const sid_length{::GetLengthSid(psid)};
 
-        sid_t result;
-        result.buffer_.resize(sid_length);
-        if (!::CopySid(sid_length, (PSID)result.as_sid(), psid))
+        namespace internal
         {
-            SPDLOG_THROW(win32_error("CopySid"));
-        }
 
-        return result;
-    }
+            struct local_alloc_t
+            {
+                HLOCAL hlocal_;
 
-    /** Retrieves pointer to the internal buffer contents as SID* */
-    SID *as_sid() const
-    {
-        return buffer_.empty() ? nullptr : (SID *)buffer_.data();
-    }
+                SPDLOG_CONSTEXPR local_alloc_t() SPDLOG_NOEXCEPT : hlocal_(nullptr) {}
 
-    /** Get SID for the current user */
-    static sid_t get_current_user_sid()
-    {
-        /* create and init RAII holder for process token */
-        struct process_token_t
-        {
-            HANDLE token_handle_ = INVALID_HANDLE_VALUE;
-            explicit process_token_t(HANDLE process)
-            {
-                if (!::OpenProcessToken(process, TOKEN_QUERY, &token_handle_))
+                local_alloc_t(local_alloc_t const &)            = delete;
+                local_alloc_t &operator=(local_alloc_t const &) = delete;
+
+                ~local_alloc_t() SPDLOG_NOEXCEPT
                 {
-                    SPDLOG_THROW(win32_error("OpenProcessToken"));
+                    if(hlocal_)
+                    {
+                        LocalFree(hlocal_);
+                    }
                 }
-            }
+            };
 
-            ~process_token_t()
+            /** Windows error */
+            struct win32_error : public spdlog_ex
             {
-                ::CloseHandle(token_handle_);
-            }
+                /** Formats an error report line: "user-message: error-code (system message)" */
+                static std::string format(std::string const &user_message, DWORD error_code = GetLastError())
+                {
+                    std::string system_message;
+
+                    local_alloc_t format_message_result{};
+                    auto          format_message_succeeded = ::FormatMessageA(
+                        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                        nullptr,
+                        error_code,
+                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                        (LPSTR) &format_message_result.hlocal_,
+                        0,
+                        nullptr);
+
+                    if(format_message_succeeded && format_message_result.hlocal_)
+                    {
+                        system_message = fmt_lib::format(" ({})", (LPSTR) format_message_result.hlocal_);
+                    }
+
+                    return fmt_lib::format("{}: {}{}", user_message, error_code, system_message);
+                }
 
-        } current_process_token(::GetCurrentProcess()); // GetCurrentProcess returns pseudohandle, no leak here!
+                explicit win32_error(std::string const &func_name, DWORD error = GetLastError()) :
+                    spdlog_ex(format(func_name, error))
+                {
+                }
+            };
 
-        // Get the required size, this is expected to fail with ERROR_INSUFFICIENT_BUFFER and return the token size
-        DWORD tusize = 0;
-        if (::GetTokenInformation(current_process_token.token_handle_, TokenUser, NULL, 0, &tusize))
-        {
-            SPDLOG_THROW(win32_error("GetTokenInformation should fail"));
-        }
+            /** Wrapper for security identifiers (SID) on Windows */
+            struct sid_t
+            {
+                std::vector<char> buffer_;
 
-        // get user token
-        std::vector<unsigned char> buffer(static_cast<size_t>(tusize));
-        if (!::GetTokenInformation(current_process_token.token_handle_, TokenUser, (LPVOID)buffer.data(), tusize, &tusize))
-        {
-            SPDLOG_THROW(win32_error("GetTokenInformation"));
-        }
+            public:
+                sid_t() {}
 
-        // create a wrapper of the SID data as stored in the user token
-        return sid_t::duplicate_sid(((TOKEN_USER *)buffer.data())->User.Sid);
-    }
-};
+                /** creates a wrapped SID copy */
+                static sid_t duplicate_sid(PSID psid)
+                {
+                    if(!::IsValidSid(psid))
+                    {
+                        throw_spdlog_ex("sid_t::sid_t(): invalid SID received");
+                    }
 
-struct eventlog
-{
-    static WORD get_event_type(details::log_msg const &msg)
-    {
-        switch (msg.level)
-        {
-        case level::trace:
-        case level::debug:
-            return EVENTLOG_SUCCESS;
+                    auto const sid_length{::GetLengthSid(psid)};
 
-        case level::info:
-            return EVENTLOG_INFORMATION_TYPE;
+                    sid_t result;
+                    result.buffer_.resize(sid_length);
+                    if(!::CopySid(sid_length, (PSID) result.as_sid(), psid))
+                    {
+                        SPDLOG_THROW(win32_error("CopySid"));
+                    }
 
-        case level::warn:
-            return EVENTLOG_WARNING_TYPE;
+                    return result;
+                }
 
-        case level::err:
-        case level::critical:
-        case level::off:
-            return EVENTLOG_ERROR_TYPE;
+                /** Retrieves pointer to the internal buffer contents as SID* */
+                SID *as_sid() const { return buffer_.empty() ? nullptr : (SID *) buffer_.data(); }
 
-        default:
-            return EVENTLOG_INFORMATION_TYPE;
-        }
-    }
+                /** Get SID for the current user */
+                static sid_t get_current_user_sid()
+                {
+                    /* create and init RAII holder for process token */
+                    struct process_token_t
+                    {
+                        HANDLE token_handle_ = INVALID_HANDLE_VALUE;
+                        explicit process_token_t(HANDLE process)
+                        {
+                            if(!::OpenProcessToken(process, TOKEN_QUERY, &token_handle_))
+                            {
+                                SPDLOG_THROW(win32_error("OpenProcessToken"));
+                            }
+                        }
+
+                        ~process_token_t() { ::CloseHandle(token_handle_); }
+
+                    } current_process_token(
+                        ::GetCurrentProcess()); // GetCurrentProcess returns pseudohandle, no leak here!
+
+                    // Get the required size, this is expected to fail with ERROR_INSUFFICIENT_BUFFER and return the
+                    // token size
+                    DWORD tusize = 0;
+                    if(::GetTokenInformation(current_process_token.token_handle_, TokenUser, NULL, 0, &tusize))
+                    {
+                        SPDLOG_THROW(win32_error("GetTokenInformation should fail"));
+                    }
+
+                    // get user token
+                    std::vector<unsigned char> buffer(static_cast<size_t>(tusize));
+                    if(!::GetTokenInformation(
+                           current_process_token.token_handle_, TokenUser, (LPVOID) buffer.data(), tusize, &tusize))
+                    {
+                        SPDLOG_THROW(win32_error("GetTokenInformation"));
+                    }
+
+                    // create a wrapper of the SID data as stored in the user token
+                    return sid_t::duplicate_sid(((TOKEN_USER *) buffer.data())->User.Sid);
+                }
+            };
 
-    static WORD get_event_category(details::log_msg const &msg)
-    {
-        return (WORD)msg.level;
-    }
-};
+            struct eventlog
+            {
+                static WORD get_event_type(details::log_msg const &msg)
+                {
+                    switch(msg.level)
+                    {
+                        case level::trace:
+                        case level::debug:
+                            return EVENTLOG_SUCCESS;
+
+                        case level::info:
+                            return EVENTLOG_INFORMATION_TYPE;
+
+                        case level::warn:
+                            return EVENTLOG_WARNING_TYPE;
+
+                        case level::err:
+                        case level::critical:
+                        case level::off:
+                            return EVENTLOG_ERROR_TYPE;
+
+                        default:
+                            return EVENTLOG_INFORMATION_TYPE;
+                    }
+                }
 
-} // namespace internal
+                static WORD get_event_category(details::log_msg const &msg) { return (WORD) msg.level; }
+            };
 
-/*
- * Windows Event Log sink
- */
-template<typename Mutex>
-class win_eventlog_sink : public base_sink<Mutex>
-{
-private:
-    HANDLE hEventLog_{NULL};
-    internal::sid_t current_user_sid_;
-    std::string source_;
-    WORD event_id_;
+        } // namespace internal
 
-    HANDLE event_log_handle()
-    {
-        if (!hEventLog_)
+        /*
+         * Windows Event Log sink
+         */
+        template <typename Mutex>
+        class win_eventlog_sink : public base_sink<Mutex>
         {
-            hEventLog_ = ::RegisterEventSourceA(nullptr, source_.c_str());
-            if (!hEventLog_ || hEventLog_ == (HANDLE)ERROR_ACCESS_DENIED)
+        private:
+            HANDLE          hEventLog_{NULL};
+            internal::sid_t current_user_sid_;
+            std::string     source_;
+            WORD            event_id_;
+
+            HANDLE event_log_handle()
             {
-                SPDLOG_THROW(internal::win32_error("RegisterEventSource"));
-            }
-        }
+                if(!hEventLog_)
+                {
+                    hEventLog_ = ::RegisterEventSourceA(nullptr, source_.c_str());
+                    if(!hEventLog_ || hEventLog_ == (HANDLE) ERROR_ACCESS_DENIED)
+                    {
+                        SPDLOG_THROW(internal::win32_error("RegisterEventSource"));
+                    }
+                }
 
-        return hEventLog_;
-    }
+                return hEventLog_;
+            }
 
-protected:
-    void sink_it_(const details::log_msg &msg) override
-    {
-        using namespace internal;
+        protected:
+            void sink_it_(const details::log_msg &msg) override
+            {
+                using namespace internal;
 
-        bool succeeded;
-        memory_buf_t formatted;
-        base_sink<Mutex>::formatter_->format(msg, formatted);
-        formatted.push_back('\0');
+                bool         succeeded;
+                memory_buf_t formatted;
+                base_sink<Mutex>::formatter_->format(msg, formatted);
+                formatted.push_back('\0');
 
 #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
-        wmemory_buf_t buf;
-        details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), buf);
-
-        LPCWSTR lp_wstr = buf.data();
-        succeeded = static_cast<bool>(::ReportEventW(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
-            current_user_sid_.as_sid(), 1, 0, &lp_wstr, nullptr));
+                wmemory_buf_t buf;
+                details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), buf);
+
+                LPCWSTR lp_wstr = buf.data();
+                succeeded       = static_cast<bool>(::ReportEventW(
+                    event_log_handle(),
+                    eventlog::get_event_type(msg),
+                    eventlog::get_event_category(msg),
+                    event_id_,
+                    current_user_sid_.as_sid(),
+                    1,
+                    0,
+                    &lp_wstr,
+                    nullptr));
 #else
-        LPCSTR lp_str = formatted.data();
-        succeeded = static_cast<bool>(::ReportEventA(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
-            current_user_sid_.as_sid(), 1, 0, &lp_str, nullptr));
+                LPCSTR lp_str = formatted.data();
+                succeeded     = static_cast<bool>(::ReportEventA(
+                    event_log_handle(),
+                    eventlog::get_event_type(msg),
+                    eventlog::get_event_category(msg),
+                    event_id_,
+                    current_user_sid_.as_sid(),
+                    1,
+                    0,
+                    &lp_str,
+                    nullptr));
 #endif
 
-        if (!succeeded)
-        {
-            SPDLOG_THROW(win32_error("ReportEvent"));
-        }
-    }
+                if(!succeeded)
+                {
+                    SPDLOG_THROW(win32_error("ReportEvent"));
+                }
+            }
 
-    void flush_() override {}
+            void flush_() override {}
 
-public:
-    win_eventlog_sink(std::string const &source, WORD event_id = 1000 /* according to mscoree.dll */)
-        : source_(source)
-        , event_id_(event_id)
-    {
-        try
-        {
-            current_user_sid_ = internal::sid_t::get_current_user_sid();
-        }
-        catch (...)
-        {
-            // get_current_user_sid() is unlikely to fail and if it does, we can still proceed without
-            // current_user_sid but in the event log the record will have no user name
-        }
-    }
+        public:
+            win_eventlog_sink(std::string const &source, WORD event_id = 1000 /* according to mscoree.dll */) :
+                source_(source), event_id_(event_id)
+            {
+                try
+                {
+                    current_user_sid_ = internal::sid_t::get_current_user_sid();
+                }
+                catch(...)
+                {
+                    // get_current_user_sid() is unlikely to fail and if it does, we can still proceed without
+                    // current_user_sid but in the event log the record will have no user name
+                }
+            }
 
-    ~win_eventlog_sink()
-    {
-        if (hEventLog_)
-            DeregisterEventSource(hEventLog_);
-    }
-};
+            ~win_eventlog_sink()
+            {
+                if(hEventLog_)
+                    DeregisterEventSource(hEventLog_);
+            }
+        };
 
-} // namespace win_eventlog
+    } // namespace win_eventlog
 
-using win_eventlog_sink_mt = win_eventlog::win_eventlog_sink<std::mutex>;
-using win_eventlog_sink_st = win_eventlog::win_eventlog_sink<details::null_mutex>;
+    using win_eventlog_sink_mt = win_eventlog::win_eventlog_sink<std::mutex>;
+    using win_eventlog_sink_st = win_eventlog::win_eventlog_sink<details::null_mutex>;
 
 } // namespace sinks
 } // namespace spdlog
diff --git a/include/spdlog/sinks/wincolor_sink-inl.h b/include/spdlog/sinks/wincolor_sink-inl.h
index 8311929e4..18b033368 100644
--- a/include/spdlog/sinks/wincolor_sink-inl.h
+++ b/include/spdlog/sinks/wincolor_sink-inl.h
@@ -4,172 +4,176 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/sinks/wincolor_sink.h>
+#include <spdlog/sinks/wincolor_sink.h>
 #endif
 
-#include <spdlog/details/windows_include.h>
-#include <wincon.h>
-
 #include <spdlog/common.h>
+#include <spdlog/details/windows_include.h>
 #include <spdlog/pattern_formatter.h>
+#include <wincon.h>
 
-namespace spdlog {
-namespace sinks {
-template<typename ConsoleMutex>
-SPDLOG_INLINE wincolor_sink<ConsoleMutex>::wincolor_sink(void *out_handle, color_mode mode)
-    : out_handle_(out_handle)
-    , mutex_(ConsoleMutex::mutex())
-    , formatter_(details::make_unique<spdlog::pattern_formatter>())
-{
-
-    set_color_mode_impl(mode);
-    // set level colors
-    colors_[level::trace] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;     // white
-    colors_[level::debug] = FOREGROUND_GREEN | FOREGROUND_BLUE;                      // cyan
-    colors_[level::info] = FOREGROUND_GREEN;                                         // green
-    colors_[level::warn] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; // intense yellow
-    colors_[level::err] = FOREGROUND_RED | FOREGROUND_INTENSITY;                     // intense red
-    colors_[level::critical] =
-        BACKGROUND_RED | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; // intense white on red background
-    colors_[level::off] = 0;
-}
-
-template<typename ConsoleMutex>
-SPDLOG_INLINE wincolor_sink<ConsoleMutex>::~wincolor_sink()
+namespace spdlog
 {
-    this->flush();
-}
-
-// change the color for the given level
-template<typename ConsoleMutex>
-void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color(level::level_enum level, std::uint16_t color)
+namespace sinks
 {
-    std::lock_guard<mutex_t> lock(mutex_);
-    colors_[static_cast<size_t>(level)] = color;
-}
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE wincolor_sink<ConsoleMutex>::wincolor_sink(void *out_handle, color_mode mode) :
+        out_handle_(out_handle),
+        mutex_(ConsoleMutex::mutex()),
+        formatter_(details::make_unique<spdlog::pattern_formatter>())
+    {
+        set_color_mode_impl(mode);
+        // set level colors
+        colors_[level::trace]    = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;      // white
+        colors_[level::debug]    = FOREGROUND_GREEN | FOREGROUND_BLUE;                       // cyan
+        colors_[level::info]     = FOREGROUND_GREEN;                                         // green
+        colors_[level::warn]     = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; // intense yellow
+        colors_[level::err]      = FOREGROUND_RED | FOREGROUND_INTENSITY;                    // intense red
+        colors_[level::critical] = BACKGROUND_RED | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
+                                   FOREGROUND_INTENSITY; // intense white on red background
+        colors_[level::off] = 0;
+    }
 
-template<typename ConsoleMutex>
-void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
-{
-    if (out_handle_ == nullptr || out_handle_ == INVALID_HANDLE_VALUE)
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE wincolor_sink<ConsoleMutex>::~wincolor_sink()
     {
-        return;
+        this->flush();
     }
 
-    std::lock_guard<mutex_t> lock(mutex_);
-    msg.color_range_start = 0;
-    msg.color_range_end = 0;
-    memory_buf_t formatted;
-    formatter_->format(msg, formatted);
-    if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
+    // change the color for the given level
+    template <typename ConsoleMutex>
+    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color(level::level_enum level, std::uint16_t color)
     {
-        // before color range
-        print_range_(formatted, 0, msg.color_range_start);
-        // in color range
-        auto orig_attribs = static_cast<WORD>(set_foreground_color_(colors_[static_cast<size_t>(msg.level)]));
-        print_range_(formatted, msg.color_range_start, msg.color_range_end);
-        // reset to orig colors
-        ::SetConsoleTextAttribute(static_cast<HANDLE>(out_handle_), orig_attribs);
-        print_range_(formatted, msg.color_range_end, formatted.size());
+        std::lock_guard<mutex_t> lock(mutex_);
+        colors_[static_cast<size_t>(level)] = color;
     }
-    else // print without colors if color range is invalid (or color is disabled)
+
+    template <typename ConsoleMutex>
+    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
     {
-        write_to_file_(formatted);
+        if(out_handle_ == nullptr || out_handle_ == INVALID_HANDLE_VALUE)
+        {
+            return;
+        }
+
+        std::lock_guard<mutex_t> lock(mutex_);
+        msg.color_range_start = 0;
+        msg.color_range_end   = 0;
+        memory_buf_t formatted;
+        formatter_->format(msg, formatted);
+        if(should_do_colors_ && msg.color_range_end > msg.color_range_start)
+        {
+            // before color range
+            print_range_(formatted, 0, msg.color_range_start);
+            // in color range
+            auto orig_attribs = static_cast<WORD>(set_foreground_color_(colors_[static_cast<size_t>(msg.level)]));
+            print_range_(formatted, msg.color_range_start, msg.color_range_end);
+            // reset to orig colors
+            ::SetConsoleTextAttribute(static_cast<HANDLE>(out_handle_), orig_attribs);
+            print_range_(formatted, msg.color_range_end, formatted.size());
+        }
+        else // print without colors if color range is invalid (or color is disabled)
+        {
+            write_to_file_(formatted);
+        }
     }
-}
 
-template<typename ConsoleMutex>
-void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::flush()
-{
-    // windows console always flushed?
-}
+    template <typename ConsoleMutex>
+    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::flush()
+    {
+        // windows console always flushed?
+    }
 
-template<typename ConsoleMutex>
-void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_pattern(const std::string &pattern)
-{
-    std::lock_guard<mutex_t> lock(mutex_);
-    formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
-}
+    template <typename ConsoleMutex>
+    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_pattern(const std::string &pattern)
+    {
+        std::lock_guard<mutex_t> lock(mutex_);
+        formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
+    }
 
-template<typename ConsoleMutex>
-void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
-{
-    std::lock_guard<mutex_t> lock(mutex_);
-    formatter_ = std::move(sink_formatter);
-}
+    template <typename ConsoleMutex>
+    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
+    {
+        std::lock_guard<mutex_t> lock(mutex_);
+        formatter_ = std::move(sink_formatter);
+    }
 
-template<typename ConsoleMutex>
-void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
-{
-    std::lock_guard<mutex_t> lock(mutex_);
-    set_color_mode_impl(mode);
-}
+    template <typename ConsoleMutex>
+    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
+    {
+        std::lock_guard<mutex_t> lock(mutex_);
+        set_color_mode_impl(mode);
+    }
 
-template<typename ConsoleMutex>
-void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode_impl(color_mode mode)
-{
-    if (mode == color_mode::automatic)
+    template <typename ConsoleMutex>
+    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode_impl(color_mode mode)
     {
-        // should do colors only if out_handle_  points to actual console.
-        DWORD console_mode;
-        bool in_console = ::GetConsoleMode(static_cast<HANDLE>(out_handle_), &console_mode) != 0;
-        should_do_colors_ = in_console;
+        if(mode == color_mode::automatic)
+        {
+            // should do colors only if out_handle_  points to actual console.
+            DWORD console_mode;
+            bool  in_console  = ::GetConsoleMode(static_cast<HANDLE>(out_handle_), &console_mode) != 0;
+            should_do_colors_ = in_console;
+        }
+        else
+        {
+            should_do_colors_ = mode == color_mode::always ? true : false;
+        }
     }
-    else
+
+    // set foreground color and return the orig console attributes (for resetting later)
+    template <typename ConsoleMutex>
+    std::uint16_t SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_foreground_color_(std::uint16_t attribs)
     {
-        should_do_colors_ = mode == color_mode::always ? true : false;
+        CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
+        if(!::GetConsoleScreenBufferInfo(static_cast<HANDLE>(out_handle_), &orig_buffer_info))
+        {
+            // just return white if failed getting console info
+            return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+        }
+
+        // change only the foreground bits (lowest 4 bits)
+        auto new_attribs = static_cast<WORD>(attribs) | (orig_buffer_info.wAttributes & 0xfff0);
+        auto ignored     = ::SetConsoleTextAttribute(static_cast<HANDLE>(out_handle_), static_cast<WORD>(new_attribs));
+        (void) (ignored);
+        return static_cast<std::uint16_t>(orig_buffer_info.wAttributes); // return orig attribs
     }
-}
 
-// set foreground color and return the orig console attributes (for resetting later)
-template<typename ConsoleMutex>
-std::uint16_t SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_foreground_color_(std::uint16_t attribs)
-{
-    CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
-    if (!::GetConsoleScreenBufferInfo(static_cast<HANDLE>(out_handle_), &orig_buffer_info))
+    // print a range of formatted message to console
+    template <typename ConsoleMutex>
+    void SPDLOG_INLINE
+    wincolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
     {
-        // just return white if failed getting console info
-        return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+        if(end > start)
+        {
+            auto size = static_cast<DWORD>(end - start);
+            auto ignored =
+                ::WriteConsoleA(static_cast<HANDLE>(out_handle_), formatted.data() + start, size, nullptr, nullptr);
+            (void) (ignored);
+        }
     }
 
-    // change only the foreground bits (lowest 4 bits)
-    auto new_attribs = static_cast<WORD>(attribs) | (orig_buffer_info.wAttributes & 0xfff0);
-    auto ignored = ::SetConsoleTextAttribute(static_cast<HANDLE>(out_handle_), static_cast<WORD>(new_attribs));
-    (void)(ignored);
-    return static_cast<std::uint16_t>(orig_buffer_info.wAttributes); // return orig attribs
-}
+    template <typename ConsoleMutex>
+    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::write_to_file_(const memory_buf_t &formatted)
+    {
+        auto  size          = static_cast<DWORD>(formatted.size());
+        DWORD bytes_written = 0;
+        auto  ignored = ::WriteFile(static_cast<HANDLE>(out_handle_), formatted.data(), size, &bytes_written, nullptr);
+        (void) (ignored);
+    }
 
-// print a range of formatted message to console
-template<typename ConsoleMutex>
-void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
-{
-    if (end > start)
+    // wincolor_stdout_sink
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE wincolor_stdout_sink<ConsoleMutex>::wincolor_stdout_sink(color_mode mode) :
+        wincolor_sink<ConsoleMutex>(::GetStdHandle(STD_OUTPUT_HANDLE), mode)
     {
-        auto size = static_cast<DWORD>(end - start);
-        auto ignored = ::WriteConsoleA(static_cast<HANDLE>(out_handle_), formatted.data() + start, size, nullptr, nullptr);
-        (void)(ignored);
     }
-}
 
-template<typename ConsoleMutex>
-void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::write_to_file_(const memory_buf_t &formatted)
-{
-    auto size = static_cast<DWORD>(formatted.size());
-    DWORD bytes_written = 0;
-    auto ignored = ::WriteFile(static_cast<HANDLE>(out_handle_), formatted.data(), size, &bytes_written, nullptr);
-    (void)(ignored);
-}
-
-// wincolor_stdout_sink
-template<typename ConsoleMutex>
-SPDLOG_INLINE wincolor_stdout_sink<ConsoleMutex>::wincolor_stdout_sink(color_mode mode)
-    : wincolor_sink<ConsoleMutex>(::GetStdHandle(STD_OUTPUT_HANDLE), mode)
-{}
-
-// wincolor_stderr_sink
-template<typename ConsoleMutex>
-SPDLOG_INLINE wincolor_stderr_sink<ConsoleMutex>::wincolor_stderr_sink(color_mode mode)
-    : wincolor_sink<ConsoleMutex>(::GetStdHandle(STD_ERROR_HANDLE), mode)
-{}
+    // wincolor_stderr_sink
+    template <typename ConsoleMutex>
+    SPDLOG_INLINE wincolor_stderr_sink<ConsoleMutex>::wincolor_stderr_sink(color_mode mode) :
+        wincolor_sink<ConsoleMutex>(::GetStdHandle(STD_ERROR_HANDLE), mode)
+    {
+    }
 } // namespace sinks
 } // namespace spdlog
diff --git a/include/spdlog/sinks/wincolor_sink.h b/include/spdlog/sinks/wincolor_sink.h
index 9b030fc13..43e6b575f 100644
--- a/include/spdlog/sinks/wincolor_sink.h
+++ b/include/spdlog/sinks/wincolor_sink.h
@@ -3,83 +3,84 @@
 
 #pragma once
 
+#include <array>
+#include <cstdint>
+#include <memory>
+#include <mutex>
 #include <spdlog/common.h>
 #include <spdlog/details/console_globals.h>
 #include <spdlog/details/null_mutex.h>
 #include <spdlog/sinks/sink.h>
-
-#include <memory>
-#include <mutex>
 #include <string>
-#include <array>
-#include <cstdint>
 
-namespace spdlog {
-namespace sinks {
-/*
- * Windows color console sink. Uses WriteConsoleA to write to the console with
- * colors
- */
-template<typename ConsoleMutex>
-class wincolor_sink : public sink
+namespace spdlog
 {
-public:
-    wincolor_sink(void *out_handle, color_mode mode);
-    ~wincolor_sink() override;
-
-    wincolor_sink(const wincolor_sink &other) = delete;
-    wincolor_sink &operator=(const wincolor_sink &other) = delete;
-
-    // change the color for the given level
-    void set_color(level::level_enum level, std::uint16_t color);
-    void log(const details::log_msg &msg) final override;
-    void flush() final override;
-    void set_pattern(const std::string &pattern) override final;
-    void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override final;
-    void set_color_mode(color_mode mode);
-
-protected:
-    using mutex_t = typename ConsoleMutex::mutex_t;
-    void *out_handle_;
-    mutex_t &mutex_;
-    bool should_do_colors_;
-    std::unique_ptr<spdlog::formatter> formatter_;
-    std::array<std::uint16_t, level::n_levels> colors_;
-
-    // set foreground color and return the orig console attributes (for resetting later)
-    std::uint16_t set_foreground_color_(std::uint16_t attribs);
-
-    // print a range of formatted message to console
-    void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
-
-    // in case we are redirected to file (not in console mode)
-    void write_to_file_(const memory_buf_t &formatted);
-
-    void set_color_mode_impl(color_mode mode);
-};
-
-template<typename ConsoleMutex>
-class wincolor_stdout_sink : public wincolor_sink<ConsoleMutex>
-{
-public:
-    explicit wincolor_stdout_sink(color_mode mode = color_mode::automatic);
-};
-
-template<typename ConsoleMutex>
-class wincolor_stderr_sink : public wincolor_sink<ConsoleMutex>
+namespace sinks
 {
-public:
-    explicit wincolor_stderr_sink(color_mode mode = color_mode::automatic);
-};
-
-using wincolor_stdout_sink_mt = wincolor_stdout_sink<details::console_mutex>;
-using wincolor_stdout_sink_st = wincolor_stdout_sink<details::console_nullmutex>;
-
-using wincolor_stderr_sink_mt = wincolor_stderr_sink<details::console_mutex>;
-using wincolor_stderr_sink_st = wincolor_stderr_sink<details::console_nullmutex>;
+    /*
+     * Windows color console sink. Uses WriteConsoleA to write to the console with
+     * colors
+     */
+    template <typename ConsoleMutex>
+    class wincolor_sink : public sink
+    {
+    public:
+        wincolor_sink(void *out_handle, color_mode mode);
+        ~wincolor_sink() override;
+
+        wincolor_sink(const wincolor_sink &other)            = delete;
+        wincolor_sink &operator=(const wincolor_sink &other) = delete;
+
+        // change the color for the given level
+        void set_color(level::level_enum level, std::uint16_t color);
+        void log(const details::log_msg &msg) final override;
+        void flush() final override;
+        void set_pattern(const std::string &pattern) override final;
+        void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override final;
+        void set_color_mode(color_mode mode);
+
+    protected:
+        using mutex_t = typename ConsoleMutex::mutex_t;
+        void                                      *out_handle_;
+        mutex_t                                   &mutex_;
+        bool                                       should_do_colors_;
+        std::unique_ptr<spdlog::formatter>         formatter_;
+        std::array<std::uint16_t, level::n_levels> colors_;
+
+        // set foreground color and return the orig console attributes (for resetting later)
+        std::uint16_t set_foreground_color_(std::uint16_t attribs);
+
+        // print a range of formatted message to console
+        void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
+
+        // in case we are redirected to file (not in console mode)
+        void write_to_file_(const memory_buf_t &formatted);
+
+        void set_color_mode_impl(color_mode mode);
+    };
+
+    template <typename ConsoleMutex>
+    class wincolor_stdout_sink : public wincolor_sink<ConsoleMutex>
+    {
+    public:
+        explicit wincolor_stdout_sink(color_mode mode = color_mode::automatic);
+    };
+
+    template <typename ConsoleMutex>
+    class wincolor_stderr_sink : public wincolor_sink<ConsoleMutex>
+    {
+    public:
+        explicit wincolor_stderr_sink(color_mode mode = color_mode::automatic);
+    };
+
+    using wincolor_stdout_sink_mt = wincolor_stdout_sink<details::console_mutex>;
+    using wincolor_stdout_sink_st = wincolor_stdout_sink<details::console_nullmutex>;
+
+    using wincolor_stderr_sink_mt = wincolor_stderr_sink<details::console_mutex>;
+    using wincolor_stderr_sink_st = wincolor_stderr_sink<details::console_nullmutex>;
 } // namespace sinks
 } // namespace spdlog
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "wincolor_sink-inl.h"
+#include "wincolor_sink-inl.h"
 #endif
diff --git a/include/spdlog/spdlog-inl.h b/include/spdlog/spdlog-inl.h
index 708399c19..f4adac1e6 100644
--- a/include/spdlog/spdlog-inl.h
+++ b/include/spdlog/spdlog-inl.h
@@ -4,13 +4,14 @@
 #pragma once
 
 #ifndef SPDLOG_HEADER_ONLY
-#    include <spdlog/spdlog.h>
+#include <spdlog/spdlog.h>
 #endif
 
 #include <spdlog/common.h>
 #include <spdlog/pattern_formatter.h>
 
-namespace spdlog {
+namespace spdlog
+{
 
 SPDLOG_INLINE void initialize_logger(std::shared_ptr<logger> logger)
 {
diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h
index ee83e8de3..4dafb5ce3 100644
--- a/include/spdlog/spdlog.h
+++ b/include/spdlog/spdlog.h
@@ -9,18 +9,18 @@
 
 #pragma once
 
+#include <chrono>
+#include <functional>
+#include <memory>
 #include <spdlog/common.h>
 #include <spdlog/details/registry.h>
+#include <spdlog/details/synchronous_factory.h>
 #include <spdlog/logger.h>
 #include <spdlog/version.h>
-#include <spdlog/details/synchronous_factory.h>
-
-#include <chrono>
-#include <functional>
-#include <memory>
 #include <string>
 
-namespace spdlog {
+namespace spdlog
+{
 
 using default_factory = synchronous_factory;
 
@@ -30,8 +30,8 @@ using default_factory = synchronous_factory;
 //
 // Example:
 //   spdlog::create<daily_file_sink_st>("logger_name", "dailylog_filename", 11, 59);
-template<typename Sink, typename... SinkArgs>
-inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... sink_args)
+template <typename Sink, typename... SinkArgs>
+inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&...sink_args)
 {
     return default_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
 }
@@ -81,7 +81,7 @@ SPDLOG_API void flush_on(level::level_enum log_level);
 
 // Start/Restart a periodic flusher thread
 // Warning: Use only if all your loggers are thread safe!
-template<typename Rep, typename Period>
+template <typename Rep, typename Period>
 inline void flush_every(std::chrono::duration<Rep, Period> interval)
 {
     details::registry::instance().flush_every(interval);
@@ -131,147 +131,147 @@ SPDLOG_API spdlog::logger *default_logger_raw();
 
 SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
 
-template<typename... Args>
-inline void log(source_loc source, level::level_enum lvl, format_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void log(source_loc source, level::level_enum lvl, format_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->log(source, lvl, fmt, std::forward<Args>(args)...);
 }
 
-template<typename... Args>
-inline void log(level::level_enum lvl, format_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void log(level::level_enum lvl, format_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
 }
 
-template<typename... Args>
-inline void trace(format_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void trace(format_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->trace(fmt, std::forward<Args>(args)...);
 }
 
-template<typename... Args>
-inline void debug(format_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void debug(format_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->debug(fmt, std::forward<Args>(args)...);
 }
 
-template<typename... Args>
-inline void info(format_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void info(format_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->info(fmt, std::forward<Args>(args)...);
 }
 
-template<typename... Args>
-inline void warn(format_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void warn(format_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->warn(fmt, std::forward<Args>(args)...);
 }
 
-template<typename... Args>
-inline void error(format_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void error(format_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->error(fmt, std::forward<Args>(args)...);
 }
 
-template<typename... Args>
-inline void critical(format_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void critical(format_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->critical(fmt, std::forward<Args>(args)...);
 }
 
-template<typename T>
+template <typename T>
 inline void log(source_loc source, level::level_enum lvl, const T &msg)
 {
     default_logger_raw()->log(source, lvl, msg);
 }
 
-template<typename T>
+template <typename T>
 inline void log(level::level_enum lvl, const T &msg)
 {
     default_logger_raw()->log(lvl, msg);
 }
 
 #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
-template<typename... Args>
-inline void log(source_loc source, level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void log(source_loc source, level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->log(source, lvl, fmt, std::forward<Args>(args)...);
 }
 
-template<typename... Args>
-inline void log(level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void log(level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
 }
 
-template<typename... Args>
-inline void trace(wformat_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void trace(wformat_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->trace(fmt, std::forward<Args>(args)...);
 }
 
-template<typename... Args>
-inline void debug(wformat_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void debug(wformat_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->debug(fmt, std::forward<Args>(args)...);
 }
 
-template<typename... Args>
-inline void info(wformat_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void info(wformat_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->info(fmt, std::forward<Args>(args)...);
 }
 
-template<typename... Args>
-inline void warn(wformat_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void warn(wformat_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->warn(fmt, std::forward<Args>(args)...);
 }
 
-template<typename... Args>
-inline void error(wformat_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void error(wformat_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->error(fmt, std::forward<Args>(args)...);
 }
 
-template<typename... Args>
-inline void critical(wformat_string_t<Args...> fmt, Args &&... args)
+template <typename... Args>
+inline void critical(wformat_string_t<Args...> fmt, Args &&...args)
 {
     default_logger_raw()->critical(fmt, std::forward<Args>(args)...);
 }
 #endif
 
-template<typename T>
+template <typename T>
 inline void trace(const T &msg)
 {
     default_logger_raw()->trace(msg);
 }
 
-template<typename T>
+template <typename T>
 inline void debug(const T &msg)
 {
     default_logger_raw()->debug(msg);
 }
 
-template<typename T>
+template <typename T>
 inline void info(const T &msg)
 {
     default_logger_raw()->info(msg);
 }
 
-template<typename T>
+template <typename T>
 inline void warn(const T &msg)
 {
     default_logger_raw()->warn(msg);
 }
 
-template<typename T>
+template <typename T>
 inline void error(const T &msg)
 {
     default_logger_raw()->error(msg);
 }
 
-template<typename T>
+template <typename T>
 inline void critical(const T &msg)
 {
     default_logger_raw()->critical(msg);
@@ -293,62 +293,62 @@ inline void critical(const T &msg)
 //
 
 #ifndef SPDLOG_NO_SOURCE_LOC
-#    define SPDLOG_LOGGER_CALL(logger, level, ...)                                                                                         \
-        (logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, level, __VA_ARGS__)
+#define SPDLOG_LOGGER_CALL(logger, level, ...)                                                                         \
+    (logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, level, __VA_ARGS__)
 #else
-#    define SPDLOG_LOGGER_CALL(logger, level, ...) (logger)->log(spdlog::source_loc{}, level, __VA_ARGS__)
+#define SPDLOG_LOGGER_CALL(logger, level, ...) (logger)->log(spdlog::source_loc{}, level, __VA_ARGS__)
 #endif
 
 #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__)
+#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_LOGGER_TRACE(logger, ...) (void)0
-#    define SPDLOG_TRACE(...) (void)0
+#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__)
+#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_LOGGER_DEBUG(logger, ...) (void)0
-#    define SPDLOG_DEBUG(...) (void)0
+#define SPDLOG_LOGGER_DEBUG(logger, ...) (void) 0
+#define SPDLOG_DEBUG(...)                (void) 0
 #endif
 
 #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__)
+#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_LOGGER_INFO(logger, ...) (void)0
-#    define SPDLOG_INFO(...) (void)0
+#define SPDLOG_LOGGER_INFO(logger, ...) (void) 0
+#define SPDLOG_INFO(...)                (void) 0
 #endif
 
 #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__)
+#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
+#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__)
+#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
+#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__)
+#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
+#define SPDLOG_LOGGER_CRITICAL(logger, ...) (void) 0
+#define SPDLOG_CRITICAL(...)                (void) 0
 #endif
 
 #ifdef SPDLOG_HEADER_ONLY
-#    include "spdlog-inl.h"
+#include "spdlog-inl.h"
 #endif
 
 #endif // SPDLOG_H
diff --git a/include/spdlog/stopwatch.h b/include/spdlog/stopwatch.h
index 5d1f2dc5e..0ceffd79f 100644
--- a/include/spdlog/stopwatch.h
+++ b/include/spdlog/stopwatch.h
@@ -3,8 +3,8 @@
 
 #pragma once
 
-#include <spdlog/fmt/fmt.h>
 #include <chrono>
+#include <spdlog/fmt/fmt.h>
 
 // Stopwatch support for spdlog  (using std::chrono::steady_clock).
 // Displays elapsed seconds since construction as double.
@@ -17,7 +17,8 @@
 // spdlog::info("Elapsed: {:.6} seconds", sw);  =>  "Elapsed 0.005163 seconds"
 //
 //
-// If other units are needed (e.g. millis instead of double), include "fmt/chrono.h" and use "duration_cast<..>(sw.elapsed())":
+// If other units are needed (e.g. millis instead of double), include "fmt/chrono.h" and use
+// "duration_cast<..>(sw.elapsed())":
 //
 // #include <spdlog/fmt/chrono.h>
 //..
@@ -25,26 +26,19 @@
 // using std::chrono::milliseconds;
 // spdlog::info("Elapsed {}", duration_cast<milliseconds>(sw.elapsed())); => "Elapsed 5ms"
 
-namespace spdlog {
+namespace spdlog
+{
 class stopwatch
 {
     using clock = std::chrono::steady_clock;
     std::chrono::time_point<clock> start_tp_;
 
 public:
-    stopwatch()
-        : start_tp_{clock::now()}
-    {}
+    stopwatch() : start_tp_{clock::now()} {}
 
-    std::chrono::duration<double> elapsed() const
-    {
-        return std::chrono::duration<double>(clock::now() - start_tp_);
-    }
+    std::chrono::duration<double> elapsed() const { return std::chrono::duration<double>(clock::now() - start_tp_); }
 
-    void reset()
-    {
-        start_tp_ = clock::now();
-    }
+    void reset() { start_tp_ = clock::now(); }
 };
 } // namespace spdlog
 
@@ -56,11 +50,10 @@ namespace
     fmt
 #endif
 {
-
-template<>
+template <>
 struct formatter<spdlog::stopwatch> : formatter<double>
 {
-    template<typename FormatContext>
+    template <typename FormatContext>
     auto format(const spdlog::stopwatch &sw, FormatContext &ctx) -> decltype(ctx.out())
     {
         return formatter<double>::format(sw.elapsed().count(), ctx);
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index 9965c7526..0ba50c19b 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -1,20 +1,20 @@
 /*
-* PeTrack - Software for tracking pedestrians movement in videos
-* Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
-*
-* This program is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation, either version 3 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program.  If not, see <https://www.gnu.org/licenses/>.
-*/
+ * PeTrack - Software for tracking pedestrians movement in videos
+ * Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
 
 #include "logwindow.h"
 #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
@@ -23,26 +23,24 @@
 
 LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
 {
-   mMainWindow = (class Petrack *) parent;
-   if(!ui)
-   {
-       mUi = new Ui::LogWindow();
-   }
-   else
-   {
-       mUi = ui;
-   }
-
-   mUi->setupUi(this);
-   mUi->logText->setReadOnly(true);
-
-   std::vector<spdlog::sink_ptr> sinks;
-   sinks.push_back(std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>());
-   sinks.push_back(std::make_shared<spdlog::sinks::qt_sink_mt>(mUi->logText, "appendPlainText"));
-
-   mLogger = std::make_shared<spdlog::logger>("Logging", std::begin(sinks), std::end(sinks));
-   mLogger->set_pattern("%! in %s line %#: %v");
-   spdlog::register_logger(mLogger);
-
+    mMainWindow = (class Petrack *) parent;
+    if(!ui)
+    {
+        mUi = new Ui::LogWindow();
+    }
+    else
+    {
+        mUi = ui;
+    }
+
+    mUi->setupUi(this);
+    mUi->logText->setReadOnly(true);
+
+    std::vector<spdlog::sink_ptr> sinks;
+    sinks.push_back(std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>());
+    sinks.push_back(std::make_shared<spdlog::sinks::qt_sink_mt>(mUi->logText, "appendPlainText"));
+
+    mLogger = std::make_shared<spdlog::logger>("Logging", std::begin(sinks), std::end(sinks));
+    mLogger->set_pattern("%! in %s line %#: %v");
+    spdlog::register_logger(mLogger);
 }
-
-- 
GitLab


From d1a84987c3b800db56807fa58a188f2db110b790 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Tue, 29 Nov 2022 11:08:07 +0100
Subject: [PATCH 07/22] remove unused imports + format CMakeLists.txt

---
 CMakeLists.txt      | 6 +++---
 include/logwindow.h | 2 --
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index bc9937a6b..dd7f71c51 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -384,7 +384,7 @@ target_sources(petrack_core PRIVATE
     include/frameRange.h
     include/pdoublespinbox.h
     include/pspinbox.h
-        include/logwindow.h
+    include/logwindow.h
     )
 
 target_sources(petrack_core PRIVATE
@@ -448,7 +448,7 @@ target_sources(petrack_core PRIVATE
     src/manualTrackpointMover.cpp
     src/pdoublespinbox.cpp
     src/pspinbox.cpp
-        src/logwindow.cpp
+    src/logwindow.cpp
     ui/about.ui
     ui/codeMarker.ui
     ui/colorMarker.ui
@@ -458,7 +458,7 @@ target_sources(petrack_core PRIVATE
     ui/control.ui
     ui/openMoCapDialog.ui
     ui/moCapSelectionWidget.ui
-        ui/logwindow.ui
+    ui/logwindow.ui
 )
 
 target_sources(petrack PRIVATE
diff --git a/include/logwindow.h b/include/logwindow.h
index 028bf8df4..b3e2e73d3 100644
--- a/include/logwindow.h
+++ b/include/logwindow.h
@@ -24,9 +24,7 @@
 #include "spdlog/sinks/qt_sinks.h"
 
 #include <QWidget>
-#include <experimental/source_location>
 #include <petrack.h>
-#include <sstream>
 
 namespace Ui
 {
-- 
GitLab


From e7c11d6f259d13a31df7b916f67378ea62cda0e2 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Tue, 24 Jan 2023 11:40:02 +0100
Subject: [PATCH 08/22] increased log window size, workaround for logger

---
 include/spdlog/async.h                        |  104 -
 include/spdlog/async_logger-inl.h             |   99 -
 include/spdlog/async_logger.h                 |   79 -
 include/spdlog/cfg/argv.h                     |   46 -
 include/spdlog/cfg/env.h                      |   40 -
 include/spdlog/cfg/helpers-inl.h              |  125 -
 include/spdlog/cfg/helpers.h                  |   32 -
 include/spdlog/common-inl.h                   |   82 -
 include/spdlog/common.h                       |  415 --
 include/spdlog/details/backtracer-inl.h       |   71 -
 include/spdlog/details/backtracer.h           |   46 -
 include/spdlog/details/circular_q.h           |  129 -
 include/spdlog/details/console_globals.h      |   34 -
 include/spdlog/details/file_helper-inl.h      |  173 -
 include/spdlog/details/file_helper.h          |   63 -
 include/spdlog/details/fmt_helper.h           |  167 -
 include/spdlog/details/log_msg-inl.h          |   52 -
 include/spdlog/details/log_msg.h              |   44 -
 include/spdlog/details/log_msg_buffer-inl.h   |   60 -
 include/spdlog/details/log_msg_buffer.h       |   35 -
 include/spdlog/details/mpmc_blocking_q.h      |  131 -
 include/spdlog/details/null_mutex.h           |   39 -
 include/spdlog/details/os-inl.h               |  625 ---
 include/spdlog/details/os.h                   |  122 -
 include/spdlog/details/periodic_worker-inl.h  |   30 -
 include/spdlog/details/periodic_worker.h      |   64 -
 include/spdlog/details/registry-inl.h         |  306 --
 include/spdlog/details/registry.h             |  122 -
 include/spdlog/details/synchronous_factory.h  |   25 -
 include/spdlog/details/tcp_client-windows.h   |  160 -
 include/spdlog/details/tcp_client.h           |  139 -
 include/spdlog/details/thread_pool-inl.h      |  163 -
 include/spdlog/details/thread_pool.h          |  123 -
 include/spdlog/details/udp_client-windows.h   |  118 -
 include/spdlog/details/udp_client.h           |   93 -
 include/spdlog/details/windows_include.h      |   11 -
 include/spdlog/fmt/bin_to_hex.h               |  240 -
 include/spdlog/fmt/bundled/args.h             |  260 -
 include/spdlog/fmt/bundled/chrono.h           | 2396 ---------
 include/spdlog/fmt/bundled/color.h            |  717 ---
 include/spdlog/fmt/bundled/compile.h          |  709 ---
 include/spdlog/fmt/bundled/core.h             | 3685 -------------
 include/spdlog/fmt/bundled/fmt.license.rst    |   27 -
 include/spdlog/fmt/bundled/format-inl.h       | 1781 ------
 include/spdlog/fmt/bundled/format.h           | 4767 -----------------
 include/spdlog/fmt/bundled/locale.h           |    2 -
 include/spdlog/fmt/bundled/os.h               |  495 --
 include/spdlog/fmt/bundled/ostream.h          |  259 -
 include/spdlog/fmt/bundled/printf.h           |  718 ---
 include/spdlog/fmt/bundled/ranges.h           |  806 ---
 include/spdlog/fmt/bundled/std.h              |  179 -
 include/spdlog/fmt/bundled/xchar.h            |  278 -
 include/spdlog/fmt/chrono.h                   |   22 -
 include/spdlog/fmt/compile.h                  |   22 -
 include/spdlog/fmt/fmt.h                      |   33 -
 include/spdlog/fmt/ostr.h                     |   22 -
 include/spdlog/fmt/ranges.h                   |   22 -
 include/spdlog/fmt/std.h                      |   23 -
 include/spdlog/fmt/xchar.h                    |   22 -
 include/spdlog/formatter.h                    |   19 -
 include/spdlog/fwd.h                          |   21 -
 include/spdlog/logger-inl.h                   |  261 -
 include/spdlog/logger.h                       |  421 --
 include/spdlog/pattern_formatter-inl.h        | 1374 -----
 include/spdlog/pattern_formatter.h            |  123 -
 include/spdlog/sinks/android_sink.h           |  146 -
 include/spdlog/sinks/ansicolor_sink-inl.h     |  150 -
 include/spdlog/sinks/ansicolor_sink.h         |  120 -
 include/spdlog/sinks/base_sink-inl.h          |   64 -
 include/spdlog/sinks/base_sink.h              |   54 -
 include/spdlog/sinks/basic_file_sink-inl.h    |   49 -
 include/spdlog/sinks/basic_file_sink.h        |   70 -
 include/spdlog/sinks/daily_file_sink.h        |  331 --
 include/spdlog/sinks/dist_sink.h              |   94 -
 include/spdlog/sinks/dup_filter_sink.h        |   99 -
 include/spdlog/sinks/hourly_file_sink.h       |  221 -
 include/spdlog/sinks/mongo_sink.h             |  118 -
 include/spdlog/sinks/msvc_sink.h              |   59 -
 include/spdlog/sinks/null_sink.h              |   45 -
 include/spdlog/sinks/ostream_sink.h           |   45 -
 include/spdlog/sinks/qt_sinks.h               |  113 -
 include/spdlog/sinks/ringbuffer_sink.h        |   71 -
 include/spdlog/sinks/rotating_file_sink-inl.h |  160 -
 include/spdlog/sinks/rotating_file_sink.h     |   96 -
 include/spdlog/sinks/sink-inl.h               |   25 -
 include/spdlog/sinks/sink.h                   |   37 -
 include/spdlog/sinks/stdout_color_sinks-inl.h |   39 -
 include/spdlog/sinks/stdout_color_sinks.h     |   47 -
 include/spdlog/sinks/stdout_sinks-inl.h       |  140 -
 include/spdlog/sinks/stdout_sinks.h           |   89 -
 include/spdlog/sinks/syslog_sink.h            |  117 -
 include/spdlog/sinks/systemd_sink.h           |  119 -
 include/spdlog/sinks/tcp_sink.h               |   79 -
 include/spdlog/sinks/udp_sink.h               |   71 -
 include/spdlog/sinks/win_eventlog_sink.h      |  306 --
 include/spdlog/sinks/wincolor_sink-inl.h      |  179 -
 include/spdlog/sinks/wincolor_sink.h          |   86 -
 include/spdlog/spdlog-inl.h                   |  121 -
 include/spdlog/spdlog.h                       |  354 --
 include/spdlog/stopwatch.h                    |   62 -
 include/spdlog/tweakme.h                      |  140 -
 include/spdlog/version.h                      |   10 -
 src/logwindow.cpp                             |   10 +-
 src/petrack.cpp                               |    3 +-
 ui/logwindow.ui                               |    4 +-
 105 files changed, 9 insertions(+), 28005 deletions(-)
 delete mode 100644 include/spdlog/async.h
 delete mode 100644 include/spdlog/async_logger-inl.h
 delete mode 100644 include/spdlog/async_logger.h
 delete mode 100644 include/spdlog/cfg/argv.h
 delete mode 100644 include/spdlog/cfg/env.h
 delete mode 100644 include/spdlog/cfg/helpers-inl.h
 delete mode 100644 include/spdlog/cfg/helpers.h
 delete mode 100644 include/spdlog/common-inl.h
 delete mode 100644 include/spdlog/common.h
 delete mode 100644 include/spdlog/details/backtracer-inl.h
 delete mode 100644 include/spdlog/details/backtracer.h
 delete mode 100644 include/spdlog/details/circular_q.h
 delete mode 100644 include/spdlog/details/console_globals.h
 delete mode 100644 include/spdlog/details/file_helper-inl.h
 delete mode 100644 include/spdlog/details/file_helper.h
 delete mode 100644 include/spdlog/details/fmt_helper.h
 delete mode 100644 include/spdlog/details/log_msg-inl.h
 delete mode 100644 include/spdlog/details/log_msg.h
 delete mode 100644 include/spdlog/details/log_msg_buffer-inl.h
 delete mode 100644 include/spdlog/details/log_msg_buffer.h
 delete mode 100644 include/spdlog/details/mpmc_blocking_q.h
 delete mode 100644 include/spdlog/details/null_mutex.h
 delete mode 100644 include/spdlog/details/os-inl.h
 delete mode 100644 include/spdlog/details/os.h
 delete mode 100644 include/spdlog/details/periodic_worker-inl.h
 delete mode 100644 include/spdlog/details/periodic_worker.h
 delete mode 100644 include/spdlog/details/registry-inl.h
 delete mode 100644 include/spdlog/details/registry.h
 delete mode 100644 include/spdlog/details/synchronous_factory.h
 delete mode 100644 include/spdlog/details/tcp_client-windows.h
 delete mode 100644 include/spdlog/details/tcp_client.h
 delete mode 100644 include/spdlog/details/thread_pool-inl.h
 delete mode 100644 include/spdlog/details/thread_pool.h
 delete mode 100644 include/spdlog/details/udp_client-windows.h
 delete mode 100644 include/spdlog/details/udp_client.h
 delete mode 100644 include/spdlog/details/windows_include.h
 delete mode 100644 include/spdlog/fmt/bin_to_hex.h
 delete mode 100644 include/spdlog/fmt/bundled/args.h
 delete mode 100644 include/spdlog/fmt/bundled/chrono.h
 delete mode 100644 include/spdlog/fmt/bundled/color.h
 delete mode 100644 include/spdlog/fmt/bundled/compile.h
 delete mode 100644 include/spdlog/fmt/bundled/core.h
 delete mode 100644 include/spdlog/fmt/bundled/fmt.license.rst
 delete mode 100644 include/spdlog/fmt/bundled/format-inl.h
 delete mode 100644 include/spdlog/fmt/bundled/format.h
 delete mode 100644 include/spdlog/fmt/bundled/locale.h
 delete mode 100644 include/spdlog/fmt/bundled/os.h
 delete mode 100644 include/spdlog/fmt/bundled/ostream.h
 delete mode 100644 include/spdlog/fmt/bundled/printf.h
 delete mode 100644 include/spdlog/fmt/bundled/ranges.h
 delete mode 100644 include/spdlog/fmt/bundled/std.h
 delete mode 100644 include/spdlog/fmt/bundled/xchar.h
 delete mode 100644 include/spdlog/fmt/chrono.h
 delete mode 100644 include/spdlog/fmt/compile.h
 delete mode 100644 include/spdlog/fmt/fmt.h
 delete mode 100644 include/spdlog/fmt/ostr.h
 delete mode 100644 include/spdlog/fmt/ranges.h
 delete mode 100644 include/spdlog/fmt/std.h
 delete mode 100644 include/spdlog/fmt/xchar.h
 delete mode 100644 include/spdlog/formatter.h
 delete mode 100644 include/spdlog/fwd.h
 delete mode 100644 include/spdlog/logger-inl.h
 delete mode 100644 include/spdlog/logger.h
 delete mode 100644 include/spdlog/pattern_formatter-inl.h
 delete mode 100644 include/spdlog/pattern_formatter.h
 delete mode 100644 include/spdlog/sinks/android_sink.h
 delete mode 100644 include/spdlog/sinks/ansicolor_sink-inl.h
 delete mode 100644 include/spdlog/sinks/ansicolor_sink.h
 delete mode 100644 include/spdlog/sinks/base_sink-inl.h
 delete mode 100644 include/spdlog/sinks/base_sink.h
 delete mode 100644 include/spdlog/sinks/basic_file_sink-inl.h
 delete mode 100644 include/spdlog/sinks/basic_file_sink.h
 delete mode 100644 include/spdlog/sinks/daily_file_sink.h
 delete mode 100644 include/spdlog/sinks/dist_sink.h
 delete mode 100644 include/spdlog/sinks/dup_filter_sink.h
 delete mode 100644 include/spdlog/sinks/hourly_file_sink.h
 delete mode 100644 include/spdlog/sinks/mongo_sink.h
 delete mode 100644 include/spdlog/sinks/msvc_sink.h
 delete mode 100644 include/spdlog/sinks/null_sink.h
 delete mode 100644 include/spdlog/sinks/ostream_sink.h
 delete mode 100644 include/spdlog/sinks/qt_sinks.h
 delete mode 100644 include/spdlog/sinks/ringbuffer_sink.h
 delete mode 100644 include/spdlog/sinks/rotating_file_sink-inl.h
 delete mode 100644 include/spdlog/sinks/rotating_file_sink.h
 delete mode 100644 include/spdlog/sinks/sink-inl.h
 delete mode 100644 include/spdlog/sinks/sink.h
 delete mode 100644 include/spdlog/sinks/stdout_color_sinks-inl.h
 delete mode 100644 include/spdlog/sinks/stdout_color_sinks.h
 delete mode 100644 include/spdlog/sinks/stdout_sinks-inl.h
 delete mode 100644 include/spdlog/sinks/stdout_sinks.h
 delete mode 100644 include/spdlog/sinks/syslog_sink.h
 delete mode 100644 include/spdlog/sinks/systemd_sink.h
 delete mode 100644 include/spdlog/sinks/tcp_sink.h
 delete mode 100644 include/spdlog/sinks/udp_sink.h
 delete mode 100644 include/spdlog/sinks/win_eventlog_sink.h
 delete mode 100644 include/spdlog/sinks/wincolor_sink-inl.h
 delete mode 100644 include/spdlog/sinks/wincolor_sink.h
 delete mode 100644 include/spdlog/spdlog-inl.h
 delete mode 100644 include/spdlog/spdlog.h
 delete mode 100644 include/spdlog/stopwatch.h
 delete mode 100644 include/spdlog/tweakme.h
 delete mode 100644 include/spdlog/version.h

diff --git a/include/spdlog/async.h b/include/spdlog/async.h
deleted file mode 100644
index fa47f7d59..000000000
--- a/include/spdlog/async.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-//
-// Async logging using global thread pool
-// All loggers created here share same global thread pool.
-// Each log message is pushed to a queue along with a shared pointer to the
-// logger.
-// If a logger deleted while having pending messages in the queue, it's actual
-// destruction will defer
-// until all its messages are processed by the thread pool.
-// This is because each message in the queue holds a shared_ptr to the
-// originating logger.
-
-#include <functional>
-#include <memory>
-#include <mutex>
-#include <spdlog/async_logger.h>
-#include <spdlog/details/registry.h>
-#include <spdlog/details/thread_pool.h>
-
-namespace spdlog
-{
-
-namespace details
-{
-    static const size_t default_async_q_size = 8192;
-}
-
-// async logger factory - creates async loggers backed with thread pool.
-// if a global thread pool doesn't already exist, create it with default queue
-// size of 8192 items and single thread.
-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(std::string logger_name, SinkArgs &&...args)
-    {
-        auto &registry_inst = details::registry::instance();
-
-        // create global thread pool if not already exists..
-
-        auto                                 &mutex = registry_inst.tp_mutex();
-        std::lock_guard<std::recursive_mutex> tp_lock(mutex);
-        auto                                  tp = registry_inst.get_tp();
-        if(tp == nullptr)
-        {
-            tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1U);
-            registry_inst.set_tp(tp);
-        }
-
-        auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
-        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;
-    }
-};
-
-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(std::string logger_name, 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(std::string logger_name, SinkArgs &&...sink_args)
-{
-    return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
-}
-
-// set global thread pool.
-inline void init_thread_pool(
-    size_t                q_size,
-    size_t                thread_count,
-    std::function<void()> on_thread_start,
-    std::function<void()> on_thread_stop)
-{
-    auto tp = std::make_shared<details::thread_pool>(q_size, thread_count, on_thread_start, on_thread_stop);
-    details::registry::instance().set_tp(std::move(tp));
-}
-
-inline void init_thread_pool(size_t q_size, size_t thread_count, std::function<void()> on_thread_start)
-{
-    init_thread_pool(q_size, thread_count, on_thread_start, [] {});
-}
-
-inline void init_thread_pool(size_t q_size, size_t thread_count)
-{
-    init_thread_pool(
-        q_size, thread_count, [] {}, [] {});
-}
-
-// get the global thread pool.
-inline std::shared_ptr<spdlog::details::thread_pool> thread_pool()
-{
-    return details::registry::instance().get_tp();
-}
-} // namespace spdlog
diff --git a/include/spdlog/async_logger-inl.h b/include/spdlog/async_logger-inl.h
deleted file mode 100644
index 82bfd4ea7..000000000
--- a/include/spdlog/async_logger-inl.h
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/async_logger.h>
-#endif
-
-#include <memory>
-#include <spdlog/details/thread_pool.h>
-#include <spdlog/sinks/sink.h>
-#include <string>
-
-SPDLOG_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(), std::move(tp), overflow_policy)
-{
-}
-
-SPDLOG_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), {std::move(single_sink)}, std::move(tp), overflow_policy)
-{
-}
-
-// send the log message to the thread pool
-SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg)
-{
-    if(auto pool_ptr = thread_pool_.lock())
-    {
-        pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
-    }
-    else
-    {
-        throw_spdlog_ex("async log: thread pool doesn't exist anymore");
-    }
-}
-
-// send flush request to the thread pool
-SPDLOG_INLINE void spdlog::async_logger::flush_()
-{
-    if(auto pool_ptr = thread_pool_.lock())
-    {
-        pool_ptr->post_flush(shared_from_this(), overflow_policy_);
-    }
-    else
-    {
-        throw_spdlog_ex("async flush: thread pool doesn't exist anymore");
-    }
-}
-
-//
-// backend functions - called from the thread pool to do the actual job
-//
-SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg)
-{
-    for(auto &sink : sinks_)
-    {
-        if(sink->should_log(msg.level))
-        {
-            SPDLOG_TRY
-            {
-                sink->log(msg);
-            }
-            SPDLOG_LOGGER_CATCH(msg.source)
-        }
-    }
-
-    if(should_flush_(msg))
-    {
-        backend_flush_();
-    }
-}
-
-SPDLOG_INLINE void spdlog::async_logger::backend_flush_()
-{
-    for(auto &sink : sinks_)
-    {
-        SPDLOG_TRY
-        {
-            sink->flush();
-        }
-        SPDLOG_LOGGER_CATCH(source_loc())
-    }
-}
-
-SPDLOG_INLINE std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name)
-{
-    auto cloned   = std::make_shared<spdlog::async_logger>(*this);
-    cloned->name_ = std::move(new_name);
-    return cloned;
-}
diff --git a/include/spdlog/async_logger.h b/include/spdlog/async_logger.h
deleted file mode 100644
index 7ec2fbb8a..000000000
--- a/include/spdlog/async_logger.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-// Fast asynchronous logger.
-// Uses pre allocated queue.
-// Creates a single back thread to pop messages from the queue and log them.
-//
-// Upon each log write the logger:
-//    1. Checks if its log level is enough to log the message
-//    2. Push a new copy of the message to a queue (or block the caller until
-//    space is available in the queue)
-// Upon destruction, logs all remaining messages in the queue before
-// destructing..
-
-#include <spdlog/logger.h>
-
-namespace spdlog
-{
-
-// Async overflow policy - block by default.
-enum class async_overflow_policy
-{
-    block,         // Block until message can be enqueued
-    overrun_oldest // Discard oldest message in the queue if full when trying to
-                   // add new item.
-};
-
-namespace details
-{
-    class thread_pool;
-}
-
-class SPDLOG_API async_logger final : public std::enable_shared_from_this<async_logger>, public logger
-{
-    friend class details::thread_pool;
-
-public:
-    template <typename It>
-    async_logger(
-        std::string                         logger_name,
-        It                                  begin,
-        It                                  end,
-        std::weak_ptr<details::thread_pool> tp,
-        async_overflow_policy               overflow_policy = async_overflow_policy::block) :
-        logger(std::move(logger_name), begin, end), thread_pool_(std::move(tp)), overflow_policy_(overflow_policy)
-    {
-    }
-
-    async_logger(
-        std::string                         logger_name,
-        sinks_init_list                     sinks_list,
-        std::weak_ptr<details::thread_pool> tp,
-        async_overflow_policy               overflow_policy = async_overflow_policy::block);
-
-    async_logger(
-        std::string                         logger_name,
-        sink_ptr                            single_sink,
-        std::weak_ptr<details::thread_pool> tp,
-        async_overflow_policy               overflow_policy = async_overflow_policy::block);
-
-    std::shared_ptr<logger> clone(std::string new_name) override;
-
-protected:
-    void sink_it_(const details::log_msg &msg) override;
-    void flush_() override;
-    void backend_sink_it_(const details::log_msg &incoming_log_msg);
-    void backend_flush_();
-
-private:
-    std::weak_ptr<details::thread_pool> thread_pool_;
-    async_overflow_policy               overflow_policy_;
-};
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "async_logger-inl.h"
-#endif
diff --git a/include/spdlog/cfg/argv.h b/include/spdlog/cfg/argv.h
deleted file mode 100644
index 264f2a88a..000000000
--- a/include/spdlog/cfg/argv.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-#include <spdlog/cfg/helpers.h>
-#include <spdlog/details/registry.h>
-
-//
-// Init log levels using each argv entry that starts with "SPDLOG_LEVEL="
-//
-// set all loggers to debug level:
-// example.exe "SPDLOG_LEVEL=debug"
-
-// set logger1 to trace level
-// example.exe "SPDLOG_LEVEL=logger1=trace"
-
-// turn off all logging except for logger1 and logger2:
-// example.exe "SPDLOG_LEVEL=off,logger1=debug,logger2=info"
-
-namespace spdlog
-{
-namespace cfg
-{
-
-    // search for SPDLOG_LEVEL= in the args and use it to init the levels
-    inline void load_argv_levels(int argc, const char **argv)
-    {
-        const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
-        for(int i = 1; i < argc; i++)
-        {
-            std::string arg = argv[i];
-            if(arg.find(spdlog_level_prefix) == 0)
-            {
-                auto levels_string = arg.substr(spdlog_level_prefix.size());
-                helpers::load_levels(levels_string);
-            }
-        }
-    }
-
-    inline void load_argv_levels(int argc, char **argv)
-    {
-        load_argv_levels(argc, const_cast<const char **>(argv));
-    }
-
-} // namespace cfg
-} // namespace spdlog
diff --git a/include/spdlog/cfg/env.h b/include/spdlog/cfg/env.h
deleted file mode 100644
index 7102370df..000000000
--- a/include/spdlog/cfg/env.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-#include <spdlog/cfg/helpers.h>
-#include <spdlog/details/os.h>
-#include <spdlog/details/registry.h>
-
-//
-// Init levels and patterns from env variables SPDLOG_LEVEL
-// Inspired from Rust's "env_logger" crate (https://crates.io/crates/env_logger).
-// Note - fallback to "info" level on unrecognized levels
-//
-// Examples:
-//
-// set global level to debug:
-// export SPDLOG_LEVEL=debug
-//
-// turn off all logging except for logger1:
-// export SPDLOG_LEVEL="*=off,logger1=debug"
-//
-
-// turn off all logging except for logger1 and logger2:
-// export SPDLOG_LEVEL="off,logger1=debug,logger2=info"
-
-namespace spdlog
-{
-namespace cfg
-{
-    inline void load_env_levels()
-    {
-        auto env_val = details::os::getenv("SPDLOG_LEVEL");
-        if(!env_val.empty())
-        {
-            helpers::load_levels(env_val);
-        }
-    }
-
-} // namespace cfg
-} // namespace spdlog
diff --git a/include/spdlog/cfg/helpers-inl.h b/include/spdlog/cfg/helpers-inl.h
deleted file mode 100644
index 48129777e..000000000
--- a/include/spdlog/cfg/helpers-inl.h
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/cfg/helpers.h>
-#endif
-
-#include <algorithm>
-#include <spdlog/details/os.h>
-#include <spdlog/details/registry.h>
-#include <spdlog/spdlog.h>
-#include <sstream>
-#include <string>
-#include <utility>
-
-namespace spdlog
-{
-namespace cfg
-{
-    namespace helpers
-    {
-
-        // inplace convert to lowercase
-        inline std::string &to_lower_(std::string &str)
-        {
-            std::transform(
-                str.begin(),
-                str.end(),
-                str.begin(),
-                [](char ch) { return static_cast<char>((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch); });
-            return str;
-        }
-
-        // inplace trim spaces
-        inline std::string &trim_(std::string &str)
-        {
-            const char *spaces = " \n\r\t";
-            str.erase(str.find_last_not_of(spaces) + 1);
-            str.erase(0, str.find_first_not_of(spaces));
-            return str;
-        }
-
-        // return (name,value) trimmed pair from given "name=value" string.
-        // return empty string on missing parts
-        // "key=val" => ("key", "val")
-        // " key  =  val " => ("key", "val")
-        // "key=" => ("key", "")
-        // "val" => ("", "val")
-
-        inline std::pair<std::string, std::string> extract_kv_(char sep, const std::string &str)
-        {
-            auto        n = str.find(sep);
-            std::string k, v;
-            if(n == std::string::npos)
-            {
-                v = str;
-            }
-            else
-            {
-                k = str.substr(0, n);
-                v = str.substr(n + 1);
-            }
-            return std::make_pair(trim_(k), trim_(v));
-        }
-
-        // return vector of key/value pairs from sequence of "K1=V1,K2=V2,.."
-        // "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
-        inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str)
-        {
-            std::string                                  token;
-            std::istringstream                           token_stream(str);
-            std::unordered_map<std::string, std::string> rv{};
-            while(std::getline(token_stream, token, ','))
-            {
-                if(token.empty())
-                {
-                    continue;
-                }
-                auto kv      = extract_kv_('=', token);
-                rv[kv.first] = kv.second;
-            }
-            return rv;
-        }
-
-        SPDLOG_INLINE void load_levels(const std::string &input)
-        {
-            if(input.empty() || input.size() > 512)
-            {
-                return;
-            }
-
-            auto                                               key_vals = extract_key_vals_(input);
-            std::unordered_map<std::string, level::level_enum> levels;
-            level::level_enum                                  global_level       = level::info;
-            bool                                               global_level_found = false;
-
-            for(auto &name_level : key_vals)
-            {
-                auto &logger_name = name_level.first;
-                auto  level_name  = to_lower_(name_level.second);
-                auto  level       = level::from_str(level_name);
-                // ignore unrecognized level names
-                if(level == level::off && level_name != "off")
-                {
-                    continue;
-                }
-                if(logger_name.empty()) // no logger name indicate global level
-                {
-                    global_level_found = true;
-                    global_level       = level;
-                }
-                else
-                {
-                    levels[logger_name] = level;
-                }
-            }
-
-            details::registry::instance().set_levels(std::move(levels), global_level_found ? &global_level : nullptr);
-        }
-
-    } // namespace helpers
-} // namespace cfg
-} // namespace spdlog
diff --git a/include/spdlog/cfg/helpers.h b/include/spdlog/cfg/helpers.h
deleted file mode 100644
index 5312c3dfb..000000000
--- a/include/spdlog/cfg/helpers.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <spdlog/common.h>
-#include <unordered_map>
-
-namespace spdlog
-{
-namespace cfg
-{
-    namespace helpers
-    {
-        //
-        // Init levels from given string
-        //
-        // Examples:
-        //
-        // set global level to debug: "debug"
-        // turn off all logging except for logger1: "off,logger1=debug"
-        // turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
-        //
-        SPDLOG_API void load_levels(const std::string &txt);
-    } // namespace helpers
-
-} // namespace cfg
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "helpers-inl.h"
-#endif // SPDLOG_HEADER_ONLY
diff --git a/include/spdlog/common-inl.h b/include/spdlog/common-inl.h
deleted file mode 100644
index 5ebcf69a5..000000000
--- a/include/spdlog/common-inl.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/common.h>
-#endif
-
-#include <algorithm>
-#include <iterator>
-
-namespace spdlog
-{
-namespace level
-{
-
-#if __cplusplus >= 201703L
-    constexpr
-#endif
-        static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
-
-    static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
-
-    SPDLOG_INLINE const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
-    {
-        return level_string_views[l];
-    }
-
-    SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
-    {
-        return short_level_names[l];
-    }
-
-    SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
-    {
-        auto it = std::find(std::begin(level_string_views), std::end(level_string_views), name);
-        if(it != std::end(level_string_views))
-            return static_cast<level::level_enum>(std::distance(std::begin(level_string_views), it));
-
-        // check also for "warn" and "err" before giving up..
-        if(name == "warn")
-        {
-            return level::warn;
-        }
-        if(name == "err")
-        {
-            return level::err;
-        }
-        return level::off;
-    }
-} // namespace level
-
-SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg) : msg_(std::move(msg)) {}
-
-SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno)
-{
-#ifdef SPDLOG_USE_STD_FORMAT
-    msg_ = std::system_error(std::error_code(last_errno, std::generic_category()), msg).what();
-#else
-    memory_buf_t outbuf;
-    fmt::format_system_error(outbuf, last_errno, msg.c_str());
-    msg_ = fmt::to_string(outbuf);
-#endif
-}
-
-SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT
-{
-    return msg_.c_str();
-}
-
-SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno)
-{
-    SPDLOG_THROW(spdlog_ex(msg, last_errno));
-}
-
-SPDLOG_INLINE void throw_spdlog_ex(std::string msg)
-{
-    SPDLOG_THROW(spdlog_ex(std::move(msg)));
-}
-
-} // namespace spdlog
diff --git a/include/spdlog/common.h b/include/spdlog/common.h
deleted file mode 100644
index 127d7a672..000000000
--- a/include/spdlog/common.h
+++ /dev/null
@@ -1,415 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <atomic>
-#include <chrono>
-#include <cstdio>
-#include <exception>
-#include <functional>
-#include <initializer_list>
-#include <memory>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/tweakme.h>
-#include <string>
-#include <type_traits>
-
-#ifdef SPDLOG_USE_STD_FORMAT
-#include <version>
-#if __cpp_lib_format >= 202207L
-#include <format>
-#else
-#include <string_view>
-#endif
-#endif
-
-#ifdef SPDLOG_COMPILED_LIB
-#undef SPDLOG_HEADER_ONLY
-#if defined(SPDLOG_SHARED_LIB)
-#if defined(_WIN32)
-#ifdef spdlog_EXPORTS
-#define SPDLOG_API __declspec(dllexport)
-#else // !spdlog_EXPORTS
-#define SPDLOG_API __declspec(dllimport)
-#endif
-#else // !defined(_WIN32)
-#define SPDLOG_API __attribute__((visibility("default")))
-#endif
-#else // !defined(SPDLOG_SHARED_LIB)
-#define SPDLOG_API
-#endif
-#define SPDLOG_INLINE
-#else // !defined(SPDLOG_COMPILED_LIB)
-#define SPDLOG_API
-#define SPDLOG_HEADER_ONLY
-#define SPDLOG_INLINE inline
-#endif // #ifdef SPDLOG_COMPILED_LIB
-
-#include <spdlog/fmt/fmt.h>
-
-#if !defined(SPDLOG_USE_STD_FORMAT) && FMT_VERSION >= 80000 // backward compatibility with fmt versions older than 8
-#define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string)
-#define SPDLOG_FMT_STRING(format_string)  FMT_STRING(format_string)
-#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
-#include <spdlog/fmt/xchar.h>
-#endif
-#else
-#define SPDLOG_FMT_RUNTIME(format_string) format_string
-#define SPDLOG_FMT_STRING(format_string)  format_string
-#endif
-
-// visual studio up to 2013 does not support noexcept nor constexpr
-#if defined(_MSC_VER) && (_MSC_VER < 1900)
-#define SPDLOG_NOEXCEPT _NOEXCEPT
-#define SPDLOG_CONSTEXPR
-#define SPDLOG_CONSTEXPR_FUNC inline
-#else
-#define SPDLOG_NOEXCEPT  noexcept
-#define SPDLOG_CONSTEXPR constexpr
-#if __cplusplus >= 201402L
-#define SPDLOG_CONSTEXPR_FUNC constexpr
-#else
-#define SPDLOG_CONSTEXPR_FUNC inline
-#endif
-#endif
-
-#if defined(__GNUC__) || defined(__clang__)
-#define SPDLOG_DEPRECATED __attribute__((deprecated))
-#elif defined(_MSC_VER)
-#define SPDLOG_DEPRECATED __declspec(deprecated)
-#else
-#define SPDLOG_DEPRECATED
-#endif
-
-// 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
-
-#ifndef SPDLOG_FUNCTION
-#define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__)
-#endif
-
-#ifdef SPDLOG_NO_EXCEPTIONS
-#define SPDLOG_TRY
-#define SPDLOG_THROW(ex)                                                                                               \
-    do                                                                                                                 \
-    {                                                                                                                  \
-        printf("spdlog fatal error: %s\n", ex.what());                                                                 \
-        std::abort();                                                                                                  \
-    } while(0)
-#define SPDLOG_CATCH_STD
-#else
-#define SPDLOG_TRY       try
-#define SPDLOG_THROW(ex) throw(ex)
-#define SPDLOG_CATCH_STD                                                                                               \
-    catch(const std::exception &) {}
-#endif
-
-namespace spdlog
-{
-
-class formatter;
-
-namespace sinks
-{
-    class sink;
-}
-
-#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
-using filename_t = std::wstring;
-// allow macro expansion to occur in SPDLOG_FILENAME_T
-#define SPDLOG_FILENAME_T_INNER(s) L##s
-#define SPDLOG_FILENAME_T(s)       SPDLOG_FILENAME_T_INNER(s)
-#else
-using filename_t  = std::string;
-#define SPDLOG_FILENAME_T(s) s
-#endif
-
-using log_clock       = std::chrono::system_clock;
-using sink_ptr        = std::shared_ptr<sinks::sink>;
-using sinks_init_list = std::initializer_list<sink_ptr>;
-using err_handler     = std::function<void(const std::string &err_msg)>;
-#ifdef SPDLOG_USE_STD_FORMAT
-namespace fmt_lib = std;
-
-using string_view_t = std::string_view;
-using memory_buf_t  = std::string;
-
-template <typename... Args>
-#if __cpp_lib_format >= 202207L
-using format_string_t = std::format_string<Args...>;
-#else
-using format_string_t = std::string_view;
-#endif
-
-template <class T, class Char = char>
-struct is_convertible_to_basic_format_string
-    : std::integral_constant<bool, std::is_convertible<T, std::basic_string_view<Char>>::value>
-{
-};
-
-#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
-using wstring_view_t = std::wstring_view;
-using wmemory_buf_t  = std::wstring;
-
-template <typename... Args>
-#if __cpp_lib_format >= 202207L
-using wformat_string_t = std::wformat_string<Args...>;
-#else
-using wformat_string_t = std::wstring_view;
-#endif
-#endif
-#define SPDLOG_BUF_TO_STRING(x) x
-#else // use fmt lib instead of std::format
-namespace fmt_lib = fmt;
-
-using string_view_t = fmt::basic_string_view<char>;
-using memory_buf_t  = fmt::basic_memory_buffer<char, 250>;
-
-template <typename... Args>
-using format_string_t = fmt::format_string<Args...>;
-
-template <class T>
-using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
-
-// clang doesn't like SFINAE disabled constructor in std::is_convertible<> so have to repeat the condition from
-// basic_format_string here, in addition, fmt::basic_runtime<Char> is only convertible to basic_format_string<Char> but
-// not basic_string_view<Char>
-template <class T, class Char = char>
-struct is_convertible_to_basic_format_string : std::integral_constant<
-                                                   bool,
-                                                   std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
-                                                       std::is_same<remove_cvref_t<T>, fmt::basic_runtime<Char>>::value>
-{
-};
-
-#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
-using wstring_view_t = fmt::basic_string_view<wchar_t>;
-using wmemory_buf_t  = fmt::basic_memory_buffer<wchar_t, 250>;
-
-template <typename... Args>
-using wformat_string_t = fmt::wformat_string<Args...>;
-#endif
-#define SPDLOG_BUF_TO_STRING(x) fmt::to_string(x)
-#endif
-
-#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
-#ifndef _WIN32
-#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
-#endif // _WIN32
-#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
-
-template <class T>
-struct is_convertible_to_any_format_string : std::integral_constant<
-                                                 bool,
-                                                 is_convertible_to_basic_format_string<T, char>::value ||
-                                                     is_convertible_to_basic_format_string<T, wchar_t>::value>
-{
-};
-
-#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 : int
-    {
-        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,
-        n_levels
-    };
-
-#define SPDLOG_LEVEL_NAME_TRACE    spdlog::string_view_t("trace", 5)
-#define SPDLOG_LEVEL_NAME_DEBUG    spdlog::string_view_t("debug", 5)
-#define SPDLOG_LEVEL_NAME_INFO     spdlog::string_view_t("info", 4)
-#define SPDLOG_LEVEL_NAME_WARNING  spdlog::string_view_t("warning", 7)
-#define SPDLOG_LEVEL_NAME_ERROR    spdlog::string_view_t("error", 5)
-#define SPDLOG_LEVEL_NAME_CRITICAL spdlog::string_view_t("critical", 8)
-#define SPDLOG_LEVEL_NAME_OFF      spdlog::string_view_t("off", 3)
-
-#if !defined(SPDLOG_LEVEL_NAMES)
-#define SPDLOG_LEVEL_NAMES                                                                                             \
-    {                                                                                                                  \
-        SPDLOG_LEVEL_NAME_TRACE, SPDLOG_LEVEL_NAME_DEBUG, SPDLOG_LEVEL_NAME_INFO, SPDLOG_LEVEL_NAME_WARNING,           \
-            SPDLOG_LEVEL_NAME_ERROR, SPDLOG_LEVEL_NAME_CRITICAL, SPDLOG_LEVEL_NAME_OFF                                 \
-    }
-#endif
-
-#if !defined(SPDLOG_SHORT_LEVEL_NAMES)
-
-#define SPDLOG_SHORT_LEVEL_NAMES                                                                                       \
-    {                                                                                                                  \
-        "T", "D", "I", "W", "E", "C", "O"                                                                              \
-    }
-#endif
-
-    SPDLOG_API const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
-    SPDLOG_API const char          *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
-    SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
-
-} // namespace level
-
-//
-// Color mode used by sinks with color support.
-//
-enum class color_mode
-{
-    always,
-    automatic,
-    never
-};
-
-//
-// Pattern time - specific time getting to use for pattern_formatter.
-// local time by default
-//
-enum class pattern_time_type
-{
-    local, // log localtime
-    utc    // log utc
-};
-
-//
-// Log exception
-//
-class SPDLOG_API spdlog_ex : public std::exception
-{
-public:
-    explicit spdlog_ex(std::string msg);
-    spdlog_ex(const std::string &msg, int last_errno);
-    const char *what() const SPDLOG_NOEXCEPT override;
-
-private:
-    std::string msg_;
-};
-
-[[noreturn]] SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
-[[noreturn]] SPDLOG_API void throw_spdlog_ex(std::string msg);
-
-struct source_loc
-{
-    SPDLOG_CONSTEXPR source_loc() = default;
-    SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in) :
-        filename{filename_in}, line{line_in}, funcname{funcname_in}
-    {
-    }
-
-    SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT { return line == 0; }
-    const char           *filename{nullptr};
-    int                   line{0};
-    const char           *funcname{nullptr};
-};
-
-struct file_event_handlers
-{
-    file_event_handlers() : before_open(nullptr), after_open(nullptr), before_close(nullptr), after_close(nullptr) {}
-
-    std::function<void(const filename_t &filename)>                         before_open;
-    std::function<void(const filename_t &filename, std::FILE *file_stream)> after_open;
-    std::function<void(const filename_t &filename, std::FILE *file_stream)> before_close;
-    std::function<void(const filename_t &filename)>                         after_close;
-};
-
-namespace details
-{
-
-    // to_string_view
-
-    SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(const memory_buf_t &buf) SPDLOG_NOEXCEPT
-    {
-        return spdlog::string_view_t{buf.data(), buf.size()};
-    }
-
-    SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(spdlog::string_view_t str) SPDLOG_NOEXCEPT
-    {
-        return str;
-    }
-
-#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
-    SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(const wmemory_buf_t &buf) SPDLOG_NOEXCEPT
-    {
-        return spdlog::wstring_view_t{buf.data(), buf.size()};
-    }
-
-    SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(spdlog::wstring_view_t str) SPDLOG_NOEXCEPT
-    {
-        return str;
-    }
-#endif
-
-#ifndef SPDLOG_USE_STD_FORMAT
-    template <typename T, typename... Args>
-    inline fmt::basic_string_view<T> to_string_view(fmt::basic_format_string<T, Args...> fmt)
-    {
-        return fmt;
-    }
-#elif __cpp_lib_format >= 202207L
-    template <typename T, typename... Args>
-    SPDLOG_CONSTEXPR_FUNC std::basic_string_view<T>
-                          to_string_view(std::basic_format_string<T, Args...> fmt) SPDLOG_NOEXCEPT
-    {
-        return fmt.get();
-    }
-#endif
-
-    // make_unique support for pre c++14
-
-#if __cplusplus >= 201402L // C++14 and beyond
-    using std::enable_if_t;
-    using std::make_unique;
-#else
-    template <bool B, class T = void>
-    using enable_if_t = typename std::enable_if<B, T>::type;
-
-    template <typename T, typename... Args>
-    std::unique_ptr<T> make_unique(Args &&...args)
-    {
-                 static_assert(!std::is_array<T>::value, "arrays not supported");
-                 return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-    }
-#endif
-
-    // to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
-    template <typename T, typename U, enable_if_t<!std::is_same<T, U>::value, int> = 0>
-    constexpr T conditional_static_cast(U value)
-    {
-        return static_cast<T>(value);
-    }
-
-    template <typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>
-    constexpr T conditional_static_cast(U value)
-    {
-        return value;
-    }
-
-} // namespace details
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "common-inl.h"
-#endif
diff --git a/include/spdlog/details/backtracer-inl.h b/include/spdlog/details/backtracer-inl.h
deleted file mode 100644
index 19f73fcca..000000000
--- a/include/spdlog/details/backtracer-inl.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/details/backtracer.h>
-#endif
-namespace spdlog
-{
-namespace details
-{
-    SPDLOG_INLINE backtracer::backtracer(const backtracer &other)
-    {
-        std::lock_guard<std::mutex> lock(other.mutex_);
-        enabled_  = other.enabled();
-        messages_ = other.messages_;
-    }
-
-    SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT
-    {
-        std::lock_guard<std::mutex> lock(other.mutex_);
-        enabled_  = other.enabled();
-        messages_ = std::move(other.messages_);
-    }
-
-    SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other)
-    {
-        std::lock_guard<std::mutex> lock(mutex_);
-        enabled_  = other.enabled();
-        messages_ = std::move(other.messages_);
-        return *this;
-    }
-
-    SPDLOG_INLINE void backtracer::enable(size_t size)
-    {
-        std::lock_guard<std::mutex> lock{mutex_};
-        enabled_.store(true, std::memory_order_relaxed);
-        messages_ = circular_q<log_msg_buffer>{size};
-    }
-
-    SPDLOG_INLINE void backtracer::disable()
-    {
-        std::lock_guard<std::mutex> lock{mutex_};
-        enabled_.store(false, std::memory_order_relaxed);
-    }
-
-    SPDLOG_INLINE bool backtracer::enabled() const
-    {
-        return enabled_.load(std::memory_order_relaxed);
-    }
-
-    SPDLOG_INLINE void backtracer::push_back(const log_msg &msg)
-    {
-        std::lock_guard<std::mutex> lock{mutex_};
-        messages_.push_back(log_msg_buffer{msg});
-    }
-
-    // pop all items in the q and apply the given fun on each of them.
-    SPDLOG_INLINE void backtracer::foreach_pop(std::function<void(const details::log_msg &)> fun)
-    {
-        std::lock_guard<std::mutex> lock{mutex_};
-        while(!messages_.empty())
-        {
-            auto &front_msg = messages_.front();
-            fun(front_msg);
-            messages_.pop_front();
-        }
-    }
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/backtracer.h b/include/spdlog/details/backtracer.h
deleted file mode 100644
index c7709289c..000000000
--- a/include/spdlog/details/backtracer.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <atomic>
-#include <functional>
-#include <mutex>
-#include <spdlog/details/circular_q.h>
-#include <spdlog/details/log_msg_buffer.h>
-
-// Store log messages in circular buffer.
-// Useful for storing debug data in case of error/warning happens.
-
-namespace spdlog
-{
-namespace details
-{
-    class SPDLOG_API backtracer
-    {
-        mutable std::mutex         mutex_;
-        std::atomic<bool>          enabled_{false};
-        circular_q<log_msg_buffer> messages_;
-
-    public:
-        backtracer() = default;
-        backtracer(const backtracer &other);
-
-        backtracer(backtracer &&other) SPDLOG_NOEXCEPT;
-        backtracer &operator=(backtracer other);
-
-        void enable(size_t size);
-        void disable();
-        bool enabled() const;
-        void push_back(const log_msg &msg);
-
-        // pop all items in the q and apply the given fun on each of them.
-        void foreach_pop(std::function<void(const details::log_msg &)> fun);
-    };
-
-} // namespace details
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "backtracer-inl.h"
-#endif
diff --git a/include/spdlog/details/circular_q.h b/include/spdlog/details/circular_q.h
deleted file mode 100644
index 1fd067a0b..000000000
--- a/include/spdlog/details/circular_q.h
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-// circular q view of std::vector.
-#pragma once
-
-#include <cassert>
-#include <vector>
-
-namespace spdlog
-{
-namespace details
-{
-    template <typename T>
-    class circular_q
-    {
-        size_t                             max_items_       = 0;
-        typename std::vector<T>::size_type head_            = 0;
-        typename std::vector<T>::size_type tail_            = 0;
-        size_t                             overrun_counter_ = 0;
-        std::vector<T>                     v_;
-
-    public:
-        using value_type = T;
-
-        // empty ctor - create a disabled queue with no elements allocated at all
-        circular_q() = default;
-
-        explicit circular_q(size_t max_items) :
-            max_items_(max_items + 1) // one item is reserved as marker for full q
-            ,
-            v_(max_items_)
-        {
-        }
-
-        circular_q(const circular_q &)            = default;
-        circular_q &operator=(const circular_q &) = default;
-
-        // move cannot be default,
-        // since we need to reset head_, tail_, etc to zero in the moved object
-        circular_q(circular_q &&other) SPDLOG_NOEXCEPT { copy_moveable(std::move(other)); }
-
-        circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT
-        {
-            copy_moveable(std::move(other));
-            return *this;
-        }
-
-        // push back, overrun (oldest) item if no room left
-        void push_back(T &&item)
-        {
-            if(max_items_ > 0)
-            {
-                v_[tail_] = std::move(item);
-                tail_     = (tail_ + 1) % max_items_;
-
-                if(tail_ == head_) // overrun last item if full
-                {
-                    head_ = (head_ + 1) % max_items_;
-                    ++overrun_counter_;
-                }
-            }
-        }
-
-        // Return reference to the front item.
-        // If there are no elements in the container, the behavior is undefined.
-        const T &front() const { return v_[head_]; }
-
-        T &front() { return v_[head_]; }
-
-        // Return number of elements actually stored
-        size_t size() const
-        {
-            if(tail_ >= head_)
-            {
-                return tail_ - head_;
-            }
-            else
-            {
-                return max_items_ - (head_ - tail_);
-            }
-        }
-
-        // Return const reference to item by index.
-        // If index is out of range 0…size()-1, the behavior is undefined.
-        const T &at(size_t i) const
-        {
-            assert(i < size());
-            return v_[(head_ + i) % max_items_];
-        }
-
-        // Pop item from front.
-        // If there are no elements in the container, the behavior is undefined.
-        void pop_front() { head_ = (head_ + 1) % max_items_; }
-
-        bool empty() const { return tail_ == head_; }
-
-        bool full() const
-        {
-            // head is ahead of the tail by 1
-            if(max_items_ > 0)
-            {
-                return ((tail_ + 1) % max_items_) == head_;
-            }
-            return false;
-        }
-
-        size_t overrun_counter() const { return overrun_counter_; }
-
-        void reset_overrun_counter() { overrun_counter_ = 0; }
-
-    private:
-        // copy from other&& and reset it to disabled state
-        void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT
-        {
-            max_items_       = other.max_items_;
-            head_            = other.head_;
-            tail_            = other.tail_;
-            overrun_counter_ = other.overrun_counter_;
-            v_               = std::move(other.v_);
-
-            // put &&other in disabled, but valid state
-            other.max_items_ = 0;
-            other.head_ = other.tail_ = 0;
-            other.overrun_counter_    = 0;
-        }
-    };
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/console_globals.h b/include/spdlog/details/console_globals.h
deleted file mode 100644
index 67295c6f8..000000000
--- a/include/spdlog/details/console_globals.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <mutex>
-#include <spdlog/details/null_mutex.h>
-
-namespace spdlog
-{
-namespace details
-{
-
-    struct console_mutex
-    {
-        using mutex_t = std::mutex;
-        static mutex_t &mutex()
-        {
-            static mutex_t s_mutex;
-            return s_mutex;
-        }
-    };
-
-    struct console_nullmutex
-    {
-        using mutex_t = null_mutex;
-        static mutex_t &mutex()
-        {
-            static mutex_t s_mutex;
-            return s_mutex;
-        }
-    };
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/file_helper-inl.h b/include/spdlog/details/file_helper-inl.h
deleted file mode 100644
index ae4b9df30..000000000
--- a/include/spdlog/details/file_helper-inl.h
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/details/file_helper.h>
-#endif
-
-#include <cerrno>
-#include <chrono>
-#include <cstdio>
-#include <spdlog/common.h>
-#include <spdlog/details/os.h>
-#include <string>
-#include <thread>
-#include <tuple>
-
-namespace spdlog
-{
-namespace details
-{
-
-    SPDLOG_INLINE file_helper::file_helper(const file_event_handlers &event_handlers) : event_handlers_(event_handlers)
-    {
-    }
-
-    SPDLOG_INLINE file_helper::~file_helper()
-    {
-        close();
-    }
-
-    SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
-    {
-        close();
-        filename_ = fname;
-
-        auto *mode       = SPDLOG_FILENAME_T("ab");
-        auto *trunc_mode = SPDLOG_FILENAME_T("wb");
-
-        if(event_handlers_.before_open)
-        {
-            event_handlers_.before_open(filename_);
-        }
-        for(int tries = 0; tries < open_tries_; ++tries)
-        {
-            // create containing folder if not exists already.
-            os::create_dir(os::dir_name(fname));
-            if(truncate)
-            {
-                // Truncate by opening-and-closing a tmp file in "wb" mode, always
-                // opening the actual log-we-write-to in "ab" mode, since that
-                // interacts more politely with eternal processes that might
-                // rotate/truncate the file underneath us.
-                std::FILE *tmp;
-                if(os::fopen_s(&tmp, fname, trunc_mode))
-                {
-                    continue;
-                }
-                std::fclose(tmp);
-            }
-            if(!os::fopen_s(&fd_, fname, mode))
-            {
-                if(event_handlers_.after_open)
-                {
-                    event_handlers_.after_open(filename_, fd_);
-                }
-                return;
-            }
-
-            details::os::sleep_for_millis(open_interval_);
-        }
-
-        throw_spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing", errno);
-    }
-
-    SPDLOG_INLINE void file_helper::reopen(bool truncate)
-    {
-        if(filename_.empty())
-        {
-            throw_spdlog_ex("Failed re opening file - was not opened before");
-        }
-        this->open(filename_, truncate);
-    }
-
-    SPDLOG_INLINE void file_helper::flush()
-    {
-        if(std::fflush(fd_) != 0)
-        {
-            throw_spdlog_ex("Failed flush to file " + os::filename_to_str(filename_), errno);
-        }
-    }
-
-    SPDLOG_INLINE void file_helper::close()
-    {
-        if(fd_ != nullptr)
-        {
-            if(event_handlers_.before_close)
-            {
-                event_handlers_.before_close(filename_, fd_);
-            }
-
-            std::fclose(fd_);
-            fd_ = nullptr;
-
-            if(event_handlers_.after_close)
-            {
-                event_handlers_.after_close(filename_);
-            }
-        }
-    }
-
-    SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf)
-    {
-        size_t msg_size = buf.size();
-        auto   data     = buf.data();
-        if(std::fwrite(data, 1, msg_size, fd_) != msg_size)
-        {
-            throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno);
-        }
-    }
-
-    SPDLOG_INLINE size_t file_helper::size() const
-    {
-        if(fd_ == nullptr)
-        {
-            throw_spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_));
-        }
-        return os::filesize(fd_);
-    }
-
-    SPDLOG_INLINE const filename_t &file_helper::filename() const
-    {
-        return filename_;
-    }
-
-    //
-    // return file path and its extension:
-    //
-    // "mylog.txt" => ("mylog", ".txt")
-    // "mylog" => ("mylog", "")
-    // "mylog." => ("mylog.", "")
-    // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
-    //
-    // the starting dot in filenames is ignored (hidden files):
-    //
-    // ".mylog" => (".mylog". "")
-    // "my_folder/.mylog" => ("my_folder/.mylog", "")
-    // "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
-    SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension(const filename_t &fname)
-    {
-        auto ext_index = fname.rfind('.');
-
-        // no valid extension found - return whole path and empty string as
-        // extension
-        if(ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
-        {
-            return std::make_tuple(fname, filename_t());
-        }
-
-        // treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
-        auto folder_index = fname.find_last_of(details::os::folder_seps_filename);
-        if(folder_index != filename_t::npos && folder_index >= ext_index - 1)
-        {
-            return std::make_tuple(fname, filename_t());
-        }
-
-        // finally - return a valid base and extension tuple
-        return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
-    }
-
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/file_helper.h b/include/spdlog/details/file_helper.h
deleted file mode 100644
index b6b558ea3..000000000
--- a/include/spdlog/details/file_helper.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <spdlog/common.h>
-#include <tuple>
-
-namespace spdlog
-{
-namespace details
-{
-
-    // Helper class for file sinks.
-    // When failing to open a file, retry several times(5) with a delay interval(10 ms).
-    // Throw spdlog_ex exception on errors.
-
-    class SPDLOG_API file_helper
-    {
-    public:
-        file_helper() = default;
-        explicit file_helper(const file_event_handlers &event_handlers);
-
-        file_helper(const file_helper &)            = delete;
-        file_helper &operator=(const file_helper &) = delete;
-        ~file_helper();
-
-        void              open(const filename_t &fname, bool truncate = false);
-        void              reopen(bool truncate);
-        void              flush();
-        void              close();
-        void              write(const memory_buf_t &buf);
-        size_t            size() const;
-        const filename_t &filename() const;
-
-        //
-        // return file path and its extension:
-        //
-        // "mylog.txt" => ("mylog", ".txt")
-        // "mylog" => ("mylog", "")
-        // "mylog." => ("mylog.", "")
-        // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
-        //
-        // the starting dot in filenames is ignored (hidden files):
-        //
-        // ".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_extension(const filename_t &fname);
-
-    private:
-        const int           open_tries_    = 5;
-        const unsigned int  open_interval_ = 10;
-        std::FILE          *fd_{nullptr};
-        filename_t          filename_;
-        file_event_handlers event_handlers_;
-    };
-} // namespace details
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "file_helper-inl.h"
-#endif
diff --git a/include/spdlog/details/fmt_helper.h b/include/spdlog/details/fmt_helper.h
deleted file mode 100644
index 40560c721..000000000
--- a/include/spdlog/details/fmt_helper.h
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-#pragma once
-
-#include <chrono>
-#include <iterator>
-#include <spdlog/common.h>
-#include <spdlog/fmt/fmt.h>
-#include <type_traits>
-
-#ifdef SPDLOG_USE_STD_FORMAT
-#include <charconv>
-#include <limits>
-#endif
-
-// Some fmt helpers to efficiently format and pad ints and strings
-namespace spdlog
-{
-namespace details
-{
-    namespace fmt_helper
-    {
-
-        inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest)
-        {
-            auto *buf_ptr = view.data();
-            dest.append(buf_ptr, buf_ptr + view.size());
-        }
-
-#ifdef SPDLOG_USE_STD_FORMAT
-        template <typename T>
-        inline void append_int(T n, memory_buf_t &dest)
-        {
-            // Buffer should be large enough to hold all digits (digits10 + 1) and a sign
-            SPDLOG_CONSTEXPR const auto BUF_SIZE = std::numeric_limits<T>::digits10 + 2;
-            char                        buf[BUF_SIZE];
-
-            auto [ptr, ec] = std::to_chars(buf, buf + BUF_SIZE, n, 10);
-            if(ec == std::errc())
-            {
-                dest.append(buf, ptr);
-            }
-            else
-            {
-                throw_spdlog_ex("Failed to format int", static_cast<int>(ec));
-            }
-        }
-#else
-        template <typename T>
-        inline void append_int(T n, memory_buf_t &dest)
-        {
-            fmt::format_int i(n);
-            dest.append(i.data(), i.data() + i.size());
-        }
-#endif
-
-        template <typename T>
-        SPDLOG_CONSTEXPR_FUNC unsigned int count_digits_fallback(T n)
-        {
-            // taken from fmt: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/format.h#L899-L912
-            unsigned 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
-                // "Three Optimization Tips for C++". See speed-test for a comparison.
-                if(n < 10)
-                    return count;
-                if(n < 100)
-                    return count + 1;
-                if(n < 1000)
-                    return count + 2;
-                if(n < 10000)
-                    return count + 3;
-                n /= 10000u;
-                count += 4;
-            }
-        }
-
-        template <typename T>
-        inline unsigned int count_digits(T n)
-        {
-            using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
-#ifdef SPDLOG_USE_STD_FORMAT
-            return count_digits_fallback(static_cast<count_type>(n));
-#else
-            return static_cast<unsigned int>(fmt::
-// fmt 7.0.0 renamed the internal namespace to detail.
-// See: https://github.com/fmtlib/fmt/issues/1538
-#if FMT_VERSION < 70000
-                                                 internal
-#else
-                                                 detail
-#endif
-                                             ::count_digits(static_cast<count_type>(n)));
-#endif
-        }
-
-        inline void pad2(int n, memory_buf_t &dest)
-        {
-            if(n >= 0 && n < 100) // 0-99
-            {
-                dest.push_back(static_cast<char>('0' + n / 10));
-                dest.push_back(static_cast<char>('0' + n % 10));
-            }
-            else // unlikely, but just in case, let fmt deal with it
-            {
-                fmt_lib::format_to(std::back_inserter(dest), SPDLOG_FMT_STRING("{:02}"), n);
-            }
-        }
-
-        template <typename T>
-        inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
-        {
-            static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
-            for(auto digits = count_digits(n); digits < width; digits++)
-            {
-                dest.push_back('0');
-            }
-            append_int(n, dest);
-        }
-
-        template <typename T>
-        inline void pad3(T n, memory_buf_t &dest)
-        {
-            static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T");
-            if(n < 1000)
-            {
-                dest.push_back(static_cast<char>(n / 100 + '0'));
-                n = n % 100;
-                dest.push_back(static_cast<char>((n / 10) + '0'));
-                dest.push_back(static_cast<char>((n % 10) + '0'));
-            }
-            else
-            {
-                append_int(n, dest);
-            }
-        }
-
-        template <typename T>
-        inline void pad6(T n, memory_buf_t &dest)
-        {
-            pad_uint(n, 6, dest);
-        }
-
-        template <typename T>
-        inline void pad9(T n, memory_buf_t &dest)
-        {
-            pad_uint(n, 9, dest);
-        }
-
-        // return fraction of a second of the given time_point.
-        // e.g.
-        // fraction<std::milliseconds>(tp) -> will return the millis part of the second
-        template <typename ToDuration>
-        inline ToDuration time_fraction(log_clock::time_point tp)
-        {
-            using std::chrono::duration_cast;
-            using std::chrono::seconds;
-            auto duration = tp.time_since_epoch();
-            auto secs     = duration_cast<seconds>(duration);
-            return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
-        }
-
-    } // namespace fmt_helper
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/log_msg-inl.h b/include/spdlog/details/log_msg-inl.h
deleted file mode 100644
index fb2ea9057..000000000
--- a/include/spdlog/details/log_msg-inl.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/details/log_msg.h>
-#endif
-
-#include <spdlog/details/os.h>
-
-namespace spdlog
-{
-namespace details
-{
-
-    SPDLOG_INLINE log_msg::log_msg(
-        spdlog::log_clock::time_point log_time,
-        spdlog::source_loc            loc,
-        string_view_t                 a_logger_name,
-        spdlog::level::level_enum     lvl,
-        spdlog::string_view_t         msg) :
-        logger_name(a_logger_name),
-        level(lvl),
-        time(log_time)
-#ifndef SPDLOG_NO_THREAD_ID
-        ,
-        thread_id(os::thread_id())
-#endif
-        ,
-        source(loc),
-        payload(msg)
-    {
-    }
-
-    SPDLOG_INLINE log_msg::log_msg(
-        spdlog::source_loc        loc,
-        string_view_t             a_logger_name,
-        spdlog::level::level_enum lvl,
-        spdlog::string_view_t     msg) :
-        log_msg(os::now(), loc, a_logger_name, lvl, msg)
-    {
-    }
-
-    SPDLOG_INLINE
-    log_msg::log_msg(string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg) :
-        log_msg(os::now(), source_loc{}, a_logger_name, lvl, msg)
-    {
-    }
-
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/log_msg.h b/include/spdlog/details/log_msg.h
deleted file mode 100644
index 218ed72cf..000000000
--- a/include/spdlog/details/log_msg.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <spdlog/common.h>
-#include <string>
-
-namespace spdlog
-{
-namespace details
-{
-    struct SPDLOG_API log_msg
-    {
-        log_msg() = default;
-        log_msg(
-            log_clock::time_point log_time,
-            source_loc            loc,
-            string_view_t         logger_name,
-            level::level_enum     lvl,
-            string_view_t         msg);
-        log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
-        log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
-        log_msg(const log_msg &other)            = default;
-        log_msg &operator=(const log_msg &other) = default;
-
-        string_view_t         logger_name;
-        level::level_enum     level{level::off};
-        log_clock::time_point time;
-        size_t                thread_id{0};
-
-        // 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;
-        string_view_t payload;
-    };
-} // namespace details
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "log_msg-inl.h"
-#endif
diff --git a/include/spdlog/details/log_msg_buffer-inl.h b/include/spdlog/details/log_msg_buffer-inl.h
deleted file mode 100644
index 5852a0eb4..000000000
--- a/include/spdlog/details/log_msg_buffer-inl.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/details/log_msg_buffer.h>
-#endif
-
-namespace spdlog
-{
-namespace details
-{
-
-    SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg) : log_msg{orig_msg}
-    {
-        buffer.append(logger_name.begin(), logger_name.end());
-        buffer.append(payload.begin(), payload.end());
-        update_string_views();
-    }
-
-    SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other) : log_msg{other}
-    {
-        buffer.append(logger_name.begin(), logger_name.end());
-        buffer.append(payload.begin(), payload.end());
-        update_string_views();
-    }
-
-    SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT
-        : log_msg{other},
-          buffer{std::move(other.buffer)}
-    {
-        update_string_views();
-    }
-
-    SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other)
-    {
-        log_msg::operator=(other);
-        buffer.clear();
-        buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size());
-        update_string_views();
-        return *this;
-    }
-
-    SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT
-    {
-        log_msg::operator=(other);
-        buffer = std::move(other.buffer);
-        update_string_views();
-        return *this;
-    }
-
-    SPDLOG_INLINE void log_msg_buffer::update_string_views()
-    {
-        logger_name = string_view_t{buffer.data(), logger_name.size()};
-        payload     = string_view_t{buffer.data() + logger_name.size(), payload.size()};
-    }
-
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/log_msg_buffer.h b/include/spdlog/details/log_msg_buffer.h
deleted file mode 100644
index 46261b555..000000000
--- a/include/spdlog/details/log_msg_buffer.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <spdlog/details/log_msg.h>
-
-namespace spdlog
-{
-namespace details
-{
-
-    // Extend log_msg with internal buffer to store its payload.
-    // This is needed since log_msg holds string_views that points to stack data.
-
-    class SPDLOG_API log_msg_buffer : public log_msg
-    {
-        memory_buf_t buffer;
-        void         update_string_views();
-
-    public:
-        log_msg_buffer() = default;
-        explicit log_msg_buffer(const log_msg &orig_msg);
-        log_msg_buffer(const log_msg_buffer &other);
-        log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
-        log_msg_buffer &operator=(const log_msg_buffer &other);
-        log_msg_buffer &operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
-    };
-
-} // namespace details
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "log_msg_buffer-inl.h"
-#endif
diff --git a/include/spdlog/details/mpmc_blocking_q.h b/include/spdlog/details/mpmc_blocking_q.h
deleted file mode 100644
index 471bc2bec..000000000
--- a/include/spdlog/details/mpmc_blocking_q.h
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-// multi producer-multi consumer blocking queue.
-// enqueue(..) - will block until room found to put the new message.
-// enqueue_nowait(..) - will return immediately with false if no room left in
-// the queue.
-// dequeue_for(..) - will block until the queue is not empty or timeout have
-// passed.
-
-#include <condition_variable>
-#include <mutex>
-#include <spdlog/details/circular_q.h>
-
-namespace spdlog
-{
-namespace details
-{
-
-    template <typename T>
-    class mpmc_blocking_queue
-    {
-    public:
-        using item_type = T;
-        explicit mpmc_blocking_queue(size_t max_items) : q_(max_items) {}
-
-#ifndef __MINGW32__
-        // try to enqueue and block if no room left
-        void enqueue(T &&item)
-        {
-            {
-                std::unique_lock<std::mutex> lock(queue_mutex_);
-                pop_cv_.wait(lock, [this] { return !this->q_.full(); });
-                q_.push_back(std::move(item));
-            }
-            push_cv_.notify_one();
-        }
-
-        // enqueue immediately. overrun oldest message in the queue if no room left.
-        void enqueue_nowait(T &&item)
-        {
-            {
-                std::unique_lock<std::mutex> lock(queue_mutex_);
-                q_.push_back(std::move(item));
-            }
-            push_cv_.notify_one();
-        }
-
-        // try to dequeue item. if no item found. wait up to timeout and try again
-        // Return true, if succeeded dequeue item, false otherwise
-        bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
-        {
-            {
-                std::unique_lock<std::mutex> lock(queue_mutex_);
-                if(!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
-                {
-                    return false;
-                }
-                popped_item = std::move(q_.front());
-                q_.pop_front();
-            }
-            pop_cv_.notify_one();
-            return true;
-        }
-
-#else
-        // apparently mingw deadlocks if the mutex is released before cv.notify_one(),
-        // so release the mutex at the very end each function.
-
-        // try to enqueue and block if no room left
-        void enqueue(T &&item)
-        {
-            std::unique_lock<std::mutex> lock(queue_mutex_);
-            pop_cv_.wait(lock, [this] { return !this->q_.full(); });
-            q_.push_back(std::move(item));
-            push_cv_.notify_one();
-        }
-
-        // enqueue immediately. overrun oldest message in the queue if no room left.
-        void enqueue_nowait(T &&item)
-        {
-            std::unique_lock<std::mutex> lock(queue_mutex_);
-            q_.push_back(std::move(item));
-            push_cv_.notify_one();
-        }
-
-        // try to dequeue item. if no item found. wait up to timeout and try again
-        // Return true, if succeeded dequeue item, false otherwise
-        bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
-        {
-            std::unique_lock<std::mutex> lock(queue_mutex_);
-            if(!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
-            {
-                return false;
-            }
-            popped_item = std::move(q_.front());
-            q_.pop_front();
-            pop_cv_.notify_one();
-            return true;
-        }
-
-#endif
-
-        size_t overrun_counter()
-        {
-            std::unique_lock<std::mutex> lock(queue_mutex_);
-            return q_.overrun_counter();
-        }
-
-        size_t size()
-        {
-            std::unique_lock<std::mutex> lock(queue_mutex_);
-            return q_.size();
-        }
-
-        void reset_overrun_counter()
-        {
-            std::unique_lock<std::mutex> lock(queue_mutex_);
-            q_.reset_overrun_counter();
-        }
-
-    private:
-        std::mutex                     queue_mutex_;
-        std::condition_variable        push_cv_;
-        std::condition_variable        pop_cv_;
-        spdlog::details::circular_q<T> q_;
-    };
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/null_mutex.h b/include/spdlog/details/null_mutex.h
deleted file mode 100644
index 2b70596a7..000000000
--- a/include/spdlog/details/null_mutex.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <atomic>
-#include <utility>
-// null, no cost dummy "mutex" and dummy "atomic" int
-
-namespace spdlog
-{
-namespace details
-{
-    struct null_mutex
-    {
-        void lock() const {}
-        void unlock() const {}
-    };
-
-    struct null_atomic_int
-    {
-        int value;
-        null_atomic_int() = default;
-
-        explicit null_atomic_int(int new_value) : value(new_value) {}
-
-        int load(std::memory_order = std::memory_order_relaxed) const { return value; }
-
-        void store(int new_value, std::memory_order = std::memory_order_relaxed) { value = new_value; }
-
-        int exchange(int new_value, std::memory_order = std::memory_order_relaxed)
-        {
-            std::swap(new_value, value);
-            return new_value; // return value before the call
-        }
-    };
-
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h
deleted file mode 100644
index 1bc1ee840..000000000
--- a/include/spdlog/details/os-inl.h
+++ /dev/null
@@ -1,625 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/details/os.h>
-#endif
-
-#include <algorithm>
-#include <array>
-#include <chrono>
-#include <cstdio>
-#include <cstdlib>
-#include <cstring>
-#include <ctime>
-#include <spdlog/common.h>
-#include <string>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <thread>
-
-#ifdef _WIN32
-
-#include <io.h>      // _get_osfhandle and _isatty support
-#include <process.h> //  _get_pid support
-#include <spdlog/details/windows_include.h>
-
-#ifdef __MINGW32__
-#include <share.h>
-#endif
-
-#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
-#include <limits>
-#endif
-
-#include <direct.h> // for _mkdir/_wmkdir
-
-#else // unix
-
-#include <fcntl.h>
-#include <unistd.h>
-
-#ifdef __linux__
-#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
-
-#elif defined(_AIX)
-#include <pthread.h> // for pthread_getthrds_np
-
-#elif defined(__DragonFly__) || defined(__FreeBSD__)
-#include <pthread_np.h> // for pthread_getthreadid_np
-
-#elif defined(__NetBSD__)
-#include <lwp.h> // for _lwp_self
-
-#elif defined(__sun)
-#include <thread.h> // for thr_self
-#endif
-
-#endif // unix
-
-#ifndef __has_feature      // Clang - feature checking macros.
-#define __has_feature(x) 0 // Compatibility with non-clang compilers.
-#endif
-
-namespace spdlog
-{
-namespace details
-{
-    namespace os
-    {
-
-        SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
-        {
-#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
-            timespec ts;
-            ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
-            return std::chrono::time_point<log_clock, typename log_clock::duration>(
-                std::chrono::duration_cast<typename log_clock::duration>(
-                    std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
-
-#else
-            return log_clock::now();
-#endif
-        }
-        SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
-        {
-#ifdef _WIN32
-            std::tm tm;
-            ::localtime_s(&tm, &time_tt);
-#else
-            std::tm tm;
-            ::localtime_r(&time_tt, &tm);
-#endif
-            return tm;
-        }
-
-        SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT
-        {
-            std::time_t now_t = ::time(nullptr);
-            return localtime(now_t);
-        }
-
-        SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
-        {
-#ifdef _WIN32
-            std::tm tm;
-            ::gmtime_s(&tm, &time_tt);
-#else
-            std::tm tm;
-            ::gmtime_r(&time_tt, &tm);
-#endif
-            return tm;
-        }
-
-        SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
-        {
-            std::time_t now_t = ::time(nullptr);
-            return gmtime(now_t);
-        }
-
-        // fopen_s on non windows for writing
-        SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
-        {
-#ifdef _WIN32
-#ifdef SPDLOG_WCHAR_FILENAMES
-            *fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
-#else
-            *fp          = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
-#endif
-#if defined(SPDLOG_PREVENT_CHILD_FD)
-            if(*fp != nullptr)
-            {
-                auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
-                if(!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
-                {
-                    ::fclose(*fp);
-                    *fp = nullptr;
-                }
-            }
-#endif
-#else // unix
-#if defined(SPDLOG_PREVENT_CHILD_FD)
-            const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
-            const int fd        = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
-            if(fd == -1)
-            {
-                return true;
-            }
-            *fp = ::fdopen(fd, mode.c_str());
-            if(*fp == nullptr)
-            {
-                ::close(fd);
-            }
-#else
-            *fp            = ::fopen((filename.c_str()), mode.c_str());
-#endif
-#endif
-
-            return *fp == nullptr;
-        }
-
-        SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT
-        {
-#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
-            return ::_wremove(filename.c_str());
-#else
-            return std::remove(filename.c_str());
-#endif
-        }
-
-        SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT
-        {
-            return path_exists(filename) ? remove(filename) : 0;
-        }
-
-        SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
-        {
-#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
-            return ::_wrename(filename1.c_str(), filename2.c_str());
-#else
-            return std::rename(filename1.c_str(), filename2.c_str());
-#endif
-        }
-
-        // Return true if path exists (file or directory)
-        SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT
-        {
-#ifdef _WIN32
-#ifdef SPDLOG_WCHAR_FILENAMES
-            auto attribs = ::GetFileAttributesW(filename.c_str());
-#else
-            auto attribs = ::GetFileAttributesA(filename.c_str());
-#endif
-            return attribs != INVALID_FILE_ATTRIBUTES;
-#else // common linux/unix all have the stat system call
-            struct stat buffer;
-            return (::stat(filename.c_str(), &buffer) == 0);
-#endif
-        }
-
-#ifdef _MSC_VER
-// avoid warning about unreachable statement at the end of filesize()
-#pragma warning(push)
-#pragma warning(disable : 4702)
-#endif
-
-        // Return file size according to open FILE* object
-        SPDLOG_INLINE size_t filesize(FILE *f)
-        {
-            if(f == nullptr)
-            {
-                throw_spdlog_ex("Failed getting file size. fd is null");
-            }
-#if defined(_WIN32) && !defined(__CYGWIN__)
-            int fd = ::_fileno(f);
-#if defined(_WIN64) // 64 bits
-            __int64 ret = ::_filelengthi64(fd);
-            if(ret >= 0)
-            {
-                return static_cast<size_t>(ret);
-            }
-
-#else // windows 32 bits
-            long ret     = ::_filelength(fd);
-            if(ret >= 0)
-            {
-                return static_cast<size_t>(ret);
-            }
-#endif
-
-#else // unix
-// OpenBSD and AIX doesn't compile with :: before the fileno(..)
-#if defined(__OpenBSD__) || defined(_AIX)
-            int fd = fileno(f);
-#else
-            int         fd = ::fileno(f);
-#endif
-// 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
-#if(defined(__linux__) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64))
-            struct stat64 st;
-            if(::fstat64(fd, &st) == 0)
-            {
-                return static_cast<size_t>(st.st_size);
-            }
-#else // other unix or linux 32 bits or cygwin
-            struct stat st;
-            if(::fstat(fd, &st) == 0)
-            {
-                return static_cast<size_t>(st.st_size);
-            }
-#endif
-#endif
-            throw_spdlog_ex("Failed getting file size from fd", errno);
-            return 0; // will not be reached.
-        }
-
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
-        // Return utc offset in minutes or throw spdlog_ex on failure
-        SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
-        {
-#ifdef _WIN32
-#if _WIN32_WINNT < _WIN32_WINNT_WS08
-            TIME_ZONE_INFORMATION tzinfo;
-            auto                  rv = ::GetTimeZoneInformation(&tzinfo);
-#else
-            DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
-            auto                          rv = ::GetDynamicTimeZoneInformation(&tzinfo);
-#endif
-            if(rv == TIME_ZONE_ID_INVALID)
-                throw_spdlog_ex("Failed getting timezone info. ", errno);
-
-            int offset = -tzinfo.Bias;
-            if(tm.tm_isdst)
-            {
-                offset -= tzinfo.DaylightBias;
-            }
-            else
-            {
-                offset -= tzinfo.StandardBias;
-            }
-            return offset;
-#else
-
-#if defined(sun) || defined(__sun) || defined(_AIX) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE))
-            // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
-            struct helper
-            {
-                static long int calculate_gmt_offset(
-                    const std::tm &localtm = details::os::localtime(),
-                    const std::tm &gmtm    = details::os::gmtime())
-                {
-                    int local_year = localtm.tm_year + (1900 - 1);
-                    int gmt_year   = gmtm.tm_year + (1900 - 1);
-
-                    long int days = (
-                        // difference in day of year
-                        localtm.tm_yday -
-                        gmtm.tm_yday
-
-                        // + intervening leap days
-                        + ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
-                        ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
-
-                        // + difference in years * 365 */
-                        + static_cast<long int>(local_year - gmt_year) * 365);
-
-                    long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
-                    long int mins  = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
-                    long int secs  = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
-
-                    return secs;
-                }
-            };
-
-            auto offset_seconds = helper::calculate_gmt_offset(tm);
-#else
-            auto offset_seconds = tm.tm_gmtoff;
-#endif
-
-            return static_cast<int>(offset_seconds / 60);
-#endif
-        }
-
-        // Return current thread id as size_t
-        // It exists because the std::this_thread::get_id() is much slower(especially
-        // under VS 2013)
-        SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT
-        {
-#ifdef _WIN32
-            return static_cast<size_t>(::GetCurrentThreadId());
-#elif defined(__linux__)
-#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
-#define SYS_gettid __NR_gettid
-#endif
-            return static_cast<size_t>(::syscall(SYS_gettid));
-#elif defined(_AIX)
-            struct __pthrdsinfo buf;
-            int                 reg_size = 0;
-            pthread_t           pt       = pthread_self();
-            int retval = pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_TID, &buf, sizeof(buf), NULL, &reg_size);
-            int tid    = (!retval) ? buf.__pi_tid : 0;
-            return static_cast<size_t>(tid);
-#elif defined(__DragonFly__) || defined(__FreeBSD__)
-            return static_cast<size_t>(::pthread_getthreadid_np());
-#elif defined(__NetBSD__)
-            return static_cast<size_t>(::_lwp_self());
-#elif defined(__OpenBSD__)
-            return static_cast<size_t>(::getthrid());
-#elif defined(__sun)
-            return static_cast<size_t>(::thr_self());
-#elif __APPLE__
-            uint64_t tid;
-            pthread_threadid_np(nullptr, &tid);
-            return static_cast<size_t>(tid);
-#else // Default to standard C++11 (other Unix)
-            return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
-#endif
-        }
-
-        // Return current thread id as size_t (from thread local storage)
-        SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT
-        {
-#if defined(SPDLOG_NO_TLS)
-            return _thread_id();
-#else // cache thread id in tls
-            static thread_local const size_t tid = _thread_id();
-            return tid;
-#endif
-        }
-
-        // This is avoid msvc issue in sleep_for that happens if the clock changes.
-        // See https://github.com/gabime/spdlog/issues/609
-        SPDLOG_INLINE void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT
-        {
-#if defined(_WIN32)
-            ::Sleep(milliseconds);
-#else
-            std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
-#endif
-        }
-
-// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
-#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
-        SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
-        {
-            memory_buf_t buf;
-            wstr_to_utf8buf(filename, buf);
-            return SPDLOG_BUF_TO_STRING(buf);
-        }
-#else
-        SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
-        {
-            return filename;
-        }
-#endif
-
-        SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT
-        {
-#ifdef _WIN32
-            return conditional_static_cast<int>(::GetCurrentProcessId());
-#else
-            return conditional_static_cast<int>(::getpid());
-#endif
-        }
-
-        // Determine if the terminal supports colors
-        // Based on: https://github.com/agauniyal/rang/
-        SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
-        {
-#ifdef _WIN32
-            return true;
-#else
-
-            static const bool result = []()
-            {
-                const char *env_colorterm_p = std::getenv("COLORTERM");
-                if(env_colorterm_p != nullptr)
-                {
-                    return true;
-                }
-
-                static constexpr std::array<const char *, 16> terms = {
-                    {"ansi",
-                     "color",
-                     "console",
-                     "cygwin",
-                     "gnome",
-                     "konsole",
-                     "kterm",
-                     "linux",
-                     "msys",
-                     "putty",
-                     "rxvt",
-                     "screen",
-                     "vt100",
-                     "xterm",
-                     "alacritty",
-                     "vt102"}};
-
-                const char *env_term_p = std::getenv("TERM");
-                if(env_term_p == nullptr)
-                {
-                    return false;
-                }
-
-                return std::any_of(
-                    terms.begin(),
-                    terms.end(),
-                    [&](const char *term) { return std::strstr(env_term_p, term) != nullptr; });
-            }();
-
-            return result;
-#endif
-        }
-
-        // Determine if the terminal attached
-        // Source: https://github.com/agauniyal/rang/
-        SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
-        {
-#ifdef _WIN32
-            return ::_isatty(_fileno(file)) != 0;
-#else
-            return ::isatty(fileno(file)) != 0;
-#endif
-        }
-
-#if(defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
-        SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
-        {
-            if(wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 2 - 1)
-            {
-                throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
-            }
-
-            int wstr_size = static_cast<int>(wstr.size());
-            if(wstr_size == 0)
-            {
-                target.resize(0);
-                return;
-            }
-
-            int result_size = static_cast<int>(target.capacity());
-            if((wstr_size + 1) * 2 > result_size)
-            {
-                result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
-            }
-
-            if(result_size > 0)
-            {
-                target.resize(result_size);
-                result_size =
-                    ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(), result_size, NULL, NULL);
-
-                if(result_size > 0)
-                {
-                    target.resize(result_size);
-                    return;
-                }
-            }
-
-            throw_spdlog_ex(fmt_lib::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
-        }
-
-        SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target)
-        {
-            if(str.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) - 1)
-            {
-                throw_spdlog_ex("UTF-8 string is too big to be converted to UTF-16");
-            }
-
-            int str_size = static_cast<int>(str.size());
-            if(str_size == 0)
-            {
-                target.resize(0);
-                return;
-            }
-
-            int result_size = static_cast<int>(target.capacity());
-            if(str_size + 1 > result_size)
-            {
-                result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0);
-            }
-
-            if(result_size > 0)
-            {
-                target.resize(result_size);
-                result_size = ::MultiByteToWideChar(
-                    CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, target.data(), result_size);
-
-                if(result_size > 0)
-                {
-                    target.resize(result_size);
-                    return;
-                }
-            }
-
-            throw_spdlog_ex(fmt_lib::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError()));
-        }
-#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
-
-        // return true on success
-        static SPDLOG_INLINE bool mkdir_(const filename_t &path)
-        {
-#ifdef _WIN32
-#ifdef SPDLOG_WCHAR_FILENAMES
-            return ::_wmkdir(path.c_str()) == 0;
-#else
-            return ::_mkdir(path.c_str()) == 0;
-#endif
-#else
-            return ::mkdir(path.c_str(), mode_t(0755)) == 0;
-#endif
-        }
-
-        // create the given directory - and all directories leading to it
-        // return true on success or if the directory already exists
-        SPDLOG_INLINE bool create_dir(const filename_t &path)
-        {
-            if(path_exists(path))
-            {
-                return true;
-            }
-
-            if(path.empty())
-            {
-                return false;
-            }
-
-            size_t search_offset = 0;
-            do
-            {
-                auto token_pos = path.find_first_of(folder_seps_filename, search_offset);
-                // treat the entire path as a folder if no folder separator not found
-                if(token_pos == filename_t::npos)
-                {
-                    token_pos = path.size();
-                }
-
-                auto subdir = path.substr(0, token_pos);
-
-                if(!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir))
-                {
-                    return false; // return error if failed creating dir
-                }
-                search_offset = token_pos + 1;
-            } while(search_offset < path.size());
-
-            return true;
-        }
-
-        // Return directory name from given path or empty string
-        // "abc/file" => "abc"
-        // "abc/" => "abc"
-        // "abc" => ""
-        // "abc///" => "abc//"
-        SPDLOG_INLINE filename_t dir_name(const filename_t &path)
-        {
-            auto pos = path.find_last_of(folder_seps_filename);
-            return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
-        }
-
-        std::string SPDLOG_INLINE getenv(const char *field)
-        {
-#if defined(_MSC_VER)
-#if defined(__cplusplus_winrt)
-            return std::string{}; // not supported under uwp
-#else
-            size_t len = 0;
-            char   buf[128];
-            bool   ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
-            return ok ? buf : std::string{};
-#endif
-#else // revert to getenv
-            char *buf = ::getenv(field);
-            return buf ? buf : std::string{};
-#endif
-        }
-
-    } // namespace os
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/os.h b/include/spdlog/details/os.h
deleted file mode 100644
index 35a5fa4b8..000000000
--- a/include/spdlog/details/os.h
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <ctime> // std::time_t
-#include <spdlog/common.h>
-
-namespace spdlog
-{
-namespace details
-{
-    namespace os
-    {
-
-        SPDLOG_API spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
-
-        SPDLOG_API std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
-
-        SPDLOG_API std::tm localtime() SPDLOG_NOEXCEPT;
-
-        SPDLOG_API std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
-
-        SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
-
-// eol definition
-#if !defined(SPDLOG_EOL)
-#ifdef _WIN32
-#define SPDLOG_EOL "\r\n"
-#else
-#define SPDLOG_EOL "\n"
-#endif
-#endif
-
-        SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
-
-// folder separator
-#if !defined(SPDLOG_FOLDER_SEPS)
-#ifdef _WIN32
-#define SPDLOG_FOLDER_SEPS "\\/"
-#else
-#define SPDLOG_FOLDER_SEPS "/"
-#endif
-#endif
-
-        SPDLOG_CONSTEXPR static const char                   folder_seps[] = SPDLOG_FOLDER_SEPS;
-        SPDLOG_CONSTEXPR static const filename_t::value_type folder_seps_filename[] =
-            SPDLOG_FILENAME_T(SPDLOG_FOLDER_SEPS);
-
-        // fopen_s on non windows for writing
-        SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
-
-        // Remove filename. return 0 on success
-        SPDLOG_API int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
-
-        // Remove file if exists. return 0 on success
-        // Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
-        SPDLOG_API int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
-
-        SPDLOG_API int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
-
-        // Return if file exists.
-        SPDLOG_API bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
-
-        // Return file size according to open FILE* object
-        SPDLOG_API size_t filesize(FILE *f);
-
-        // Return utc offset in minutes or throw spdlog_ex on failure
-        SPDLOG_API int utc_minutes_offset(const std::tm &tm = details::os::localtime());
-
-        // Return current thread id as size_t
-        // It exists because the std::this_thread::get_id() is much slower(especially
-        // under VS 2013)
-        SPDLOG_API size_t _thread_id() SPDLOG_NOEXCEPT;
-
-        // Return current thread id as size_t (from thread local storage)
-        SPDLOG_API size_t thread_id() SPDLOG_NOEXCEPT;
-
-        // This is avoid msvc issue in sleep_for that happens if the clock changes.
-        // See https://github.com/gabime/spdlog/issues/609
-        SPDLOG_API void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT;
-
-        SPDLOG_API std::string filename_to_str(const filename_t &filename);
-
-        SPDLOG_API int pid() SPDLOG_NOEXCEPT;
-
-        // Determine if the terminal supports colors
-        // Source: https://github.com/agauniyal/rang/
-        SPDLOG_API bool is_color_terminal() SPDLOG_NOEXCEPT;
-
-        // Determine if the terminal attached
-        // Source: https://github.com/agauniyal/rang/
-        SPDLOG_API bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
-
-#if(defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
-        SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
-
-        SPDLOG_API void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target);
-#endif
-
-        // Return directory name from given path or empty string
-        // "abc/file" => "abc"
-        // "abc/" => "abc"
-        // "abc" => ""
-        // "abc///" => "abc//"
-        SPDLOG_API filename_t dir_name(const filename_t &path);
-
-        // Create a dir from the given path.
-        // Return true if succeeded or if this dir already exists.
-        SPDLOG_API bool create_dir(const filename_t &path);
-
-        // non thread safe, cross platform getenv/getenv_s
-        // return empty string if field not found
-        SPDLOG_API std::string getenv(const char *field);
-
-    } // namespace os
-} // namespace details
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "os-inl.h"
-#endif
diff --git a/include/spdlog/details/periodic_worker-inl.h b/include/spdlog/details/periodic_worker-inl.h
deleted file mode 100644
index 2751bcf89..000000000
--- a/include/spdlog/details/periodic_worker-inl.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/details/periodic_worker.h>
-#endif
-
-namespace spdlog
-{
-namespace details
-{
-
-    // stop the worker thread and join it
-    SPDLOG_INLINE periodic_worker::~periodic_worker()
-    {
-        if(worker_thread_.joinable())
-        {
-            {
-                std::lock_guard<std::mutex> lock(mutex_);
-                active_ = false;
-            }
-            cv_.notify_one();
-            worker_thread_.join();
-        }
-    }
-
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/periodic_worker.h b/include/spdlog/details/periodic_worker.h
deleted file mode 100644
index 8eb6ac337..000000000
--- a/include/spdlog/details/periodic_worker.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-// periodic worker thread - periodically executes the given callback function.
-//
-// RAII over the owned thread:
-//    creates the thread on construction.
-//    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>
-#include <functional>
-#include <mutex>
-#include <thread>
-namespace spdlog
-{
-namespace details
-{
-
-    class SPDLOG_API periodic_worker
-    {
-    public:
-        template <typename Rep, typename Period>
-        periodic_worker(const std::function<void()> &callback_fun, std::chrono::duration<Rep, Period> interval)
-        {
-            active_ = (interval > std::chrono::duration<Rep, Period>::zero());
-            if(!active_)
-            {
-                return;
-            }
-
-            worker_thread_ = std::thread(
-                [this, callback_fun, interval]()
-                {
-                    for(;;)
-                    {
-                        std::unique_lock<std::mutex> lock(this->mutex_);
-                        if(this->cv_.wait_for(lock, interval, [this] { return !this->active_; }))
-                        {
-                            return; // active_ == false, so exit this thread
-                        }
-                        callback_fun();
-                    }
-                });
-        }
-        periodic_worker(const periodic_worker &)            = delete;
-        periodic_worker &operator=(const periodic_worker &) = delete;
-        // stop the worker thread and join it
-        ~periodic_worker();
-
-    private:
-        bool                    active_;
-        std::thread             worker_thread_;
-        std::mutex              mutex_;
-        std::condition_variable cv_;
-    };
-} // namespace details
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "periodic_worker-inl.h"
-#endif
diff --git a/include/spdlog/details/registry-inl.h b/include/spdlog/details/registry-inl.h
deleted file mode 100644
index 8b9dbf98d..000000000
--- a/include/spdlog/details/registry-inl.h
+++ /dev/null
@@ -1,306 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/details/registry.h>
-#endif
-
-#include <spdlog/common.h>
-#include <spdlog/details/periodic_worker.h>
-#include <spdlog/logger.h>
-#include <spdlog/pattern_formatter.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>
-#include <string>
-#include <unordered_map>
-
-namespace spdlog
-{
-namespace details
-{
-
-    SPDLOG_INLINE registry::registry() : 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
-    }
-
-    SPDLOG_INLINE registry::~registry() = default;
-
-    SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger)
-    {
-        std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        register_logger_(std::move(new_logger));
-    }
-
-    SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger)
-    {
-        std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        new_logger->set_formatter(formatter_->clone());
-
-        if(err_handler_)
-        {
-            new_logger->set_error_handler(err_handler_);
-        }
-
-        // set new level according to previously configured level or default level
-        auto it        = log_levels_.find(new_logger->name());
-        auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
-        new_logger->set_level(new_level);
-
-        new_logger->flush_on(flush_level_);
-
-        if(backtrace_n_messages_ > 0)
-        {
-            new_logger->enable_backtrace(backtrace_n_messages_);
-        }
-
-        if(automatic_registration_)
-        {
-            register_logger_(std::move(new_logger));
-        }
-    }
-
-    SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_name)
-    {
-        std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        auto                        found = loggers_.find(logger_name);
-        return found == loggers_.end() ? nullptr : found->second;
-    }
-
-    SPDLOG_INLINE std::shared_ptr<logger> registry::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.
-    SPDLOG_INLINE logger *registry::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.
-    SPDLOG_INLINE void registry::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);
-    }
-
-    SPDLOG_INLINE void registry::set_tp(std::shared_ptr<thread_pool> tp)
-    {
-        std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
-        tp_ = std::move(tp);
-    }
-
-    SPDLOG_INLINE std::shared_ptr<thread_pool> registry::get_tp()
-    {
-        std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
-        return tp_;
-    }
-
-    // Set global formatter. Each sink in each logger will get a clone of this object
-    SPDLOG_INLINE void registry::set_formatter(std::unique_ptr<formatter> formatter)
-    {
-        std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        formatter_ = std::move(formatter);
-        for(auto &l : loggers_)
-        {
-            l.second->set_formatter(formatter_->clone());
-        }
-    }
-
-    SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages)
-    {
-        std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        backtrace_n_messages_ = n_messages;
-
-        for(auto &l : loggers_)
-        {
-            l.second->enable_backtrace(n_messages);
-        }
-    }
-
-    SPDLOG_INLINE void registry::disable_backtrace()
-    {
-        std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        backtrace_n_messages_ = 0;
-        for(auto &l : loggers_)
-        {
-            l.second->disable_backtrace();
-        }
-    }
-
-    SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
-    {
-        std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        for(auto &l : loggers_)
-        {
-            l.second->set_level(log_level);
-        }
-        global_log_level_ = log_level;
-    }
-
-    SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
-    {
-        std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        for(auto &l : loggers_)
-        {
-            l.second->flush_on(log_level);
-        }
-        flush_level_ = log_level;
-    }
-
-    SPDLOG_INLINE void registry::set_error_handler(err_handler handler)
-    {
-        std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        for(auto &l : loggers_)
-        {
-            l.second->set_error_handler(handler);
-        }
-        err_handler_ = std::move(handler);
-    }
-
-    SPDLOG_INLINE void registry::apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun)
-    {
-        std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        for(auto &l : loggers_)
-        {
-            fun(l.second);
-        }
-    }
-
-    SPDLOG_INLINE void registry::flush_all()
-    {
-        std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        for(auto &l : loggers_)
-        {
-            l.second->flush();
-        }
-    }
-
-    SPDLOG_INLINE void registry::drop(const std::string &logger_name)
-    {
-        std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        loggers_.erase(logger_name);
-        if(default_logger_ && default_logger_->name() == logger_name)
-        {
-            default_logger_.reset();
-        }
-    }
-
-    SPDLOG_INLINE void registry::drop_all()
-    {
-        std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        loggers_.clear();
-        default_logger_.reset();
-    }
-
-    // clean all resources and threads started by the registry
-    SPDLOG_INLINE void registry::shutdown()
-    {
-        {
-            std::lock_guard<std::mutex> lock(flusher_mutex_);
-            periodic_flusher_.reset();
-        }
-
-        drop_all();
-
-        {
-            std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
-            tp_.reset();
-        }
-    }
-
-    SPDLOG_INLINE std::recursive_mutex &registry::tp_mutex()
-    {
-        return tp_mutex_;
-    }
-
-    SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registration)
-    {
-        std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        automatic_registration_ = automatic_registration;
-    }
-
-    SPDLOG_INLINE void registry::set_levels(log_levels levels, level::level_enum *global_level)
-    {
-        std::lock_guard<std::mutex> lock(logger_map_mutex_);
-        log_levels_                 = std::move(levels);
-        auto global_level_requested = global_level != nullptr;
-        global_log_level_           = global_level_requested ? *global_level : global_log_level_;
-
-        for(auto &logger : loggers_)
-        {
-            auto logger_entry = log_levels_.find(logger.first);
-            if(logger_entry != log_levels_.end())
-            {
-                logger.second->set_level(logger_entry->second);
-            }
-            else if(global_level_requested)
-            {
-                logger.second->set_level(*global_level);
-            }
-        }
-    }
-
-    SPDLOG_INLINE registry &registry::instance()
-    {
-        static registry s_instance;
-        return s_instance;
-    }
-
-    SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name)
-    {
-        if(loggers_.find(logger_name) != loggers_.end())
-        {
-            throw_spdlog_ex("logger with name '" + logger_name + "' already exists");
-        }
-    }
-
-    SPDLOG_INLINE void registry::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);
-    }
-
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/registry.h b/include/spdlog/details/registry.h
deleted file mode 100644
index 2c00585f7..000000000
--- a/include/spdlog/details/registry.h
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-// Loggers registry of unique name->logger pointer
-// An attempt to create a logger with an already existing name will result with spdlog_ex exception.
-// If user requests a non existing logger, nullptr will be returned
-// This class is thread safe
-
-#include <chrono>
-#include <functional>
-#include <memory>
-#include <mutex>
-#include <spdlog/common.h>
-#include <spdlog/details/periodic_worker.h>
-#include <string>
-#include <unordered_map>
-
-namespace spdlog
-{
-class logger;
-
-namespace details
-{
-    class thread_pool;
-
-    class SPDLOG_API registry
-    {
-    public:
-        using log_levels                      = std::unordered_map<std::string, level::level_enum>;
-        registry(const registry &)            = delete;
-        registry &operator=(const registry &) = delete;
-
-        void                    register_logger(std::shared_ptr<logger> new_logger);
-        void                    initialize_logger(std::shared_ptr<logger> new_logger);
-        std::shared_ptr<logger> get(const std::string &logger_name);
-        std::shared_ptr<logger> 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();
-
-        // 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);
-
-        void set_tp(std::shared_ptr<thread_pool> tp);
-
-        std::shared_ptr<thread_pool> get_tp();
-
-        // Set global formatter. Each sink in each logger will get a clone of this object
-        void set_formatter(std::unique_ptr<formatter> formatter);
-
-        void enable_backtrace(size_t n_messages);
-
-        void disable_backtrace();
-
-        void set_level(level::level_enum log_level);
-
-        void flush_on(level::level_enum log_level);
-
-        template <typename Rep, typename Period>
-        void flush_every(std::chrono::duration<Rep, Period> interval)
-        {
-            std::lock_guard<std::mutex> lock(flusher_mutex_);
-            auto                        clbk = [this]() { this->flush_all(); };
-            periodic_flusher_                = details::make_unique<periodic_worker>(clbk, interval);
-        }
-
-        void set_error_handler(err_handler handler);
-
-        void apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun);
-
-        void flush_all();
-
-        void drop(const std::string &logger_name);
-
-        void drop_all();
-
-        // clean all resources and threads started by the registry
-        void shutdown();
-
-        std::recursive_mutex &tp_mutex();
-
-        void set_automatic_registration(bool automatic_registration);
-
-        // set levels for all existing/future loggers. global_level can be null if should not set.
-        void set_levels(log_levels levels, level::level_enum *global_level);
-
-        static registry &instance();
-
-    private:
-        registry();
-        ~registry();
-
-        void                                                     throw_if_exists_(const std::string &logger_name);
-        void                                                     register_logger_(std::shared_ptr<logger> new_logger);
-        bool                                                     set_level_from_cfg_(logger *logger);
-        std::mutex                                               logger_map_mutex_, flusher_mutex_;
-        std::recursive_mutex                                     tp_mutex_;
-        std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
-        log_levels                                               log_levels_;
-        std::unique_ptr<formatter>                               formatter_;
-        spdlog::level::level_enum                                global_log_level_ = level::info;
-        level::level_enum                                        flush_level_      = level::off;
-        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;
-        size_t                                                   backtrace_n_messages_   = 0;
-    };
-
-} // namespace details
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "registry-inl.h"
-#endif
diff --git a/include/spdlog/details/synchronous_factory.h b/include/spdlog/details/synchronous_factory.h
deleted file mode 100644
index 732300c45..000000000
--- a/include/spdlog/details/synchronous_factory.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include "registry.h"
-
-namespace spdlog
-{
-
-// Default logger factory-  creates synchronous loggers
-class logger;
-
-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<spdlog::logger>(std::move(logger_name), std::move(sink));
-        details::registry::instance().initialize_logger(new_logger);
-        return new_logger;
-    }
-};
-} // namespace spdlog
diff --git a/include/spdlog/details/tcp_client-windows.h b/include/spdlog/details/tcp_client-windows.h
deleted file mode 100644
index d651df5e7..000000000
--- a/include/spdlog/details/tcp_client-windows.h
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#define WIN32_LEAN_AND_MEAN
-// tcp client helper
-#include <spdlog/common.h>
-#include <spdlog/details/os.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string>
-#include <windows.h>
-#include <winsock2.h>
-#include <ws2tcpip.h>
-
-#pragma comment(lib, "Ws2_32.lib")
-#pragma comment(lib, "Mswsock.lib")
-#pragma comment(lib, "AdvApi32.lib")
-
-namespace spdlog
-{
-namespace details
-{
-    class tcp_client
-    {
-        SOCKET socket_ = INVALID_SOCKET;
-
-        static void init_winsock_()
-        {
-            WSADATA wsaData;
-            auto    rv = WSAStartup(MAKEWORD(2, 2), &wsaData);
-            if(rv != 0)
-            {
-                throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
-            }
-        }
-
-        static void throw_winsock_error_(const std::string &msg, int last_error)
-        {
-            char buf[512];
-            ::FormatMessageA(
-                FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
-                NULL,
-                last_error,
-                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                buf,
-                (sizeof(buf) / sizeof(char)),
-                NULL);
-
-            throw_spdlog_ex(fmt_lib::format("tcp_sink - {}: {}", msg, buf));
-        }
-
-    public:
-        tcp_client() { init_winsock_(); }
-
-        ~tcp_client()
-        {
-            close();
-            ::WSACleanup();
-        }
-
-        bool is_connected() const { return socket_ != INVALID_SOCKET; }
-
-        void close()
-        {
-            ::closesocket(socket_);
-            socket_ = INVALID_SOCKET;
-        }
-
-        SOCKET fd() const { return socket_; }
-
-        // try to connect or throw on failure
-        void connect(const std::string &host, int port)
-        {
-            if(is_connected())
-            {
-                close();
-            }
-            struct addrinfo hints
-            {
-            };
-            ZeroMemory(&hints, sizeof(hints));
-
-            hints.ai_family   = AF_INET;        // IPv4
-            hints.ai_socktype = SOCK_STREAM;    // TCP
-            hints.ai_flags    = AI_NUMERICSERV; // port passed as as numeric value
-            hints.ai_protocol = 0;
-
-            auto             port_str = std::to_string(port);
-            struct addrinfo *addrinfo_result;
-            auto             rv         = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
-            int              last_error = 0;
-            if(rv != 0)
-            {
-                last_error = ::WSAGetLastError();
-                WSACleanup();
-                throw_winsock_error_("getaddrinfo failed", last_error);
-            }
-
-            // Try each address until we successfully connect(2).
-
-            for(auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
-            {
-                socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
-                if(socket_ == INVALID_SOCKET)
-                {
-                    last_error = ::WSAGetLastError();
-                    WSACleanup();
-                    continue;
-                }
-                if(::connect(socket_, rp->ai_addr, (int) rp->ai_addrlen) == 0)
-                {
-                    break;
-                }
-                else
-                {
-                    last_error = ::WSAGetLastError();
-                    close();
-                }
-            }
-            ::freeaddrinfo(addrinfo_result);
-            if(socket_ == INVALID_SOCKET)
-            {
-                WSACleanup();
-                throw_winsock_error_("connect failed", last_error);
-            }
-
-            // set TCP_NODELAY
-            int enable_flag = 1;
-            ::setsockopt(
-                socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
-        }
-
-        // Send exactly n_bytes of the given data.
-        // On error close the connection and throw.
-        void send(const char *data, size_t n_bytes)
-        {
-            size_t bytes_sent = 0;
-            while(bytes_sent < n_bytes)
-            {
-                const int send_flags   = 0;
-                auto      write_result = ::send(socket_, data + bytes_sent, (int) (n_bytes - bytes_sent), send_flags);
-                if(write_result == SOCKET_ERROR)
-                {
-                    int last_error = ::WSAGetLastError();
-                    close();
-                    throw_winsock_error_("send failed", last_error);
-                }
-
-                if(write_result == 0) // (probably should not happen but in any case..)
-                {
-                    break;
-                }
-                bytes_sent += static_cast<size_t>(write_result);
-            }
-        }
-    };
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/tcp_client.h b/include/spdlog/details/tcp_client.h
deleted file mode 100644
index d7ae7ffdc..000000000
--- a/include/spdlog/details/tcp_client.h
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifdef _WIN32
-#error include tcp_client-windows.h instead
-#endif
-
-// tcp client helper
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <netinet/tcp.h>
-#include <spdlog/common.h>
-#include <spdlog/details/os.h>
-#include <string>
-#include <sys/socket.h>
-#include <unistd.h>
-
-namespace spdlog
-{
-namespace details
-{
-    class tcp_client
-    {
-        int socket_ = -1;
-
-    public:
-        bool is_connected() const { return socket_ != -1; }
-
-        void close()
-        {
-            if(is_connected())
-            {
-                ::close(socket_);
-                socket_ = -1;
-            }
-        }
-
-        int fd() const { return socket_; }
-
-        ~tcp_client() { close(); }
-
-        // try to connect or throw on failure
-        void connect(const std::string &host, int port)
-        {
-            close();
-            struct addrinfo hints
-            {
-            };
-            memset(&hints, 0, sizeof(struct addrinfo));
-            hints.ai_family   = AF_INET;        // IPv4
-            hints.ai_socktype = SOCK_STREAM;    // TCP
-            hints.ai_flags    = AI_NUMERICSERV; // port passed as as numeric value
-            hints.ai_protocol = 0;
-
-            auto             port_str = std::to_string(port);
-            struct addrinfo *addrinfo_result;
-            auto             rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
-            if(rv != 0)
-            {
-                throw_spdlog_ex(fmt_lib::format("::getaddrinfo failed: {}", gai_strerror(rv)));
-            }
-
-            // Try each address until we successfully connect(2).
-            int last_errno = 0;
-            for(auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
-            {
-#if defined(SOCK_CLOEXEC)
-                const int flags = SOCK_CLOEXEC;
-#else
-                const int flags      = 0;
-#endif
-                socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
-                if(socket_ == -1)
-                {
-                    last_errno = errno;
-                    continue;
-                }
-                rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
-                if(rv == 0)
-                {
-                    break;
-                }
-                last_errno = errno;
-                ::close(socket_);
-                socket_ = -1;
-            }
-            ::freeaddrinfo(addrinfo_result);
-            if(socket_ == -1)
-            {
-                throw_spdlog_ex("::connect failed", last_errno);
-            }
-
-            // set TCP_NODELAY
-            int enable_flag = 1;
-            ::setsockopt(
-                socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
-
-            // prevent sigpipe on systems where MSG_NOSIGNAL is not available
-#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
-            ::setsockopt(
-                socket_, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
-#endif
-
-#if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
-#error "tcp_sink would raise SIGPIPE since niether SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
-#endif
-        }
-
-        // Send exactly n_bytes of the given data.
-        // On error close the connection and throw.
-        void send(const char *data, size_t n_bytes)
-        {
-            size_t bytes_sent = 0;
-            while(bytes_sent < n_bytes)
-            {
-#if defined(MSG_NOSIGNAL)
-                const int send_flags = MSG_NOSIGNAL;
-#else
-                const int send_flags = 0;
-#endif
-                auto write_result = ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
-                if(write_result < 0)
-                {
-                    close();
-                    throw_spdlog_ex("write(2) failed", errno);
-                }
-
-                if(write_result == 0) // (probably should not happen but in any case..)
-                {
-                    break;
-                }
-                bytes_sent += static_cast<size_t>(write_result);
-            }
-        }
-    };
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/thread_pool-inl.h b/include/spdlog/details/thread_pool-inl.h
deleted file mode 100644
index cb944d483..000000000
--- a/include/spdlog/details/thread_pool-inl.h
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/details/thread_pool.h>
-#endif
-
-#include <cassert>
-#include <spdlog/common.h>
-
-namespace spdlog
-{
-namespace details
-{
-
-    SPDLOG_INLINE thread_pool::thread_pool(
-        size_t                q_max_items,
-        size_t                threads_n,
-        std::function<void()> on_thread_start,
-        std::function<void()> on_thread_stop) :
-        q_(q_max_items)
-    {
-        if(threads_n == 0 || threads_n > 1000)
-        {
-            throw_spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid "
-                            "range is 1-1000)");
-        }
-        for(size_t i = 0; i < threads_n; i++)
-        {
-            threads_.emplace_back(
-                [this, on_thread_start, on_thread_stop]
-                {
-                    on_thread_start();
-                    this->thread_pool::worker_loop_();
-                    on_thread_stop();
-                });
-        }
-    }
-
-    SPDLOG_INLINE
-    thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start) :
-        thread_pool(q_max_items, threads_n, on_thread_start, [] {})
-    {
-    }
-
-    SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n) :
-        thread_pool(
-            q_max_items,
-            threads_n,
-            [] {},
-            [] {})
-    {
-    }
-
-    // message all threads to terminate gracefully join them
-    SPDLOG_INLINE thread_pool::~thread_pool()
-    {
-        SPDLOG_TRY
-        {
-            for(size_t i = 0; i < threads_.size(); i++)
-            {
-                post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
-            }
-
-            for(auto &t : threads_)
-            {
-                t.join();
-            }
-        }
-        SPDLOG_CATCH_STD
-    }
-
-    void SPDLOG_INLINE thread_pool::post_log(
-        async_logger_ptr      &&worker_ptr,
-        const details::log_msg &msg,
-        async_overflow_policy   overflow_policy)
-    {
-        async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
-        post_async_msg_(std::move(async_m), overflow_policy);
-    }
-
-    void SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy)
-    {
-        post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
-    }
-
-    size_t SPDLOG_INLINE thread_pool::overrun_counter()
-    {
-        return q_.overrun_counter();
-    }
-
-    void SPDLOG_INLINE thread_pool::reset_overrun_counter()
-    {
-        q_.reset_overrun_counter();
-    }
-
-    size_t SPDLOG_INLINE thread_pool::queue_size()
-    {
-        return q_.size();
-    }
-
-    void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
-    {
-        if(overflow_policy == async_overflow_policy::block)
-        {
-            q_.enqueue(std::move(new_msg));
-        }
-        else
-        {
-            q_.enqueue_nowait(std::move(new_msg));
-        }
-    }
-
-    void SPDLOG_INLINE thread_pool::worker_loop_()
-    {
-        while(process_next_msg_())
-        {
-        }
-    }
-
-    // process next message in the queue
-    // return true if this thread should still be active (while no terminate msg
-    // was received)
-    bool SPDLOG_INLINE thread_pool::process_next_msg_()
-    {
-        async_msg incoming_async_msg;
-        bool      dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10));
-        if(!dequeued)
-        {
-            return true;
-        }
-
-        switch(incoming_async_msg.msg_type)
-        {
-            case async_msg_type::log:
-            {
-                incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg);
-                return true;
-            }
-            case async_msg_type::flush:
-            {
-                incoming_async_msg.worker_ptr->backend_flush_();
-                return true;
-            }
-
-            case async_msg_type::terminate:
-            {
-                return false;
-            }
-
-            default:
-            {
-                assert(false);
-            }
-        }
-
-        return true;
-    }
-
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/thread_pool.h b/include/spdlog/details/thread_pool.h
deleted file mode 100644
index 280c72d0c..000000000
--- a/include/spdlog/details/thread_pool.h
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <chrono>
-#include <functional>
-#include <memory>
-#include <spdlog/details/log_msg_buffer.h>
-#include <spdlog/details/mpmc_blocking_q.h>
-#include <spdlog/details/os.h>
-#include <thread>
-#include <vector>
-
-namespace spdlog
-{
-class async_logger;
-
-namespace details
-{
-
-    using async_logger_ptr = std::shared_ptr<spdlog::async_logger>;
-
-    enum class async_msg_type
-    {
-        log,
-        flush,
-        terminate
-    };
-
-    // Async msg to move to/from the queue
-    // Movable only. should never be copied
-    struct async_msg : log_msg_buffer
-    {
-        async_msg_type   msg_type{async_msg_type::log};
-        async_logger_ptr worker_ptr;
-
-        async_msg()  = default;
-        ~async_msg() = default;
-
-        // should only be moved in or out of the queue..
-        async_msg(const async_msg &) = delete;
-
-// support for vs2013 move
-#if defined(_MSC_VER) && _MSC_VER <= 1800
-        async_msg(async_msg &&other) :
-            log_msg_buffer(std::move(other)), msg_type(other.msg_type), worker_ptr(std::move(other.worker_ptr))
-        {
-        }
-
-        async_msg &operator=(async_msg &&other)
-        {
-            *static_cast<log_msg_buffer *>(this) = std::move(other);
-            msg_type                             = other.msg_type;
-            worker_ptr                           = std::move(other.worker_ptr);
-            return *this;
-        }
-#else // (_MSC_VER) && _MSC_VER <= 1800
-        async_msg(async_msg &&)            = default;
-        async_msg &operator=(async_msg &&) = default;
-#endif
-
-        // construct from log_msg with given type
-        async_msg(async_logger_ptr &&worker, async_msg_type the_type, const details::log_msg &m) :
-            log_msg_buffer{m}, msg_type{the_type}, worker_ptr{std::move(worker)}
-        {
-        }
-
-        async_msg(async_logger_ptr &&worker, async_msg_type the_type) :
-            log_msg_buffer{}, msg_type{the_type}, worker_ptr{std::move(worker)}
-        {
-        }
-
-        explicit async_msg(async_msg_type the_type) : async_msg{nullptr, the_type} {}
-    };
-
-    class SPDLOG_API thread_pool
-    {
-    public:
-        using item_type = async_msg;
-        using q_type    = details::mpmc_blocking_queue<item_type>;
-
-        thread_pool(
-            size_t                q_max_items,
-            size_t                threads_n,
-            std::function<void()> on_thread_start,
-            std::function<void()> on_thread_stop);
-        thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start);
-        thread_pool(size_t q_max_items, size_t threads_n);
-
-        // message all threads to terminate gracefully and join them
-        ~thread_pool();
-
-        thread_pool(const thread_pool &)       = delete;
-        thread_pool &operator=(thread_pool &&) = delete;
-
-        void
-        post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
-        void   post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
-        size_t overrun_counter();
-        void   reset_overrun_counter();
-        size_t queue_size();
-
-    private:
-        q_type q_;
-
-        std::vector<std::thread> threads_;
-
-        void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy);
-        void worker_loop_();
-
-        // process next message in the queue
-        // return true if this thread should still be active (while no terminate msg
-        // was received)
-        bool process_next_msg_();
-    };
-
-} // namespace details
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "thread_pool-inl.h"
-#endif
diff --git a/include/spdlog/details/udp_client-windows.h b/include/spdlog/details/udp_client-windows.h
deleted file mode 100644
index 00dd37c6b..000000000
--- a/include/spdlog/details/udp_client-windows.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-// Helper RAII over winsock udp client socket.
-// Will throw on construction if socket creation failed.
-
-#include <spdlog/common.h>
-#include <spdlog/details/os.h>
-#include <spdlog/details/windows_include.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string>
-#include <winsock2.h>
-#include <ws2tcpip.h>
-
-#pragma comment(lib, "Ws2_32.lib")
-#pragma comment(lib, "Mswsock.lib")
-#pragma comment(lib, "AdvApi32.lib")
-
-namespace spdlog
-{
-namespace details
-{
-    class udp_client
-    {
-        static constexpr int TX_BUFFER_SIZE = 1024 * 10;
-        SOCKET               socket_        = INVALID_SOCKET;
-        sockaddr_in          addr_          = {0};
-
-        static void init_winsock_()
-        {
-            WSADATA wsaData;
-            auto    rv = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
-            if(rv != 0)
-            {
-                throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
-            }
-        }
-
-        static void throw_winsock_error_(const std::string &msg, int last_error)
-        {
-            char buf[512];
-            ::FormatMessageA(
-                FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
-                NULL,
-                last_error,
-                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                buf,
-                (sizeof(buf) / sizeof(char)),
-                NULL);
-
-            throw_spdlog_ex(fmt_lib::format("udp_sink - {}: {}", msg, buf));
-        }
-
-        void cleanup_()
-        {
-            if(socket_ != INVALID_SOCKET)
-            {
-                ::closesocket(socket_);
-            }
-            socket_ = INVALID_SOCKET;
-            ::WSACleanup();
-        }
-
-    public:
-        udp_client(const std::string &host, uint16_t port)
-        {
-            init_winsock_();
-
-            addr_.sin_family      = PF_INET;
-            addr_.sin_port        = htons(port);
-            addr_.sin_addr.s_addr = INADDR_ANY;
-            if(InetPtonA(PF_INET, host.c_str(), &addr_.sin_addr.s_addr) != 1)
-            {
-                int last_error = ::WSAGetLastError();
-                ::WSACleanup();
-                throw_winsock_error_("error: Invalid address!", last_error);
-            }
-
-            socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
-            if(socket_ == INVALID_SOCKET)
-            {
-                int last_error = ::WSAGetLastError();
-                ::WSACleanup();
-                throw_winsock_error_("error: Create Socket failed", last_error);
-            }
-
-            int option_value = TX_BUFFER_SIZE;
-            if(::setsockopt(
-                   socket_,
-                   SOL_SOCKET,
-                   SO_SNDBUF,
-                   reinterpret_cast<const char *>(&option_value),
-                   sizeof(option_value)) < 0)
-            {
-                int last_error = ::WSAGetLastError();
-                cleanup_();
-                throw_winsock_error_("error: setsockopt(SO_SNDBUF) Failed!", last_error);
-            }
-        }
-
-        ~udp_client() { cleanup_(); }
-
-        SOCKET fd() const { return socket_; }
-
-        void send(const char *data, size_t n_bytes)
-        {
-            socklen_t tolen = sizeof(struct sockaddr);
-            if(::sendto(socket_, data, static_cast<int>(n_bytes), 0, (struct sockaddr *) &addr_, tolen) == -1)
-            {
-                throw_spdlog_ex("sendto(2) failed", errno);
-            }
-        }
-    };
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/udp_client.h b/include/spdlog/details/udp_client.h
deleted file mode 100644
index eecdafe2d..000000000
--- a/include/spdlog/details/udp_client.h
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-// Helper RAII over unix udp client socket.
-// Will throw on construction if the socket creation failed.
-
-#ifdef _WIN32
-#error "include udp_client-windows.h instead"
-#endif
-
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <netinet/udp.h>
-#include <spdlog/common.h>
-#include <spdlog/details/os.h>
-#include <string>
-#include <sys/socket.h>
-#include <unistd.h>
-
-namespace spdlog
-{
-namespace details
-{
-
-    class udp_client
-    {
-        static constexpr int TX_BUFFER_SIZE = 1024 * 10;
-        int                  socket_        = -1;
-        struct sockaddr_in   sockAddr_;
-
-        void cleanup_()
-        {
-            if(socket_ != -1)
-            {
-                ::close(socket_);
-                socket_ = -1;
-            }
-        }
-
-    public:
-        udp_client(const std::string &host, uint16_t port)
-        {
-            socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
-            if(socket_ < 0)
-            {
-                throw_spdlog_ex("error: Create Socket Failed!");
-            }
-
-            int option_value = TX_BUFFER_SIZE;
-            if(::setsockopt(
-                   socket_,
-                   SOL_SOCKET,
-                   SO_SNDBUF,
-                   reinterpret_cast<const char *>(&option_value),
-                   sizeof(option_value)) < 0)
-            {
-                cleanup_();
-                throw_spdlog_ex("error: setsockopt(SO_SNDBUF) Failed!");
-            }
-
-            sockAddr_.sin_family = AF_INET;
-            sockAddr_.sin_port   = htons(port);
-
-            if(::inet_aton(host.c_str(), &sockAddr_.sin_addr) == 0)
-            {
-                cleanup_();
-                throw_spdlog_ex("error: Invalid address!");
-            }
-
-            ::memset(sockAddr_.sin_zero, 0x00, sizeof(sockAddr_.sin_zero));
-        }
-
-        ~udp_client() { cleanup_(); }
-
-        int fd() const { return socket_; }
-
-        // Send exactly n_bytes of the given data.
-        // On error close the connection and throw.
-        void send(const char *data, size_t n_bytes)
-        {
-            ssize_t   toslen = 0;
-            socklen_t tolen  = sizeof(struct sockaddr);
-            if((toslen = ::sendto(socket_, data, n_bytes, 0, (struct sockaddr *) &sockAddr_, tolen)) == -1)
-            {
-                throw_spdlog_ex("sendto(2) failed", errno);
-            }
-        }
-    };
-} // namespace details
-} // namespace spdlog
diff --git a/include/spdlog/details/windows_include.h b/include/spdlog/details/windows_include.h
deleted file mode 100644
index 6a2f14f9c..000000000
--- a/include/spdlog/details/windows_include.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#ifndef NOMINMAX
-#define NOMINMAX // prevent windows redefining min/max
-#endif
-
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN
-#endif
-
-#include <windows.h>
diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h
deleted file mode 100644
index c00099b84..000000000
--- a/include/spdlog/fmt/bin_to_hex.h
+++ /dev/null
@@ -1,240 +0,0 @@
-//
-// Copyright(c) 2015 Gabi Melman.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-//
-
-#pragma once
-
-#include <cctype>
-#include <spdlog/common.h>
-
-#if defined(__has_include)
-#if __has_include(<version>)
-#include <version>
-#endif
-#endif
-
-#if __cpp_lib_span >= 202002L
-#include <span>
-#endif
-
-//
-// Support for logging binary data as hex
-// format flags, any combination of the following:
-// {:X} - print in uppercase.
-// {:s} - don't separate each byte with space.
-// {:p} - don't print the position on each line start.
-// {:n} - don't split the output to lines.
-// {:a} - show ASCII if :n is not set
-
-//
-// Examples:
-//
-// std::vector<char> v(200, 0x0b);
-// logger->info("Some buffer {}", spdlog::to_hex(v));
-// char buf[128];
-// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
-// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf), 16));
-
-namespace spdlog
-{
-namespace details
-{
-
-    template <typename It>
-    class dump_info
-    {
-    public:
-        dump_info(It range_begin, It range_end, size_t size_per_line) :
-            begin_(range_begin), end_(range_end), size_per_line_(size_per_line)
-        {
-        }
-
-        // do not use begin() and end() to avoid collision with fmt/ranges
-        It     get_begin() const { return begin_; }
-        It     get_end() const { return end_; }
-        size_t size_per_line() const { return size_per_line_; }
-
-    private:
-        It     begin_, end_;
-        size_t size_per_line_;
-    };
-} // namespace details
-
-// create a dump_info that wraps the given container
-template <typename Container>
-inline details::dump_info<typename Container::const_iterator>
-to_hex(const Container &container, size_t size_per_line = 32)
-{
-    static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
-    using Iter = typename Container::const_iterator;
-    return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
-}
-
-#if __cpp_lib_span >= 202002L
-
-template <typename Value, size_t Extent>
-inline details::dump_info<typename std::span<Value, Extent>::iterator>
-to_hex(const std::span<Value, Extent> &container, size_t size_per_line = 32)
-{
-    using Container = std::span<Value, Extent>;
-    static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
-    using Iter = typename Container::iterator;
-    return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
-}
-
-#endif
-
-// create dump_info from ranges
-template <typename It>
-inline details::dump_info<It> to_hex(const It range_begin, const It range_end, size_t size_per_line = 32)
-{
-    return details::dump_info<It>(range_begin, range_end, size_per_line);
-}
-
-} // namespace spdlog
-
-namespace
-#ifdef SPDLOG_USE_STD_FORMAT
-    std
-#else
-    fmt
-#endif
-{
-template <typename T>
-struct formatter<spdlog::details::dump_info<T>, char>
-{
-    const char delimiter      = ' ';
-    bool       put_newlines   = true;
-    bool       put_delimiters = true;
-    bool       use_uppercase  = false;
-    bool       put_positions  = true; // position on start of each line
-    bool       show_ascii     = false;
-
-    // parse the format string flags
-    template <typename ParseContext>
-    SPDLOG_CONSTEXPR_FUNC auto parse(ParseContext &ctx) -> decltype(ctx.begin())
-    {
-        auto it = ctx.begin();
-        while(it != ctx.end() && *it != '}')
-        {
-            switch(*it)
-            {
-                case 'X':
-                    use_uppercase = true;
-                    break;
-                case 's':
-                    put_delimiters = false;
-                    break;
-                case 'p':
-                    put_positions = false;
-                    break;
-                case 'n':
-                    put_newlines = false;
-                    show_ascii   = false;
-                    break;
-                case 'a':
-                    if(put_newlines)
-                    {
-                        show_ascii = true;
-                    }
-                    break;
-            }
-
-            ++it;
-        }
-        return it;
-    }
-
-    // format the given bytes range as hex
-    template <typename FormatContext, typename Container>
-    auto format(const spdlog::details::dump_info<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
-    {
-        SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
-        SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
-        const char                  *hex_chars = use_uppercase ? hex_upper : hex_lower;
-
-#if !defined(SPDLOG_USE_STD_FORMAT) && FMT_VERSION < 60000
-        auto inserter = ctx.begin();
-#else
-        auto inserter = ctx.out();
-#endif
-
-        int  size_per_line = static_cast<int>(the_range.size_per_line());
-        auto start_of_line = the_range.get_begin();
-        for(auto i = the_range.get_begin(); i != the_range.get_end(); i++)
-        {
-            auto ch = static_cast<unsigned char>(*i);
-
-            if(put_newlines && (i == the_range.get_begin() || i - start_of_line >= size_per_line))
-            {
-                if(show_ascii && i != the_range.get_begin())
-                {
-                    *inserter++ = delimiter;
-                    *inserter++ = delimiter;
-                    for(auto j = start_of_line; j < i; j++)
-                    {
-                        auto pc     = static_cast<unsigned char>(*j);
-                        *inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
-                    }
-                }
-
-                put_newline(inserter, static_cast<size_t>(i - the_range.get_begin()));
-
-                // put first byte without delimiter in front of it
-                *inserter++   = hex_chars[(ch >> 4) & 0x0f];
-                *inserter++   = hex_chars[ch & 0x0f];
-                start_of_line = i;
-                continue;
-            }
-
-            if(put_delimiters)
-            {
-                *inserter++ = delimiter;
-            }
-
-            *inserter++ = hex_chars[(ch >> 4) & 0x0f];
-            *inserter++ = hex_chars[ch & 0x0f];
-        }
-        if(show_ascii) // add ascii to last line
-        {
-            if(the_range.get_end() - the_range.get_begin() > size_per_line)
-            {
-                auto blank_num = size_per_line - (the_range.get_end() - start_of_line);
-                while(blank_num-- > 0)
-                {
-                    *inserter++ = delimiter;
-                    *inserter++ = delimiter;
-                    if(put_delimiters)
-                    {
-                        *inserter++ = delimiter;
-                    }
-                }
-            }
-            *inserter++ = delimiter;
-            *inserter++ = delimiter;
-            for(auto j = start_of_line; j != the_range.get_end(); j++)
-            {
-                auto pc     = static_cast<unsigned char>(*j);
-                *inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
-            }
-        }
-        return inserter;
-    }
-
-    // put newline(and position header)
-    template <typename It>
-    void put_newline(It inserter, std::size_t pos)
-    {
-#ifdef _WIN32
-        *inserter++ = '\r';
-#endif
-        *inserter++ = '\n';
-
-        if(put_positions)
-        {
-            spdlog::fmt_lib::format_to(inserter, SPDLOG_FMT_STRING("{:04X}: "), pos);
-        }
-    }
-};
-} // namespace std
diff --git a/include/spdlog/fmt/bundled/args.h b/include/spdlog/fmt/bundled/args.h
deleted file mode 100644
index fcbc63a09..000000000
--- a/include/spdlog/fmt/bundled/args.h
+++ /dev/null
@@ -1,260 +0,0 @@
-// Formatting library for C++ - dynamic format arguments
-//
-// Copyright (c) 2012 - present, Victor Zverovich
-// All rights reserved.
-//
-// For the license information refer to format.h.
-
-#ifndef FMT_ARGS_H_
-#define FMT_ARGS_H_
-
-#include "core.h"
-
-#include <functional> // std::reference_wrapper
-#include <memory>     // std::unique_ptr
-#include <vector>
-
-FMT_BEGIN_NAMESPACE
-
-namespace detail
-{
-
-template <typename T>
-struct is_reference_wrapper : std::false_type
-{
-};
-template <typename T>
-struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type
-{
-};
-
-template <typename T>
-const T &unwrap(const T &v)
-{
-    return v;
-}
-template <typename T>
-const T &unwrap(const std::reference_wrapper<T> &v)
-{
-    return static_cast<const T &>(v);
-}
-
-class dynamic_arg_list
-{
-    // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
-    // templates it doesn't complain about inability to deduce single translation
-    // unit for placing vtable. So storage_node_base is made a fake template.
-    template <typename = void>
-    struct node
-    {
-        virtual ~node() = default;
-        std::unique_ptr<node<>> next;
-    };
-
-    template <typename T>
-    struct typed_node : node<>
-    {
-        T value;
-
-        template <typename Arg>
-        FMT_CONSTEXPR typed_node(const Arg &arg) : value(arg)
-        {
-        }
-
-        template <typename Char>
-        FMT_CONSTEXPR typed_node(const basic_string_view<Char> &arg) : value(arg.data(), arg.size())
-        {
-        }
-    };
-
-    std::unique_ptr<node<>> head_;
-
-public:
-    template <typename T, typename Arg>
-    const T &push(const Arg &arg)
-    {
-        auto  new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
-        auto &value    = new_node->value;
-        new_node->next = std::move(head_);
-        head_          = std::move(new_node);
-        return value;
-    }
-};
-} // namespace detail
-
-/**
-  \rst
-  A dynamic version of `fmt::format_arg_store`.
-  It's equipped with a storage to potentially temporary objects which lifetimes
-  could be shorter than the format arguments object.
-
-  It can be implicitly converted into `~fmt::basic_format_args` for passing
-  into type-erased formatting functions such as `~fmt::vformat`.
-  \endrst
- */
-template <typename Context>
-class dynamic_format_arg_store
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
-    // Workaround a GCC template argument substitution bug.
-    : public basic_format_args<Context>
-#endif
-{
-private:
-    using char_type = typename Context::char_type;
-
-    template <typename T>
-    struct need_copy
-    {
-        static constexpr detail::type mapped_type = detail::mapped_type_constant<T, Context>::value;
-
-        enum
-        {
-            value =
-                !(detail::is_reference_wrapper<T>::value || std::is_same<T, basic_string_view<char_type>>::value ||
-                  std::is_same<T, detail::std_string_view<char_type>>::value ||
-                  (mapped_type != detail::type::cstring_type && mapped_type != detail::type::string_type &&
-                   mapped_type != detail::type::custom_type))
-        };
-    };
-
-    template <typename T>
-    using stored_type = conditional_t<
-        std::is_convertible<T, std::basic_string<char_type>>::value && !detail::is_reference_wrapper<T>::value,
-        std::basic_string<char_type>,
-        T>;
-
-    // Storage of basic_format_arg must be contiguous.
-    std::vector<basic_format_arg<Context>>         data_;
-    std::vector<detail::named_arg_info<char_type>> named_info_;
-
-    // Storage of arguments not fitting into basic_format_arg must grow
-    // without relocation because items in data_ refer to it.
-    detail::dynamic_arg_list dynamic_args_;
-
-    friend class basic_format_args<Context>;
-
-    unsigned long long get_types() const
-    {
-        return detail::is_unpacked_bit | data_.size() |
-               (named_info_.empty() ? 0ULL : static_cast<unsigned long long>(detail::has_named_args_bit));
-    }
-
-    const basic_format_arg<Context> *data() const { return named_info_.empty() ? data_.data() : data_.data() + 1; }
-
-    template <typename T>
-    void emplace_arg(const T &arg)
-    {
-        data_.emplace_back(detail::make_arg<Context>(arg));
-    }
-
-    template <typename T>
-    void emplace_arg(const detail::named_arg<char_type, T> &arg)
-    {
-        if(named_info_.empty())
-        {
-            constexpr const detail::named_arg_info<char_type> *zero_ptr{nullptr};
-            data_.insert(data_.begin(), {zero_ptr, 0});
-        }
-        data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
-        auto pop_one = [](std::vector<basic_format_arg<Context>> *data) { data->pop_back(); };
-        std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)> guard{&data_, pop_one};
-        named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
-        data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
-        guard.release();
-    }
-
-public:
-    constexpr dynamic_format_arg_store() = default;
-
-    /**
-      \rst
-      Adds an argument into the dynamic store for later passing to a formatting
-      function.
-
-      Note that custom types and string types (but not string views) are copied
-      into the store dynamically allocating memory if necessary.
-
-      **Example**::
-
-        fmt::dynamic_format_arg_store<fmt::format_context> store;
-        store.push_back(42);
-        store.push_back("abc");
-        store.push_back(1.5f);
-        std::string result = fmt::vformat("{} and {} and {}", store);
-      \endrst
-    */
-    template <typename T>
-    void push_back(const T &arg)
-    {
-        if(detail::const_check(need_copy<T>::value))
-            emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
-        else
-            emplace_arg(detail::unwrap(arg));
-    }
-
-    /**
-      \rst
-      Adds a reference to the argument into the dynamic store for later passing to
-      a formatting function.
-
-      **Example**::
-
-        fmt::dynamic_format_arg_store<fmt::format_context> store;
-        char band[] = "Rolling Stones";
-        store.push_back(std::cref(band));
-        band[9] = 'c'; // Changing str affects the output.
-        std::string result = fmt::vformat("{}", store);
-        // result == "Rolling Scones"
-      \endrst
-    */
-    template <typename T>
-    void push_back(std::reference_wrapper<T> arg)
-    {
-        static_assert(need_copy<T>::value, "objects of built-in types and string views are always copied");
-        emplace_arg(arg.get());
-    }
-
-    /**
-      Adds named argument into the dynamic store for later passing to a formatting
-      function. ``std::reference_wrapper`` is supported to avoid copying of the
-      argument. The name is always copied into the store.
-    */
-    template <typename T>
-    void push_back(const detail::named_arg<char_type, T> &arg)
-    {
-        const char_type *arg_name = dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
-        if(detail::const_check(need_copy<T>::value))
-        {
-            emplace_arg(fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
-        }
-        else
-        {
-            emplace_arg(fmt::arg(arg_name, arg.value));
-        }
-    }
-
-    /** Erase all elements from the store */
-    void clear()
-    {
-        data_.clear();
-        named_info_.clear();
-        dynamic_args_ = detail::dynamic_arg_list();
-    }
-
-    /**
-      \rst
-      Reserves space to store at least *new_cap* arguments including
-      *new_cap_named* named arguments.
-      \endrst
-    */
-    void reserve(size_t new_cap, size_t new_cap_named)
-    {
-        FMT_ASSERT(new_cap >= new_cap_named, "Set of arguments includes set of named arguments");
-        data_.reserve(new_cap);
-        named_info_.reserve(new_cap_named);
-    }
-};
-
-FMT_END_NAMESPACE
-
-#endif // FMT_ARGS_H_
diff --git a/include/spdlog/fmt/bundled/chrono.h b/include/spdlog/fmt/bundled/chrono.h
deleted file mode 100644
index 3d76d3c89..000000000
--- a/include/spdlog/fmt/bundled/chrono.h
+++ /dev/null
@@ -1,2396 +0,0 @@
-// 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 <algorithm>
-#include <chrono>
-#include <cmath>   // std::isfinite
-#include <cstring> // std::memcpy
-#include <ctime>
-#include <iterator>
-#include <locale>
-#include <ostream>
-#include <type_traits>
-
-FMT_BEGIN_NAMESPACE
-
-// Enable tzset.
-#ifndef FMT_USE_TZSET
-// UWP doesn't provide _tzset.
-#if FMT_HAS_INCLUDE("winapifamily.h")
-#include <winapifamily.h>
-#endif
-#if defined(_WIN32) && (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
-#define FMT_USE_TZSET 1
-#else
-#define FMT_USE_TZSET 0
-#endif
-#endif
-
-// Enable safe chrono durations, unless explicitly disabled.
-#ifndef FMT_SAFE_DURATION_CAST
-#define FMT_SAFE_DURATION_CAST 1
-#endif
-#if FMT_SAFE_DURATION_CAST
-
-// For conversion between std::chrono::durations without undefined
-// behaviour or erroneous results.
-// This is a stripped down version of duration_cast, for inclusion in fmt.
-// See https://github.com/pauldreik/safe_duration_cast
-//
-// Copyright Paul Dreik 2019
-namespace safe_duration_cast
-{
-
-template <
-    typename To,
-    typename From,
-    FMT_ENABLE_IF(
-        !std::is_same<From, To>::value && std::numeric_limits<From>::is_signed == std::numeric_limits<To>::is_signed)>
-FMT_CONSTEXPR To lossless_integral_conversion(const From from, int &ec)
-{
-    ec      = 0;
-    using F = std::numeric_limits<From>;
-    using T = std::numeric_limits<To>;
-    static_assert(F::is_integer, "From must be integral");
-    static_assert(T::is_integer, "To must be integral");
-
-    // A and B are both signed, or both unsigned.
-    if(detail::const_check(F::digits <= T::digits))
-    {
-        // From fits in To without any problem.
-    }
-    else
-    {
-        // From does not always fit in To, resort to a dynamic check.
-        if(from < (T::min)() || from > (T::max)())
-        {
-            // outside range.
-            ec = 1;
-            return {};
-        }
-    }
-    return static_cast<To>(from);
-}
-
-/**
- * converts From to To, without loss. If the dynamic value of from
- * can't be converted to To without loss, ec is set.
- */
-template <
-    typename To,
-    typename From,
-    FMT_ENABLE_IF(
-        !std::is_same<From, To>::value && std::numeric_limits<From>::is_signed != std::numeric_limits<To>::is_signed)>
-FMT_CONSTEXPR To lossless_integral_conversion(const From from, int &ec)
-{
-    ec      = 0;
-    using F = std::numeric_limits<From>;
-    using T = std::numeric_limits<To>;
-    static_assert(F::is_integer, "From must be integral");
-    static_assert(T::is_integer, "To must be integral");
-
-    if(detail::const_check(F::is_signed && !T::is_signed))
-    {
-        // From may be negative, not allowed!
-        if(fmt::detail::is_negative(from))
-        {
-            ec = 1;
-            return {};
-        }
-        // From is positive. Can it always fit in To?
-        if(detail::const_check(F::digits > T::digits) && from > static_cast<From>(detail::max_value<To>()))
-        {
-            ec = 1;
-            return {};
-        }
-    }
-
-    if(detail::const_check(!F::is_signed && T::is_signed && F::digits >= T::digits) &&
-       from > static_cast<From>(detail::max_value<To>()))
-    {
-        ec = 1;
-        return {};
-    }
-    return static_cast<To>(from); // Lossless conversion.
-}
-
-template <typename To, typename From, FMT_ENABLE_IF(std::is_same<From, To>::value)>
-FMT_CONSTEXPR To lossless_integral_conversion(const From from, int &ec)
-{
-    ec = 0;
-    return from;
-} // function
-
-// clang-format off
-/**
- * converts From to To if possible, otherwise ec is set.
- *
- * input                            |    output
- * ---------------------------------|---------------
- * NaN                              | NaN
- * Inf                              | Inf
- * normal, fits in output           | converted (possibly lossy)
- * normal, does not fit in output   | ec is set
- * subnormal                        | best effort
- * -Inf                             | -Inf
- */
-// clang-format on
-template <typename To, typename From, FMT_ENABLE_IF(!std::is_same<From, To>::value)>
-FMT_CONSTEXPR To safe_float_conversion(const From from, int &ec)
-{
-    ec      = 0;
-    using T = std::numeric_limits<To>;
-    static_assert(std::is_floating_point<From>::value, "From must be floating");
-    static_assert(std::is_floating_point<To>::value, "To must be floating");
-
-    // catch the only happy case
-    if(std::isfinite(from))
-    {
-        if(from >= T::lowest() && from <= (T::max)())
-        {
-            return static_cast<To>(from);
-        }
-        // not within range.
-        ec = 1;
-        return {};
-    }
-
-    // nan and inf will be preserved
-    return static_cast<To>(from);
-} // function
-
-template <typename To, typename From, FMT_ENABLE_IF(std::is_same<From, To>::value)>
-FMT_CONSTEXPR To safe_float_conversion(const From from, int &ec)
-{
-    ec = 0;
-    static_assert(std::is_floating_point<From>::value, "From must be floating");
-    return from;
-}
-
-/**
- * safe duration cast between integral durations
- */
-template <
-    typename To,
-    typename FromRep,
-    typename FromPeriod,
-    FMT_ENABLE_IF(std::is_integral<FromRep>::value),
-    FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
-To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from, int &ec)
-{
-    using From = std::chrono::duration<FromRep, FromPeriod>;
-    ec         = 0;
-    // the basic idea is that we need to convert from count() in the from type
-    // to count() in the To type, by multiplying it with this:
-    struct Factor : std::ratio_divide<typename From::period, typename To::period>
-    {
-    };
-
-    static_assert(Factor::num > 0, "num must be positive");
-    static_assert(Factor::den > 0, "den must be positive");
-
-    // the conversion is like this: multiply from.count() with Factor::num
-    // /Factor::den and convert it to To::rep, all this without
-    // overflow/underflow. let's start by finding a suitable type that can hold
-    // both To, From and Factor::num
-    using IntermediateRep =
-        typename std::common_type<typename From::rep, typename To::rep, decltype(Factor::num)>::type;
-
-    // safe conversion to IntermediateRep
-    IntermediateRep count = lossless_integral_conversion<IntermediateRep>(from.count(), ec);
-    if(ec)
-        return {};
-    // multiply with Factor::num without overflow or underflow
-    if(detail::const_check(Factor::num != 1))
-    {
-        const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
-        if(count > max1)
-        {
-            ec = 1;
-            return {};
-        }
-        const auto min1 = (std::numeric_limits<IntermediateRep>::min)() / Factor::num;
-        if(!std::is_unsigned<IntermediateRep>::value && count < min1)
-        {
-            ec = 1;
-            return {};
-        }
-        count *= Factor::num;
-    }
-
-    if(detail::const_check(Factor::den != 1))
-        count /= Factor::den;
-    auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
-    return ec ? To() : To(tocount);
-}
-
-/**
- * safe duration_cast between floating point durations
- */
-template <
-    typename To,
-    typename FromRep,
-    typename FromPeriod,
-    FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
-    FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
-To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from, int &ec)
-{
-    using From = std::chrono::duration<FromRep, FromPeriod>;
-    ec         = 0;
-    if(std::isnan(from.count()))
-    {
-        // nan in, gives nan out. easy.
-        return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
-    }
-    // maybe we should also check if from is denormal, and decide what to do about
-    // it.
-
-    // +-inf should be preserved.
-    if(std::isinf(from.count()))
-    {
-        return To{from.count()};
-    }
-
-    // the basic idea is that we need to convert from count() in the from type
-    // to count() in the To type, by multiplying it with this:
-    struct Factor : std::ratio_divide<typename From::period, typename To::period>
-    {
-    };
-
-    static_assert(Factor::num > 0, "num must be positive");
-    static_assert(Factor::den > 0, "den must be positive");
-
-    // the conversion is like this: multiply from.count() with Factor::num
-    // /Factor::den and convert it to To::rep, all this without
-    // overflow/underflow. let's start by finding a suitable type that can hold
-    // both To, From and Factor::num
-    using IntermediateRep =
-        typename std::common_type<typename From::rep, typename To::rep, decltype(Factor::num)>::type;
-
-    // force conversion of From::rep -> IntermediateRep to be safe,
-    // even if it will never happen be narrowing in this context.
-    IntermediateRep count = safe_float_conversion<IntermediateRep>(from.count(), ec);
-    if(ec)
-    {
-        return {};
-    }
-
-    // multiply with Factor::num without overflow or underflow
-    if(detail::const_check(Factor::num != 1))
-    {
-        constexpr auto max1 = detail::max_value<IntermediateRep>() / static_cast<IntermediateRep>(Factor::num);
-        if(count > max1)
-        {
-            ec = 1;
-            return {};
-        }
-        constexpr auto min1 =
-            std::numeric_limits<IntermediateRep>::lowest() / static_cast<IntermediateRep>(Factor::num);
-        if(count < min1)
-        {
-            ec = 1;
-            return {};
-        }
-        count *= static_cast<IntermediateRep>(Factor::num);
-    }
-
-    // this can't go wrong, right? den>0 is checked earlier.
-    if(detail::const_check(Factor::den != 1))
-    {
-        using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
-        count /= static_cast<common_t>(Factor::den);
-    }
-
-    // convert to the to type, safely
-    using ToRep = typename To::rep;
-
-    const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
-    if(ec)
-    {
-        return {};
-    }
-    return To{tocount};
-}
-} // namespace safe_duration_cast
-#endif
-
-// Prevents expansion of a preceding token as a function-style macro.
-// Usage: f FMT_NOMACRO()
-#define FMT_NOMACRO
-
-namespace detail
-{
-template <typename T = void>
-struct null
-{
-};
-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<>();
-}
-
-inline const std::locale &get_classic_locale()
-{
-    static const auto &locale = std::locale::classic();
-    return locale;
-}
-
-template <typename CodeUnit>
-struct codecvt_result
-{
-    static constexpr const size_t max_size = 32;
-    CodeUnit                      buf[max_size];
-    CodeUnit                     *end;
-};
-template <typename CodeUnit>
-constexpr const size_t codecvt_result<CodeUnit>::max_size;
-
-template <typename CodeUnit>
-void write_codecvt(codecvt_result<CodeUnit> &out, string_view in_buf, const std::locale &loc)
-{
-#if FMT_CLANG_VERSION
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated"
-    auto &f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
-#pragma clang diagnostic pop
-#else
-    auto &f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
-#endif
-    auto        mb        = std::mbstate_t();
-    const char *from_next = nullptr;
-    auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next, std::begin(out.buf), std::end(out.buf), out.end);
-    if(result != std::codecvt_base::ok)
-        FMT_THROW(format_error("failed to format time"));
-}
-
-template <typename OutputIt>
-auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale &loc) -> OutputIt
-{
-    if(detail::is_utf8() && loc != get_classic_locale())
-    {
-        // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
-        // gcc-4.
-#if FMT_MSC_VERSION != 0 || (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
-        // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
-        // and newer.
-        using code_unit = wchar_t;
-#else
-        using code_unit = char32_t;
-#endif
-
-        using unit_t = codecvt_result<code_unit>;
-        unit_t unit;
-        write_codecvt(unit, in, loc);
-        // In UTF-8 is used one to four one-byte code units.
-        auto &&buf = basic_memory_buffer<char, unit_t::max_size * 4>();
-        for(code_unit *p = unit.buf; p != unit.end; ++p)
-        {
-            uint32_t c = static_cast<uint32_t>(*p);
-            if(sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff)
-            {
-                // surrogate pair
-                ++p;
-                if(p == unit.end || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00)
-                {
-                    FMT_THROW(format_error("failed to format time"));
-                }
-                c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
-            }
-            if(c < 0x80)
-            {
-                buf.push_back(static_cast<char>(c));
-            }
-            else if(c < 0x800)
-            {
-                buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
-                buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
-            }
-            else if((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff))
-            {
-                buf.push_back(static_cast<char>(0xe0 | (c >> 12)));
-                buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
-                buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
-            }
-            else if(c >= 0x10000 && c <= 0x10ffff)
-            {
-                buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
-                buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
-                buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
-                buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
-            }
-            else
-            {
-                FMT_THROW(format_error("failed to format time"));
-            }
-        }
-        return copy_str<char>(buf.data(), buf.data() + buf.size(), out);
-    }
-    return copy_str<char>(in.data(), in.data() + in.size(), out);
-}
-
-template <typename Char, typename OutputIt, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
-auto write_tm_str(OutputIt out, string_view sv, const std::locale &loc) -> OutputIt
-{
-    codecvt_result<Char> unit;
-    write_codecvt(unit, sv, loc);
-    return copy_str<Char>(unit.buf, unit.end, out);
-}
-
-template <typename Char, typename OutputIt, FMT_ENABLE_IF(std::is_same<Char, char>::value)>
-auto write_tm_str(OutputIt out, string_view sv, const std::locale &loc) -> OutputIt
-{
-    return write_encoded_tm_str(out, sv, loc);
-}
-
-template <typename Char>
-inline void do_write(buffer<Char> &buf, const std::tm &time, const std::locale &loc, char format, char modifier)
-{
-    auto &&format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
-    auto &&os         = std::basic_ostream<Char>(&format_buf);
-    os.imbue(loc);
-    using iterator    = std::ostreambuf_iterator<Char>;
-    const auto &facet = std::use_facet<std::time_put<Char, iterator>>(loc);
-    auto        end   = facet.put(os, os, Char(' '), &time, format, modifier);
-    if(end.failed())
-        FMT_THROW(format_error("failed to format time"));
-}
-
-template <typename Char, typename OutputIt, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
-auto write(OutputIt out, const std::tm &time, const std::locale &loc, char format, char modifier = 0) -> OutputIt
-{
-    auto &&buf = get_buffer<Char>(out);
-    do_write<Char>(buf, time, loc, format, modifier);
-    return buf.out();
-}
-
-template <typename Char, typename OutputIt, FMT_ENABLE_IF(std::is_same<Char, char>::value)>
-auto write(OutputIt out, const std::tm &time, const std::locale &loc, char format, char modifier = 0) -> OutputIt
-{
-    auto &&buf = basic_memory_buffer<Char>();
-    do_write<char>(buf, time, loc, format, modifier);
-    return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
-}
-
-} // namespace detail
-
-FMT_MODULE_EXPORT_BEGIN
-
-/**
-  Converts given time since epoch as ``std::time_t`` value into calendar time,
-  expressed in local time. Unlike ``std::localtime``, this function is
-  thread-safe on most platforms.
- */
-inline std::tm localtime(std::time_t time)
-{
-    struct dispatcher
-    {
-        std::time_t time_;
-        std::tm     tm_;
-
-        dispatcher(std::time_t t) : time_(t) {}
-
-        bool run()
-        {
-            using namespace fmt::detail;
-            return handle(localtime_r(&time_, &tm_));
-        }
-
-        bool handle(std::tm *tm) { return tm != nullptr; }
-
-        bool handle(detail::null<>)
-        {
-            using namespace fmt::detail;
-            return fallback(localtime_s(&tm_, &time_));
-        }
-
-        bool fallback(int res) { return res == 0; }
-
-#if !FMT_MSC_VERSION
-        bool fallback(detail::null<>)
-        {
-            using namespace fmt::detail;
-            std::tm *tm = std::localtime(&time_);
-            if(tm)
-                tm_ = *tm;
-            return tm != nullptr;
-        }
-#endif
-    };
-    dispatcher lt(time);
-    // Too big time values may be unsupported.
-    if(!lt.run())
-        FMT_THROW(format_error("time_t value out of range"));
-    return lt.tm_;
-}
-
-inline std::tm localtime(std::chrono::time_point<std::chrono::system_clock> time_point)
-{
-    return localtime(std::chrono::system_clock::to_time_t(time_point));
-}
-
-/**
-  Converts given time since epoch as ``std::time_t`` value into calendar time,
-  expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
-  function is thread-safe on most platforms.
- */
-inline std::tm gmtime(std::time_t time)
-{
-    struct dispatcher
-    {
-        std::time_t time_;
-        std::tm     tm_;
-
-        dispatcher(std::time_t t) : time_(t) {}
-
-        bool run()
-        {
-            using namespace fmt::detail;
-            return handle(gmtime_r(&time_, &tm_));
-        }
-
-        bool handle(std::tm *tm) { return tm != nullptr; }
-
-        bool handle(detail::null<>)
-        {
-            using namespace fmt::detail;
-            return fallback(gmtime_s(&tm_, &time_));
-        }
-
-        bool fallback(int res) { return res == 0; }
-
-#if !FMT_MSC_VERSION
-        bool fallback(detail::null<>)
-        {
-            std::tm *tm = std::gmtime(&time_);
-            if(tm)
-                tm_ = *tm;
-            return tm != nullptr;
-        }
-#endif
-    };
-    dispatcher gt(time);
-    // Too big time values may be unsupported.
-    if(!gt.run())
-        FMT_THROW(format_error("time_t value out of range"));
-    return gt.tm_;
-}
-
-inline std::tm gmtime(std::chrono::time_point<std::chrono::system_clock> time_point)
-{
-    return gmtime(std::chrono::system_clock::to_time_t(time_point));
-}
-
-FMT_BEGIN_DETAIL_NAMESPACE
-
-// Writes two-digit numbers a, b and c separated by sep to buf.
-// The method by Pavel Novikov based on
-// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
-inline void write_digit2_separated(char *buf, unsigned a, unsigned b, unsigned c, char sep)
-{
-    unsigned long long digits = a | (b << 24) | (static_cast<unsigned long long>(c) << 48);
-    // Convert each value to BCD.
-    // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
-    // The difference is
-    //   y - x = a * 6
-    // a can be found from x:
-    //   a = floor(x / 10)
-    // then
-    //   y = x + a * 6 = x + floor(x / 10) * 6
-    // floor(x / 10) is (x * 205) >> 11 (needs 16 bits).
-    digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6;
-    // Put low nibbles to high bytes and high nibbles to low bytes.
-    digits    = ((digits & 0x00f00000f00000f0) >> 4) | ((digits & 0x000f00000f00000f) << 8);
-    auto usep = static_cast<unsigned long long>(sep);
-    // Add ASCII '0' to each digit byte and insert separators.
-    digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
-
-    constexpr const size_t len = 8;
-    if(const_check(is_big_endian()))
-    {
-        char tmp[len];
-        std::memcpy(tmp, &digits, len);
-        std::reverse_copy(tmp, tmp + len, buf);
-    }
-    else
-    {
-        std::memcpy(buf, &digits, len);
-    }
-}
-
-template <typename Period>
-FMT_CONSTEXPR inline const char *get_units()
-{
-    if(std::is_same<Period, std::atto>::value)
-        return "as";
-    if(std::is_same<Period, std::femto>::value)
-        return "fs";
-    if(std::is_same<Period, std::pico>::value)
-        return "ps";
-    if(std::is_same<Period, std::nano>::value)
-        return "ns";
-    if(std::is_same<Period, std::micro>::value)
-        return "µs";
-    if(std::is_same<Period, std::milli>::value)
-        return "ms";
-    if(std::is_same<Period, std::centi>::value)
-        return "cs";
-    if(std::is_same<Period, std::deci>::value)
-        return "ds";
-    if(std::is_same<Period, std::ratio<1>>::value)
-        return "s";
-    if(std::is_same<Period, std::deca>::value)
-        return "das";
-    if(std::is_same<Period, std::hecto>::value)
-        return "hs";
-    if(std::is_same<Period, std::kilo>::value)
-        return "ks";
-    if(std::is_same<Period, std::mega>::value)
-        return "Ms";
-    if(std::is_same<Period, std::giga>::value)
-        return "Gs";
-    if(std::is_same<Period, std::tera>::value)
-        return "Ts";
-    if(std::is_same<Period, std::peta>::value)
-        return "Ps";
-    if(std::is_same<Period, std::exa>::value)
-        return "Es";
-    if(std::is_same<Period, std::ratio<60>>::value)
-        return "m";
-    if(std::is_same<Period, std::ratio<3600>>::value)
-        return "h";
-    return nullptr;
-}
-
-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)
-            FMT_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;
-            }
-            // Year:
-            case 'Y':
-                handler.on_year(numeric_system::standard);
-                break;
-            case 'y':
-                handler.on_short_year(numeric_system::standard);
-                break;
-            case 'C':
-                handler.on_century(numeric_system::standard);
-                break;
-            case 'G':
-                handler.on_iso_week_based_year();
-                break;
-            case 'g':
-                handler.on_iso_week_based_short_year();
-                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':
-            case 'h':
-                handler.on_abbr_month();
-                break;
-            case 'B':
-                handler.on_full_month();
-                break;
-            case 'm':
-                handler.on_dec_month(numeric_system::standard);
-                break;
-            // Day of the year/month:
-            case 'U':
-                handler.on_dec0_week_of_year(numeric_system::standard);
-                break;
-            case 'W':
-                handler.on_dec1_week_of_year(numeric_system::standard);
-                break;
-            case 'V':
-                handler.on_iso_week_of_year(numeric_system::standard);
-                break;
-            case 'j':
-                handler.on_day_of_year();
-                break;
-            case 'd':
-                handler.on_day_of_month(numeric_system::standard);
-                break;
-            case 'e':
-                handler.on_day_of_month_space(numeric_system::standard);
-                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 'Q':
-                handler.on_duration_value();
-                break;
-            case 'q':
-                handler.on_duration_unit();
-                break;
-            case 'z':
-                handler.on_utc_offset();
-                break;
-            case 'Z':
-                handler.on_tz_name();
-                break;
-            // Alternative representation:
-            case 'E':
-            {
-                if(ptr == end)
-                    FMT_THROW(format_error("invalid format"));
-                c = *ptr++;
-                switch(c)
-                {
-                    case 'Y':
-                        handler.on_year(numeric_system::alternative);
-                        break;
-                    case 'y':
-                        handler.on_offset_year();
-                        break;
-                    case 'C':
-                        handler.on_century(numeric_system::alternative);
-                        break;
-                    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:
-                        FMT_THROW(format_error("invalid format"));
-                }
-                break;
-            }
-            case 'O':
-                if(ptr == end)
-                    FMT_THROW(format_error("invalid format"));
-                c = *ptr++;
-                switch(c)
-                {
-                    case 'y':
-                        handler.on_short_year(numeric_system::alternative);
-                        break;
-                    case 'm':
-                        handler.on_dec_month(numeric_system::alternative);
-                        break;
-                    case 'U':
-                        handler.on_dec0_week_of_year(numeric_system::alternative);
-                        break;
-                    case 'W':
-                        handler.on_dec1_week_of_year(numeric_system::alternative);
-                        break;
-                    case 'V':
-                        handler.on_iso_week_of_year(numeric_system::alternative);
-                        break;
-                    case 'd':
-                        handler.on_day_of_month(numeric_system::alternative);
-                        break;
-                    case 'e':
-                        handler.on_day_of_month_space(numeric_system::alternative);
-                        break;
-                    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:
-                        FMT_THROW(format_error("invalid format"));
-                }
-                break;
-            default:
-                FMT_THROW(format_error("invalid format"));
-        }
-        begin = ptr;
-    }
-    if(begin != ptr)
-        handler.on_text(begin, ptr);
-    return ptr;
-}
-
-template <typename Derived>
-struct null_chrono_spec_handler
-{
-    FMT_CONSTEXPR void unsupported() { static_cast<Derived *>(this)->unsupported(); }
-    FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_offset_year() { unsupported(); }
-    FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); }
-    FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); }
-    FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
-    FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
-    FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
-    FMT_CONSTEXPR void on_full_month() { unsupported(); }
-    FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_day_of_year() { unsupported(); }
-    FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); }
-    FMT_CONSTEXPR void on_us_date() { unsupported(); }
-    FMT_CONSTEXPR void on_iso_date() { unsupported(); }
-    FMT_CONSTEXPR void on_12_hour_time() { unsupported(); }
-    FMT_CONSTEXPR void on_24_hour_time() { unsupported(); }
-    FMT_CONSTEXPR void on_iso_time() { unsupported(); }
-    FMT_CONSTEXPR void on_am_pm() { unsupported(); }
-    FMT_CONSTEXPR void on_duration_value() { unsupported(); }
-    FMT_CONSTEXPR void on_duration_unit() { unsupported(); }
-    FMT_CONSTEXPR void on_utc_offset() { unsupported(); }
-    FMT_CONSTEXPR void on_tz_name() { unsupported(); }
-};
-
-struct tm_format_checker : null_chrono_spec_handler<tm_format_checker>
-{
-    FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
-
-    template <typename Char>
-    FMT_CONSTEXPR void on_text(const Char *, const Char *)
-    {
-    }
-    FMT_CONSTEXPR void on_year(numeric_system) {}
-    FMT_CONSTEXPR void on_short_year(numeric_system) {}
-    FMT_CONSTEXPR void on_offset_year() {}
-    FMT_CONSTEXPR void on_century(numeric_system) {}
-    FMT_CONSTEXPR void on_iso_week_based_year() {}
-    FMT_CONSTEXPR void on_iso_week_based_short_year() {}
-    FMT_CONSTEXPR void on_abbr_weekday() {}
-    FMT_CONSTEXPR void on_full_weekday() {}
-    FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {}
-    FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {}
-    FMT_CONSTEXPR void on_abbr_month() {}
-    FMT_CONSTEXPR void on_full_month() {}
-    FMT_CONSTEXPR void on_dec_month(numeric_system) {}
-    FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {}
-    FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {}
-    FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {}
-    FMT_CONSTEXPR void on_day_of_year() {}
-    FMT_CONSTEXPR void on_day_of_month(numeric_system) {}
-    FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {}
-    FMT_CONSTEXPR void on_24_hour(numeric_system) {}
-    FMT_CONSTEXPR void on_12_hour(numeric_system) {}
-    FMT_CONSTEXPR void on_minute(numeric_system) {}
-    FMT_CONSTEXPR void on_second(numeric_system) {}
-    FMT_CONSTEXPR void on_datetime(numeric_system) {}
-    FMT_CONSTEXPR void on_loc_date(numeric_system) {}
-    FMT_CONSTEXPR void on_loc_time(numeric_system) {}
-    FMT_CONSTEXPR void on_us_date() {}
-    FMT_CONSTEXPR void on_iso_date() {}
-    FMT_CONSTEXPR void on_12_hour_time() {}
-    FMT_CONSTEXPR void on_24_hour_time() {}
-    FMT_CONSTEXPR void on_iso_time() {}
-    FMT_CONSTEXPR void on_am_pm() {}
-    FMT_CONSTEXPR void on_utc_offset() {}
-    FMT_CONSTEXPR void on_tz_name() {}
-};
-
-inline const char *tm_wday_full_name(int wday)
-{
-    static constexpr const char *full_name_list[] = {
-        "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
-    return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?";
-}
-inline const char *tm_wday_short_name(int wday)
-{
-    static constexpr const char *short_name_list[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
-    return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???";
-}
-
-inline const char *tm_mon_full_name(int mon)
-{
-    static constexpr const char *full_name_list[] = {
-        "January",
-        "February",
-        "March",
-        "April",
-        "May",
-        "June",
-        "July",
-        "August",
-        "September",
-        "October",
-        "November",
-        "December"};
-    return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?";
-}
-inline const char *tm_mon_short_name(int mon)
-{
-    static constexpr const char *short_name_list[] = {
-        "Jan",
-        "Feb",
-        "Mar",
-        "Apr",
-        "May",
-        "Jun",
-        "Jul",
-        "Aug",
-        "Sep",
-        "Oct",
-        "Nov",
-        "Dec",
-    };
-    return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???";
-}
-
-template <typename T, typename = void>
-struct has_member_data_tm_gmtoff : std::false_type
-{
-};
-template <typename T>
-struct has_member_data_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>> : std::true_type
-{
-};
-
-template <typename T, typename = void>
-struct has_member_data_tm_zone : std::false_type
-{
-};
-template <typename T>
-struct has_member_data_tm_zone<T, void_t<decltype(T::tm_zone)>> : std::true_type
-{
-};
-
-#if FMT_USE_TZSET
-inline void tzset_once()
-{
-    static bool init = []() -> bool
-    {
-        _tzset();
-        return true;
-    }();
-    ignore_unused(init);
-}
-#endif
-
-template <typename OutputIt, typename Char>
-class tm_writer
-{
-private:
-    static constexpr int days_per_week = 7;
-
-    const std::locale &loc_;
-    const bool         is_classic_;
-    OutputIt           out_;
-    const std::tm     &tm_;
-
-    auto tm_sec() const noexcept -> int
-    {
-        FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, "");
-        return tm_.tm_sec;
-    }
-    auto tm_min() const noexcept -> int
-    {
-        FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, "");
-        return tm_.tm_min;
-    }
-    auto tm_hour() const noexcept -> int
-    {
-        FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, "");
-        return tm_.tm_hour;
-    }
-    auto tm_mday() const noexcept -> int
-    {
-        FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, "");
-        return tm_.tm_mday;
-    }
-    auto tm_mon() const noexcept -> int
-    {
-        FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, "");
-        return tm_.tm_mon;
-    }
-    auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; }
-    auto tm_wday() const noexcept -> int
-    {
-        FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, "");
-        return tm_.tm_wday;
-    }
-    auto tm_yday() const noexcept -> int
-    {
-        FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, "");
-        return tm_.tm_yday;
-    }
-
-    auto tm_hour12() const noexcept -> int
-    {
-        const auto h = tm_hour();
-        const auto z = h < 12 ? h : h - 12;
-        return z == 0 ? 12 : z;
-    }
-
-    // POSIX and the C Standard are unclear or inconsistent about what %C and %y
-    // do if the year is negative or exceeds 9999. Use the convention that %C
-    // concatenated with %y yields the same output as %Y, and that %Y contains at
-    // least 4 characters, with more only if necessary.
-    auto split_year_lower(long long year) const noexcept -> int
-    {
-        auto l = year % 100;
-        if(l < 0)
-            l = -l; // l in [0, 99]
-        return static_cast<int>(l);
-    }
-
-    // Algorithm:
-    // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
-    auto iso_year_weeks(long long curr_year) const noexcept -> int
-    {
-        const auto prev_year = curr_year - 1;
-        const auto curr_p    = (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % days_per_week;
-        const auto prev_p    = (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % days_per_week;
-        return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
-    }
-    auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int
-    {
-        return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) / days_per_week;
-    }
-    auto tm_iso_week_year() const noexcept -> long long
-    {
-        const auto year = tm_year();
-        const auto w    = iso_week_num(tm_yday(), tm_wday());
-        if(w < 1)
-            return year - 1;
-        if(w > iso_year_weeks(year))
-            return year + 1;
-        return year;
-    }
-    auto tm_iso_week_of_year() const noexcept -> int
-    {
-        const auto year = tm_year();
-        const auto w    = iso_week_num(tm_yday(), tm_wday());
-        if(w < 1)
-            return iso_year_weeks(year - 1);
-        if(w > iso_year_weeks(year))
-            return 1;
-        return w;
-    }
-
-    void write1(int value) { *out_++ = static_cast<char>('0' + to_unsigned(value) % 10); }
-    void write2(int value)
-    {
-        const char *d = digits2(to_unsigned(value) % 100);
-        *out_++       = *d++;
-        *out_++       = *d;
-    }
-
-    void write_year_extended(long long year)
-    {
-        // At least 4 characters.
-        int width = 4;
-        if(year < 0)
-        {
-            *out_++ = '-';
-            year    = 0 - year;
-            --width;
-        }
-        uint32_or_64_or_128_t<long long> n          = to_unsigned(year);
-        const int                        num_digits = count_digits(n);
-        if(width > num_digits)
-            out_ = std::fill_n(out_, width - num_digits, '0');
-        out_ = format_decimal<Char>(out_, n, num_digits).end;
-    }
-    void write_year(long long year)
-    {
-        if(year >= 0 && year < 10000)
-        {
-            write2(static_cast<int>(year / 100));
-            write2(static_cast<int>(year % 100));
-        }
-        else
-        {
-            write_year_extended(year);
-        }
-    }
-
-    void write_utc_offset(long offset)
-    {
-        if(offset < 0)
-        {
-            *out_++ = '-';
-            offset  = -offset;
-        }
-        else
-        {
-            *out_++ = '+';
-        }
-        offset /= 60;
-        write2(static_cast<int>(offset / 60));
-        write2(static_cast<int>(offset % 60));
-    }
-    template <typename T, FMT_ENABLE_IF(has_member_data_tm_gmtoff<T>::value)>
-    void format_utc_offset_impl(const T &tm)
-    {
-        write_utc_offset(tm.tm_gmtoff);
-    }
-    template <typename T, FMT_ENABLE_IF(!has_member_data_tm_gmtoff<T>::value)>
-    void format_utc_offset_impl(const T &tm)
-    {
-#if defined(_WIN32) && defined(_UCRT)
-#if FMT_USE_TZSET
-        tzset_once();
-#endif
-        long offset = 0;
-        _get_timezone(&offset);
-        if(tm.tm_isdst)
-        {
-            long dstbias = 0;
-            _get_dstbias(&dstbias);
-            offset += dstbias;
-        }
-        write_utc_offset(-offset);
-#else
-        ignore_unused(tm);
-        format_localized('z');
-#endif
-    }
-
-    template <typename T, FMT_ENABLE_IF(has_member_data_tm_zone<T>::value)>
-    void format_tz_name_impl(const T &tm)
-    {
-        if(is_classic_)
-            out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
-        else
-            format_localized('Z');
-    }
-    template <typename T, FMT_ENABLE_IF(!has_member_data_tm_zone<T>::value)>
-    void format_tz_name_impl(const T &)
-    {
-        format_localized('Z');
-    }
-
-    void format_localized(char format, char modifier = 0)
-    {
-        out_ = write<Char>(out_, tm_, loc_, format, modifier);
-    }
-
-public:
-    tm_writer(const std::locale &loc, OutputIt out, const std::tm &tm) :
-        loc_(loc), is_classic_(loc_ == get_classic_locale()), out_(out), tm_(tm)
-    {
-    }
-
-    OutputIt out() const
-    {
-        return out_;
-    }
-
-    FMT_CONSTEXPR void on_text(const Char *begin, const Char *end)
-    {
-        out_ = copy_str<Char>(begin, end, out_);
-    }
-
-    void on_abbr_weekday()
-    {
-        if(is_classic_)
-            out_ = write(out_, tm_wday_short_name(tm_wday()));
-        else
-            format_localized('a');
-    }
-    void on_full_weekday()
-    {
-        if(is_classic_)
-            out_ = write(out_, tm_wday_full_name(tm_wday()));
-        else
-            format_localized('A');
-    }
-    void on_dec0_weekday(numeric_system ns)
-    {
-        if(is_classic_ || ns == numeric_system::standard)
-            return write1(tm_wday());
-        format_localized('w', 'O');
-    }
-    void on_dec1_weekday(numeric_system ns)
-    {
-        if(is_classic_ || ns == numeric_system::standard)
-        {
-            auto wday = tm_wday();
-            write1(wday == 0 ? days_per_week : wday);
-        }
-        else
-        {
-            format_localized('u', 'O');
-        }
-    }
-
-    void on_abbr_month()
-    {
-        if(is_classic_)
-            out_ = write(out_, tm_mon_short_name(tm_mon()));
-        else
-            format_localized('b');
-    }
-    void on_full_month()
-    {
-        if(is_classic_)
-            out_ = write(out_, tm_mon_full_name(tm_mon()));
-        else
-            format_localized('B');
-    }
-
-    void on_datetime(numeric_system ns)
-    {
-        if(is_classic_)
-        {
-            on_abbr_weekday();
-            *out_++ = ' ';
-            on_abbr_month();
-            *out_++ = ' ';
-            on_day_of_month_space(numeric_system::standard);
-            *out_++ = ' ';
-            on_iso_time();
-            *out_++ = ' ';
-            on_year(numeric_system::standard);
-        }
-        else
-        {
-            format_localized('c', ns == numeric_system::standard ? '\0' : 'E');
-        }
-    }
-    void on_loc_date(numeric_system ns)
-    {
-        if(is_classic_)
-            on_us_date();
-        else
-            format_localized('x', ns == numeric_system::standard ? '\0' : 'E');
-    }
-    void on_loc_time(numeric_system ns)
-    {
-        if(is_classic_)
-            on_iso_time();
-        else
-            format_localized('X', ns == numeric_system::standard ? '\0' : 'E');
-    }
-    void on_us_date()
-    {
-        char buf[8];
-        write_digit2_separated(
-            buf, to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), to_unsigned(split_year_lower(tm_year())), '/');
-        out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
-    }
-    void on_iso_date()
-    {
-        auto   year = tm_year();
-        char   buf[10];
-        size_t offset = 0;
-        if(year >= 0 && year < 10000)
-        {
-            copy2(buf, digits2(static_cast<size_t>(year / 100)));
-        }
-        else
-        {
-            offset = 4;
-            write_year_extended(year);
-            year = 0;
-        }
-        write_digit2_separated(
-            buf + 2, static_cast<unsigned>(year % 100), to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), '-');
-        out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_);
-    }
-
-    void on_utc_offset()
-    {
-        format_utc_offset_impl(tm_);
-    }
-    void on_tz_name()
-    {
-        format_tz_name_impl(tm_);
-    }
-
-    void on_year(numeric_system ns)
-    {
-        if(is_classic_ || ns == numeric_system::standard)
-            return write_year(tm_year());
-        format_localized('Y', 'E');
-    }
-    void on_short_year(numeric_system ns)
-    {
-        if(is_classic_ || ns == numeric_system::standard)
-            return write2(split_year_lower(tm_year()));
-        format_localized('y', 'O');
-    }
-    void on_offset_year()
-    {
-        if(is_classic_)
-            return write2(split_year_lower(tm_year()));
-        format_localized('y', 'E');
-    }
-
-    void on_century(numeric_system ns)
-    {
-        if(is_classic_ || ns == numeric_system::standard)
-        {
-            auto year  = tm_year();
-            auto upper = year / 100;
-            if(year >= -99 && year < 0)
-            {
-                // Zero upper on negative year.
-                *out_++ = '-';
-                *out_++ = '0';
-            }
-            else if(upper >= 0 && upper < 100)
-            {
-                write2(static_cast<int>(upper));
-            }
-            else
-            {
-                out_ = write<Char>(out_, upper);
-            }
-        }
-        else
-        {
-            format_localized('C', 'E');
-        }
-    }
-
-    void on_dec_month(numeric_system ns)
-    {
-        if(is_classic_ || ns == numeric_system::standard)
-            return write2(tm_mon() + 1);
-        format_localized('m', 'O');
-    }
-
-    void on_dec0_week_of_year(numeric_system ns)
-    {
-        if(is_classic_ || ns == numeric_system::standard)
-            return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week);
-        format_localized('U', 'O');
-    }
-    void on_dec1_week_of_year(numeric_system ns)
-    {
-        if(is_classic_ || ns == numeric_system::standard)
-        {
-            auto wday = tm_wday();
-            write2((tm_yday() + days_per_week - (wday == 0 ? (days_per_week - 1) : (wday - 1))) / days_per_week);
-        }
-        else
-        {
-            format_localized('W', 'O');
-        }
-    }
-    void on_iso_week_of_year(numeric_system ns)
-    {
-        if(is_classic_ || ns == numeric_system::standard)
-            return write2(tm_iso_week_of_year());
-        format_localized('V', 'O');
-    }
-
-    void on_iso_week_based_year()
-    {
-        write_year(tm_iso_week_year());
-    }
-    void on_iso_week_based_short_year()
-    {
-        write2(split_year_lower(tm_iso_week_year()));
-    }
-
-    void on_day_of_year()
-    {
-        auto yday = tm_yday() + 1;
-        write1(yday / 100);
-        write2(yday % 100);
-    }
-    void on_day_of_month(numeric_system ns)
-    {
-        if(is_classic_ || ns == numeric_system::standard)
-            return write2(tm_mday());
-        format_localized('d', 'O');
-    }
-    void on_day_of_month_space(numeric_system ns)
-    {
-        if(is_classic_ || ns == numeric_system::standard)
-        {
-            auto        mday = to_unsigned(tm_mday()) % 100;
-            const char *d2   = digits2(mday);
-            *out_++          = mday < 10 ? ' ' : d2[0];
-            *out_++          = d2[1];
-        }
-        else
-        {
-            format_localized('e', 'O');
-        }
-    }
-
-    void on_24_hour(numeric_system ns)
-    {
-        if(is_classic_ || ns == numeric_system::standard)
-            return write2(tm_hour());
-        format_localized('H', 'O');
-    }
-    void on_12_hour(numeric_system ns)
-    {
-        if(is_classic_ || ns == numeric_system::standard)
-            return write2(tm_hour12());
-        format_localized('I', 'O');
-    }
-    void on_minute(numeric_system ns)
-    {
-        if(is_classic_ || ns == numeric_system::standard)
-            return write2(tm_min());
-        format_localized('M', 'O');
-    }
-    void on_second(numeric_system ns)
-    {
-        if(is_classic_ || ns == numeric_system::standard)
-            return write2(tm_sec());
-        format_localized('S', 'O');
-    }
-
-    void on_12_hour_time()
-    {
-        if(is_classic_)
-        {
-            char buf[8];
-            write_digit2_separated(buf, to_unsigned(tm_hour12()), to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
-            out_    = copy_str<Char>(std::begin(buf), std::end(buf), out_);
-            *out_++ = ' ';
-            on_am_pm();
-        }
-        else
-        {
-            format_localized('r');
-        }
-    }
-    void on_24_hour_time()
-    {
-        write2(tm_hour());
-        *out_++ = ':';
-        write2(tm_min());
-    }
-    void on_iso_time()
-    {
-        char buf[8];
-        write_digit2_separated(buf, to_unsigned(tm_hour()), to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
-        out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_);
-    }
-
-    void on_am_pm()
-    {
-        if(is_classic_)
-        {
-            *out_++ = tm_hour() < 12 ? 'A' : 'P';
-            *out_++ = 'M';
-        }
-        else
-        {
-            format_localized('p');
-        }
-    }
-
-    // These apply to chrono durations but not tm.
-    void on_duration_value() {}
-    void on_duration_unit() {}
-};
-
-struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker>
-{
-    FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
-
-    template <typename Char>
-    FMT_CONSTEXPR void on_text(const Char *, const Char *)
-    {
-    }
-    FMT_CONSTEXPR void on_24_hour(numeric_system) {}
-    FMT_CONSTEXPR void on_12_hour(numeric_system) {}
-    FMT_CONSTEXPR void on_minute(numeric_system) {}
-    FMT_CONSTEXPR void on_second(numeric_system) {}
-    FMT_CONSTEXPR void on_12_hour_time() {}
-    FMT_CONSTEXPR void on_24_hour_time() {}
-    FMT_CONSTEXPR void on_iso_time() {}
-    FMT_CONSTEXPR void on_am_pm() {}
-    FMT_CONSTEXPR void on_duration_value() {}
-    FMT_CONSTEXPR void on_duration_unit() {}
-};
-
-template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-inline bool isfinite(T)
-{
-    return true;
-}
-
-// Converts value to Int and checks that it's in the range [0, upper).
-template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
-inline Int to_nonnegative_int(T value, Int upper)
-{
-    FMT_ASSERT(
-        std::is_unsigned<Int>::value || (value >= 0 && to_unsigned(value) <= to_unsigned(upper)), "invalid value");
-    (void) upper;
-    return static_cast<Int>(value);
-}
-template <typename T, typename Int, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-inline Int to_nonnegative_int(T value, Int upper)
-{
-    if(value < 0 || value > static_cast<T>(upper))
-        FMT_THROW(format_error("invalid value"));
-    return static_cast<Int>(value);
-}
-
-template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-inline T mod(T x, int y)
-{
-    return x % static_cast<T>(y);
-}
-template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
-inline T mod(T x, int y)
-{
-    return std::fmod(x, static_cast<T>(y));
-}
-
-// If T is an integral type, maps T to its unsigned counterpart, otherwise
-// leaves it unchanged (unlike std::make_unsigned).
-template <typename T, bool INTEGRAL = std::is_integral<T>::value>
-struct make_unsigned_or_unchanged
-{
-    using type = T;
-};
-
-template <typename T>
-struct make_unsigned_or_unchanged<T, true>
-{
-    using type = typename std::make_unsigned<T>::type;
-};
-
-#if FMT_SAFE_DURATION_CAST
-// throwing version of safe_duration_cast
-template <typename To, typename FromRep, typename FromPeriod>
-To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from)
-{
-    int ec;
-    To  to = safe_duration_cast::safe_duration_cast<To>(from, ec);
-    if(ec)
-        FMT_THROW(format_error("cannot format duration"));
-    return to;
-}
-#endif
-
-template <typename Rep, typename Period, FMT_ENABLE_IF(std::is_integral<Rep>::value)>
-inline std::chrono::duration<Rep, std::milli> get_milliseconds(std::chrono::duration<Rep, Period> d)
-{
-    // this may overflow and/or the result may not fit in the
-    // target type.
-#if FMT_SAFE_DURATION_CAST
-    using CommonSecondsType       = typename std::common_type<decltype(d), std::chrono::seconds>::type;
-    const auto d_as_common        = fmt_safe_duration_cast<CommonSecondsType>(d);
-    const auto d_as_whole_seconds = fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
-    // this conversion should be nonproblematic
-    const auto diff = d_as_common - d_as_whole_seconds;
-    const auto ms   = fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
-    return ms;
-#else
-    auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
-    return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
-#endif
-}
-
-// Counts the number of fractional digits in the range [0, 18] according to the
-// C++20 spec. If more than 18 fractional digits are required then returns 6 for
-// microseconds precision.
-template <long long Num, long long Den, int N = 0, bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
-struct count_fractional_digits
-{
-    static constexpr int value = Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
-};
-
-// Base case that doesn't instantiate any more templates
-// in order to avoid overflow.
-template <long long Num, long long Den, int N>
-struct count_fractional_digits<Num, Den, N, false>
-{
-    static constexpr int value = (Num % Den == 0) ? N : 6;
-};
-
-constexpr long long pow10(std::uint32_t n)
-{
-    return n == 0 ? 1 : 10 * pow10(n - 1);
-}
-
-template <class Rep, class Period, FMT_ENABLE_IF(std::numeric_limits<Rep>::is_signed)>
-constexpr std::chrono::duration<Rep, Period> abs(std::chrono::duration<Rep, Period> d)
-{
-    // We need to compare the duration using the count() method directly
-    // due to a compiler bug in clang-11 regarding the spaceship operator,
-    // when -Wzero-as-null-pointer-constant is enabled.
-    // In clang-12 the bug has been fixed. See
-    // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example:
-    // https://www.godbolt.org/z/Knbb5joYx.
-    return d.count() >= d.zero().count() ? d : -d;
-}
-
-template <class Rep, class Period, FMT_ENABLE_IF(!std::numeric_limits<Rep>::is_signed)>
-constexpr std::chrono::duration<Rep, Period> abs(std::chrono::duration<Rep, Period> d)
-{
-    return d;
-}
-
-template <typename Char, typename Rep, typename OutputIt, FMT_ENABLE_IF(std::is_integral<Rep>::value)>
-OutputIt format_duration_value(OutputIt out, Rep val, int)
-{
-    return write<Char>(out, val);
-}
-
-template <typename Char, typename Rep, typename OutputIt, FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
-OutputIt format_duration_value(OutputIt out, Rep val, int precision)
-{
-    auto specs      = basic_format_specs<Char>();
-    specs.precision = precision;
-    specs.type      = precision >= 0 ? presentation_type::fixed_lower : presentation_type::general_lower;
-    return write<Char>(out, val, specs);
-}
-
-template <typename Char, typename OutputIt>
-OutputIt copy_unit(string_view unit, OutputIt out, Char)
-{
-    return std::copy(unit.begin(), unit.end(), out);
-}
-
-template <typename OutputIt>
-OutputIt copy_unit(string_view unit, OutputIt out, wchar_t)
-{
-    // This works when wchar_t is UTF-32 because units only contain characters
-    // that have the same representation in UTF-16 and UTF-32.
-    utf8_to_utf16 u(unit);
-    return std::copy(u.c_str(), u.c_str() + u.size(), out);
-}
-
-template <typename Char, typename Period, typename OutputIt>
-OutputIt format_duration_unit(OutputIt out)
-{
-    if(const char *unit = get_units<Period>())
-        return copy_unit(string_view(unit), out, Char());
-    *out++ = '[';
-    out    = write<Char>(out, Period::num);
-    if(const_check(Period::den != 1))
-    {
-        *out++ = '/';
-        out    = write<Char>(out, Period::den);
-    }
-    *out++ = ']';
-    *out++ = 's';
-    return out;
-}
-
-class get_locale
-{
-private:
-    union
-    {
-        std::locale locale_;
-    };
-    bool has_locale_ = false;
-
-public:
-    get_locale(bool localized, locale_ref loc) : has_locale_(localized)
-    {
-        if(localized)
-            ::new(&locale_) std::locale(loc.template get<std::locale>());
-    }
-    ~get_locale()
-    {
-        if(has_locale_)
-            locale_.~locale();
-    }
-    operator const std::locale &() const { return has_locale_ ? locale_ : get_classic_locale(); }
-};
-
-template <typename FormatContext, typename OutputIt, typename Rep, typename Period>
-struct chrono_formatter
-{
-    FormatContext &context;
-    OutputIt       out;
-    int            precision;
-    bool           localized = false;
-    // rep is unsigned to avoid overflow.
-    using rep = conditional_t<
-        std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
-        unsigned,
-        typename make_unsigned_or_unchanged<Rep>::type>;
-    rep val;
-    using seconds = std::chrono::duration<rep>;
-    seconds s;
-    using milliseconds = std::chrono::duration<rep, std::milli>;
-    bool negative;
-
-    using char_type      = typename FormatContext::char_type;
-    using tm_writer_type = tm_writer<OutputIt, char_type>;
-
-    chrono_formatter(FormatContext &ctx, OutputIt o, std::chrono::duration<Rep, Period> d) :
-        context(ctx), out(o), val(static_cast<rep>(d.count())), negative(false)
-    {
-        if(d.count() < 0)
-        {
-            val      = 0 - val;
-            negative = true;
-        }
-
-        // this may overflow and/or the result may not fit in the
-        // target type.
-#if FMT_SAFE_DURATION_CAST
-        // might need checked conversion (rep!=Rep)
-        auto tmpval = std::chrono::duration<rep, Period>(val);
-        s           = fmt_safe_duration_cast<seconds>(tmpval);
-#else
-        s = std::chrono::duration_cast<seconds>(std::chrono::duration<rep, Period>(val));
-#endif
-    }
-
-    // returns true if nan or inf, writes to out.
-    bool handle_nan_inf()
-    {
-        if(isfinite(val))
-        {
-            return false;
-        }
-        if(isnan(val))
-        {
-            write_nan();
-            return true;
-        }
-        // must be +-inf
-        if(val > 0)
-        {
-            write_pinf();
-        }
-        else
-        {
-            write_ninf();
-        }
-        return true;
-    }
-
-    Rep hour() const
-    {
-        return static_cast<Rep>(mod((s.count() / 3600), 24));
-    }
-
-    Rep hour12() const
-    {
-        Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
-        return hour <= 0 ? 12 : hour;
-    }
-
-    Rep minute() const
-    {
-        return static_cast<Rep>(mod((s.count() / 60), 60));
-    }
-    Rep second() const
-    {
-        return static_cast<Rep>(mod(s.count(), 60));
-    }
-
-    std::tm time() const
-    {
-        auto time    = std::tm();
-        time.tm_hour = to_nonnegative_int(hour(), 24);
-        time.tm_min  = to_nonnegative_int(minute(), 60);
-        time.tm_sec  = to_nonnegative_int(second(), 60);
-        return time;
-    }
-
-    void write_sign()
-    {
-        if(negative)
-        {
-            *out++   = '-';
-            negative = false;
-        }
-    }
-
-    void write(Rep value, int width)
-    {
-        write_sign();
-        if(isnan(value))
-            return write_nan();
-        uint32_or_64_or_128_t<int> n          = to_unsigned(to_nonnegative_int(value, max_value<int>()));
-        int                        num_digits = detail::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).end;
-    }
-
-    template <typename Duration>
-    void write_fractional_seconds(Duration d)
-    {
-        FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
-        constexpr auto num_fractional_digits =
-            count_fractional_digits<Duration::period::num, Duration::period::den>::value;
-
-        using subsecond_precision = std::chrono::duration<
-            typename std::common_type<typename Duration::rep, std::chrono::seconds::rep>::type,
-            std::ratio<1, detail::pow10(num_fractional_digits)>>;
-        if(std::ratio_less<typename subsecond_precision::period, std::chrono::seconds::period>::value)
-        {
-            *out++          = '.';
-            auto fractional = detail::abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d);
-            auto subseconds = std::chrono::treat_as_floating_point<typename subsecond_precision::rep>::value ?
-                                  fractional.count() :
-                                  std::chrono::duration_cast<subsecond_precision>(fractional).count();
-            uint32_or_64_or_128_t<long long> n = to_unsigned(to_nonnegative_int(subseconds, max_value<long long>()));
-            int                              num_digits = detail::count_digits(n);
-            if(num_fractional_digits > num_digits)
-                out = std::fill_n(out, num_fractional_digits - num_digits, '0');
-            out = format_decimal<char_type>(out, n, num_digits).end;
-        }
-    }
-
-    void write_nan()
-    {
-        std::copy_n("nan", 3, out);
-    }
-    void write_pinf()
-    {
-        std::copy_n("inf", 3, out);
-    }
-    void write_ninf()
-    {
-        std::copy_n("-inf", 4, out);
-    }
-
-    template <typename Callback, typename... Args>
-    void format_tm(const tm &time, Callback cb, Args... args)
-    {
-        if(isnan(val))
-            return write_nan();
-        get_locale loc(localized, context.locale());
-        auto       w = tm_writer_type(loc, out, time);
-        (w.*cb)(args...);
-        out = w.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_year(numeric_system) {}
-    void on_short_year(numeric_system) {}
-    void on_offset_year() {}
-    void on_century(numeric_system) {}
-    void on_iso_week_based_year() {}
-    void on_iso_week_based_short_year() {}
-    void on_dec_month(numeric_system) {}
-    void on_dec0_week_of_year(numeric_system) {}
-    void on_dec1_week_of_year(numeric_system) {}
-    void on_iso_week_of_year(numeric_system) {}
-    void on_day_of_year() {}
-    void on_day_of_month(numeric_system) {}
-    void on_day_of_month_space(numeric_system) {}
-
-    void on_24_hour(numeric_system ns)
-    {
-        if(handle_nan_inf())
-            return;
-
-        if(ns == numeric_system::standard)
-            return write(hour(), 2);
-        auto time    = tm();
-        time.tm_hour = to_nonnegative_int(hour(), 24);
-        format_tm(time, &tm_writer_type::on_24_hour, ns);
-    }
-
-    void on_12_hour(numeric_system ns)
-    {
-        if(handle_nan_inf())
-            return;
-
-        if(ns == numeric_system::standard)
-            return write(hour12(), 2);
-        auto time    = tm();
-        time.tm_hour = to_nonnegative_int(hour12(), 12);
-        format_tm(time, &tm_writer_type::on_12_hour, ns);
-    }
-
-    void on_minute(numeric_system ns)
-    {
-        if(handle_nan_inf())
-            return;
-
-        if(ns == numeric_system::standard)
-            return write(minute(), 2);
-        auto time   = tm();
-        time.tm_min = to_nonnegative_int(minute(), 60);
-        format_tm(time, &tm_writer_type::on_minute, ns);
-    }
-
-    void on_second(numeric_system ns)
-    {
-        if(handle_nan_inf())
-            return;
-
-        if(ns == numeric_system::standard)
-        {
-            if(std::is_floating_point<rep>::value)
-            {
-                constexpr auto num_fractional_digits = count_fractional_digits<Period::num, Period::den>::value;
-                auto           buf                   = memory_buffer();
-                format_to(
-                    std::back_inserter(buf),
-                    runtime("{:.{}f}"),
-                    std::fmod(
-                        val * static_cast<rep>(Period::num) / static_cast<rep>(Period::den), static_cast<rep>(60)),
-                    num_fractional_digits);
-                if(negative)
-                    *out++ = '-';
-                if(buf.size() < 2 || buf[1] == '.')
-                    *out++ = '0';
-                out = std::copy(buf.begin(), buf.end(), out);
-            }
-            else
-            {
-                write(second(), 2);
-                write_fractional_seconds(std::chrono::duration<rep, Period>(val));
-            }
-            return;
-        }
-        auto time   = tm();
-        time.tm_sec = to_nonnegative_int(second(), 60);
-        format_tm(time, &tm_writer_type::on_second, ns);
-    }
-
-    void on_12_hour_time()
-    {
-        if(handle_nan_inf())
-            return;
-        format_tm(time(), &tm_writer_type::on_12_hour_time);
-    }
-
-    void on_24_hour_time()
-    {
-        if(handle_nan_inf())
-        {
-            *out++ = ':';
-            handle_nan_inf();
-            return;
-        }
-
-        write(hour(), 2);
-        *out++ = ':';
-        write(minute(), 2);
-    }
-
-    void on_iso_time()
-    {
-        on_24_hour_time();
-        *out++ = ':';
-        if(handle_nan_inf())
-            return;
-        on_second(numeric_system::standard);
-    }
-
-    void on_am_pm()
-    {
-        if(handle_nan_inf())
-            return;
-        format_tm(time(), &tm_writer_type::on_am_pm);
-    }
-
-    void on_duration_value()
-    {
-        if(handle_nan_inf())
-            return;
-        write_sign();
-        out = format_duration_value<char_type>(out, val, precision);
-    }
-
-    void on_duration_unit()
-    {
-        out = format_duration_unit<char_type, Period>(out);
-    }
-};
-
-FMT_END_DETAIL_NAMESPACE
-
-#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
-using weekday = std::chrono::weekday;
-#else
-// A fallback version of weekday.
-class weekday
-{
-private:
-    unsigned char value;
-
-public:
-    weekday() = default;
-    explicit constexpr weekday(unsigned wd) noexcept : value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
-    constexpr unsigned c_encoding() const noexcept { return value; }
-};
-
-class year_month_day
-{
-};
-#endif
-
-// A rudimentary weekday formatter.
-template <typename Char>
-struct formatter<weekday, Char>
-{
-private:
-    bool localized = false;
-
-public:
-    FMT_CONSTEXPR auto parse(basic_format_parse_context<Char> &ctx) -> decltype(ctx.begin())
-    {
-        auto begin = ctx.begin(), end = ctx.end();
-        if(begin != end && *begin == 'L')
-        {
-            ++begin;
-            localized = true;
-        }
-        return begin;
-    }
-
-    template <typename FormatContext>
-    auto format(weekday wd, FormatContext &ctx) const -> decltype(ctx.out())
-    {
-        auto time    = std::tm();
-        time.tm_wday = static_cast<int>(wd.c_encoding());
-        detail::get_locale loc(localized, ctx.locale());
-        auto               w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
-        w.on_abbr_weekday();
-        return w.out();
-    }
-};
-
-template <typename Rep, typename Period, typename Char>
-struct formatter<std::chrono::duration<Rep, Period>, Char>
-{
-private:
-    basic_format_specs<Char> specs;
-    int                      precision = -1;
-    using arg_ref_type                 = detail::arg_ref<Char>;
-    arg_ref_type            width_ref;
-    arg_ref_type            precision_ref;
-    bool                    localized = false;
-    basic_string_view<Char> format_str;
-    using duration = std::chrono::duration<Rep, Period>;
-
-    struct spec_handler
-    {
-        formatter                        &f;
-        basic_format_parse_context<Char> &context;
-        basic_string_view<Char>           format_str;
-
-        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(basic_string_view<Char> arg_id)
-        {
-            context.check_arg_id(arg_id);
-            return arg_ref_type(arg_id);
-        }
-
-        FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) { return arg_ref_type(context.next_arg_id()); }
-
-        void               on_error(const char *msg) { FMT_THROW(format_error(msg)); }
-        FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) { f.specs.fill = fill; }
-        FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; }
-        FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; }
-        FMT_CONSTEXPR void on_precision(int _precision) { f.precision = _precision; }
-        FMT_CONSTEXPR void end_precision() {}
-
-        template <typename Id>
-        FMT_CONSTEXPR void on_dynamic_width(Id arg_id)
-        {
-            f.width_ref = make_arg_ref(arg_id);
-        }
-
-        template <typename Id>
-        FMT_CONSTEXPR void on_dynamic_precision(Id arg_id)
-        {
-            f.precision_ref = make_arg_ref(arg_id);
-        }
-    };
-
-    using iterator = typename basic_format_parse_context<Char>::iterator;
-    struct parse_range
-    {
-        iterator begin;
-        iterator end;
-    };
-
-    FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char> &ctx)
-    {
-        auto begin = ctx.begin(), end = ctx.end();
-        if(begin == end || *begin == '}')
-            return {begin, begin};
-        spec_handler handler{*this, ctx, format_str};
-        begin = detail::parse_align(begin, end, handler);
-        if(begin == end)
-            return {begin, begin};
-        begin = detail::parse_width(begin, end, handler);
-        if(begin == end)
-            return {begin, begin};
-        if(*begin == '.')
-        {
-            if(std::is_floating_point<Rep>::value)
-                begin = detail::parse_precision(begin, end, handler);
-            else
-                handler.on_error("precision not allowed for this argument type");
-        }
-        if(begin != end && *begin == 'L')
-        {
-            ++begin;
-            localized = true;
-        }
-        end = detail::parse_chrono_format(begin, end, detail::chrono_format_checker());
-        return {begin, end};
-    }
-
-public:
-    FMT_CONSTEXPR auto parse(basic_format_parse_context<Char> &ctx) -> decltype(ctx.begin())
-    {
-        auto range = do_parse(ctx);
-        format_str = basic_string_view<Char>(&*range.begin, detail::to_unsigned(range.end - range.begin));
-        return range.end;
-    }
-
-    template <typename FormatContext>
-    auto format(const duration &d, FormatContext &ctx) const -> decltype(ctx.out())
-    {
-        auto specs_copy     = specs;
-        auto precision_copy = precision;
-        auto begin = format_str.begin(), end = format_str.end();
-        // As a possible future optimization, we could avoid extra copying if width
-        // is not specified.
-        basic_memory_buffer<Char> buf;
-        auto                      out = std::back_inserter(buf);
-        detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width, width_ref, ctx);
-        detail::handle_dynamic_spec<detail::precision_checker>(precision_copy, precision_ref, ctx);
-        if(begin == end || *begin == '}')
-        {
-            out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
-            detail::format_duration_unit<Char, Period>(out);
-        }
-        else
-        {
-            detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(ctx, out, d);
-            f.precision = precision_copy;
-            f.localized = localized;
-            detail::parse_chrono_format(begin, end, f);
-        }
-        return detail::write(ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
-    }
-};
-
-template <typename Char, typename Duration>
-struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>, Char> : formatter<std::tm, Char>
-{
-    FMT_CONSTEXPR formatter()
-    {
-        basic_string_view<Char> default_specs = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
-        this->do_parse(default_specs.begin(), default_specs.end());
-    }
-
-    template <typename FormatContext>
-    auto format(std::chrono::time_point<std::chrono::system_clock> val, FormatContext &ctx) const -> decltype(ctx.out())
-    {
-        return formatter<std::tm, Char>::format(localtime(val), ctx);
-    }
-};
-
-template <typename Char>
-struct formatter<std::tm, Char>
-{
-private:
-    enum class spec
-    {
-        unknown,
-        year_month_day,
-        hh_mm_ss,
-    };
-    spec                    spec_ = spec::unknown;
-    basic_string_view<Char> specs;
-
-protected:
-    template <typename It>
-    FMT_CONSTEXPR auto do_parse(It begin, It end) -> It
-    {
-        if(begin != end && *begin == ':')
-            ++begin;
-        end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
-        // Replace default spec only if the new spec is not empty.
-        if(end != begin)
-            specs = {begin, detail::to_unsigned(end - begin)};
-        return end;
-    }
-
-public:
-    FMT_CONSTEXPR auto parse(basic_format_parse_context<Char> &ctx) -> decltype(ctx.begin())
-    {
-        auto end = this->do_parse(ctx.begin(), ctx.end());
-        // basic_string_view<>::compare isn't constexpr before C++17.
-        if(specs.size() == 2 && specs[0] == Char('%'))
-        {
-            if(specs[1] == Char('F'))
-                spec_ = spec::year_month_day;
-            else if(specs[1] == Char('T'))
-                spec_ = spec::hh_mm_ss;
-        }
-        return end;
-    }
-
-    template <typename FormatContext>
-    auto format(const std::tm &tm, FormatContext &ctx) const -> decltype(ctx.out())
-    {
-        const auto         loc_ref = ctx.locale();
-        detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
-        auto               w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), tm);
-        if(spec_ == spec::year_month_day)
-            w.on_iso_date();
-        else if(spec_ == spec::hh_mm_ss)
-            w.on_iso_time();
-        else
-            detail::parse_chrono_format(specs.begin(), specs.end(), w);
-        return w.out();
-    }
-};
-
-FMT_MODULE_EXPORT_END
-FMT_END_NAMESPACE
-
-#endif // FMT_CHRONO_H_
diff --git a/include/spdlog/fmt/bundled/color.h b/include/spdlog/fmt/bundled/color.h
deleted file mode 100644
index d087bb849..000000000
--- a/include/spdlog/fmt/bundled/color.h
+++ /dev/null
@@ -1,717 +0,0 @@
-// 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
-FMT_MODULE_EXPORT_BEGIN
-
-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 emphasis : uint8_t
-{
-    bold          = 1,
-    faint         = 1 << 1,
-    italic        = 1 << 2,
-    underline     = 1 << 3,
-    blink         = 1 << 4,
-    reverse       = 1 << 5,
-    conceal       = 1 << 6,
-    strikethrough = 1 << 7,
-};
-
-// rgb is a struct for red, green and blue colors.
-// Using the name "rgb" makes some editors show the color in a tooltip.
-struct rgb
-{
-    FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
-    FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
-    FMT_CONSTEXPR rgb(uint32_t hex) : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
-    FMT_CONSTEXPR 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;
-};
-
-FMT_BEGIN_DETAIL_NAMESPACE
-
-// color is a struct of either a rgb color or a terminal color.
-struct color_type
-{
-    FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
-    FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{}
-    {
-        value.rgb_color = static_cast<uint32_t>(rgb_color);
-    }
-    FMT_CONSTEXPR color_type(rgb rgb_color) 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) 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;
-};
-
-FMT_END_DETAIL_NAMESPACE
-
-/** A text style consisting of foreground and background colors and emphasis. */
-class text_style
-{
-public:
-    FMT_CONSTEXPR text_style(emphasis em = emphasis()) 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)
-                FMT_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)
-                FMT_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 bool has_foreground() const noexcept { return set_foreground_color; }
-    FMT_CONSTEXPR bool has_background() const noexcept { return set_background_color; }
-    FMT_CONSTEXPR bool has_emphasis() const noexcept { return static_cast<uint8_t>(ems) != 0; }
-    FMT_CONSTEXPR detail::color_type get_foreground() const noexcept
-    {
-        FMT_ASSERT(has_foreground(), "no foreground specified for this style");
-        return foreground_color;
-    }
-    FMT_CONSTEXPR detail::color_type get_background() const noexcept
-    {
-        FMT_ASSERT(has_background(), "no background specified for this style");
-        return background_color;
-    }
-    FMT_CONSTEXPR emphasis get_emphasis() const noexcept
-    {
-        FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
-        return ems;
-    }
-
-private:
-    FMT_CONSTEXPR text_style(bool is_foreground, detail::color_type text_color) 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 text_style fg(detail::color_type foreground) noexcept;
-
-    friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept;
-
-    detail::color_type foreground_color;
-    detail::color_type background_color;
-    bool               set_foreground_color;
-    bool               set_background_color;
-    emphasis           ems;
-};
-
-/** Creates a text style from the foreground (text) color. */
-FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept
-{
-    return text_style(true, foreground);
-}
-
-/** Creates a text style from the background color. */
-FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept
-{
-    return text_style(false, background);
-}
-
-FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept
-{
-    return text_style(lhs) | rhs;
-}
-
-FMT_BEGIN_DETAIL_NAMESPACE
-
-template <typename Char>
-struct ansi_color_escape
-{
-    FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, const char *esc) noexcept
-    {
-        // If we have a terminal color, we need to output another escape code
-        // sequence.
-        if(!text_color.is_rgb)
-        {
-            bool     is_background = esc == string_view("\x1b[48;2;");
-            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;
-
-            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) noexcept
-    {
-        uint8_t em_codes[num_emphases] = {};
-        if(has_emphasis(em, emphasis::bold))
-            em_codes[0] = 1;
-        if(has_emphasis(em, emphasis::faint))
-            em_codes[1] = 2;
-        if(has_emphasis(em, emphasis::italic))
-            em_codes[2] = 3;
-        if(has_emphasis(em, emphasis::underline))
-            em_codes[3] = 4;
-        if(has_emphasis(em, emphasis::blink))
-            em_codes[4] = 5;
-        if(has_emphasis(em, emphasis::reverse))
-            em_codes[5] = 7;
-        if(has_emphasis(em, emphasis::conceal))
-            em_codes[6] = 8;
-        if(has_emphasis(em, emphasis::strikethrough))
-            em_codes[7] = 9;
-
-        size_t index = 0;
-        for(size_t i = 0; i < num_emphases; ++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 noexcept { return buffer; }
-
-    FMT_CONSTEXPR const Char             *begin() const noexcept { return buffer; }
-    FMT_CONSTEXPR_CHAR_TRAITS const Char *end() const noexcept
-    {
-        return buffer + std::char_traits<Char>::length(buffer);
-    }
-
-private:
-    static constexpr size_t num_emphases = 8;
-    Char                    buffer[7u + 3u * num_emphases + 1u];
-
-    static FMT_CONSTEXPR void to_esc(uint8_t c, Char *out, char delimiter) 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);
-    }
-    static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept
-    {
-        return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
-    }
-};
-
-template <typename Char>
-FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(detail::color_type foreground) noexcept
-{
-    return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
-}
-
-template <typename Char>
-FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(detail::color_type background) noexcept
-{
-    return ansi_color_escape<Char>(background, "\x1b[48;2;");
-}
-
-template <typename Char>
-FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept
-{
-    return ansi_color_escape<Char>(em);
-}
-
-template <typename Char>
-inline void fputs(const Char *chars, FILE *stream)
-{
-    int result = std::fputs(chars, stream);
-    if(result < 0)
-        FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
-}
-
-template <>
-inline void fputs<wchar_t>(const wchar_t *chars, FILE *stream)
-{
-    int result = std::fputws(chars, stream);
-    if(result < 0)
-        FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
-}
-
-template <typename Char>
-inline void reset_color(FILE *stream)
-{
-    fputs("\x1b[0m", stream);
-}
-
-template <>
-inline void reset_color<wchar_t>(FILE *stream)
-{
-    fputs(L"\x1b[0m", stream);
-}
-
-template <typename Char>
-inline void reset_color(buffer<Char> &buffer)
-{
-    auto reset_color = string_view("\x1b[0m");
-    buffer.append(reset_color.begin(), reset_color.end());
-}
-
-template <typename T>
-struct styled_arg
-{
-    const T   &value;
-    text_style style;
-};
-
-template <typename Char>
-void vformat_to(
-    buffer<Char>                                            &buf,
-    const text_style                                        &ts,
-    basic_string_view<Char>                                  format_str,
-    basic_format_args<buffer_context<type_identity_t<Char>>> args)
-{
-    bool has_style = false;
-    if(ts.has_emphasis())
-    {
-        has_style     = true;
-        auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
-        buf.append(emphasis.begin(), emphasis.end());
-    }
-    if(ts.has_foreground())
-    {
-        has_style       = true;
-        auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
-        buf.append(foreground.begin(), foreground.end());
-    }
-    if(ts.has_background())
-    {
-        has_style       = true;
-        auto background = detail::make_background_color<Char>(ts.get_background());
-        buf.append(background.begin(), background.end());
-    }
-    detail::vformat_to(buf, format_str, args, {});
-    if(has_style)
-        detail::reset_color<Char>(buf);
-}
-
-FMT_END_DETAIL_NAMESPACE
-
-template <typename S, typename Char = char_t<S>>
-void vprint(
-    std::FILE                                               *f,
-    const text_style                                        &ts,
-    const S                                                 &format,
-    basic_format_args<buffer_context<type_identity_t<Char>>> args)
-{
-    basic_memory_buffer<Char> buf;
-    detail::vformat_to(buf, ts, detail::to_string_view(format), args);
-    if(detail::is_utf8())
-    {
-        detail::print(f, basic_string_view<Char>(buf.begin(), buf.size()));
-    }
-    else
-    {
-        buf.push_back(Char(0));
-        detail::fputs(buf.data(), f);
-    }
-}
-
-/**
-  \rst
-  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);
-  \endrst
- */
-template <typename S, typename... Args, FMT_ENABLE_IF(detail::is_string<S>::value)>
-void print(std::FILE *f, const text_style &ts, const S &format_str, const Args &...args)
-{
-    vprint(f, ts, format_str, fmt::make_format_args<buffer_context<char_t<S>>>(args...));
-}
-
-/**
-  \rst
-  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);
-  \endrst
- */
-template <typename S, typename... Args, FMT_ENABLE_IF(detail::is_string<S>::value)>
-void print(const text_style &ts, const S &format_str, const Args &...args)
-{
-    return print(stdout, ts, format_str, args...);
-}
-
-template <typename S, typename Char = char_t<S>>
-inline std::basic_string<Char>
-vformat(const text_style &ts, const S &format_str, basic_format_args<buffer_context<type_identity_t<Char>>> args)
-{
-    basic_memory_buffer<Char> buf;
-    detail::vformat_to(buf, ts, detail::to_string_view(format_str), args);
-    return fmt::to_string(buf);
-}
-
-/**
-  \rst
-  Formats arguments and returns the result as a string using ANSI
-  escape sequences to specify text formatting.
-
-  **Example**::
-
-    #include <fmt/color.h>
-    std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
-                                      "The answer is {}", 42);
-  \endrst
-*/
-template <typename S, typename... Args, typename Char = char_t<S>>
-inline std::basic_string<Char> format(const text_style &ts, const S &format_str, const Args &...args)
-{
-    return fmt::vformat(ts, detail::to_string_view(format_str), fmt::make_format_args<buffer_context<Char>>(args...));
-}
-
-/**
-  Formats a string with the given text_style and writes the output to ``out``.
- */
-template <typename OutputIt, typename Char, FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
-OutputIt vformat_to(
-    OutputIt                                                 out,
-    const text_style                                        &ts,
-    basic_string_view<Char>                                  format_str,
-    basic_format_args<buffer_context<type_identity_t<Char>>> args)
-{
-    auto &&buf = detail::get_buffer<Char>(out);
-    detail::vformat_to(buf, ts, format_str, args);
-    return detail::get_iterator(buf);
-}
-
-/**
-  \rst
-  Formats arguments with the given text_style, writes the result to the output
-  iterator ``out`` and returns the iterator past the end of the output range.
-
-  **Example**::
-
-    std::vector<char> out;
-    fmt::format_to(std::back_inserter(out),
-                   fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
-  \endrst
-*/
-template <
-    typename OutputIt,
-    typename S,
-    typename... Args,
-    bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value &&detail::is_string<S>::value>
-inline auto format_to(OutputIt out, const text_style &ts, const S &format_str, Args &&...args) ->
-    typename std::enable_if<enable, OutputIt>::type
-{
-    return vformat_to(
-        out, ts, detail::to_string_view(format_str), fmt::make_format_args<buffer_context<char_t<S>>>(args...));
-}
-
-template <typename T, typename Char>
-struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char>
-{
-    template <typename FormatContext>
-    auto format(const detail::styled_arg<T> &arg, FormatContext &ctx) const -> decltype(ctx.out())
-    {
-        const auto &ts    = arg.style;
-        const auto &value = arg.value;
-        auto        out   = ctx.out();
-
-        bool has_style = false;
-        if(ts.has_emphasis())
-        {
-            has_style     = true;
-            auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
-            out           = std::copy(emphasis.begin(), emphasis.end(), out);
-        }
-        if(ts.has_foreground())
-        {
-            has_style       = true;
-            auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
-            out             = std::copy(foreground.begin(), foreground.end(), out);
-        }
-        if(ts.has_background())
-        {
-            has_style       = true;
-            auto background = detail::make_background_color<Char>(ts.get_background());
-            out             = std::copy(background.begin(), background.end(), out);
-        }
-        out = formatter<T, Char>::format(value, ctx);
-        if(has_style)
-        {
-            auto reset_color = string_view("\x1b[0m");
-            out              = std::copy(reset_color.begin(), reset_color.end(), out);
-        }
-        return out;
-    }
-};
-
-/**
-  \rst
-  Returns an argument that will be formatted using ANSI escape sequences,
-  to be used in a formatting function.
-
-  **Example**::
-
-    fmt::print("Elapsed time: {0:.2f} seconds",
-               fmt::styled(1.23, fmt::fg(fmt::color::green) |
-                                 fmt::bg(fmt::color::blue)));
-  \endrst
- */
-template <typename T>
-FMT_CONSTEXPR auto styled(const T &value, text_style ts) -> detail::styled_arg<remove_cvref_t<T>>
-{
-    return detail::styled_arg<remove_cvref_t<T>>{value, ts};
-}
-
-FMT_MODULE_EXPORT_END
-FMT_END_NAMESPACE
-
-#endif // FMT_COLOR_H_
diff --git a/include/spdlog/fmt/bundled/compile.h b/include/spdlog/fmt/bundled/compile.h
deleted file mode 100644
index bcb81b6ea..000000000
--- a/include/spdlog/fmt/bundled/compile.h
+++ /dev/null
@@ -1,709 +0,0 @@
-// Formatting library for C++ - experimental format string compilation
-//
-// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
-// All rights reserved.
-//
-// For the license information refer to format.h.
-
-#ifndef FMT_COMPILE_H_
-#define FMT_COMPILE_H_
-
-#include "format.h"
-
-FMT_BEGIN_NAMESPACE
-namespace detail
-{
-
-template <typename Char, typename InputIt>
-FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end, counting_iterator it)
-{
-    return it + (end - begin);
-}
-
-template <typename OutputIt>
-class truncating_iterator_base
-{
-protected:
-    OutputIt out_;
-    size_t   limit_;
-    size_t   count_ = 0;
-
-    truncating_iterator_base() : out_(), limit_(0) {}
-
-    truncating_iterator_base(OutputIt out, size_t limit) : out_(out), limit_(limit) {}
-
-public:
-    using iterator_category = std::output_iterator_tag;
-    using value_type        = typename std::iterator_traits<OutputIt>::value_type;
-    using difference_type   = std::ptrdiff_t;
-    using pointer           = void;
-    using reference         = void;
-    FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
-
-    OutputIt base() const { return out_; }
-    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<OutputIt, std::false_type> : public truncating_iterator_base<OutputIt>
-{
-    mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
-
-public:
-    using value_type = typename truncating_iterator_base<OutputIt>::value_type;
-
-    truncating_iterator() = default;
-
-    truncating_iterator(OutputIt out, size_t limit) : truncating_iterator_base<OutputIt>(out, limit) {}
-
-    truncating_iterator &operator++()
-    {
-        if(this->count_++ < this->limit_)
-            ++this->out_;
-        return *this;
-    }
-
-    truncating_iterator operator++(int)
-    {
-        auto it = *this;
-        ++*this;
-        return it;
-    }
-
-    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:
-    truncating_iterator() = default;
-
-    truncating_iterator(OutputIt out, size_t limit) : truncating_iterator_base<OutputIt>(out, limit) {}
-
-    template <typename T>
-    truncating_iterator &operator=(T 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; }
-};
-
-// A compile-time string which is compiled into fast formatting code.
-class compiled_string
-{
-};
-
-template <typename S>
-struct is_compiled_string : std::is_base_of<compiled_string, S>
-{
-};
-
-/**
-  \rst
-  Converts a string literal *s* into a format string that will be parsed at
-  compile time and converted into efficient formatting code. Requires C++17
-  ``constexpr if`` compiler support.
-
-  **Example**::
-
-    // Converts 42 into std::string using the most efficient method and no
-    // runtime format string processing.
-    std::string s = fmt::format(FMT_COMPILE("{}"), 42);
-  \endrst
- */
-#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
-#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
-#else
-#define FMT_COMPILE(s) FMT_STRING(s)
-#endif
-
-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
-template <typename Char, size_t N, fmt::detail_exported::fixed_string<Char, N> Str>
-struct udl_compiled_string : compiled_string
-{
-    using char_type = Char;
-    explicit constexpr operator basic_string_view<char_type>() const { return {Str.data, N - 1}; }
-};
-#endif
-
-template <typename T, typename... Tail>
-const T &first(const T &value, const Tail &...)
-{
-    return value;
-}
-
-#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
-template <typename... Args>
-struct type_list
-{
-};
-
-// Returns a reference to the argument at index N from [first, rest...].
-template <int N, typename T, typename... Args>
-constexpr const auto &get([[maybe_unused]] const T &first, [[maybe_unused]] const Args &...rest)
-{
-    static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
-    if constexpr(N == 0)
-        return first;
-    else
-        return detail::get<N - 1>(rest...);
-}
-
-template <typename Char, typename... Args>
-constexpr int get_arg_index_by_name(basic_string_view<Char> name, type_list<Args...>)
-{
-    return get_arg_index_by_name<Args...>(name);
-}
-
-template <int N, typename>
-struct get_type_impl;
-
-template <int N, typename... Args>
-struct get_type_impl<N, type_list<Args...>>
-{
-    using type = remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
-};
-
-template <int N, typename T>
-using get_type = typename get_type_impl<N, T>::type;
-
-template <typename T>
-struct is_compiled_format : std::false_type
-{
-};
-
-template <typename Char>
-struct text
-{
-    basic_string_view<Char> data;
-    using char_type = Char;
-
-    template <typename OutputIt, typename... Args>
-    constexpr OutputIt format(OutputIt out, const Args &...) const
-    {
-        return write<Char>(out, data);
-    }
-};
-
-template <typename Char>
-struct is_compiled_format<text<Char>> : std::true_type
-{
-};
-
-template <typename Char>
-constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos, size_t size)
-{
-    return {{&s[pos], size}};
-}
-
-template <typename Char>
-struct code_unit
-{
-    Char value;
-    using char_type = Char;
-
-    template <typename OutputIt, typename... Args>
-    constexpr OutputIt format(OutputIt out, const Args &...) const
-    {
-        return write<Char>(out, value);
-    }
-};
-
-// This ensures that the argument type is convertible to `const T&`.
-template <typename T, int N, typename... Args>
-constexpr const T &get_arg_checked(const Args &...args)
-{
-    const auto &arg = detail::get<N>(args...);
-    if constexpr(detail::is_named_arg<remove_cvref_t<decltype(arg)>>())
-    {
-        return arg.value;
-    }
-    else
-    {
-        return arg;
-    }
-}
-
-template <typename Char>
-struct is_compiled_format<code_unit<Char>> : std::true_type
-{
-};
-
-// A replacement field that refers to argument N.
-template <typename Char, typename T, int N>
-struct field
-{
-    using char_type = Char;
-
-    template <typename OutputIt, typename... Args>
-    constexpr OutputIt format(OutputIt out, const Args &...args) const
-    {
-        return write<Char>(out, get_arg_checked<T, N>(args...));
-    }
-};
-
-template <typename Char, typename T, int N>
-struct is_compiled_format<field<Char, T, N>> : std::true_type
-{
-};
-
-// A replacement field that refers to argument with name.
-template <typename Char>
-struct runtime_named_field
-{
-    using char_type = Char;
-    basic_string_view<Char> name;
-
-    template <typename OutputIt, typename T>
-    constexpr static bool try_format_argument(
-        OutputIt &out,
-        // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
-        [[maybe_unused]] basic_string_view<Char> arg_name,
-        const T                                 &arg)
-    {
-        if constexpr(is_named_arg<typename std::remove_cv<T>::type>::value)
-        {
-            if(arg_name == arg.name)
-            {
-                out = write<Char>(out, arg.value);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    template <typename OutputIt, typename... Args>
-    constexpr OutputIt format(OutputIt out, const Args &...args) const
-    {
-        bool found = (try_format_argument(out, name, args) || ...);
-        if(!found)
-        {
-            FMT_THROW(format_error("argument with specified name is not found"));
-        }
-        return out;
-    }
-};
-
-template <typename Char>
-struct is_compiled_format<runtime_named_field<Char>> : std::true_type
-{
-};
-
-// A replacement field that refers to argument N and has format specifiers.
-template <typename Char, typename T, int N>
-struct spec_field
-{
-    using char_type = Char;
-    formatter<T, Char> fmt;
-
-    template <typename OutputIt, typename... Args>
-    constexpr FMT_INLINE OutputIt format(OutputIt out, const Args &...args) const
-    {
-        const auto &vargs = fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
-        basic_format_context<OutputIt, Char> ctx(out, vargs);
-        return fmt.format(get_arg_checked<T, N>(args...), ctx);
-    }
-};
-
-template <typename Char, typename T, int N>
-struct is_compiled_format<spec_field<Char, T, N>> : std::true_type
-{
-};
-
-template <typename L, typename R>
-struct concat
-{
-    L lhs;
-    R rhs;
-    using char_type = typename L::char_type;
-
-    template <typename OutputIt, typename... Args>
-    constexpr OutputIt format(OutputIt out, const Args &...args) const
-    {
-        out = lhs.format(out, args...);
-        return rhs.format(out, args...);
-    }
-};
-
-template <typename L, typename R>
-struct is_compiled_format<concat<L, R>> : std::true_type
-{
-};
-
-template <typename L, typename R>
-constexpr concat<L, R> make_concat(L lhs, R rhs)
-{
-    return {lhs, rhs};
-}
-
-struct unknown_format
-{
-};
-
-template <typename Char>
-constexpr size_t parse_text(basic_string_view<Char> str, size_t pos)
-{
-    for(size_t size = str.size(); pos != size; ++pos)
-    {
-        if(str[pos] == '{' || str[pos] == '}')
-            break;
-    }
-    return pos;
-}
-
-template <typename Args, size_t POS, int ID, typename S>
-constexpr auto compile_format_string(S format_str);
-
-template <typename Args, size_t POS, int ID, typename T, typename S>
-constexpr auto parse_tail(T head, S format_str)
-{
-    if constexpr(POS != basic_string_view<typename S::char_type>(format_str).size())
-    {
-        constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
-        if constexpr(std::is_same<remove_cvref_t<decltype(tail)>, unknown_format>())
-            return tail;
-        else
-            return make_concat(head, tail);
-    }
-    else
-    {
-        return head;
-    }
-}
-
-template <typename T, typename Char>
-struct parse_specs_result
-{
-    formatter<T, Char> fmt;
-    size_t             end;
-    int                next_arg_id;
-};
-
-constexpr int manual_indexing_id = -1;
-
-template <typename T, typename Char>
-constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, size_t pos, int next_arg_id)
-{
-    str.remove_prefix(pos);
-    auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, {}, next_arg_id);
-    auto f   = formatter<T, Char>();
-    auto end = f.parse(ctx);
-    return {
-        f, pos + fmt::detail::to_unsigned(end - str.data()), next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
-}
-
-template <typename Char>
-struct arg_id_handler
-{
-    arg_ref<Char> arg_id;
-
-    constexpr int operator()()
-    {
-        FMT_ASSERT(false, "handler cannot be used with automatic indexing");
-        return 0;
-    }
-    constexpr int operator()(int id)
-    {
-        arg_id = arg_ref<Char>(id);
-        return 0;
-    }
-    constexpr int operator()(basic_string_view<Char> id)
-    {
-        arg_id = arg_ref<Char>(id);
-        return 0;
-    }
-
-    constexpr void on_error(const char *message) { FMT_THROW(format_error(message)); }
-};
-
-template <typename Char>
-struct parse_arg_id_result
-{
-    arg_ref<Char> arg_id;
-    const Char   *arg_id_end;
-};
-
-template <int ID, typename Char>
-constexpr auto parse_arg_id(const Char *begin, const Char *end)
-{
-    auto handler    = arg_id_handler<Char>{arg_ref<Char>{}};
-    auto arg_id_end = parse_arg_id(begin, end, handler);
-    return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
-}
-
-template <typename T, typename Enable = void>
-struct field_type
-{
-    using type = remove_cvref_t<T>;
-};
-
-template <typename T>
-struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>>
-{
-    using type = remove_cvref_t<decltype(T::value)>;
-};
-
-template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID, typename S>
-constexpr auto parse_replacement_field_then_tail(S format_str)
-{
-    using char_type         = typename S::char_type;
-    constexpr auto      str = basic_string_view<char_type>(format_str);
-    constexpr char_type c   = END_POS != str.size() ? str[END_POS] : char_type();
-    if constexpr(c == '}')
-    {
-        return parse_tail<Args, END_POS + 1, NEXT_ID>(
-            field<char_type, typename field_type<T>::type, ARG_INDEX>(), format_str);
-    }
-    else if constexpr(c != ':')
-    {
-        FMT_THROW(format_error("expected ':'"));
-    }
-    else
-    {
-        constexpr auto result =
-            parse_specs<typename field_type<T>::type>(str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
-        if constexpr(result.end >= str.size() || str[result.end] != '}')
-        {
-            FMT_THROW(format_error("expected '}'"));
-            return 0;
-        }
-        else
-        {
-            return parse_tail<Args, result.end + 1, result.next_arg_id>(
-                spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{result.fmt}, format_str);
-        }
-    }
-}
-
-// Compiles a non-empty format string and returns the compiled representation
-// or unknown_format() on unrecognized input.
-template <typename Args, size_t POS, int ID, typename S>
-constexpr auto compile_format_string(S format_str)
-{
-    using char_type    = typename S::char_type;
-    constexpr auto str = basic_string_view<char_type>(format_str);
-    if constexpr(str[POS] == '{')
-    {
-        if constexpr(POS + 1 == str.size())
-            FMT_THROW(format_error("unmatched '{' in format string"));
-        if constexpr(str[POS + 1] == '{')
-        {
-            return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
-        }
-        else if constexpr(str[POS + 1] == '}' || str[POS + 1] == ':')
-        {
-            static_assert(ID != manual_indexing_id, "cannot switch from manual to automatic argument indexing");
-            constexpr auto next_id = ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
-            return parse_replacement_field_then_tail<get_type<ID, Args>, Args, POS + 1, ID, next_id>(format_str);
-        }
-        else
-        {
-            constexpr auto      arg_id_result  = parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
-            constexpr auto      arg_id_end_pos = arg_id_result.arg_id_end - str.data();
-            constexpr char_type c              = arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
-            static_assert(c == '}' || c == ':', "missing '}' in format string");
-            if constexpr(arg_id_result.arg_id.kind == arg_id_kind::index)
-            {
-                static_assert(
-                    ID == manual_indexing_id || ID == 0, "cannot switch from automatic to manual argument indexing");
-                constexpr auto arg_index = arg_id_result.arg_id.val.index;
-                return parse_replacement_field_then_tail<
-                    get_type<arg_index, Args>,
-                    Args,
-                    arg_id_end_pos,
-                    arg_index,
-                    manual_indexing_id>(format_str);
-            }
-            else if constexpr(arg_id_result.arg_id.kind == arg_id_kind::name)
-            {
-                constexpr auto arg_index = get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
-                if constexpr(arg_index != invalid_arg_index)
-                {
-                    constexpr auto next_id = ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
-                    return parse_replacement_field_then_tail<
-                        decltype(get_type<arg_index, Args>::value),
-                        Args,
-                        arg_id_end_pos,
-                        arg_index,
-                        next_id>(format_str);
-                }
-                else
-                {
-                    if constexpr(c == '}')
-                    {
-                        return parse_tail<Args, arg_id_end_pos + 1, ID>(
-                            runtime_named_field<char_type>{arg_id_result.arg_id.val.name}, format_str);
-                    }
-                    else if constexpr(c == ':')
-                    {
-                        return unknown_format(); // no type info for specs parsing
-                    }
-                }
-            }
-        }
-    }
-    else if constexpr(str[POS] == '}')
-    {
-        if constexpr(POS + 1 == str.size())
-            FMT_THROW(format_error("unmatched '}' in format string"));
-        return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
-    }
-    else
-    {
-        constexpr auto end = parse_text(str, POS + 1);
-        if constexpr(end - POS > 1)
-        {
-            return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), format_str);
-        }
-        else
-        {
-            return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, format_str);
-        }
-    }
-}
-
-template <typename... Args, typename S, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-constexpr auto compile(S format_str)
-{
-    constexpr auto str = basic_string_view<typename S::char_type>(format_str);
-    if constexpr(str.size() == 0)
-    {
-        return detail::make_text(str, 0, 0);
-    }
-    else
-    {
-        constexpr auto result = detail::compile_format_string<detail::type_list<Args...>, 0, 0>(format_str);
-        return result;
-    }
-}
-#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
-} // namespace detail
-
-FMT_MODULE_EXPORT_BEGIN
-
-#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
-
-template <
-    typename CompiledFormat,
-    typename... Args,
-    typename Char = typename CompiledFormat::char_type,
-    FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
-FMT_INLINE std::basic_string<Char> format(const CompiledFormat &cf, const Args &...args)
-{
-    auto s = std::basic_string<Char>();
-    cf.format(std::back_inserter(s), args...);
-    return s;
-}
-
-template <
-    typename OutputIt,
-    typename CompiledFormat,
-    typename... Args,
-    FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
-constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat &cf, const Args &...args)
-{
-    return cf.format(out, args...);
-}
-
-template <typename S, typename... Args, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-FMT_INLINE std::basic_string<typename S::char_type> format(const S &, Args &&...args)
-{
-    if constexpr(std::is_same<typename S::char_type, char>::value)
-    {
-        constexpr auto str = basic_string_view<typename S::char_type>(S());
-        if constexpr(str.size() == 2 && str[0] == '{' && str[1] == '}')
-        {
-            const auto &first = detail::first(args...);
-            if constexpr(detail::is_named_arg<remove_cvref_t<decltype(first)>>::value)
-            {
-                return fmt::to_string(first.value);
-            }
-            else
-            {
-                return fmt::to_string(first);
-            }
-        }
-    }
-    constexpr auto compiled = detail::compile<Args...>(S());
-    if constexpr(std::is_same<remove_cvref_t<decltype(compiled)>, detail::unknown_format>())
-    {
-        return fmt::format(static_cast<basic_string_view<typename S::char_type>>(S()), std::forward<Args>(args)...);
-    }
-    else
-    {
-        return fmt::format(compiled, std::forward<Args>(args)...);
-    }
-}
-
-template <typename OutputIt, typename S, typename... Args, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S &, Args &&...args)
-{
-    constexpr auto compiled = detail::compile<Args...>(S());
-    if constexpr(std::is_same<remove_cvref_t<decltype(compiled)>, detail::unknown_format>())
-    {
-        return fmt::format_to(
-            out, static_cast<basic_string_view<typename S::char_type>>(S()), std::forward<Args>(args)...);
-    }
-    else
-    {
-        return fmt::format_to(out, compiled, std::forward<Args>(args)...);
-    }
-}
-#endif
-
-template <typename OutputIt, typename S, typename... Args, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S &format_str, Args &&...args)
-{
-    auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n), format_str, std::forward<Args>(args)...);
-    return {it.base(), it.count()};
-}
-
-template <typename S, typename... Args, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-FMT_CONSTEXPR20 size_t formatted_size(const S &format_str, const Args &...args)
-{
-    return fmt::format_to(detail::counting_iterator(), format_str, args...).count();
-}
-
-template <typename S, typename... Args, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-void print(std::FILE *f, const S &format_str, const Args &...args)
-{
-    memory_buffer buffer;
-    fmt::format_to(std::back_inserter(buffer), format_str, args...);
-    detail::print(f, {buffer.data(), buffer.size()});
-}
-
-template <typename S, typename... Args, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-void print(const S &format_str, const Args &...args)
-{
-    print(stdout, format_str, args...);
-}
-
-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
-inline namespace literals
-{
-template <detail_exported::fixed_string Str>
-constexpr auto operator""_cf()
-{
-    using char_t = remove_cvref_t<decltype(Str.data[0])>;
-    return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t), Str>();
-}
-} // namespace literals
-#endif
-
-FMT_MODULE_EXPORT_END
-FMT_END_NAMESPACE
-
-#endif // FMT_COMPILE_H_
diff --git a/include/spdlog/fmt/bundled/core.h b/include/spdlog/fmt/bundled/core.h
deleted file mode 100644
index c4a806c98..000000000
--- a/include/spdlog/fmt/bundled/core.h
+++ /dev/null
@@ -1,3685 +0,0 @@
-// Formatting library for C++ - the core API for char/UTF-8
-//
-// Copyright (c) 2012 - present, Victor Zverovich
-// All rights reserved.
-//
-// For the license information refer to format.h.
-
-#ifndef FMT_CORE_H_
-#define FMT_CORE_H_
-
-#include <cstddef> // std::byte
-#include <cstdio>  // std::FILE
-#include <cstring> // std::strlen
-#include <iterator>
-#include <limits>
-#include <string>
-#include <type_traits>
-
-// The fmt library version in the form major * 10000 + minor * 100 + patch.
-#define FMT_VERSION 90100
-
-#if defined(__clang__) && !defined(__ibmxl__)
-#define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
-#else
-#define FMT_CLANG_VERSION 0
-#endif
-
-#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__NVCOMPILER)
-#define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#else
-#define FMT_GCC_VERSION 0
-#endif
-
-#ifndef FMT_GCC_PRAGMA
-// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884.
-#if FMT_GCC_VERSION >= 504
-#define FMT_GCC_PRAGMA(arg) _Pragma(arg)
-#else
-#define FMT_GCC_PRAGMA(arg)
-#endif
-#endif
-
-#ifdef __ICL
-#define FMT_ICC_VERSION __ICL
-#elif defined(__INTEL_COMPILER)
-#define FMT_ICC_VERSION __INTEL_COMPILER
-#else
-#define FMT_ICC_VERSION 0
-#endif
-
-#ifdef _MSC_VER
-#define FMT_MSC_VERSION      _MSC_VER
-#define FMT_MSC_WARNING(...) __pragma(warning(__VA_ARGS__))
-#else
-#define FMT_MSC_VERSION 0
-#define FMT_MSC_WARNING(...)
-#endif
-
-#ifdef _MSVC_LANG
-#define FMT_CPLUSPLUS _MSVC_LANG
-#else
-#define FMT_CPLUSPLUS __cplusplus
-#endif
-
-#ifdef __has_feature
-#define FMT_HAS_FEATURE(x) __has_feature(x)
-#else
-#define FMT_HAS_FEATURE(x) 0
-#endif
-
-#if(defined(__has_include) || FMT_ICC_VERSION >= 1600 || FMT_MSC_VERSION > 1900) && !defined(__INTELLISENSE__)
-#define FMT_HAS_INCLUDE(x) __has_include(x)
-#else
-#define FMT_HAS_INCLUDE(x) 0
-#endif
-
-#ifdef __has_cpp_attribute
-#define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
-#else
-#define FMT_HAS_CPP_ATTRIBUTE(x) 0
-#endif
-
-#define FMT_HAS_CPP14_ATTRIBUTE(attribute) (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
-
-#define FMT_HAS_CPP17_ATTRIBUTE(attribute) (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
-
-// Check if relaxed C++14 constexpr is supported.
-// GCC doesn't allow throw in constexpr until version 6 (bug 67371).
-#ifndef FMT_USE_CONSTEXPR
-#if(FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VERSION >= 1912 ||                                               \
-    (FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L)) &&                                                           \
-    !FMT_ICC_VERSION && !defined(__NVCC__)
-#define FMT_USE_CONSTEXPR 1
-#else
-#define FMT_USE_CONSTEXPR 0
-#endif
-#endif
-#if FMT_USE_CONSTEXPR
-#define FMT_CONSTEXPR constexpr
-#else
-#define FMT_CONSTEXPR
-#endif
-
-#if((FMT_CPLUSPLUS >= 202002L) && (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) ||                             \
-    (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
-#define FMT_CONSTEXPR20 constexpr
-#else
-#define FMT_CONSTEXPR20
-#endif
-
-// Check if constexpr std::char_traits<>::{compare,length} are supported.
-#if defined(__GLIBCXX__)
-#if FMT_CPLUSPLUS >= 201703L && defined(_GLIBCXX_RELEASE) &&                                                           \
-    _GLIBCXX_RELEASE >= 7 // GCC 7+ libstdc++ has _GLIBCXX_RELEASE.
-#define FMT_CONSTEXPR_CHAR_TRAITS constexpr
-#endif
-#elif defined(_LIBCPP_VERSION) && FMT_CPLUSPLUS >= 201703L && _LIBCPP_VERSION >= 4000
-#define FMT_CONSTEXPR_CHAR_TRAITS constexpr
-#elif FMT_MSC_VERSION >= 1914 && FMT_CPLUSPLUS >= 201703L
-#define FMT_CONSTEXPR_CHAR_TRAITS constexpr
-#endif
-#ifndef FMT_CONSTEXPR_CHAR_TRAITS
-#define FMT_CONSTEXPR_CHAR_TRAITS
-#endif
-
-// Check if exceptions are disabled.
-#ifndef FMT_EXCEPTIONS
-#if(defined(__GNUC__) && !defined(__EXCEPTIONS)) || (FMT_MSC_VERSION && !_HAS_EXCEPTIONS)
-#define FMT_EXCEPTIONS 0
-#else
-#define FMT_EXCEPTIONS 1
-#endif
-#endif
-
-#ifndef FMT_DEPRECATED
-#if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900
-#define FMT_DEPRECATED [[deprecated]]
-#else
-#if(defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
-#define FMT_DEPRECATED __attribute__((deprecated))
-#elif FMT_MSC_VERSION
-#define FMT_DEPRECATED __declspec(deprecated)
-#else
-#define FMT_DEPRECATED /* deprecated */
-#endif
-#endif
-#endif
-
-// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code
-// warnings.
-#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VERSION && !defined(__NVCC__)
-#define FMT_NORETURN [[noreturn]]
-#else
-#define FMT_NORETURN
-#endif
-
-#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
-#define FMT_FALLTHROUGH [[fallthrough]]
-#elif defined(__clang__)
-#define FMT_FALLTHROUGH [[clang::fallthrough]]
-#elif FMT_GCC_VERSION >= 700 && (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
-#define FMT_FALLTHROUGH [[gnu::fallthrough]]
-#else
-#define FMT_FALLTHROUGH
-#endif
-
-#ifndef FMT_NODISCARD
-#if FMT_HAS_CPP17_ATTRIBUTE(nodiscard)
-#define FMT_NODISCARD [[nodiscard]]
-#else
-#define FMT_NODISCARD
-#endif
-#endif
-
-#ifndef FMT_USE_FLOAT
-#define FMT_USE_FLOAT 1
-#endif
-#ifndef FMT_USE_DOUBLE
-#define FMT_USE_DOUBLE 1
-#endif
-#ifndef FMT_USE_LONG_DOUBLE
-#define FMT_USE_LONG_DOUBLE 1
-#endif
-
-#ifndef FMT_INLINE
-#if FMT_GCC_VERSION || FMT_CLANG_VERSION
-#define FMT_INLINE inline __attribute__((always_inline))
-#else
-#define FMT_INLINE inline
-#endif
-#endif
-
-// An inline std::forward replacement.
-#define FMT_FORWARD(...) static_cast<decltype(__VA_ARGS__) &&>(__VA_ARGS__)
-
-#ifdef _MSC_VER
-#define FMT_UNCHECKED_ITERATOR(It) using _Unchecked_type = It // Mark iterator as checked.
-#else
-#define FMT_UNCHECKED_ITERATOR(It) using unchecked_type = It
-#endif
-
-#ifndef FMT_BEGIN_NAMESPACE
-#define FMT_BEGIN_NAMESPACE                                                                                            \
-    namespace fmt                                                                                                      \
-    {                                                                                                                  \
-    inline namespace v9                                                                                                \
-    {
-#define FMT_END_NAMESPACE                                                                                              \
-    }                                                                                                                  \
-    }
-#endif
-
-#ifndef FMT_MODULE_EXPORT
-#define FMT_MODULE_EXPORT
-#define FMT_MODULE_EXPORT_BEGIN
-#define FMT_MODULE_EXPORT_END
-#define FMT_BEGIN_DETAIL_NAMESPACE                                                                                     \
-    namespace detail                                                                                                   \
-    {
-#define FMT_END_DETAIL_NAMESPACE }
-#endif
-
-#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
-#define FMT_CLASS_API FMT_MSC_WARNING(suppress : 4275)
-#ifdef FMT_EXPORT
-#define FMT_API __declspec(dllexport)
-#elif defined(FMT_SHARED)
-#define FMT_API __declspec(dllimport)
-#endif
-#else
-#define FMT_CLASS_API
-#if defined(FMT_EXPORT) || defined(FMT_SHARED)
-#if defined(__GNUC__) || defined(__clang__)
-#define FMT_API __attribute__((visibility("default")))
-#endif
-#endif
-#endif
-#ifndef FMT_API
-#define FMT_API
-#endif
-
-// libc++ supports string_view in pre-c++17.
-#if FMT_HAS_INCLUDE(<string_view>) && (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION))
-#include <string_view>
-#define FMT_USE_STRING_VIEW
-#elif FMT_HAS_INCLUDE("experimental/string_view") && FMT_CPLUSPLUS >= 201402L
-#include <experimental/string_view>
-#define FMT_USE_EXPERIMENTAL_STRING_VIEW
-#endif
-
-#ifndef FMT_UNICODE
-#define FMT_UNICODE !FMT_MSC_VERSION
-#endif
-
-#ifndef FMT_CONSTEVAL
-#if((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && FMT_CPLUSPLUS >= 202002L &&                              \
-    !defined(__apple_build_version__)) ||                                                                              \
-    (defined(__cpp_consteval) && (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704))
-// consteval is broken in MSVC before VS2022 and Apple clang 13.
-#define FMT_CONSTEVAL consteval
-#define FMT_HAS_CONSTEVAL
-#else
-#define FMT_CONSTEVAL
-#endif
-#endif
-
-#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS
-#if defined(__cpp_nontype_template_args) &&                                                                            \
-    ((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || __cpp_nontype_template_args >= 201911L) &&                \
-    !defined(__NVCOMPILER)
-#define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
-#else
-#define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
-#endif
-#endif
-
-// Enable minimal optimizations for more compact code in debug mode.
-FMT_GCC_PRAGMA("GCC push_options")
-#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER)
-FMT_GCC_PRAGMA("GCC optimize(\"Og\")")
-#endif
-
-FMT_BEGIN_NAMESPACE
-FMT_MODULE_EXPORT_BEGIN
-
-// Implementations of enable_if_t and other metafunctions for older systems.
-template <bool B, typename T = void>
-using enable_if_t = typename std::enable_if<B, T>::type;
-template <bool B, typename T, typename F>
-using conditional_t = typename std::conditional<B, T, F>::type;
-template <bool B>
-using bool_constant = std::integral_constant<bool, B>;
-template <typename T>
-using remove_reference_t = typename std::remove_reference<T>::type;
-template <typename T>
-using remove_const_t = typename std::remove_const<T>::type;
-template <typename T>
-using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
-template <typename T>
-struct type_identity
-{
-    using type = T;
-};
-template <typename T>
-using type_identity_t = typename type_identity<T>::type;
-template <typename T>
-using underlying_t = typename std::underlying_type<T>::type;
-
-template <typename...>
-struct disjunction : std::false_type
-{
-};
-template <typename P>
-struct disjunction<P> : P
-{
-};
-template <typename P1, typename... Pn>
-struct disjunction<P1, Pn...> : conditional_t<bool(P1::value), P1, disjunction<Pn...>>
-{
-};
-
-template <typename...>
-struct conjunction : std::true_type
-{
-};
-template <typename P>
-struct conjunction<P> : P
-{
-};
-template <typename P1, typename... Pn>
-struct conjunction<P1, Pn...> : conditional_t<bool(P1::value), conjunction<Pn...>, P1>
-{
-};
-
-struct monostate
-{
-    constexpr monostate() {}
-};
-
-// An enable_if helper to be used in template parameters which results in much
-// shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed
-// to workaround a bug in MSVC 2019 (see #1140 and #1186).
-#ifdef FMT_DOC
-#define FMT_ENABLE_IF(...)
-#else
-#define FMT_ENABLE_IF(...) enable_if_t<(__VA_ARGS__), int> = 0
-#endif
-
-FMT_BEGIN_DETAIL_NAMESPACE
-
-// Suppresses "unused variable" warnings with the method described in
-// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
-// (void)var does not work on many Intel compilers.
-template <typename... T>
-FMT_CONSTEXPR void ignore_unused(const T &...)
-{
-}
-
-constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false) noexcept -> bool
-{
-#ifdef __cpp_lib_is_constant_evaluated
-    ignore_unused(default_value);
-    return std::is_constant_evaluated();
-#else
-    return default_value;
-#endif
-}
-
-// Suppresses "conditional expression is constant" warnings.
-template <typename T>
-constexpr FMT_INLINE auto const_check(T value) -> T
-{
-    return value;
-}
-
-FMT_NORETURN FMT_API void assert_fail(const char *file, int line, const char *message);
-
-#ifndef FMT_ASSERT
-#ifdef NDEBUG
-// FMT_ASSERT is not empty to avoid -Wempty-body.
-#define FMT_ASSERT(condition, message) ::fmt::detail::ignore_unused((condition), (message))
-#else
-#define FMT_ASSERT(condition, message)                                                                                 \
-    ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */                                            \
-         ?                                                                                                             \
-         (void) 0 :                                                                                                    \
-         ::fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
-#endif
-#endif
-
-#if defined(FMT_USE_STRING_VIEW)
-template <typename Char>
-using std_string_view = std::basic_string_view<Char>;
-#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
-template <typename Char>
-using std_string_view = std::experimental::basic_string_view<Char>;
-#else
-template <typename T>
-struct std_string_view
-{
-};
-#endif
-
-#ifdef FMT_USE_INT128
-// Do nothing.
-#elif defined(__SIZEOF_INT128__) && !defined(__NVCC__) && !(FMT_CLANG_VERSION && FMT_MSC_VERSION)
-#define FMT_USE_INT128 1
-using int128_opt = __int128_t; // An optional native 128-bit integer.
-using uint128_opt = __uint128_t;
-template <typename T>
-inline auto convert_for_visit(T value) -> T
-{
-    return value;
-}
-#else
-#define FMT_USE_INT128 0
-#endif
-#if !FMT_USE_INT128
-enum class int128_opt
-{
-};
-enum class uint128_opt
-{
-};
-// Reduce template instantiations.
-template <typename T>
-auto convert_for_visit(T) -> monostate
-{
-    return {};
-}
-#endif
-
-// Casts a nonnegative integer to unsigned.
-template <typename Int>
-FMT_CONSTEXPR auto to_unsigned(Int value) -> typename std::make_unsigned<Int>::type
-{
-    FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value");
-    return static_cast<typename std::make_unsigned<Int>::type>(value);
-}
-
-FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5";
-
-constexpr auto is_utf8() -> bool
-{
-    // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297).
-    using uchar = unsigned char;
-    return FMT_UNICODE || (sizeof(micro) == 3 && uchar(micro[0]) == 0xC2 && uchar(micro[1]) == 0xB5);
-}
-FMT_END_DETAIL_NAMESPACE
-
-/**
-  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
-  if ``std::string_view`` is available to prevent issues when a library is
-  compiled with a different ``-std`` option than the client code (which is not
-  recommended).
- */
-template <typename Char>
-class basic_string_view
-{
-private:
-    const Char *data_;
-    size_t      size_;
-
-public:
-    using value_type = Char;
-    using iterator   = const Char *;
-
-    constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
-
-    /** Constructs a string reference object from a C string and a size. */
-    constexpr basic_string_view(const Char *s, size_t count) noexcept : data_(s), size_(count) {}
-
-    /**
-      \rst
-      Constructs a string reference object from a C string computing
-      the size with ``std::char_traits<Char>::length``.
-      \endrst
-     */
-    FMT_CONSTEXPR_CHAR_TRAITS
-    FMT_INLINE
-    basic_string_view(const Char *s) :
-        data_(s),
-        size_(
-            detail::const_check(std::is_same<Char, char>::value && !detail::is_constant_evaluated(true)) ?
-                std::strlen(reinterpret_cast<const char *>(s)) :
-                std::char_traits<Char>::length(s))
-    {
-    }
-
-    /** Constructs a string reference from a ``std::basic_string`` object. */
-    template <typename Traits, typename Alloc>
-    FMT_CONSTEXPR basic_string_view(const std::basic_string<Char, Traits, Alloc> &s) noexcept :
-        data_(s.data()), size_(s.size())
-    {
-    }
-
-    template <typename S, FMT_ENABLE_IF(std::is_same<S, detail::std_string_view<Char>>::value)>
-    FMT_CONSTEXPR basic_string_view(S s) noexcept : data_(s.data()), size_(s.size())
-    {
-    }
-
-    /** Returns a pointer to the string data. */
-    constexpr auto data() const noexcept -> const Char * { return data_; }
-
-    /** Returns the string size. */
-    constexpr auto size() const noexcept -> size_t { return size_; }
-
-    constexpr auto begin() const noexcept -> iterator { return data_; }
-    constexpr auto end() const noexcept -> iterator { return data_ + size_; }
-
-    constexpr auto operator[](size_t pos) const noexcept -> const Char & { return data_[pos]; }
-
-    FMT_CONSTEXPR void remove_prefix(size_t n) noexcept
-    {
-        data_ += n;
-        size_ -= n;
-    }
-
-    // Lexicographically compare this string reference to other.
-    FMT_CONSTEXPR_CHAR_TRAITS auto compare(basic_string_view other) const -> int
-    {
-        size_t str_size = size_ < other.size_ ? size_ : other.size_;
-        int    result   = std::char_traits<Char>::compare(data_, other.data_, str_size);
-        if(result == 0)
-            result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
-        return result;
-    }
-
-    FMT_CONSTEXPR_CHAR_TRAITS friend auto operator==(basic_string_view lhs, basic_string_view rhs) -> bool
-    {
-        return lhs.compare(rhs) == 0;
-    }
-    friend auto operator!=(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) != 0; }
-    friend auto operator<(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) < 0; }
-    friend auto operator<=(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) <= 0; }
-    friend auto operator>(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) > 0; }
-    friend auto operator>=(basic_string_view lhs, basic_string_view rhs) -> bool { return lhs.compare(rhs) >= 0; }
-};
-
-using string_view = basic_string_view<char>;
-
-/** Specifies if ``T`` is a character type. Can be specialized by users. */
-template <typename T>
-struct is_char : std::false_type
-{
-};
-template <>
-struct is_char<char> : std::true_type
-{
-};
-
-FMT_BEGIN_DETAIL_NAMESPACE
-
-// A base class for compile-time strings.
-struct compile_string
-{
-};
-
-template <typename S>
-struct is_compile_string : std::is_base_of<compile_string, S>
-{
-};
-
-// Returns a string view of `s`.
-template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
-FMT_INLINE auto to_string_view(const Char *s) -> basic_string_view<Char>
-{
-    return s;
-}
-template <typename Char, typename Traits, typename Alloc>
-inline auto to_string_view(const std::basic_string<Char, Traits, Alloc> &s) -> basic_string_view<Char>
-{
-    return s;
-}
-template <typename Char>
-constexpr auto to_string_view(basic_string_view<Char> s) -> basic_string_view<Char>
-{
-    return s;
-}
-template <typename Char, FMT_ENABLE_IF(!std::is_empty<std_string_view<Char>>::value)>
-inline auto to_string_view(std_string_view<Char> s) -> basic_string_view<Char>
-{
-    return s;
-}
-template <typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
-constexpr auto to_string_view(const S &s) -> basic_string_view<typename S::char_type>
-{
-    return basic_string_view<typename S::char_type>(s);
-}
-void to_string_view(...);
-
-// Specifies whether S is a string type convertible to fmt::basic_string_view.
-// It should be a constexpr function but MSVC 2017 fails to compile it in
-// enable_if and MSVC 2015 fails to compile it as an alias template.
-// ADL invocation of to_string_view is DEPRECATED!
-template <typename S>
-struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))>
-{
-};
-
-template <typename S, typename = void>
-struct char_t_impl
-{
-};
-template <typename S>
-struct char_t_impl<S, enable_if_t<is_string<S>::value>>
-{
-    using result = decltype(to_string_view(std::declval<S>()));
-    using type   = typename result::value_type;
-};
-
-enum class type
-{
-    none_type,
-    // Integer types should go first,
-    int_type,
-    uint_type,
-    long_long_type,
-    ulong_long_type,
-    int128_type,
-    uint128_type,
-    bool_type,
-    char_type,
-    last_integer_type = char_type,
-    // followed by floating-point types.
-    float_type,
-    double_type,
-    long_double_type,
-    last_numeric_type = long_double_type,
-    cstring_type,
-    string_type,
-    pointer_type,
-    custom_type
-};
-
-// Maps core type T to the corresponding type enum constant.
-template <typename T, typename Char>
-struct type_constant : std::integral_constant<type, type::custom_type>
-{
-};
-
-#define FMT_TYPE_CONSTANT(Type, constant)                                                                              \
-    template <typename Char>                                                                                           \
-    struct type_constant<Type, Char> : std::integral_constant<type, type::constant>                                    \
-    {                                                                                                                  \
-    }
-
-FMT_TYPE_CONSTANT(int, int_type);
-FMT_TYPE_CONSTANT(unsigned, uint_type);
-FMT_TYPE_CONSTANT(long long, long_long_type);
-FMT_TYPE_CONSTANT(unsigned long long, ulong_long_type);
-FMT_TYPE_CONSTANT(int128_opt, int128_type);
-FMT_TYPE_CONSTANT(uint128_opt, uint128_type);
-FMT_TYPE_CONSTANT(bool, bool_type);
-FMT_TYPE_CONSTANT(Char, char_type);
-FMT_TYPE_CONSTANT(float, float_type);
-FMT_TYPE_CONSTANT(double, double_type);
-FMT_TYPE_CONSTANT(long double, long_double_type);
-FMT_TYPE_CONSTANT(const Char *, cstring_type);
-FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
-FMT_TYPE_CONSTANT(const void *, pointer_type);
-
-constexpr bool is_integral_type(type t)
-{
-    return t > type::none_type && t <= type::last_integer_type;
-}
-
-constexpr bool is_arithmetic_type(type t)
-{
-    return t > type::none_type && t <= type::last_numeric_type;
-}
-
-FMT_NORETURN FMT_API void throw_format_error(const char *message);
-
-struct error_handler
-{
-    constexpr error_handler()                      = default;
-    constexpr error_handler(const error_handler &) = default;
-
-    // This function is intentionally not constexpr to give a compile-time error.
-    FMT_NORETURN void on_error(const char *message) { throw_format_error(message); }
-};
-FMT_END_DETAIL_NAMESPACE
-
-/** String's character type. */
-template <typename S>
-using char_t = typename detail::char_t_impl<S>::type;
-
-/**
-  \rst
-  Parsing context consisting of a format string range being parsed and an
-  argument counter for automatic indexing.
-  You can use the ``format_parse_context`` type alias for ``char`` instead.
-  \endrst
- */
-template <typename Char, typename ErrorHandler = detail::error_handler>
-class basic_format_parse_context : private ErrorHandler
-{
-private:
-    basic_string_view<Char> format_str_;
-    int                     next_arg_id_;
-
-    FMT_CONSTEXPR void do_check_arg_id(int id);
-
-public:
-    using char_type = Char;
-    using iterator  = typename basic_string_view<Char>::iterator;
-
-    explicit constexpr basic_format_parse_context(
-        basic_string_view<Char> format_str,
-        ErrorHandler            eh          = {},
-        int                     next_arg_id = 0) :
-        ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id)
-    {
-    }
-
-    /**
-      Returns an iterator to the beginning of the format string range being
-      parsed.
-     */
-    constexpr auto begin() const noexcept -> iterator { return format_str_.begin(); }
-
-    /**
-      Returns an iterator past the end of the format string range being parsed.
-     */
-    constexpr auto end() const noexcept -> iterator { return format_str_.end(); }
-
-    /** Advances the begin iterator to ``it``. */
-    FMT_CONSTEXPR void advance_to(iterator it) { format_str_.remove_prefix(detail::to_unsigned(it - begin())); }
-
-    /**
-      Reports an error if using the manual argument indexing; otherwise returns
-      the next argument index and switches to the automatic indexing.
-     */
-    FMT_CONSTEXPR auto next_arg_id() -> int
-    {
-        if(next_arg_id_ < 0)
-        {
-            on_error("cannot switch from manual to automatic argument indexing");
-            return 0;
-        }
-        int id = next_arg_id_++;
-        do_check_arg_id(id);
-        return id;
-    }
-
-    /**
-      Reports an error if using the automatic argument indexing; otherwise
-      switches to the manual indexing.
-     */
-    FMT_CONSTEXPR void check_arg_id(int id)
-    {
-        if(next_arg_id_ > 0)
-        {
-            on_error("cannot switch from automatic to manual argument indexing");
-            return;
-        }
-        next_arg_id_ = -1;
-        do_check_arg_id(id);
-    }
-    FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
-    FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
-
-    FMT_CONSTEXPR void on_error(const char *message) { ErrorHandler::on_error(message); }
-
-    constexpr auto error_handler() const -> ErrorHandler { return *this; }
-};
-
-using format_parse_context = basic_format_parse_context<char>;
-
-FMT_BEGIN_DETAIL_NAMESPACE
-// A parse context with extra data used only in compile-time checks.
-template <typename Char, typename ErrorHandler = detail::error_handler>
-class compile_parse_context : public basic_format_parse_context<Char, ErrorHandler>
-{
-private:
-    int         num_args_;
-    const type *types_;
-    using base = basic_format_parse_context<Char, ErrorHandler>;
-
-public:
-    explicit FMT_CONSTEXPR compile_parse_context(
-        basic_string_view<Char> format_str,
-        int                     num_args,
-        const type             *types,
-        ErrorHandler            eh          = {},
-        int                     next_arg_id = 0) :
-        base(format_str, eh, next_arg_id), num_args_(num_args), types_(types)
-    {
-    }
-
-    constexpr auto num_args() const -> int { return num_args_; }
-    constexpr auto arg_type(int id) const -> type { return types_[id]; }
-
-    FMT_CONSTEXPR auto next_arg_id() -> int
-    {
-        int id = base::next_arg_id();
-        if(id >= num_args_)
-            this->on_error("argument not found");
-        return id;
-    }
-
-    FMT_CONSTEXPR void check_arg_id(int id)
-    {
-        base::check_arg_id(id);
-        if(id >= num_args_)
-            this->on_error("argument not found");
-    }
-    using base::check_arg_id;
-
-    FMT_CONSTEXPR void check_dynamic_spec(int arg_id)
-    {
-        if(arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id]))
-            this->on_error("width/precision is not integer");
-    }
-};
-FMT_END_DETAIL_NAMESPACE
-
-template <typename Char, typename ErrorHandler>
-FMT_CONSTEXPR void basic_format_parse_context<Char, ErrorHandler>::do_check_arg_id(int id)
-{
-    // Argument id is only checked at compile-time during parsing because
-    // formatting has its own validation.
-    if(detail::is_constant_evaluated() && FMT_GCC_VERSION >= 1200)
-    {
-        using context = detail::compile_parse_context<Char, ErrorHandler>;
-        if(id >= static_cast<context *>(this)->num_args())
-            on_error("argument not found");
-    }
-}
-
-template <typename Char, typename ErrorHandler>
-FMT_CONSTEXPR void basic_format_parse_context<Char, ErrorHandler>::check_dynamic_spec(int arg_id)
-{
-    if(detail::is_constant_evaluated())
-    {
-        using context = detail::compile_parse_context<Char, ErrorHandler>;
-        static_cast<context *>(this)->check_dynamic_spec(arg_id);
-    }
-}
-
-template <typename Context>
-class basic_format_arg;
-template <typename Context>
-class basic_format_args;
-template <typename Context>
-class dynamic_format_arg_store;
-
-// A formatter for objects of type T.
-template <typename T, typename Char = char, typename Enable = void>
-struct formatter
-{
-    // A deleted default constructor indicates a disabled formatter.
-    formatter() = delete;
-};
-
-// Specifies if T has an enabled formatter specialization. A type can be
-// formattable even if it doesn't have a formatter e.g. via a conversion.
-template <typename T, typename Context>
-using has_formatter = std::is_constructible<typename Context::template formatter_type<T>>;
-
-// Checks whether T is a container with contiguous storage.
-template <typename T>
-struct is_contiguous : std::false_type
-{
-};
-template <typename Char>
-struct is_contiguous<std::basic_string<Char>> : std::true_type
-{
-};
-
-class appender;
-
-FMT_BEGIN_DETAIL_NAMESPACE
-
-template <typename Context, typename T>
-constexpr auto has_const_formatter_impl(T *)
-    -> decltype(typename Context::template formatter_type<T>().format(std::declval<const T &>(), std::declval<Context &>()), true)
-{
-    return true;
-}
-template <typename Context>
-constexpr auto has_const_formatter_impl(...) -> bool
-{
-    return false;
-}
-template <typename T, typename Context>
-constexpr auto has_const_formatter() -> bool
-{
-    return has_const_formatter_impl<Context>(static_cast<T *>(nullptr));
-}
-
-// Extracts a reference to the container from back_insert_iterator.
-template <typename Container>
-inline auto get_container(std::back_insert_iterator<Container> it) -> Container &
-{
-    using base = std::back_insert_iterator<Container>;
-    struct accessor : base
-    {
-        accessor(base b) : base(b) {}
-        using base::container;
-    };
-    return *accessor(it).container;
-}
-
-template <typename Char, typename InputIt, typename OutputIt>
-FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) -> OutputIt
-{
-    while(begin != end)
-        *out++ = static_cast<Char>(*begin++);
-    return out;
-}
-
-template <
-    typename Char,
-    typename T,
-    typename U,
-    FMT_ENABLE_IF(std::is_same<remove_const_t<T>, U>::value &&is_char<U>::value)>
-FMT_CONSTEXPR auto copy_str(T *begin, T *end, U *out) -> U *
-{
-    if(is_constant_evaluated())
-        return copy_str<Char, T *, U *>(begin, end, out);
-    auto size = to_unsigned(end - begin);
-    memcpy(out, begin, size * sizeof(U));
-    return out + size;
-}
-
-/**
-  \rst
-  A contiguous memory buffer with an optional growing ability. It is an internal
-  class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`.
-  \endrst
- */
-template <typename T>
-class buffer
-{
-private:
-    T     *ptr_;
-    size_t size_;
-    size_t capacity_;
-
-protected:
-    // Don't initialize ptr_ since it is not accessed to save a few cycles.
-    FMT_MSC_WARNING(suppress : 26495)
-    buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {}
-
-    FMT_CONSTEXPR20 buffer(T *p = nullptr, size_t sz = 0, size_t cap = 0) noexcept : ptr_(p), size_(sz), capacity_(cap)
-    {
-    }
-
-    FMT_CONSTEXPR20 ~buffer() = default;
-    buffer(buffer &&)         = default;
-
-    /** Sets the buffer data and capacity. */
-    FMT_CONSTEXPR void set(T *buf_data, size_t buf_capacity) noexcept
-    {
-        ptr_      = buf_data;
-        capacity_ = buf_capacity;
-    }
-
-    /** Increases the buffer capacity to hold at least *capacity* elements. */
-    virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0;
-
-public:
-    using value_type      = T;
-    using const_reference = const T &;
-
-    buffer(const buffer &)         = delete;
-    void operator=(const buffer &) = delete;
-
-    auto begin() noexcept -> T * { return ptr_; }
-    auto end() noexcept -> T * { return ptr_ + size_; }
-
-    auto begin() const noexcept -> const T * { return ptr_; }
-    auto end() const noexcept -> const T * { return ptr_ + size_; }
-
-    /** Returns the size of this buffer. */
-    constexpr auto size() const noexcept -> size_t { return size_; }
-
-    /** Returns the capacity of this buffer. */
-    constexpr auto capacity() const noexcept -> size_t { return capacity_; }
-
-    /** Returns a pointer to the buffer data. */
-    FMT_CONSTEXPR auto data() noexcept -> T * { return ptr_; }
-
-    /** Returns a pointer to the buffer data. */
-    FMT_CONSTEXPR auto data() const noexcept -> const T * { return ptr_; }
-
-    /** Clears this buffer. */
-    void clear() { size_ = 0; }
-
-    // Tries resizing the buffer to contain *count* elements. If T is a POD type
-    // the new elements may not be initialized.
-    FMT_CONSTEXPR20 void try_resize(size_t count)
-    {
-        try_reserve(count);
-        size_ = count <= capacity_ ? count : capacity_;
-    }
-
-    // Tries increasing the buffer capacity to *new_capacity*. It can increase the
-    // capacity by a smaller amount than requested but guarantees there is space
-    // for at least one additional element either by increasing the capacity or by
-    // flushing the buffer if it is full.
-    FMT_CONSTEXPR20 void try_reserve(size_t new_capacity)
-    {
-        if(new_capacity > capacity_)
-            grow(new_capacity);
-    }
-
-    FMT_CONSTEXPR20 void push_back(const T &value)
-    {
-        try_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);
-
-    template <typename Idx>
-    FMT_CONSTEXPR auto operator[](Idx index) -> T &
-    {
-        return ptr_[index];
-    }
-    template <typename Idx>
-    FMT_CONSTEXPR auto operator[](Idx index) const -> const T &
-    {
-        return ptr_[index];
-    }
-};
-
-struct buffer_traits
-{
-    explicit buffer_traits(size_t) {}
-    auto count() const -> size_t { return 0; }
-    auto limit(size_t size) -> size_t { return size; }
-};
-
-class fixed_buffer_traits
-{
-private:
-    size_t count_ = 0;
-    size_t limit_;
-
-public:
-    explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
-    auto count() const -> size_t { return count_; }
-    auto limit(size_t size) -> size_t
-    {
-        size_t n = limit_ > count_ ? limit_ - count_ : 0;
-        count_ += size;
-        return size < n ? size : n;
-    }
-};
-
-// A buffer that writes to an output iterator when flushed.
-template <typename OutputIt, typename T, typename Traits = buffer_traits>
-class iterator_buffer final : public Traits, public buffer<T>
-{
-private:
-    OutputIt out_;
-    enum
-    {
-        buffer_size = 256
-    };
-    T data_[buffer_size];
-
-protected:
-    FMT_CONSTEXPR20 void grow(size_t) override
-    {
-        if(this->size() == buffer_size)
-            flush();
-    }
-
-    void flush()
-    {
-        auto size = this->size();
-        this->clear();
-        out_ = copy_str<T>(data_, data_ + this->limit(size), out_);
-    }
-
-public:
-    explicit iterator_buffer(OutputIt out, size_t n = buffer_size) :
-        Traits(n), buffer<T>(data_, 0, buffer_size), out_(out)
-    {
-    }
-    iterator_buffer(iterator_buffer &&other) : Traits(other), buffer<T>(data_, 0, buffer_size), out_(other.out_) {}
-    ~iterator_buffer() { flush(); }
-
-    auto out() -> OutputIt
-    {
-        flush();
-        return out_;
-    }
-    auto count() const -> size_t { return Traits::count() + this->size(); }
-};
-
-template <typename T>
-class iterator_buffer<T *, T, fixed_buffer_traits> final : public fixed_buffer_traits, public buffer<T>
-{
-private:
-    T *out_;
-    enum
-    {
-        buffer_size = 256
-    };
-    T data_[buffer_size];
-
-protected:
-    FMT_CONSTEXPR20 void grow(size_t) override
-    {
-        if(this->size() == this->capacity())
-            flush();
-    }
-
-    void flush()
-    {
-        size_t n = this->limit(this->size());
-        if(this->data() == out_)
-        {
-            out_ += n;
-            this->set(data_, buffer_size);
-        }
-        this->clear();
-    }
-
-public:
-    explicit iterator_buffer(T *out, size_t n = buffer_size) : fixed_buffer_traits(n), buffer<T>(out, 0, n), out_(out)
-    {
-    }
-    iterator_buffer(iterator_buffer &&other) : fixed_buffer_traits(other), buffer<T>(std::move(other)), out_(other.out_)
-    {
-        if(this->data() != out_)
-        {
-            this->set(data_, buffer_size);
-            this->clear();
-        }
-    }
-    ~iterator_buffer() { flush(); }
-
-    auto out() -> T *
-    {
-        flush();
-        return out_;
-    }
-    auto count() const -> size_t { return fixed_buffer_traits::count() + this->size(); }
-};
-
-template <typename T>
-class iterator_buffer<T *, T> final : public buffer<T>
-{
-protected:
-    FMT_CONSTEXPR20 void grow(size_t) override {}
-
-public:
-    explicit iterator_buffer(T *out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
-
-    auto out() -> T * { return &*this->end(); }
-};
-
-// A buffer that writes to a container with the contiguous storage.
-template <typename Container>
-class iterator_buffer<
-    std::back_insert_iterator<Container>,
-    enable_if_t<is_contiguous<Container>::value, typename Container::value_type>>
-    final : public buffer<typename Container::value_type>
-{
-private:
-    Container &container_;
-
-protected:
-    FMT_CONSTEXPR20 void grow(size_t capacity) override
-    {
-        container_.resize(capacity);
-        this->set(&container_[0], capacity);
-    }
-
-public:
-    explicit iterator_buffer(Container &c) : buffer<typename Container::value_type>(c.size()), container_(c) {}
-    explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0) : iterator_buffer(get_container(out))
-    {
-    }
-
-    auto out() -> std::back_insert_iterator<Container> { return std::back_inserter(container_); }
-};
-
-// A buffer that counts the number of code units written discarding the output.
-template <typename T = char>
-class counting_buffer final : public buffer<T>
-{
-private:
-    enum
-    {
-        buffer_size = 256
-    };
-    T      data_[buffer_size];
-    size_t count_ = 0;
-
-protected:
-    FMT_CONSTEXPR20 void grow(size_t) override
-    {
-        if(this->size() != buffer_size)
-            return;
-        count_ += this->size();
-        this->clear();
-    }
-
-public:
-    counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
-
-    auto count() -> size_t { return count_ + this->size(); }
-};
-
-template <typename T>
-using buffer_appender = conditional_t<std::is_same<T, char>::value, appender, std::back_insert_iterator<buffer<T>>>;
-
-// Maps an output iterator to a buffer.
-template <typename T, typename OutputIt>
-auto get_buffer(OutputIt out) -> iterator_buffer<OutputIt, T>
-{
-    return iterator_buffer<OutputIt, T>(out);
-}
-
-template <typename Buffer>
-auto get_iterator(Buffer &buf) -> decltype(buf.out())
-{
-    return buf.out();
-}
-template <typename T>
-auto get_iterator(buffer<T> &buf) -> buffer_appender<T>
-{
-    return buffer_appender<T>(buf);
-}
-
-template <typename T, typename Char = char, typename Enable = void>
-struct fallback_formatter
-{
-    fallback_formatter() = delete;
-};
-
-// Specifies if T has an enabled fallback_formatter specialization.
-template <typename T, typename Char>
-using has_fallback_formatter =
-#ifdef FMT_DEPRECATED_OSTREAM
-    std::is_constructible<fallback_formatter<T, Char>>;
-#else
-    std::false_type;
-#endif
-
-struct view
-{
-};
-
-template <typename Char, typename T>
-struct named_arg : view
-{
-    const Char *name;
-    const T    &value;
-    named_arg(const Char *n, const T &v) : name(n), value(v) {}
-};
-
-template <typename Char>
-struct named_arg_info
-{
-    const Char *name;
-    int         id;
-};
-
-template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
-struct arg_data
-{
-    // args_[0].named_args points to named_args_ to avoid bloating format_args.
-    // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
-    T                    args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
-    named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
-
-    template <typename... U>
-    arg_data(const U &...init) : args_{T(named_args_, NUM_NAMED_ARGS), init...}
-    {
-    }
-    arg_data(const arg_data &other) = delete;
-    auto args() const -> const T * { return args_ + 1; }
-    auto named_args() -> named_arg_info<Char> * { return named_args_; }
-};
-
-template <typename T, typename Char, size_t NUM_ARGS>
-struct arg_data<T, Char, NUM_ARGS, 0>
-{
-    // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
-    T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
-
-    template <typename... U>
-    FMT_CONSTEXPR FMT_INLINE arg_data(const U &...init) : args_{init...}
-    {
-    }
-    FMT_CONSTEXPR FMT_INLINE auto args() const -> const T * { return args_; }
-    FMT_CONSTEXPR FMT_INLINE auto named_args() -> std::nullptr_t { return nullptr; }
-};
-
-template <typename Char>
-inline void init_named_args(named_arg_info<Char> *, int, int)
-{
-}
-
-template <typename T>
-struct is_named_arg : std::false_type
-{
-};
-template <typename T>
-struct is_statically_named_arg : std::false_type
-{
-};
-
-template <typename T, typename Char>
-struct is_named_arg<named_arg<Char, T>> : std::true_type
-{
-};
-
-template <typename Char, typename T, typename... Tail, FMT_ENABLE_IF(!is_named_arg<T>::value)>
-void init_named_args(
-    named_arg_info<Char> *named_args,
-    int                   arg_count,
-    int                   named_arg_count,
-    const T &,
-    const Tail &...args)
-{
-    init_named_args(named_args, arg_count + 1, named_arg_count, args...);
-}
-
-template <typename Char, typename T, typename... Tail, FMT_ENABLE_IF(is_named_arg<T>::value)>
-void init_named_args(
-    named_arg_info<Char> *named_args,
-    int                   arg_count,
-    int                   named_arg_count,
-    const T              &arg,
-    const Tail &...args)
-{
-    named_args[named_arg_count++] = {arg.name, arg_count};
-    init_named_args(named_args, arg_count + 1, named_arg_count, args...);
-}
-
-template <typename... Args>
-FMT_CONSTEXPR FMT_INLINE void init_named_args(std::nullptr_t, int, int, const Args &...)
-{
-}
-
-template <bool B = false>
-constexpr auto count() -> size_t
-{
-    return B ? 1 : 0;
-}
-template <bool B1, bool B2, bool... Tail>
-constexpr auto count() -> size_t
-{
-    return (B1 ? 1 : 0) + count<B2, Tail...>();
-}
-
-template <typename... Args>
-constexpr auto count_named_args() -> size_t
-{
-    return count<is_named_arg<Args>::value...>();
-}
-
-template <typename... Args>
-constexpr auto count_statically_named_args() -> size_t
-{
-    return count<is_statically_named_arg<Args>::value...>();
-}
-
-struct unformattable
-{
-};
-struct unformattable_char : unformattable
-{
-};
-struct unformattable_const : unformattable
-{
-};
-struct unformattable_pointer : unformattable
-{
-};
-
-template <typename Char>
-struct string_value
-{
-    const Char *data;
-    size_t      size;
-};
-
-template <typename Char>
-struct named_arg_value
-{
-    const named_arg_info<Char> *data;
-    size_t                      size;
-};
-
-template <typename Context>
-struct custom_value
-{
-    using parse_context = typename Context::parse_context_type;
-    void *value;
-    void (*format)(void *arg, parse_context &parse_ctx, Context &ctx);
-};
-
-// A formatting argument value.
-template <typename Context>
-class value
-{
-public:
-    using char_type = typename Context::char_type;
-
-    union
-    {
-        monostate                  no_value;
-        int                        int_value;
-        unsigned                   uint_value;
-        long long                  long_long_value;
-        unsigned long long         ulong_long_value;
-        int128_opt                 int128_value;
-        uint128_opt                uint128_value;
-        bool                       bool_value;
-        char_type                  char_value;
-        float                      float_value;
-        double                     double_value;
-        long double                long_double_value;
-        const void                *pointer;
-        string_value<char_type>    string;
-        custom_value<Context>      custom;
-        named_arg_value<char_type> named_args;
-    };
-
-    constexpr FMT_INLINE     value() : no_value() {}
-    constexpr FMT_INLINE     value(int val) : int_value(val) {}
-    constexpr FMT_INLINE     value(unsigned val) : uint_value(val) {}
-    constexpr FMT_INLINE     value(long long val) : long_long_value(val) {}
-    constexpr FMT_INLINE     value(unsigned long long val) : ulong_long_value(val) {}
-    FMT_INLINE               value(int128_opt val) : int128_value(val) {}
-    FMT_INLINE               value(uint128_opt val) : uint128_value(val) {}
-    constexpr FMT_INLINE     value(float val) : float_value(val) {}
-    constexpr FMT_INLINE     value(double val) : double_value(val) {}
-    FMT_INLINE               value(long double val) : long_double_value(val) {}
-    constexpr FMT_INLINE     value(bool val) : bool_value(val) {}
-    constexpr FMT_INLINE     value(char_type val) : char_value(val) {}
-    FMT_CONSTEXPR FMT_INLINE value(const char_type *val)
-    {
-        string.data = val;
-        if(is_constant_evaluated())
-            string.size = {};
-    }
-    FMT_CONSTEXPR FMT_INLINE value(basic_string_view<char_type> val)
-    {
-        string.data = val.data();
-        string.size = val.size();
-    }
-    FMT_INLINE value(const void *val) : pointer(val) {}
-    FMT_INLINE value(const named_arg_info<char_type> *args, size_t size) : named_args{args, size} {}
-
-    template <typename T>
-    FMT_CONSTEXPR FMT_INLINE value(T &val)
-    {
-        using value_type = remove_cvref_t<T>;
-        custom.value     = const_cast<value_type *>(&val);
-        // Get the formatter type through the context to allow different contexts
-        // have different extension points, e.g. `formatter<T>` for `format` and
-        // `printf_formatter<T>` for `printf`.
-        custom.format = format_custom_arg<
-            value_type,
-            conditional_t<
-                has_formatter<value_type, Context>::value,
-                typename Context::template formatter_type<value_type>,
-                fallback_formatter<value_type, char_type>>>;
-    }
-    value(unformattable);
-    value(unformattable_char);
-    value(unformattable_const);
-    value(unformattable_pointer);
-
-private:
-    // Formats an argument of a custom type, such as a user-defined class.
-    template <typename T, typename Formatter>
-    static void format_custom_arg(void *arg, typename Context::parse_context_type &parse_ctx, Context &ctx)
-    {
-        auto f = Formatter();
-        parse_ctx.advance_to(f.parse(parse_ctx));
-        using qualified_type = conditional_t<has_const_formatter<T, Context>(), const T, T>;
-        ctx.advance_to(f.format(*static_cast<qualified_type *>(arg), ctx));
-    }
-};
-
-template <typename Context, typename T>
-FMT_CONSTEXPR auto make_arg(T &&value) -> basic_format_arg<Context>;
-
-// To minimize the number of types we need to deal with, long is translated
-// either to int or to long long depending on its size.
-enum
-{
-    long_short = sizeof(long) == sizeof(int)
-};
-using long_type  = conditional_t<long_short, int, long long>;
-using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
-
-#ifdef __cpp_lib_byte
-inline auto format_as(std::byte b) -> unsigned char
-{
-    return static_cast<unsigned char>(b);
-}
-#endif
-
-template <typename T>
-struct has_format_as
-{
-    template <
-        typename U,
-        typename V = decltype(format_as(U())),
-        FMT_ENABLE_IF(std::is_enum<U>::value &&std::is_integral<V>::value)>
-    static auto check(U *) -> std::true_type;
-    static auto check(...) -> std::false_type;
-
-    enum
-    {
-        value = decltype(check(static_cast<T *>(nullptr)))::value
-    };
-};
-
-// Maps formatting arguments to core types.
-// arg_mapper reports errors by returning unformattable instead of using
-// static_assert because it's used in the is_formattable trait.
-template <typename Context>
-struct arg_mapper
-{
-    using char_type = typename Context::char_type;
-
-    FMT_CONSTEXPR FMT_INLINE auto map(signed char val) -> int { return val; }
-    FMT_CONSTEXPR FMT_INLINE auto map(unsigned char val) -> unsigned { return val; }
-    FMT_CONSTEXPR FMT_INLINE auto map(short val) -> int { return val; }
-    FMT_CONSTEXPR FMT_INLINE auto map(unsigned short val) -> unsigned { return val; }
-    FMT_CONSTEXPR FMT_INLINE auto map(int val) -> int { return val; }
-    FMT_CONSTEXPR FMT_INLINE auto map(unsigned val) -> unsigned { return val; }
-    FMT_CONSTEXPR FMT_INLINE auto map(long val) -> long_type { return val; }
-    FMT_CONSTEXPR FMT_INLINE auto map(unsigned long val) -> ulong_type { return val; }
-    FMT_CONSTEXPR FMT_INLINE auto map(long long val) -> long long { return val; }
-    FMT_CONSTEXPR FMT_INLINE auto map(unsigned long long val) -> unsigned long long { return val; }
-    FMT_CONSTEXPR FMT_INLINE auto map(int128_opt val) -> int128_opt { return val; }
-    FMT_CONSTEXPR FMT_INLINE auto map(uint128_opt val) -> uint128_opt { return val; }
-    FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; }
-
-    template <typename T, FMT_ENABLE_IF(std::is_same<T, char>::value || std::is_same<T, char_type>::value)>
-    FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type
-    {
-        return val;
-    }
-    template <
-        typename T,
-        enable_if_t<
-            (std::is_same<T, wchar_t>::value ||
-#ifdef __cpp_char8_t
-             std::is_same<T, char8_t>::value ||
-#endif
-             std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value) &&
-                !std::is_same<T, char_type>::value,
-            int> = 0>
-    FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char
-    {
-        return {};
-    }
-
-    FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float
-    {
-        return val;
-    }
-    FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double
-    {
-        return val;
-    }
-    FMT_CONSTEXPR FMT_INLINE auto map(long double val) -> long double
-    {
-        return val;
-    }
-
-    FMT_CONSTEXPR FMT_INLINE auto map(char_type *val) -> const char_type *
-    {
-        return val;
-    }
-    FMT_CONSTEXPR FMT_INLINE auto map(const char_type *val) -> const char_type *
-    {
-        return val;
-    }
-    template <
-        typename T,
-        FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value && std::is_same<char_type, char_t<T>>::value)>
-    FMT_CONSTEXPR FMT_INLINE auto map(const T &val) -> basic_string_view<char_type>
-    {
-        return to_string_view(val);
-    }
-    template <
-        typename T,
-        FMT_ENABLE_IF(is_string<T>::value && !std::is_pointer<T>::value && !std::is_same<char_type, char_t<T>>::value)>
-    FMT_CONSTEXPR FMT_INLINE auto map(const T &) -> unformattable_char
-    {
-        return {};
-    }
-    template <
-        typename T,
-        FMT_ENABLE_IF(
-            std::is_convertible<T, basic_string_view<char_type>>::value && !is_string<T>::value &&
-            !has_formatter<T, Context>::value && !has_fallback_formatter<T, char_type>::value)>
-    FMT_CONSTEXPR FMT_INLINE auto map(const T &val) -> basic_string_view<char_type>
-    {
-        return basic_string_view<char_type>(val);
-    }
-    template <
-        typename T,
-        FMT_ENABLE_IF(
-            std::is_convertible<T, std_string_view<char_type>>::value &&
-            !std::is_convertible<T, basic_string_view<char_type>>::value && !is_string<T>::value &&
-            !has_formatter<T, Context>::value && !has_fallback_formatter<T, char_type>::value)>
-    FMT_CONSTEXPR FMT_INLINE auto map(const T &val) -> basic_string_view<char_type>
-    {
-        return std_string_view<char_type>(val);
-    }
-
-    FMT_CONSTEXPR FMT_INLINE auto map(void *val) -> const void *
-    {
-        return val;
-    }
-    FMT_CONSTEXPR FMT_INLINE auto map(const void *val) -> const void *
-    {
-        return val;
-    }
-    FMT_CONSTEXPR FMT_INLINE auto map(std::nullptr_t val) -> const void *
-    {
-        return val;
-    }
-
-    // We use SFINAE instead of a const T* parameter to avoid conflicting with
-    // the C array overload.
-    template <
-        typename T,
-        FMT_ENABLE_IF(
-            std::is_pointer<T>::value || std::is_member_pointer<T>::value ||
-            std::is_function<typename std::remove_pointer<T>::type>::value ||
-            (std::is_convertible<const T &, const void *>::value &&
-             !std::is_convertible<const T &, const char_type *>::value && !has_formatter<T, Context>::value))>
-    FMT_CONSTEXPR auto map(const T &) -> unformattable_pointer
-    {
-        return {};
-    }
-
-    template <typename T, std::size_t N, FMT_ENABLE_IF(!std::is_same<T, wchar_t>::value)>
-    FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N]
-    {
-        return values;
-    }
-
-    template <
-        typename T,
-        FMT_ENABLE_IF(
-            std::is_enum<T>::value &&std::is_convertible<T, int>::value && !has_format_as<T>::value &&
-            !has_formatter<T, Context>::value && !has_fallback_formatter<T, char_type>::value)>
-    FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const T &val)
-        -> decltype(std::declval<arg_mapper>().map(static_cast<underlying_t<T>>(val)))
-    {
-        return map(static_cast<underlying_t<T>>(val));
-    }
-
-    template <typename T, FMT_ENABLE_IF(has_format_as<T>::value && !has_formatter<T, Context>::value)>
-    FMT_CONSTEXPR FMT_INLINE auto map(const T &val) -> decltype(std::declval<arg_mapper>().map(format_as(T())))
-    {
-        return map(format_as(val));
-    }
-
-    template <typename T, typename U = remove_cvref_t<T>>
-    struct formattable : bool_constant<
-                             has_const_formatter<U, Context>() || !std::is_const<remove_reference_t<T>>::value ||
-                             has_fallback_formatter<U, char_type>::value>
-    {
-    };
-
-#if(FMT_MSC_VERSION != 0 && FMT_MSC_VERSION < 1910) || FMT_ICC_VERSION != 0 || defined(__NVCC__)
-    // Workaround a bug in MSVC and Intel (Issue 2746).
-    template <typename T>
-    FMT_CONSTEXPR FMT_INLINE auto do_map(T &&val) -> T &
-    {
-        return val;
-    }
-#else
-    template <typename T, FMT_ENABLE_IF(formattable<T>::value)>
-    FMT_CONSTEXPR FMT_INLINE auto do_map(T &&val) -> T &
-    {
-        return val;
-    }
-    template <typename T, FMT_ENABLE_IF(!formattable<T>::value)>
-    FMT_CONSTEXPR FMT_INLINE auto do_map(T &&) -> unformattable_const
-    {
-        return {};
-    }
-#endif
-
-    template <
-        typename T,
-        typename U = remove_cvref_t<T>,
-        FMT_ENABLE_IF(
-            !is_string<U>::value && !is_char<U>::value && !std::is_array<U>::value && !std::is_pointer<U>::value &&
-            !has_format_as<U>::value &&
-            (has_formatter<U, Context>::value || has_fallback_formatter<U, char_type>::value))>
-    FMT_CONSTEXPR FMT_INLINE auto map(T &&val) -> decltype(this->do_map(std::forward<T>(val)))
-    {
-        return do_map(std::forward<T>(val));
-    }
-
-    template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
-    FMT_CONSTEXPR FMT_INLINE auto map(const T &named_arg) -> decltype(std::declval<arg_mapper>().map(named_arg.value))
-    {
-        return map(named_arg.value);
-    }
-
-    auto map(...) -> unformattable
-    {
-        return {};
-    }
-};
-
-// A type constant after applying arg_mapper<Context>.
-template <typename T, typename Context>
-using mapped_type_constant =
-    type_constant<decltype(arg_mapper<Context>().map(std::declval<const T &>())), typename Context::char_type>;
-
-enum
-{
-    packed_arg_bits = 4
-};
-// Maximum number of arguments with packed types.
-enum
-{
-    max_packed_args = 62 / packed_arg_bits
-};
-enum : unsigned long long
-{
-    is_unpacked_bit = 1ULL << 63
-};
-enum : unsigned long long
-{
-    has_named_args_bit = 1ULL << 62
-};
-
-FMT_END_DETAIL_NAMESPACE
-
-// An output iterator that appends to a buffer.
-// It is used to reduce symbol sizes for the common case.
-class appender : public std::back_insert_iterator<detail::buffer<char>>
-{
-    using base = std::back_insert_iterator<detail::buffer<char>>;
-
-    template <typename T>
-    friend auto get_buffer(appender out) -> detail::buffer<char> &
-    {
-        return detail::get_container(out);
-    }
-
-public:
-    using std::back_insert_iterator<detail::buffer<char>>::back_insert_iterator;
-    appender(base it) noexcept : base(it) {}
-    FMT_UNCHECKED_ITERATOR(appender);
-
-    auto operator++() noexcept -> appender & { return *this; }
-    auto operator++(int) noexcept -> appender { return *this; }
-};
-
-// A formatting argument. It is a trivially copyable/constructible type to
-// allow storage in basic_memory_buffer.
-template <typename Context>
-class basic_format_arg
-{
-private:
-    detail::value<Context> value_;
-    detail::type           type_;
-
-    template <typename ContextType, typename T>
-    friend FMT_CONSTEXPR auto detail::make_arg(T &&value) -> basic_format_arg<ContextType>;
-
-    template <typename Visitor, typename Ctx>
-    friend FMT_CONSTEXPR auto visit_format_arg(Visitor &&vis, const basic_format_arg<Ctx> &arg) -> decltype(vis(0));
-
-    friend class basic_format_args<Context>;
-    friend class dynamic_format_arg_store<Context>;
-
-    using char_type = typename Context::char_type;
-
-    template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
-    friend struct detail::arg_data;
-
-    basic_format_arg(const detail::named_arg_info<char_type> *args, size_t size) : value_(args, size) {}
-
-public:
-    class handle
-    {
-    public:
-        explicit handle(detail::custom_value<Context> custom) : custom_(custom) {}
-
-        void format(typename Context::parse_context_type &parse_ctx, Context &ctx) const
-        {
-            custom_.format(custom_.value, parse_ctx, ctx);
-        }
-
-    private:
-        detail::custom_value<Context> custom_;
-    };
-
-    constexpr basic_format_arg() : type_(detail::type::none_type) {}
-
-    constexpr explicit operator bool() const noexcept { return type_ != detail::type::none_type; }
-
-    auto type() const -> detail::type { return type_; }
-
-    auto is_integral() const -> bool { return detail::is_integral_type(type_); }
-    auto is_arithmetic() const -> bool { return detail::is_arithmetic_type(type_); }
-};
-
-/**
-  \rst
-  Visits an argument dispatching to the appropriate visit method based on
-  the argument type. For example, if the argument type is ``double`` then
-  ``vis(value)`` will be called with the value of type ``double``.
-  \endrst
- */
-template <typename Visitor, typename Context>
-FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(Visitor &&vis, const basic_format_arg<Context> &arg) -> decltype(vis(0))
-{
-    switch(arg.type_)
-    {
-        case detail::type::none_type:
-            break;
-        case detail::type::int_type:
-            return vis(arg.value_.int_value);
-        case detail::type::uint_type:
-            return vis(arg.value_.uint_value);
-        case detail::type::long_long_type:
-            return vis(arg.value_.long_long_value);
-        case detail::type::ulong_long_type:
-            return vis(arg.value_.ulong_long_value);
-        case detail::type::int128_type:
-            return vis(detail::convert_for_visit(arg.value_.int128_value));
-        case detail::type::uint128_type:
-            return vis(detail::convert_for_visit(arg.value_.uint128_value));
-        case detail::type::bool_type:
-            return vis(arg.value_.bool_value);
-        case detail::type::char_type:
-            return vis(arg.value_.char_value);
-        case detail::type::float_type:
-            return vis(arg.value_.float_value);
-        case detail::type::double_type:
-            return vis(arg.value_.double_value);
-        case detail::type::long_double_type:
-            return vis(arg.value_.long_double_value);
-        case detail::type::cstring_type:
-            return vis(arg.value_.string.data);
-        case detail::type::string_type:
-            using sv = basic_string_view<typename Context::char_type>;
-            return vis(sv(arg.value_.string.data, arg.value_.string.size));
-        case detail::type::pointer_type:
-            return vis(arg.value_.pointer);
-        case detail::type::custom_type:
-            return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
-    }
-    return vis(monostate());
-}
-
-FMT_BEGIN_DETAIL_NAMESPACE
-
-template <typename Char, typename InputIt>
-auto copy_str(InputIt begin, InputIt end, appender out) -> appender
-{
-    get_container(out).append(begin, end);
-    return out;
-}
-
-template <typename Char, typename R, typename OutputIt>
-FMT_CONSTEXPR auto copy_str(R &&rng, OutputIt out) -> OutputIt
-{
-    return detail::copy_str<Char>(rng.begin(), rng.end(), out);
-}
-
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
-// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
-template <typename... Ts>
-struct void_t_impl
-{
-    using type = void;
-};
-template <typename... Ts>
-using void_t = typename detail::void_t_impl<Ts...>::type;
-#else
-template <typename...>
-using void_t = void;
-#endif
-
-template <typename It, typename T, typename Enable = void>
-struct is_output_iterator : std::false_type
-{
-};
-
-template <typename It, typename T>
-struct is_output_iterator<
-    It,
-    T,
-    void_t<typename std::iterator_traits<It>::iterator_category, decltype(*std::declval<It>() = std::declval<T>())>>
-    : std::true_type
-{
-};
-
-template <typename OutputIt>
-struct is_back_insert_iterator : std::false_type
-{
-};
-template <typename Container>
-struct is_back_insert_iterator<std::back_insert_iterator<Container>> : std::true_type
-{
-};
-
-template <typename OutputIt>
-struct is_contiguous_back_insert_iterator : std::false_type
-{
-};
-template <typename Container>
-struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>> : is_contiguous<Container>
-{
-};
-template <>
-struct is_contiguous_back_insert_iterator<appender> : std::true_type
-{
-};
-
-// A type-erased reference to an std::locale to avoid a heavy <locale> include.
-class locale_ref
-{
-private:
-    const void *locale_; // A type-erased pointer to std::locale.
-
-public:
-    constexpr locale_ref() : locale_(nullptr) {}
-    template <typename Locale>
-    explicit locale_ref(const Locale &loc);
-
-    explicit operator bool() const noexcept { return locale_ != nullptr; }
-
-    template <typename Locale>
-    auto get() const -> Locale;
-};
-
-template <typename>
-constexpr auto encode_types() -> unsigned long long
-{
-    return 0;
-}
-
-template <typename Context, typename Arg, typename... Args>
-constexpr auto encode_types() -> unsigned long long
-{
-    return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
-           (encode_types<Context, Args...>() << packed_arg_bits);
-}
-
-template <typename Context, typename T>
-FMT_CONSTEXPR FMT_INLINE auto make_value(T &&val) -> value<Context>
-{
-    const auto &arg = arg_mapper<Context>().map(FMT_FORWARD(val));
-
-    constexpr bool formattable_char = !std::is_same<decltype(arg), const unformattable_char &>::value;
-    static_assert(formattable_char, "Mixing character types is disallowed.");
-
-    constexpr bool formattable_const = !std::is_same<decltype(arg), const unformattable_const &>::value;
-    static_assert(formattable_const, "Cannot format a const argument.");
-
-    // Formatting of arbitrary pointers is disallowed. If you want to output
-    // a pointer cast it to "void *" or "const void *". In particular, this
-    // forbids formatting of "[const] volatile char *" which is printed as bool
-    // by iostreams.
-    constexpr bool formattable_pointer = !std::is_same<decltype(arg), const unformattable_pointer &>::value;
-    static_assert(formattable_pointer, "Formatting of non-void pointers is disallowed.");
-
-    constexpr bool formattable = !std::is_same<decltype(arg), const unformattable &>::value;
-    static_assert(
-        formattable,
-        "Cannot format an argument. To make type T formattable provide a "
-        "formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
-    return {arg};
-}
-
-template <typename Context, typename T>
-FMT_CONSTEXPR auto make_arg(T &&value) -> basic_format_arg<Context>
-{
-    basic_format_arg<Context> arg;
-    arg.type_  = mapped_type_constant<T, Context>::value;
-    arg.value_ = make_value<Context>(value);
-    return arg;
-}
-
-// The type template parameter is there to avoid an ODR violation when using
-// a fallback formatter in one translation unit and an implicit conversion in
-// another (not recommended).
-template <bool IS_PACKED, typename Context, type, typename T, FMT_ENABLE_IF(IS_PACKED)>
-FMT_CONSTEXPR FMT_INLINE auto make_arg(T &&val) -> value<Context>
-{
-    return make_value<Context>(val);
-}
-
-template <bool IS_PACKED, typename Context, type, typename T, FMT_ENABLE_IF(!IS_PACKED)>
-FMT_CONSTEXPR inline auto make_arg(T &&value) -> basic_format_arg<Context>
-{
-    return make_arg<Context>(value);
-}
-FMT_END_DETAIL_NAMESPACE
-
-// Formatting context.
-template <typename OutputIt, typename Char>
-class basic_format_context
-{
-public:
-    /** The character type for the output. */
-    using char_type = Char;
-
-private:
-    OutputIt                                out_;
-    basic_format_args<basic_format_context> args_;
-    detail::locale_ref                      loc_;
-
-public:
-    using iterator           = OutputIt;
-    using format_arg         = basic_format_arg<basic_format_context>;
-    using parse_context_type = basic_format_parse_context<Char>;
-    template <typename T>
-    using formatter_type = formatter<T, char_type>;
-
-    basic_format_context(basic_format_context &&)      = default;
-    basic_format_context(const basic_format_context &) = delete;
-    void operator=(const basic_format_context &)       = delete;
-    /**
-     Constructs a ``basic_format_context`` object. References to the arguments are
-     stored in the object so make sure they have appropriate lifetimes.
-     */
-    constexpr basic_format_context(
-        OutputIt                                out,
-        basic_format_args<basic_format_context> ctx_args,
-        detail::locale_ref                      loc = detail::locale_ref()) :
-        out_(out), args_(ctx_args), loc_(loc)
-    {
-    }
-
-    constexpr auto     arg(int id) const -> format_arg { return args_.get(id); }
-    FMT_CONSTEXPR auto arg(basic_string_view<char_type> name) -> format_arg { return args_.get(name); }
-    FMT_CONSTEXPR auto arg_id(basic_string_view<char_type> name) -> int { return args_.get_id(name); }
-    auto               args() const -> const basic_format_args<basic_format_context>               &{ return args_; }
-
-    FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; }
-    void               on_error(const char *message) { error_handler().on_error(message); }
-
-    // Returns an iterator to the beginning of the output range.
-    FMT_CONSTEXPR auto out() -> iterator { return out_; }
-
-    // Advances the begin iterator to ``it``.
-    void advance_to(iterator it)
-    {
-        if(!detail::is_back_insert_iterator<iterator>())
-            out_ = it;
-    }
-
-    FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; }
-};
-
-template <typename Char>
-using buffer_context = basic_format_context<detail::buffer_appender<Char>, Char>;
-using format_context = buffer_context<char>;
-
-// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164.
-#define FMT_BUFFER_CONTEXT(Char) basic_format_context<detail::buffer_appender<Char>, Char>
-
-template <typename T, typename Char = char>
-using is_formattable = bool_constant<
-    !std::is_base_of<
-        detail::unformattable,
-        decltype(detail::arg_mapper<buffer_context<Char>>().map(std::declval<T>()))>::value &&
-    !detail::has_fallback_formatter<T, Char>::value>;
-
-/**
-  \rst
-  An array of references to arguments. It can be implicitly converted into
-  `~fmt::basic_format_args` for passing into type-erased formatting functions
-  such as `~fmt::vformat`.
-  \endrst
- */
-template <typename Context, typename... Args>
-class format_arg_store
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
-    // Workaround a GCC template argument substitution bug.
-    : public basic_format_args<Context>
-#endif
-{
-private:
-    static const size_t num_args       = sizeof...(Args);
-    static const size_t num_named_args = detail::count_named_args<Args...>();
-    static const bool   is_packed      = num_args <= detail::max_packed_args;
-
-    using value_type = conditional_t<is_packed, detail::value<Context>, basic_format_arg<Context>>;
-
-    detail::arg_data<value_type, typename Context::char_type, num_args, num_named_args> data_;
-
-    friend class basic_format_args<Context>;
-
-    static constexpr unsigned long long desc =
-        (is_packed ? detail::encode_types<Context, Args...>() : detail::is_unpacked_bit | num_args) |
-        (num_named_args != 0 ? static_cast<unsigned long long>(detail::has_named_args_bit) : 0);
-
-public:
-    template <typename... T>
-    FMT_CONSTEXPR FMT_INLINE format_arg_store(T &&...args) :
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
-        basic_format_args<Context>(*this),
-#endif
-        data_{detail::make_arg<is_packed, Context, detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>(
-            FMT_FORWARD(args))...}
-    {
-        detail::init_named_args(data_.named_args(), 0, 0, args...);
-    }
-};
-
-/**
-  \rst
-  Constructs a `~fmt::format_arg_store` object that contains references to
-  arguments and can be implicitly converted to `~fmt::format_args`. `Context`
-  can be omitted in which case it defaults to `~fmt::context`.
-  See `~fmt::arg` for lifetime considerations.
-  \endrst
- */
-template <typename Context = format_context, typename... Args>
-constexpr auto make_format_args(Args &&...args) -> format_arg_store<Context, remove_cvref_t<Args>...>
-{
-    return {FMT_FORWARD(args)...};
-}
-
-/**
-  \rst
-  Returns a named argument to be used in a formatting function.
-  It should only be used in a call to a formatting function or
-  `dynamic_format_arg_store::push_back`.
-
-  **Example**::
-
-    fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23));
-  \endrst
- */
-template <typename Char, typename T>
-inline auto arg(const Char *name, const T &arg) -> detail::named_arg<Char, T>
-{
-    static_assert(!detail::is_named_arg<T>(), "nested named arguments");
-    return {name, arg};
-}
-
-/**
-  \rst
-  A view of a collection of formatting arguments. To avoid lifetime issues it
-  should only be used as a parameter type in type-erased functions such as
-  ``vformat``::
-
-    void vlog(string_view format_str, format_args args);  // OK
-    format_args args = make_format_args(42);  // Error: dangling reference
-  \endrst
- */
-template <typename Context>
-class basic_format_args
-{
-public:
-    using size_type  = int;
-    using format_arg = basic_format_arg<Context>;
-
-private:
-    // A descriptor that contains information about formatting arguments.
-    // If the number of arguments is less or equal to max_packed_args then
-    // argument types are passed in the descriptor. This reduces binary code size
-    // per formatting function call.
-    unsigned long long desc_;
-    union
-    {
-        // If is_packed() returns true then argument values are stored in values_;
-        // otherwise they are stored in args_. This is done to improve cache
-        // locality and reduce compiled code size since storing larger objects
-        // may require more code (at least on x86-64) even if the same amount of
-        // data is actually copied to stack. It saves ~10% on the bloat test.
-        const detail::value<Context> *values_;
-        const format_arg             *args_;
-    };
-
-    constexpr auto is_packed() const -> bool { return (desc_ & detail::is_unpacked_bit) == 0; }
-    auto           has_named_args() const -> bool { return (desc_ & detail::has_named_args_bit) != 0; }
-
-    FMT_CONSTEXPR auto type(int index) const -> detail::type
-    {
-        int          shift = index * detail::packed_arg_bits;
-        unsigned int mask  = (1 << detail::packed_arg_bits) - 1;
-        return static_cast<detail::type>((desc_ >> shift) & mask);
-    }
-
-    constexpr FMT_INLINE basic_format_args(unsigned long long desc, const detail::value<Context> *values) :
-        desc_(desc), values_(values)
-    {
-    }
-    constexpr basic_format_args(unsigned long long desc, const format_arg *args) : desc_(desc), args_(args) {}
-
-public:
-    constexpr basic_format_args() : desc_(0), args_(nullptr) {}
-
-    /**
-     \rst
-     Constructs a `basic_format_args` object from `~fmt::format_arg_store`.
-     \endrst
-     */
-    template <typename... Args>
-    constexpr FMT_INLINE basic_format_args(const format_arg_store<Context, Args...> &store) :
-        basic_format_args(format_arg_store<Context, Args...>::desc, store.data_.args())
-    {
-    }
-
-    /**
-     \rst
-     Constructs a `basic_format_args` object from
-     `~fmt::dynamic_format_arg_store`.
-     \endrst
-     */
-    constexpr FMT_INLINE basic_format_args(const dynamic_format_arg_store<Context> &store) :
-        basic_format_args(store.get_types(), store.data())
-    {
-    }
-
-    /**
-     \rst
-     Constructs a `basic_format_args` object from a dynamic set of arguments.
-     \endrst
-     */
-    constexpr basic_format_args(const format_arg *args, int count) :
-        basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count), args)
-    {
-    }
-
-    /** Returns the argument with the specified id. */
-    FMT_CONSTEXPR auto get(int id) const -> format_arg
-    {
-        format_arg arg;
-        if(!is_packed())
-        {
-            if(id < max_size())
-                arg = args_[id];
-            return arg;
-        }
-        if(id >= detail::max_packed_args)
-            return arg;
-        arg.type_ = type(id);
-        if(arg.type_ == detail::type::none_type)
-            return arg;
-        arg.value_ = values_[id];
-        return arg;
-    }
-
-    template <typename Char>
-    auto get(basic_string_view<Char> name) const -> format_arg
-    {
-        int id = get_id(name);
-        return id >= 0 ? get(id) : format_arg();
-    }
-
-    template <typename Char>
-    auto get_id(basic_string_view<Char> name) const -> int
-    {
-        if(!has_named_args())
-            return -1;
-        const auto &named_args = (is_packed() ? values_[-1] : args_[-1].value_).named_args;
-        for(size_t i = 0; i < named_args.size; ++i)
-        {
-            if(named_args.data[i].name == name)
-                return named_args.data[i].id;
-        }
-        return -1;
-    }
-
-    auto max_size() const -> int
-    {
-        unsigned long long max_packed = detail::max_packed_args;
-        return static_cast<int>(is_packed() ? max_packed : desc_ & ~detail::is_unpacked_bit);
-    }
-};
-
-/** An alias to ``basic_format_args<format_context>``. */
-// A separate type would result in shorter symbols but break ABI compatibility
-// between clang and gcc on ARM (#1919).
-using format_args = basic_format_args<format_context>;
-
-// We cannot use enum classes as bit fields because of a gcc bug, so we put them
-// in namespaces instead (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414).
-// Additionally, if an underlying type is specified, older gcc incorrectly warns
-// that the type is too small. Both bugs are fixed in gcc 9.3.
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 903
-#define FMT_ENUM_UNDERLYING_TYPE(type)
-#else
-#define FMT_ENUM_UNDERLYING_TYPE(type) : type
-#endif
-namespace align
-{
-enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, left, right, center, numeric};
-}
-using align_t = align::type;
-namespace sign
-{
-enum type FMT_ENUM_UNDERLYING_TYPE(unsigned char){none, minus, plus, space};
-}
-using sign_t = sign::type;
-
-FMT_BEGIN_DETAIL_NAMESPACE
-
-// Workaround an array initialization issue in gcc 4.8.
-template <typename Char>
-struct fill_t
-{
-private:
-    enum
-    {
-        max_size = 4
-    };
-    Char          data_[max_size] = {Char(' '), Char(0), Char(0), Char(0)};
-    unsigned char size_           = 1;
-
-public:
-    FMT_CONSTEXPR void operator=(basic_string_view<Char> s)
-    {
-        auto size = s.size();
-        if(size > max_size)
-            return throw_format_error("invalid fill");
-        for(size_t i = 0; i < size; ++i)
-            data_[i] = s[i];
-        size_ = static_cast<unsigned char>(size);
-    }
-
-    constexpr auto size() const -> size_t { return size_; }
-    constexpr auto data() const -> const Char * { return data_; }
-
-    FMT_CONSTEXPR auto operator[](size_t index) -> Char & { return data_[index]; }
-    FMT_CONSTEXPR auto operator[](size_t index) const -> const Char & { return data_[index]; }
-};
-FMT_END_DETAIL_NAMESPACE
-
-enum class presentation_type : unsigned char
-{
-    none,
-    // Integer types should go first,
-    dec,            // 'd'
-    oct,            // 'o'
-    hex_lower,      // 'x'
-    hex_upper,      // 'X'
-    bin_lower,      // 'b'
-    bin_upper,      // 'B'
-    hexfloat_lower, // 'a'
-    hexfloat_upper, // 'A'
-    exp_lower,      // 'e'
-    exp_upper,      // 'E'
-    fixed_lower,    // 'f'
-    fixed_upper,    // 'F'
-    general_lower,  // 'g'
-    general_upper,  // 'G'
-    chr,            // 'c'
-    string,         // 's'
-    pointer,        // 'p'
-    debug           // '?'
-};
-
-// Format specifiers for built-in and string types.
-template <typename Char>
-struct basic_format_specs
-{
-    int                  width;
-    int                  precision;
-    presentation_type    type;
-    align_t              align : 4;
-    sign_t               sign : 3;
-    bool                 alt : 1; // Alternate form ('#').
-    bool                 localized : 1;
-    detail::fill_t<Char> fill;
-
-    constexpr basic_format_specs() :
-        width(0),
-        precision(-1),
-        type(presentation_type::none),
-        align(align::none),
-        sign(sign::none),
-        alt(false),
-        localized(false)
-    {
-    }
-};
-
-using format_specs = basic_format_specs<char>;
-
-FMT_BEGIN_DETAIL_NAMESPACE
-
-enum class arg_id_kind
-{
-    none,
-    index,
-    name
-};
-
-// An argument reference.
-template <typename Char>
-struct arg_ref
-{
-    FMT_CONSTEXPR arg_ref() : kind(arg_id_kind::none), val() {}
-
-    FMT_CONSTEXPR explicit arg_ref(int index) : kind(arg_id_kind::index), val(index) {}
-    FMT_CONSTEXPR explicit arg_ref(basic_string_view<Char> name) : kind(arg_id_kind::name), val(name) {}
-
-    FMT_CONSTEXPR auto operator=(int idx) -> arg_ref &
-    {
-        kind      = arg_id_kind::index;
-        val.index = idx;
-        return *this;
-    }
-
-    arg_id_kind kind;
-    union value
-    {
-        FMT_CONSTEXPR value(int id = 0) : index{id} {}
-        FMT_CONSTEXPR value(basic_string_view<Char> n) : name(n) {}
-
-        int                     index;
-        basic_string_view<Char> name;
-    } val;
-};
-
-// Format specifiers with width and precision resolved at formatting rather
-// than parsing time to allow re-using the same parsed specifiers with
-// different sets of arguments (precompilation of format strings).
-template <typename Char>
-struct dynamic_format_specs : basic_format_specs<Char>
-{
-    arg_ref<Char> width_ref;
-    arg_ref<Char> precision_ref;
-};
-
-struct auto_id
-{
-};
-
-// A format specifier handler that sets fields in basic_format_specs.
-template <typename Char>
-class specs_setter
-{
-protected:
-    basic_format_specs<Char> &specs_;
-
-public:
-    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 void on_align(align_t align) { specs_.align = align; }
-    FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) { specs_.fill = fill; }
-    FMT_CONSTEXPR void on_sign(sign_t s) { specs_.sign = s; }
-    FMT_CONSTEXPR void on_hash() { specs_.alt = true; }
-    FMT_CONSTEXPR void on_localized() { specs_.localized = true; }
-
-    FMT_CONSTEXPR void on_zero()
-    {
-        if(specs_.align == align::none)
-            specs_.align = align::numeric;
-        specs_.fill[0] = Char('0');
-    }
-
-    FMT_CONSTEXPR void on_width(int width) { specs_.width = width; }
-    FMT_CONSTEXPR void on_precision(int precision) { specs_.precision = precision; }
-    FMT_CONSTEXPR void end_precision() {}
-
-    FMT_CONSTEXPR void on_type(presentation_type type) { specs_.type = type; }
-};
-
-// Format spec handler that saves references to arguments representing dynamic
-// width and precision to be resolved at formatting time.
-template <typename ParseContext>
-class dynamic_specs_handler : public specs_setter<typename ParseContext::char_type>
-{
-public:
-    using char_type = typename ParseContext::char_type;
-
-    FMT_CONSTEXPR dynamic_specs_handler(dynamic_format_specs<char_type> &specs, ParseContext &ctx) :
-        specs_setter<char_type>(specs), specs_(specs), context_(ctx)
-    {
-    }
-
-    FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler &other) :
-        specs_setter<char_type>(other), specs_(other.specs_), context_(other.context_)
-    {
-    }
-
-    template <typename Id>
-    FMT_CONSTEXPR void on_dynamic_width(Id arg_id)
-    {
-        specs_.width_ref = make_arg_ref(arg_id);
-    }
-
-    template <typename Id>
-    FMT_CONSTEXPR void on_dynamic_precision(Id arg_id)
-    {
-        specs_.precision_ref = make_arg_ref(arg_id);
-    }
-
-    FMT_CONSTEXPR void on_error(const char *message) { context_.on_error(message); }
-
-private:
-    dynamic_format_specs<char_type> &specs_;
-    ParseContext                    &context_;
-
-    using arg_ref_type = arg_ref<char_type>;
-
-    FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type
-    {
-        context_.check_arg_id(arg_id);
-        context_.check_dynamic_spec(arg_id);
-        return arg_ref_type(arg_id);
-    }
-
-    FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type
-    {
-        int arg_id = context_.next_arg_id();
-        context_.check_dynamic_spec(arg_id);
-        return arg_ref_type(arg_id);
-    }
-
-    FMT_CONSTEXPR auto make_arg_ref(basic_string_view<char_type> arg_id) -> arg_ref_type
-    {
-        context_.check_arg_id(arg_id);
-        basic_string_view<char_type> format_str(context_.begin(), to_unsigned(context_.end() - context_.begin()));
-        return arg_ref_type(arg_id);
-    }
-};
-
-template <typename Char>
-constexpr bool is_ascii_letter(Char c)
-{
-    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
-}
-
-// Converts a character to ASCII. Returns a number > 127 on conversion failure.
-template <typename Char, FMT_ENABLE_IF(std::is_integral<Char>::value)>
-constexpr auto to_ascii(Char c) -> Char
-{
-    return c;
-}
-template <typename Char, FMT_ENABLE_IF(std::is_enum<Char>::value)>
-constexpr auto to_ascii(Char c) -> underlying_t<Char>
-{
-    return c;
-}
-
-FMT_CONSTEXPR inline auto code_point_length_impl(char c) -> int
-{
-    return "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"[static_cast<unsigned char>(c) >> 3];
-}
-
-template <typename Char>
-FMT_CONSTEXPR auto code_point_length(const Char *begin) -> int
-{
-    if(const_check(sizeof(Char) != 1))
-        return 1;
-    int len = code_point_length_impl(static_cast<char>(*begin));
-
-    // Compute the pointer to the next character early so that the next
-    // iteration can start working on the next character. Neither Clang
-    // nor GCC figure out this reordering on their own.
-    return len + !len;
-}
-
-// Return the result via the out param to workaround gcc bug 77539.
-template <bool IS_CONSTEXPR, typename T, typename Ptr = const T *>
-FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr &out) -> bool
-{
-    for(out = first; out != last; ++out)
-    {
-        if(*out == value)
-            return true;
-    }
-    return false;
-}
-
-template <>
-inline auto find<false, char>(const char *first, const char *last, char value, const char *&out) -> bool
-{
-    out = static_cast<const char *>(std::memchr(first, value, to_unsigned(last - first)));
-    return out != nullptr;
-}
-
-// 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>
-FMT_CONSTEXPR auto parse_nonnegative_int(const Char *&begin, const Char *end, int error_value) noexcept -> int
-{
-    FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
-    unsigned value = 0, prev = 0;
-    auto     p = begin;
-    do
-    {
-        prev  = value;
-        value = value * 10 + unsigned(*p - '0');
-        ++p;
-    } while(p != end && '0' <= *p && *p <= '9');
-    auto num_digits = p - begin;
-    begin           = p;
-    if(num_digits <= std::numeric_limits<int>::digits10)
-        return static_cast<int>(value);
-    // Check for overflow.
-    const unsigned max = to_unsigned((std::numeric_limits<int>::max)());
-    return num_digits == std::numeric_limits<int>::digits10 + 1 && prev * 10ull + unsigned(p[-1] - '0') <= max ?
-               static_cast<int>(value) :
-               error_value;
-}
-
-// Parses fill and alignment.
-template <typename Char, typename Handler>
-FMT_CONSTEXPR auto parse_align(const Char *begin, const Char *end, Handler &&handler) -> const Char *
-{
-    FMT_ASSERT(begin != end, "");
-    auto align = align::none;
-    auto p     = begin + code_point_length(begin);
-    if(end - p <= 0)
-        p = begin;
-    for(;;)
-    {
-        switch(to_ascii(*p))
-        {
-            case '<':
-                align = align::left;
-                break;
-            case '>':
-                align = align::right;
-                break;
-            case '^':
-                align = align::center;
-                break;
-            default:
-                break;
-        }
-        if(align != align::none)
-        {
-            if(p != begin)
-            {
-                auto c = *begin;
-                if(c == '{')
-                    return handler.on_error("invalid fill character '{'"), begin;
-                handler.on_fill(basic_string_view<Char>(begin, to_unsigned(p - begin)));
-                begin = p + 1;
-            }
-            else
-                ++begin;
-            handler.on_align(align);
-            break;
-        }
-        else if(p == begin)
-        {
-            break;
-        }
-        p = begin;
-    }
-    return begin;
-}
-
-template <typename Char>
-FMT_CONSTEXPR bool is_name_start(Char c)
-{
-    return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
-}
-
-template <typename Char, typename IDHandler>
-FMT_CONSTEXPR auto do_parse_arg_id(const Char *begin, const Char *end, IDHandler &&handler) -> const Char *
-{
-    FMT_ASSERT(begin != end, "");
-    Char c = *begin;
-    if(c >= '0' && c <= '9')
-    {
-        int index = 0;
-        if(c != '0')
-            index = parse_nonnegative_int(begin, end, (std::numeric_limits<int>::max)());
-        else
-            ++begin;
-        if(begin == end || (*begin != '}' && *begin != ':'))
-            handler.on_error("invalid format string");
-        else
-            handler(index);
-        return begin;
-    }
-    if(!is_name_start(c))
-    {
-        handler.on_error("invalid format string");
-        return begin;
-    }
-    auto it = begin;
-    do
-    {
-        ++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;
-}
-
-template <typename Char, typename IDHandler>
-FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char *begin, const Char *end, IDHandler &&handler) -> const Char *
-{
-    Char c = *begin;
-    if(c != '}' && c != ':')
-        return do_parse_arg_id(begin, end, handler);
-    handler();
-    return begin;
-}
-
-template <typename Char, typename Handler>
-FMT_CONSTEXPR auto parse_width(const Char *begin, const Char *end, Handler &&handler) -> const Char *
-{
-    using detail::auto_id;
-    struct width_adapter
-    {
-        Handler &handler;
-
-        FMT_CONSTEXPR void operator()() { handler.on_dynamic_width(auto_id()); }
-        FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_width(id); }
-        FMT_CONSTEXPR void operator()(basic_string_view<Char> id) { handler.on_dynamic_width(id); }
-        FMT_CONSTEXPR void on_error(const char *message)
-        {
-            if(message)
-                handler.on_error(message);
-        }
-    };
-
-    FMT_ASSERT(begin != end, "");
-    if('0' <= *begin && *begin <= '9')
-    {
-        int width = parse_nonnegative_int(begin, end, -1);
-        if(width != -1)
-            handler.on_width(width);
-        else
-            handler.on_error("number is too big");
-    }
-    else if(*begin == '{')
-    {
-        ++begin;
-        if(begin != end)
-            begin = parse_arg_id(begin, end, width_adapter{handler});
-        if(begin == end || *begin != '}')
-            return handler.on_error("invalid format string"), begin;
-        ++begin;
-    }
-    return begin;
-}
-
-template <typename Char, typename Handler>
-FMT_CONSTEXPR auto parse_precision(const Char *begin, const Char *end, Handler &&handler) -> const Char *
-{
-    using detail::auto_id;
-    struct precision_adapter
-    {
-        Handler &handler;
-
-        FMT_CONSTEXPR void operator()() { handler.on_dynamic_precision(auto_id()); }
-        FMT_CONSTEXPR void operator()(int id) { handler.on_dynamic_precision(id); }
-        FMT_CONSTEXPR void operator()(basic_string_view<Char> id) { handler.on_dynamic_precision(id); }
-        FMT_CONSTEXPR void on_error(const char *message)
-        {
-            if(message)
-                handler.on_error(message);
-        }
-    };
-
-    ++begin;
-    auto c = begin != end ? *begin : Char();
-    if('0' <= c && c <= '9')
-    {
-        auto precision = parse_nonnegative_int(begin, end, -1);
-        if(precision != -1)
-            handler.on_precision(precision);
-        else
-            handler.on_error("number is too big");
-    }
-    else if(c == '{')
-    {
-        ++begin;
-        if(begin != end)
-            begin = parse_arg_id(begin, end, precision_adapter{handler});
-        if(begin == end || *begin++ != '}')
-            return handler.on_error("invalid format string"), begin;
-    }
-    else
-    {
-        return handler.on_error("missing precision specifier"), begin;
-    }
-    handler.end_precision();
-    return begin;
-}
-
-template <typename Char>
-FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type
-{
-    switch(to_ascii(type))
-    {
-        case 'd':
-            return presentation_type::dec;
-        case 'o':
-            return presentation_type::oct;
-        case 'x':
-            return presentation_type::hex_lower;
-        case 'X':
-            return presentation_type::hex_upper;
-        case 'b':
-            return presentation_type::bin_lower;
-        case 'B':
-            return presentation_type::bin_upper;
-        case 'a':
-            return presentation_type::hexfloat_lower;
-        case 'A':
-            return presentation_type::hexfloat_upper;
-        case 'e':
-            return presentation_type::exp_lower;
-        case 'E':
-            return presentation_type::exp_upper;
-        case 'f':
-            return presentation_type::fixed_lower;
-        case 'F':
-            return presentation_type::fixed_upper;
-        case 'g':
-            return presentation_type::general_lower;
-        case 'G':
-            return presentation_type::general_upper;
-        case 'c':
-            return presentation_type::chr;
-        case 's':
-            return presentation_type::string;
-        case 'p':
-            return presentation_type::pointer;
-        case '?':
-            return presentation_type::debug;
-        default:
-            return presentation_type::none;
-    }
-}
-
-// Parses standard format specifiers and sends notifications about parsed
-// components to handler.
-template <typename Char, typename SpecHandler>
-FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char *begin, const Char *end, SpecHandler &&handler)
-    -> const Char *
-{
-    if(1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) && *begin != 'L')
-    {
-        presentation_type type = parse_presentation_type(*begin++);
-        if(type == presentation_type::none)
-            handler.on_error("invalid type specifier");
-        handler.on_type(type);
-        return begin;
-    }
-
-    if(begin == end)
-        return begin;
-
-    begin = parse_align(begin, end, handler);
-    if(begin == end)
-        return begin;
-
-    // Parse sign.
-    switch(to_ascii(*begin))
-    {
-        case '+':
-            handler.on_sign(sign::plus);
-            ++begin;
-            break;
-        case '-':
-            handler.on_sign(sign::minus);
-            ++begin;
-            break;
-        case ' ':
-            handler.on_sign(sign::space);
-            ++begin;
-            break;
-        default:
-            break;
-    }
-    if(begin == end)
-        return begin;
-
-    if(*begin == '#')
-    {
-        handler.on_hash();
-        if(++begin == end)
-            return begin;
-    }
-
-    // Parse zero flag.
-    if(*begin == '0')
-    {
-        handler.on_zero();
-        if(++begin == end)
-            return begin;
-    }
-
-    begin = parse_width(begin, end, handler);
-    if(begin == end)
-        return begin;
-
-    // Parse precision.
-    if(*begin == '.')
-    {
-        begin = parse_precision(begin, end, handler);
-        if(begin == end)
-            return begin;
-    }
-
-    if(*begin == 'L')
-    {
-        handler.on_localized();
-        ++begin;
-    }
-
-    // Parse type.
-    if(begin != end && *begin != '}')
-    {
-        presentation_type type = parse_presentation_type(*begin++);
-        if(type == presentation_type::none)
-            handler.on_error("invalid type specifier");
-        handler.on_type(type);
-    }
-    return begin;
-}
-
-template <typename Char, typename Handler>
-FMT_CONSTEXPR auto parse_replacement_field(const Char *begin, const Char *end, Handler &&handler) -> const Char *
-{
-    struct id_adapter
-    {
-        Handler &handler;
-        int      arg_id;
-
-        FMT_CONSTEXPR void operator()() { arg_id = handler.on_arg_id(); }
-        FMT_CONSTEXPR void operator()(int id) { arg_id = handler.on_arg_id(id); }
-        FMT_CONSTEXPR void operator()(basic_string_view<Char> id) { arg_id = handler.on_arg_id(id); }
-        FMT_CONSTEXPR void on_error(const char *message)
-        {
-            if(message)
-                handler.on_error(message);
-        }
-    };
-
-    ++begin;
-    if(begin == end)
-        return handler.on_error("invalid format string"), end;
-    if(*begin == '}')
-    {
-        handler.on_replacement_field(handler.on_arg_id(), begin);
-    }
-    else if(*begin == '{')
-    {
-        handler.on_text(begin, begin + 1);
-    }
-    else
-    {
-        auto adapter = id_adapter{handler, 0};
-        begin        = parse_arg_id(begin, end, adapter);
-        Char c       = begin != end ? *begin : Char();
-        if(c == '}')
-        {
-            handler.on_replacement_field(adapter.arg_id, begin);
-        }
-        else if(c == ':')
-        {
-            begin = handler.on_format_specs(adapter.arg_id, begin + 1, end);
-            if(begin == end || *begin != '}')
-                return handler.on_error("unknown format specifier"), end;
-        }
-        else
-        {
-            return handler.on_error("missing '}' in format string"), end;
-        }
-    }
-    return begin + 1;
-}
-
-template <bool IS_CONSTEXPR, typename Char, typename Handler>
-FMT_CONSTEXPR FMT_INLINE void parse_format_string(basic_string_view<Char> format_str, Handler &&handler)
-{
-    // Workaround a name-lookup bug in MSVC's modules implementation.
-    using detail::find;
-
-    auto begin = format_str.data();
-    auto end   = begin + format_str.size();
-    if(end - begin < 32)
-    {
-        // Use a simple loop instead of memchr for small strings.
-        const Char *p = begin;
-        while(p != end)
-        {
-            auto c = *p++;
-            if(c == '{')
-            {
-                handler.on_text(begin, p - 1);
-                begin = p = parse_replacement_field(p - 1, end, handler);
-            }
-            else if(c == '}')
-            {
-                if(p == end || *p != '}')
-                    return handler.on_error("unmatched '}' in format string");
-                handler.on_text(begin, p);
-                begin = ++p;
-            }
-        }
-        handler.on_text(begin, end);
-        return;
-    }
-    struct writer
-    {
-        FMT_CONSTEXPR void operator()(const Char *from, const Char *to)
-        {
-            if(from == to)
-                return;
-            for(;;)
-            {
-                const Char *p = nullptr;
-                if(!find<IS_CONSTEXPR>(from, to, Char('}'), p))
-                    return handler_.on_text(from, to);
-                ++p;
-                if(p == to || *p != '}')
-                    return handler_.on_error("unmatched '}' in format string");
-                handler_.on_text(from, p);
-                from = p + 1;
-            }
-        }
-        Handler &handler_;
-    } write = {handler};
-    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.
-        const Char *p = begin;
-        if(*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, Char('{'), p))
-            return write(begin, end);
-        write(begin, p);
-        begin = parse_replacement_field(p, end, handler);
-    }
-}
-
-template <typename T, bool = is_named_arg<T>::value>
-struct strip_named_arg
-{
-    using type = T;
-};
-template <typename T>
-struct strip_named_arg<T, true>
-{
-    using type = remove_cvref_t<decltype(T::value)>;
-};
-
-template <typename T, typename ParseContext>
-FMT_CONSTEXPR auto parse_format_specs(ParseContext &ctx) -> decltype(ctx.begin())
-{
-    using char_type     = typename ParseContext::char_type;
-    using context       = buffer_context<char_type>;
-    using stripped_type = typename strip_named_arg<T>::type;
-    using mapped_type   = conditional_t<
-        mapped_type_constant<T, context>::value != type::custom_type,
-        decltype(arg_mapper<context>().map(std::declval<const T &>())),
-        stripped_type>;
-    auto f = conditional_t<
-        has_formatter<mapped_type, context>::value,
-        formatter<mapped_type, char_type>,
-        fallback_formatter<stripped_type, char_type>>();
-    return f.parse(ctx);
-}
-
-template <typename ErrorHandler>
-FMT_CONSTEXPR void check_int_type_spec(presentation_type type, ErrorHandler &&eh)
-{
-    if(type > presentation_type::bin_upper && type != presentation_type::chr)
-        eh.on_error("invalid type specifier");
-}
-
-// Checks char specs and returns true if the type spec is char (and not int).
-template <typename Char, typename ErrorHandler = error_handler>
-FMT_CONSTEXPR auto check_char_specs(const basic_format_specs<Char> &specs, ErrorHandler &&eh = {}) -> bool
-{
-    if(specs.type != presentation_type::none && specs.type != presentation_type::chr &&
-       specs.type != presentation_type::debug)
-    {
-        check_int_type_spec(specs.type, eh);
-        return false;
-    }
-    if(specs.align == align::numeric || specs.sign != sign::none || specs.alt)
-        eh.on_error("invalid format specifier for char");
-    return true;
-}
-
-// A floating-point presentation format.
-enum class float_format : unsigned char
-{
-    general, // General: exponent notation or fixed point based on magnitude.
-    exp,     // Exponent notation with the default precision of 6, e.g. 1.2e-3.
-    fixed,   // Fixed point with the default precision of 6, e.g. 0.0012.
-    hex
-};
-
-struct float_specs
-{
-    int          precision;
-    float_format format : 8;
-    sign_t       sign : 8;
-    bool         upper : 1;
-    bool         locale : 1;
-    bool         binary32 : 1;
-    bool         showpoint : 1;
-};
-
-template <typename ErrorHandler = error_handler, typename Char>
-FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs<Char> &specs, ErrorHandler &&eh = {}) -> float_specs
-{
-    auto result      = float_specs();
-    result.showpoint = specs.alt;
-    result.locale    = specs.localized;
-    switch(specs.type)
-    {
-        case presentation_type::none:
-            result.format = float_format::general;
-            break;
-        case presentation_type::general_upper:
-            result.upper = true;
-            FMT_FALLTHROUGH;
-        case presentation_type::general_lower:
-            result.format = float_format::general;
-            break;
-        case presentation_type::exp_upper:
-            result.upper = true;
-            FMT_FALLTHROUGH;
-        case presentation_type::exp_lower:
-            result.format = float_format::exp;
-            result.showpoint |= specs.precision != 0;
-            break;
-        case presentation_type::fixed_upper:
-            result.upper = true;
-            FMT_FALLTHROUGH;
-        case presentation_type::fixed_lower:
-            result.format = float_format::fixed;
-            result.showpoint |= specs.precision != 0;
-            break;
-        case presentation_type::hexfloat_upper:
-            result.upper = true;
-            FMT_FALLTHROUGH;
-        case presentation_type::hexfloat_lower:
-            result.format = float_format::hex;
-            break;
-        default:
-            eh.on_error("invalid type specifier");
-            break;
-    }
-    return result;
-}
-
-template <typename ErrorHandler = error_handler>
-FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type, ErrorHandler &&eh = {}) -> bool
-{
-    if(type == presentation_type::none || type == presentation_type::string || type == presentation_type::debug)
-        return true;
-    if(type != presentation_type::pointer)
-        eh.on_error("invalid type specifier");
-    return false;
-}
-
-template <typename ErrorHandler = error_handler>
-FMT_CONSTEXPR void check_string_type_spec(presentation_type type, ErrorHandler &&eh = {})
-{
-    if(type != presentation_type::none && type != presentation_type::string && type != presentation_type::debug)
-        eh.on_error("invalid type specifier");
-}
-
-template <typename ErrorHandler>
-FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type, ErrorHandler &&eh)
-{
-    if(type != presentation_type::none && type != presentation_type::pointer)
-        eh.on_error("invalid type specifier");
-}
-
-// A parse_format_specs handler that checks if specifiers are consistent with
-// the argument type.
-template <typename Handler>
-class specs_checker : public Handler
-{
-private:
-    detail::type arg_type_;
-
-    FMT_CONSTEXPR void require_numeric_argument()
-    {
-        if(!is_arithmetic_type(arg_type_))
-            this->on_error("format specifier requires numeric argument");
-    }
-
-public:
-    FMT_CONSTEXPR specs_checker(const Handler &handler, detail::type arg_type) : Handler(handler), arg_type_(arg_type)
-    {
-    }
-
-    FMT_CONSTEXPR void on_align(align_t align)
-    {
-        if(align == align::numeric)
-            require_numeric_argument();
-        Handler::on_align(align);
-    }
-
-    FMT_CONSTEXPR void on_sign(sign_t s)
-    {
-        require_numeric_argument();
-        if(is_integral_type(arg_type_) && arg_type_ != type::int_type && arg_type_ != type::long_long_type &&
-           arg_type_ != type::int128_type && arg_type_ != type::char_type)
-        {
-            this->on_error("format specifier requires signed argument");
-        }
-        Handler::on_sign(s);
-    }
-
-    FMT_CONSTEXPR void on_hash()
-    {
-        require_numeric_argument();
-        Handler::on_hash();
-    }
-
-    FMT_CONSTEXPR void on_localized()
-    {
-        require_numeric_argument();
-        Handler::on_localized();
-    }
-
-    FMT_CONSTEXPR void on_zero()
-    {
-        require_numeric_argument();
-        Handler::on_zero();
-    }
-
-    FMT_CONSTEXPR void end_precision()
-    {
-        if(is_integral_type(arg_type_) || arg_type_ == type::pointer_type)
-            this->on_error("precision not allowed for this argument type");
-    }
-};
-
-constexpr int invalid_arg_index = -1;
-
-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
-template <int N, typename T, typename... Args, typename Char>
-constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int
-{
-    if constexpr(detail::is_statically_named_arg<T>())
-    {
-        if(name == T::name)
-            return N;
-    }
-    if constexpr(sizeof...(Args) > 0)
-        return get_arg_index_by_name<N + 1, Args...>(name);
-    (void) name; // Workaround an MSVC bug about "unused" parameter.
-    return invalid_arg_index;
-}
-#endif
-
-template <typename... Args, typename Char>
-FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int
-{
-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
-    if constexpr(sizeof...(Args) > 0)
-        return get_arg_index_by_name<0, Args...>(name);
-#endif
-    (void) name;
-    return invalid_arg_index;
-}
-
-template <typename Char, typename ErrorHandler, typename... Args>
-class format_string_checker
-{
-private:
-    // In the future basic_format_parse_context will replace compile_parse_context
-    // here and will use is_constant_evaluated and downcasting to access the data
-    // needed for compile-time checks: https://godbolt.org/z/GvWzcTjh1.
-    using parse_context_type      = compile_parse_context<Char, ErrorHandler>;
-    static constexpr int num_args = sizeof...(Args);
-
-    // Format specifier parsing function.
-    using parse_func = const Char *(*) (parse_context_type &);
-
-    parse_context_type context_;
-    parse_func         parse_funcs_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
-    type               types_[num_args > 0 ? static_cast<size_t>(num_args) : 1];
-
-public:
-    explicit FMT_CONSTEXPR format_string_checker(basic_string_view<Char> format_str, ErrorHandler eh) :
-        context_(format_str, num_args, types_, eh),
-        parse_funcs_{&parse_format_specs<Args, parse_context_type>...},
-        types_{mapped_type_constant<Args, basic_format_context<Char *, Char>>::value...}
-    {
-    }
-
-    FMT_CONSTEXPR void on_text(const Char *, const Char *) {}
-
-    FMT_CONSTEXPR auto on_arg_id() -> int { return context_.next_arg_id(); }
-    FMT_CONSTEXPR auto on_arg_id(int id) -> int { return context_.check_arg_id(id), id; }
-    FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int
-    {
-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
-        auto index = get_arg_index_by_name<Args...>(id);
-        if(index == invalid_arg_index)
-            on_error("named argument is not found");
-        return context_.check_arg_id(index), index;
-#else
-        (void) id;
-        on_error("compile-time checks for named arguments require C++20 support");
-        return 0;
-#endif
-    }
-
-    FMT_CONSTEXPR void on_replacement_field(int, const Char *) {}
-
-    FMT_CONSTEXPR auto on_format_specs(int id, const Char *begin, const Char *) -> const Char *
-    {
-        context_.advance_to(context_.begin() + (begin - &*context_.begin()));
-        // id >= 0 check is a workaround for gcc 10 bug (#2065).
-        return id >= 0 && id < num_args ? parse_funcs_[id](context_) : begin;
-    }
-
-    FMT_CONSTEXPR void on_error(const char *message)
-    {
-        context_.on_error(message);
-    }
-};
-
-// Reports a compile-time error if S is not a valid format string.
-template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
-FMT_INLINE void check_format_string(const S &)
-{
-#ifdef FMT_ENFORCE_COMPILE_STRING
-    static_assert(
-        is_compile_string<S>::value,
-        "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
-        "FMT_STRING.");
-#endif
-}
-template <typename... Args, typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
-void check_format_string(S format_str)
-{
-    FMT_CONSTEXPR auto s = basic_string_view<typename S::char_type>(format_str);
-    using checker        = format_string_checker<typename S::char_type, error_handler, remove_cvref_t<Args>...>;
-    FMT_CONSTEXPR bool invalid_format = (parse_format_string<true>(s, checker(s, {})), true);
-    ignore_unused(invalid_format);
-}
-
-template <typename Char>
-void vformat_to(
-    buffer<Char>                                                &buf,
-    basic_string_view<Char>                                      fmt,
-    basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
-    locale_ref                                                   loc = {});
-
-FMT_API void vprint_mojibake(std::FILE *, string_view, format_args);
-#ifndef _WIN32
-inline void vprint_mojibake(std::FILE *, string_view, format_args) {}
-#endif
-FMT_END_DETAIL_NAMESPACE
-
-// A formatter specialization for the core types corresponding to detail::type
-// constants.
-template <typename T, typename Char>
-struct formatter<T, Char, enable_if_t<detail::type_constant<T, Char>::value != detail::type::custom_type>>
-{
-private:
-    detail::dynamic_format_specs<Char> specs_;
-
-public:
-    // Parses format specifiers stopping either at the end of the range or at the
-    // terminating '}'.
-    template <typename ParseContext>
-    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
-    {
-        auto begin = ctx.begin(), end = ctx.end();
-        if(begin == end)
-            return begin;
-        using handler_type = detail::dynamic_specs_handler<ParseContext>;
-        auto type          = detail::type_constant<T, Char>::value;
-        auto checker       = detail::specs_checker<handler_type>(handler_type(specs_, ctx), type);
-        auto it            = detail::parse_format_specs(begin, end, checker);
-        auto eh            = ctx.error_handler();
-        switch(type)
-        {
-            case detail::type::none_type:
-                FMT_ASSERT(false, "invalid argument type");
-                break;
-            case detail::type::bool_type:
-                if(specs_.type == presentation_type::none || specs_.type == presentation_type::string)
-                {
-                    break;
-                }
-                FMT_FALLTHROUGH;
-            case detail::type::int_type:
-            case detail::type::uint_type:
-            case detail::type::long_long_type:
-            case detail::type::ulong_long_type:
-            case detail::type::int128_type:
-            case detail::type::uint128_type:
-                detail::check_int_type_spec(specs_.type, eh);
-                break;
-            case detail::type::char_type:
-                detail::check_char_specs(specs_, eh);
-                break;
-            case detail::type::float_type:
-                if(detail::const_check(FMT_USE_FLOAT))
-                    detail::parse_float_type_spec(specs_, eh);
-                else
-                    FMT_ASSERT(false, "float support disabled");
-                break;
-            case detail::type::double_type:
-                if(detail::const_check(FMT_USE_DOUBLE))
-                    detail::parse_float_type_spec(specs_, eh);
-                else
-                    FMT_ASSERT(false, "double support disabled");
-                break;
-            case detail::type::long_double_type:
-                if(detail::const_check(FMT_USE_LONG_DOUBLE))
-                    detail::parse_float_type_spec(specs_, eh);
-                else
-                    FMT_ASSERT(false, "long double support disabled");
-                break;
-            case detail::type::cstring_type:
-                detail::check_cstring_type_spec(specs_.type, eh);
-                break;
-            case detail::type::string_type:
-                detail::check_string_type_spec(specs_.type, eh);
-                break;
-            case detail::type::pointer_type:
-                detail::check_pointer_type_spec(specs_.type, eh);
-                break;
-            case detail::type::custom_type:
-                // Custom format specifiers are checked in parse functions of
-                // formatter specializations.
-                break;
-        }
-        return it;
-    }
-
-    template <
-        detail::type U = detail::type_constant<T, Char>::value,
-        enable_if_t<
-            (U == detail::type::string_type || U == detail::type::cstring_type || U == detail::type::char_type),
-            int> = 0>
-    FMT_CONSTEXPR void set_debug_format()
-    {
-        specs_.type = presentation_type::debug;
-    }
-
-    template <typename FormatContext>
-    FMT_CONSTEXPR auto format(const T &val, FormatContext &ctx) const -> decltype(ctx.out());
-};
-
-#define FMT_FORMAT_AS(Type, Base)                                                                                      \
-    template <typename Char>                                                                                           \
-    struct formatter<Type, Char> : formatter<Base, Char>                                                               \
-    {                                                                                                                  \
-        template <typename FormatContext>                                                                              \
-        auto format(Type const &val, FormatContext &ctx) const -> decltype(ctx.out())                                  \
-        {                                                                                                              \
-            return formatter<Base, Char>::format(static_cast<Base>(val), ctx);                                         \
-        }                                                                                                              \
-    }
-
-FMT_FORMAT_AS(signed char, int);
-FMT_FORMAT_AS(unsigned char, unsigned);
-FMT_FORMAT_AS(short, int);
-FMT_FORMAT_AS(unsigned short, unsigned);
-FMT_FORMAT_AS(long, long long);
-FMT_FORMAT_AS(unsigned long, unsigned long long);
-FMT_FORMAT_AS(Char *, const Char *);
-FMT_FORMAT_AS(std::basic_string<Char>, basic_string_view<Char>);
-FMT_FORMAT_AS(std::nullptr_t, const void *);
-FMT_FORMAT_AS(detail::std_string_view<Char>, basic_string_view<Char>);
-
-template <typename Char>
-struct basic_runtime
-{
-    basic_string_view<Char> str;
-};
-
-/** A compile-time format string. */
-template <typename Char, typename... Args>
-class basic_format_string
-{
-private:
-    basic_string_view<Char> str_;
-
-public:
-    template <typename S, FMT_ENABLE_IF(std::is_convertible<const S &, basic_string_view<Char>>::value)>
-    FMT_CONSTEVAL FMT_INLINE basic_format_string(const S &s) : str_(s)
-    {
-        static_assert(
-            detail::count<(
-                    std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
-                    std::is_reference<Args>::value)...>() == 0,
-            "passing views as lvalues is disallowed");
-#ifdef FMT_HAS_CONSTEVAL
-        if constexpr(detail::count_named_args<Args...>() == detail::count_statically_named_args<Args...>())
-        {
-            using checker = detail::format_string_checker<Char, detail::error_handler, remove_cvref_t<Args>...>;
-            detail::parse_format_string<true>(str_, checker(s, {}));
-        }
-#else
-        detail::check_format_string<Args...>(s);
-#endif
-    }
-    basic_format_string(basic_runtime<Char> r) : str_(r.str) {}
-
-    FMT_INLINE operator basic_string_view<Char>() const
-    {
-        return str_;
-    }
-};
-
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
-// Workaround broken conversion on older gcc.
-template <typename...>
-using format_string = string_view;
-inline auto runtime(string_view s) -> string_view
-{
-    return s;
-}
-#else
-template <typename... Args>
-using format_string = basic_format_string<char, type_identity_t<Args>...>;
-/**
-  \rst
-  Creates a runtime format string.
-
-  **Example**::
-
-    // Check format string at runtime instead of compile-time.
-    fmt::print(fmt::runtime("{:d}"), "I am not a number");
-  \endrst
- */
-inline auto runtime(string_view s) -> basic_runtime<char>
-{
-    return {{s}};
-}
-#endif
-
-FMT_API auto vformat(string_view fmt, format_args args) -> std::string;
-
-/**
-  \rst
-  Formats ``args`` according to specifications in ``fmt`` and returns the result
-  as a string.
-
-  **Example**::
-
-    #include <fmt/core.h>
-    std::string message = fmt::format("The answer is {}.", 42);
-  \endrst
-*/
-template <typename... T>
-FMT_NODISCARD FMT_INLINE auto format(format_string<T...> fmt, T &&...args) -> std::string
-{
-    return vformat(fmt, fmt::make_format_args(args...));
-}
-
-/** Formats a string and writes the output to ``out``. */
-template <typename OutputIt, FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
-auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt
-{
-    using detail::get_buffer;
-    auto &&buf = get_buffer<char>(out);
-    detail::vformat_to(buf, fmt, args, {});
-    return detail::get_iterator(buf);
-}
-
-/**
- \rst
- Formats ``args`` according to specifications in ``fmt``, writes the result to
- the output iterator ``out`` and returns the iterator past the end of the output
- range. `format_to` does not append a terminating null character.
-
- **Example**::
-
-   auto out = std::vector<char>();
-   fmt::format_to(std::back_inserter(out), "{}", 42);
- \endrst
- */
-template <typename OutputIt, typename... T, FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
-FMT_INLINE auto format_to(OutputIt out, format_string<T...> fmt, T &&...args) -> OutputIt
-{
-    return vformat_to(out, fmt, fmt::make_format_args(args...));
-}
-
-template <typename OutputIt>
-struct format_to_n_result
-{
-    /** Iterator past the end of the output range. */
-    OutputIt out;
-    /** Total (not truncated) output size. */
-    size_t size;
-};
-
-template <typename OutputIt, typename... T, FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
-auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) -> format_to_n_result<OutputIt>
-{
-    using traits = detail::fixed_buffer_traits;
-    auto buf     = detail::iterator_buffer<OutputIt, char, traits>(out, n);
-    detail::vformat_to(buf, fmt, args, {});
-    return {buf.out(), buf.count()};
-}
-
-/**
-  \rst
-  Formats ``args`` according to specifications in ``fmt``, writes up to ``n``
-  characters of the result to the output iterator ``out`` and returns the total
-  (not truncated) output size and the iterator past the end of the output range.
-  `format_to_n` does not append a terminating null character.
-  \endrst
- */
-template <typename OutputIt, typename... T, FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
-FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string<T...> fmt, T &&...args)
-    -> format_to_n_result<OutputIt>
-{
-    return vformat_to_n(out, n, fmt, fmt::make_format_args(args...));
-}
-
-/** Returns the number of chars in the output of ``format(fmt, args...)``. */
-template <typename... T>
-FMT_NODISCARD FMT_INLINE auto formatted_size(format_string<T...> fmt, T &&...args) -> size_t
-{
-    auto buf = detail::counting_buffer<>();
-    detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...), {});
-    return buf.count();
-}
-
-FMT_API void vprint(string_view fmt, format_args args);
-FMT_API void vprint(std::FILE *f, string_view fmt, format_args args);
-
-/**
-  \rst
-  Formats ``args`` according to specifications in ``fmt`` and writes the output
-  to ``stdout``.
-
-  **Example**::
-
-    fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
-  \endrst
- */
-template <typename... T>
-FMT_INLINE void print(format_string<T...> fmt, T &&...args)
-{
-    const auto &vargs = fmt::make_format_args(args...);
-    return detail::is_utf8() ? vprint(fmt, vargs) : detail::vprint_mojibake(stdout, fmt, vargs);
-}
-
-/**
-  \rst
-  Formats ``args`` according to specifications in ``fmt`` and writes the
-  output to the file ``f``.
-
-  **Example**::
-
-    fmt::print(stderr, "Don't {}!", "panic");
-  \endrst
- */
-template <typename... T>
-FMT_INLINE void print(std::FILE *f, format_string<T...> fmt, T &&...args)
-{
-    const auto &vargs = fmt::make_format_args(args...);
-    return detail::is_utf8() ? vprint(f, fmt, vargs) : detail::vprint_mojibake(f, fmt, vargs);
-}
-
-FMT_MODULE_EXPORT_END
-FMT_GCC_PRAGMA("GCC pop_options")
-FMT_END_NAMESPACE
-
-#ifdef FMT_HEADER_ONLY
-#include "format.h"
-#endif
-#endif // FMT_CORE_H_
diff --git a/include/spdlog/fmt/bundled/fmt.license.rst b/include/spdlog/fmt/bundled/fmt.license.rst
deleted file mode 100644
index f0ec3db4d..000000000
--- a/include/spdlog/fmt/bundled/fmt.license.rst
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright (c) 2012 - present, Victor Zverovich
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
---- Optional exception to the license ---
-
-As an exception, if, as a result of your compiling your source code, portions
-of this Software are embedded into a machine-executable object form of such
-source code, you may redistribute such embedded portions in such object form
-without including the above copyright and permission notices.
diff --git a/include/spdlog/fmt/bundled/format-inl.h b/include/spdlog/fmt/bundled/format-inl.h
deleted file mode 100644
index 7bd314a8b..000000000
--- a/include/spdlog/fmt/bundled/format-inl.h
+++ /dev/null
@@ -1,1781 +0,0 @@
-// Formatting library for C++ - implementation
-//
-// Copyright (c) 2012 - 2016, Victor Zverovich
-// All rights reserved.
-//
-// For the license information refer to format.h.
-
-#ifndef FMT_FORMAT_INL_H_
-#define FMT_FORMAT_INL_H_
-
-#include <algorithm>
-#include <cctype>
-#include <cerrno> // errno
-#include <climits>
-#include <cmath>
-#include <cstdarg>
-#include <cstring> // std::memmove
-#include <cwchar>
-#include <exception>
-
-#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
-#include <locale>
-#endif
-
-#ifdef _WIN32
-#include <io.h> // _isatty
-#endif
-
-#include "format.h"
-
-FMT_BEGIN_NAMESPACE
-namespace detail
-{
-
-FMT_FUNC void assert_fail(const char *file, int line, const char *message)
-{
-    // Use unchecked std::fprintf to avoid triggering another assertion when
-    // writing to stderr fails
-    std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
-    // Chosen instead of std::abort to satisfy Clang in CUDA mode during device
-    // code pass.
-    std::terminate();
-}
-
-FMT_FUNC void throw_format_error(const char *message)
-{
-    FMT_THROW(format_error(message));
-}
-
-FMT_FUNC void format_error_code(detail::buffer<char> &out, int error_code, string_view message) noexcept
-{
-    // Report error code making sure that the output fits into
-    // inline_buffer_size to avoid dynamic memory allocation and potential
-    // bad_alloc.
-    out.try_resize(0);
-    static const char SEP[]       = ": ";
-    static const char ERROR_STR[] = "error ";
-    // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
-    size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
-    auto   abs_value       = static_cast<uint32_or_64_or_128_t<int>>(error_code);
-    if(detail::is_negative(error_code))
-    {
-        abs_value = 0 - abs_value;
-        ++error_code_size;
-    }
-    error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
-    auto it = buffer_appender<char>(out);
-    if(message.size() <= inline_buffer_size - error_code_size)
-        format_to(it, FMT_STRING("{}{}"), message, SEP);
-    format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
-    FMT_ASSERT(out.size() <= inline_buffer_size, "");
-}
-
-FMT_FUNC void report_error(format_func func, int error_code, const char *message) noexcept
-{
-    memory_buffer full_message;
-    func(full_message, error_code, message);
-    // Don't use fwrite_fully because the latter may throw.
-    if(std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0)
-        std::fputc('\n', stderr);
-}
-
-// A wrapper around fwrite that throws on error.
-inline void fwrite_fully(const void *ptr, size_t size, size_t count, FILE *stream)
-{
-    size_t written = std::fwrite(ptr, size, count, stream);
-    if(written < count)
-        FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
-}
-
-#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
-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 auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char>
-{
-    auto &facet         = std::use_facet<std::numpunct<Char>>(loc.get<std::locale>());
-    auto  grouping      = facet.grouping();
-    auto  thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
-    return {std::move(grouping), thousands_sep};
-}
-template <typename Char>
-FMT_FUNC Char decimal_point_impl(locale_ref loc)
-{
-    return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()).decimal_point();
-}
-#else
-template <typename Char>
-FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result<Char>
-{
-    return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR};
-}
-template <typename Char>
-FMT_FUNC Char decimal_point_impl(locale_ref)
-{
-    return '.';
-}
-#endif
-} // namespace detail
-
-#if !FMT_MSC_VERSION
-FMT_API FMT_FUNC format_error::~format_error() noexcept = default;
-#endif
-
-FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str, format_args args)
-{
-    auto ec = std::error_code(error_code, std::generic_category());
-    return std::system_error(ec, vformat(format_str, args));
-}
-
-namespace detail
-{
-
-template <typename F>
-inline bool operator==(basic_fp<F> x, basic_fp<F> y)
-{
-    return x.f == y.f && x.e == y.e;
-}
-
-// Compilers should be able to optimize this into the ror instruction.
-FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept
-{
-    r &= 31;
-    return (n >> r) | (n << (32 - r));
-}
-FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept
-{
-    r &= 63;
-    return (n >> r) | (n << (64 - r));
-}
-
-// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
-inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept
-{
-#if FMT_USE_INT128
-    auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
-    return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
-#elif defined(_MSC_VER) && defined(_M_X64)
-    auto result = uint128_fallback();
-    result.lo_  = _umul128(x, y, &result.hi_);
-    return result;
-#else
-    const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>());
-
-    uint64_t a = x >> 32;
-    uint64_t b = x & mask;
-    uint64_t c = y >> 32;
-    uint64_t d = y & mask;
-
-    uint64_t ac = a * c;
-    uint64_t bc = b * c;
-    uint64_t ad = a * d;
-    uint64_t bd = b * d;
-
-    uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask);
-
-    return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), (intermediate << 32) + (bd & mask)};
-#endif
-}
-
-// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox.
-namespace dragonbox
-{
-    // Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
-    inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept
-    {
-#if FMT_USE_INT128
-        auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
-        return static_cast<uint64_t>(p >> 64);
-#elif defined(_MSC_VER) && defined(_M_X64)
-        return __umulh(x, y);
-#else
-        return umul128(x, y).high();
-#endif
-    }
-
-    // Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
-    // 128-bit unsigned integer.
-    inline uint128_fallback umul192_upper128(uint64_t x, uint128_fallback y) noexcept
-    {
-        uint128_fallback r = umul128(x, y.high());
-        r += umul128_upper64(x, y.low());
-        return r;
-    }
-
-    // Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a
-    // 64-bit unsigned integer.
-    inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept
-    {
-        return umul128_upper64(static_cast<uint64_t>(x) << 32, y);
-    }
-
-    // Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a
-    // 128-bit unsigned integer.
-    inline uint128_fallback umul192_lower128(uint64_t x, uint128_fallback y) noexcept
-    {
-        uint64_t         high     = x * y.high();
-        uint128_fallback high_low = umul128(x, y.low());
-        return {high + high_low.high(), high_low.low()};
-    }
-
-    // Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a
-    // 64-bit unsigned integer.
-    inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept
-    {
-        return x * y;
-    }
-
-    // Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from
-    // https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1.
-    inline int floor_log10_pow2(int e) noexcept
-    {
-        FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent");
-        static_assert((-1 >> 1) == -1, "right shift is not arithmetic");
-        return (e * 315653) >> 20;
-    }
-
-    // Various fast log computations.
-    inline int floor_log2_pow10(int e) noexcept
-    {
-        FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
-        return (e * 1741647) >> 19;
-    }
-    inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept
-    {
-        FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent");
-        return (e * 631305 - 261663) >> 21;
-    }
-
-    static constexpr struct
-    {
-        uint32_t divisor;
-        int      shift_amount;
-    } div_small_pow10_infos[] = {{10, 16}, {100, 16}};
-
-    // Replaces n by floor(n / pow(10, N)) returning true if and only if n is
-    // divisible by pow(10, N).
-    // Precondition: n <= pow(10, N + 1).
-    template <int N>
-    bool check_divisibility_and_divide_by_pow10(uint32_t &n) noexcept
-    {
-        // The numbers below are chosen such that:
-        //   1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100,
-        //   2. nm mod 2^k < m if and only if n is divisible by d,
-        // where m is magic_number, k is shift_amount
-        // and d is divisor.
-        //
-        // Item 1 is a common technique of replacing division by a constant with
-        // multiplication, see e.g. "Division by Invariant Integers Using
-        // Multiplication" by Granlund and Montgomery (1994). magic_number (m) is set
-        // to ceil(2^k/d) for large enough k.
-        // The idea for item 2 originates from Schubfach.
-        constexpr auto info = div_small_pow10_infos[N - 1];
-        FMT_ASSERT(n <= info.divisor * 10, "n is too large");
-        constexpr uint32_t magic_number = (1u << info.shift_amount) / info.divisor + 1;
-        n *= magic_number;
-        const uint32_t comparison_mask = (1u << info.shift_amount) - 1;
-        bool           result          = (n & comparison_mask) < magic_number;
-        n >>= info.shift_amount;
-        return result;
-    }
-
-    // Computes floor(n / pow(10, N)) for small n and N.
-    // Precondition: n <= pow(10, N + 1).
-    template <int N>
-    uint32_t small_division_by_pow10(uint32_t n) noexcept
-    {
-        constexpr auto info = div_small_pow10_infos[N - 1];
-        FMT_ASSERT(n <= info.divisor * 10, "n is too large");
-        constexpr uint32_t magic_number = (1u << info.shift_amount) / info.divisor + 1;
-        return (n * magic_number) >> info.shift_amount;
-    }
-
-    // Computes floor(n / 10^(kappa + 1)) (float)
-    inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept
-    {
-        // 1374389535 = ceil(2^37/100)
-        return static_cast<uint32_t>((static_cast<uint64_t>(n) * 1374389535) >> 37);
-    }
-    // Computes floor(n / 10^(kappa + 1)) (double)
-    inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept
-    {
-        // 2361183241434822607 = ceil(2^(64+7)/1000)
-        return umul128_upper64(n, 2361183241434822607ull) >> 7;
-    }
-
-    // Various subroutines using pow10 cache
-    template <class T>
-    struct cache_accessor;
-
-    template <>
-    struct cache_accessor<float>
-    {
-        using carrier_uint     = float_info<float>::carrier_uint;
-        using cache_entry_type = uint64_t;
-
-        static uint64_t get_cached_power(int k) noexcept
-        {
-            FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k, "k is out of range");
-            static constexpr const uint64_t pow10_significands[] = {
-                0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49,
-                0xc612062576589ddb, 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, 0xf1c90080baf72cb2,
-                0x971da05074da7bef, 0xbce5086492111aeb, 0xec1e4a7db69561a6, 0x9392ee8e921d5d08, 0xb877aa3236a4b44a,
-                0xe69594bec44de15c, 0x901d7cf73ab0acda, 0xb424dc35095cd810, 0xe12e13424bb40e14, 0x8cbccc096f5088cc,
-                0xafebff0bcb24aaff, 0xdbe6fecebdedd5bf, 0x89705f4136b4a598, 0xabcc77118461cefd, 0xd6bf94d5e57a42bd,
-                0x8637bd05af6c69b6, 0xa7c5ac471b478424, 0xd1b71758e219652c, 0x83126e978d4fdf3c, 0xa3d70a3d70a3d70b,
-                0xcccccccccccccccd, 0x8000000000000000, 0xa000000000000000, 0xc800000000000000, 0xfa00000000000000,
-                0x9c40000000000000, 0xc350000000000000, 0xf424000000000000, 0x9896800000000000, 0xbebc200000000000,
-                0xee6b280000000000, 0x9502f90000000000, 0xba43b74000000000, 0xe8d4a51000000000, 0x9184e72a00000000,
-                0xb5e620f480000000, 0xe35fa931a0000000, 0x8e1bc9bf04000000, 0xb1a2bc2ec5000000, 0xde0b6b3a76400000,
-                0x8ac7230489e80000, 0xad78ebc5ac620000, 0xd8d726b7177a8000, 0x878678326eac9000, 0xa968163f0a57b400,
-                0xd3c21bcecceda100, 0x84595161401484a0, 0xa56fa5b99019a5c8, 0xcecb8f27f4200f3a, 0x813f3978f8940985,
-                0xa18f07d736b90be6, 0xc9f2c9cd04674edf, 0xfc6f7c4045812297, 0x9dc5ada82b70b59e, 0xc5371912364ce306,
-                0xf684df56c3e01bc7, 0x9a130b963a6c115d, 0xc097ce7bc90715b4, 0xf0bdc21abb48db21, 0x96769950b50d88f5,
-                0xbc143fa4e250eb32, 0xeb194f8e1ae525fe, 0x92efd1b8d0cf37bf, 0xb7abc627050305ae, 0xe596b7b0c643c71a,
-                0x8f7e32ce7bea5c70, 0xb35dbf821ae4f38c, 0xe0352f62a19e306f};
-            return pow10_significands[k - float_info<float>::min_k];
-        }
-
-        struct compute_mul_result
-        {
-            carrier_uint result;
-            bool         is_integer;
-        };
-        struct compute_mul_parity_result
-        {
-            bool parity;
-            bool is_integer;
-        };
-
-        static compute_mul_result compute_mul(carrier_uint u, const cache_entry_type &cache) noexcept
-        {
-            auto r = umul96_upper64(u, cache);
-            return {static_cast<carrier_uint>(r >> 32), static_cast<carrier_uint>(r) == 0};
-        }
-
-        static uint32_t compute_delta(const cache_entry_type &cache, int beta) noexcept
-        {
-            return static_cast<uint32_t>(cache >> (64 - 1 - beta));
-        }
-
-        static compute_mul_parity_result
-        compute_mul_parity(carrier_uint two_f, const cache_entry_type &cache, int beta) noexcept
-        {
-            FMT_ASSERT(beta >= 1, "");
-            FMT_ASSERT(beta < 64, "");
-
-            auto r = umul96_lower64(two_f, cache);
-            return {((r >> (64 - beta)) & 1) != 0, static_cast<uint32_t>(r >> (32 - beta)) == 0};
-        }
-
-        static carrier_uint
-        compute_left_endpoint_for_shorter_interval_case(const cache_entry_type &cache, int beta) noexcept
-        {
-            return static_cast<carrier_uint>(
-                (cache - (cache >> (num_significand_bits<float>() + 2))) >>
-                (64 - num_significand_bits<float>() - 1 - beta));
-        }
-
-        static carrier_uint
-        compute_right_endpoint_for_shorter_interval_case(const cache_entry_type &cache, int beta) noexcept
-        {
-            return static_cast<carrier_uint>(
-                (cache + (cache >> (num_significand_bits<float>() + 1))) >>
-                (64 - num_significand_bits<float>() - 1 - beta));
-        }
-
-        static carrier_uint compute_round_up_for_shorter_interval_case(const cache_entry_type &cache, int beta) noexcept
-        {
-            return (static_cast<carrier_uint>(cache >> (64 - num_significand_bits<float>() - 2 - beta)) + 1) / 2;
-        }
-    };
-
-    template <>
-    struct cache_accessor<double>
-    {
-        using carrier_uint     = float_info<double>::carrier_uint;
-        using cache_entry_type = uint128_fallback;
-
-        static uint128_fallback get_cached_power(int k) noexcept
-        {
-            FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k, "k is out of range");
-
-            static constexpr const uint128_fallback pow10_significands[] = {
-#if FMT_USE_FULL_CACHE_DRAGONBOX
-                {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
-                {0x9faacf3df73609b1, 0x77b191618c54e9ad},
-                {0xc795830d75038c1d, 0xd59df5b9ef6a2418},
-                {0xf97ae3d0d2446f25, 0x4b0573286b44ad1e},
-                {0x9becce62836ac577, 0x4ee367f9430aec33},
-                {0xc2e801fb244576d5, 0x229c41f793cda740},
-                {0xf3a20279ed56d48a, 0x6b43527578c11110},
-                {0x9845418c345644d6, 0x830a13896b78aaaa},
-                {0xbe5691ef416bd60c, 0x23cc986bc656d554},
-                {0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9},
-                {0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa},
-                {0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54},
-                {0xe858ad248f5c22c9, 0xd1b3400f8f9cff69},
-                {0x91376c36d99995be, 0x23100809b9c21fa2},
-                {0xb58547448ffffb2d, 0xabd40a0c2832a78b},
-                {0xe2e69915b3fff9f9, 0x16c90c8f323f516d},
-                {0x8dd01fad907ffc3b, 0xae3da7d97f6792e4},
-                {0xb1442798f49ffb4a, 0x99cd11cfdf41779d},
-                {0xdd95317f31c7fa1d, 0x40405643d711d584},
-                {0x8a7d3eef7f1cfc52, 0x482835ea666b2573},
-                {0xad1c8eab5ee43b66, 0xda3243650005eed0},
-                {0xd863b256369d4a40, 0x90bed43e40076a83},
-                {0x873e4f75e2224e68, 0x5a7744a6e804a292},
-                {0xa90de3535aaae202, 0x711515d0a205cb37},
-                {0xd3515c2831559a83, 0x0d5a5b44ca873e04},
-                {0x8412d9991ed58091, 0xe858790afe9486c3},
-                {0xa5178fff668ae0b6, 0x626e974dbe39a873},
-                {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
-                {0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a},
-                {0xa139029f6a239f72, 0x1c1fffc1ebc44e81},
-                {0xc987434744ac874e, 0xa327ffb266b56221},
-                {0xfbe9141915d7a922, 0x4bf1ff9f0062baa9},
-                {0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa},
-                {0xc4ce17b399107c22, 0xcb550fb4384d21d4},
-                {0xf6019da07f549b2b, 0x7e2a53a146606a49},
-                {0x99c102844f94e0fb, 0x2eda7444cbfc426e},
-                {0xc0314325637a1939, 0xfa911155fefb5309},
-                {0xf03d93eebc589f88, 0x793555ab7eba27cb},
-                {0x96267c7535b763b5, 0x4bc1558b2f3458df},
-                {0xbbb01b9283253ca2, 0x9eb1aaedfb016f17},
-                {0xea9c227723ee8bcb, 0x465e15a979c1cadd},
-                {0x92a1958a7675175f, 0x0bfacd89ec191eca},
-                {0xb749faed14125d36, 0xcef980ec671f667c},
-                {0xe51c79a85916f484, 0x82b7e12780e7401b},
-                {0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811},
-                {0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16},
-                {0xdfbdcece67006ac9, 0x67a791e093e1d49b},
-                {0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1},
-                {0xaecc49914078536d, 0x58fae9f773886e19},
-                {0xda7f5bf590966848, 0xaf39a475506a899f},
-                {0x888f99797a5e012d, 0x6d8406c952429604},
-                {0xaab37fd7d8f58178, 0xc8e5087ba6d33b84},
-                {0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65},
-                {0x855c3be0a17fcd26, 0x5cf2eea09a550680},
-                {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f},
-                {0xd0601d8efc57b08b, 0xf13b94daf124da27},
-                {0x823c12795db6ce57, 0x76c53d08d6b70859},
-                {0xa2cb1717b52481ed, 0x54768c4b0c64ca6f},
-                {0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a},
-                {0xfe5d54150b090b02, 0xd3f93b35435d7c4d},
-                {0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0},
-                {0xc6b8e9b0709f109a, 0x359ab6419ca1091c},
-                {0xf867241c8cc6d4c0, 0xc30163d203c94b63},
-                {0x9b407691d7fc44f8, 0x79e0de63425dcf1e},
-                {0xc21094364dfb5636, 0x985915fc12f542e5},
-                {0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e},
-                {0x979cf3ca6cec5b5a, 0xa705992ceecf9c43},
-                {0xbd8430bd08277231, 0x50c6ff782a838354},
-                {0xece53cec4a314ebd, 0xa4f8bf5635246429},
-                {0x940f4613ae5ed136, 0x871b7795e136be9a},
-                {0xb913179899f68584, 0x28e2557b59846e40},
-                {0xe757dd7ec07426e5, 0x331aeada2fe589d0},
-                {0x9096ea6f3848984f, 0x3ff0d2c85def7622},
-                {0xb4bca50b065abe63, 0x0fed077a756b53aa},
-                {0xe1ebce4dc7f16dfb, 0xd3e8495912c62895},
-                {0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d},
-                {0xb080392cc4349dec, 0xbd8d794d96aacfb4},
-                {0xdca04777f541c567, 0xecf0d7a0fc5583a1},
-                {0x89e42caaf9491b60, 0xf41686c49db57245},
-                {0xac5d37d5b79b6239, 0x311c2875c522ced6},
-                {0xd77485cb25823ac7, 0x7d633293366b828c},
-                {0x86a8d39ef77164bc, 0xae5dff9c02033198},
-                {0xa8530886b54dbdeb, 0xd9f57f830283fdfd},
-                {0xd267caa862a12d66, 0xd072df63c324fd7c},
-                {0x8380dea93da4bc60, 0x4247cb9e59f71e6e},
-                {0xa46116538d0deb78, 0x52d9be85f074e609},
-                {0xcd795be870516656, 0x67902e276c921f8c},
-                {0x806bd9714632dff6, 0x00ba1cd8a3db53b7},
-                {0xa086cfcd97bf97f3, 0x80e8a40eccd228a5},
-                {0xc8a883c0fdaf7df0, 0x6122cd128006b2ce},
-                {0xfad2a4b13d1b5d6c, 0x796b805720085f82},
-                {0x9cc3a6eec6311a63, 0xcbe3303674053bb1},
-                {0xc3f490aa77bd60fc, 0xbedbfc4411068a9d},
-                {0xf4f1b4d515acb93b, 0xee92fb5515482d45},
-                {0x991711052d8bf3c5, 0x751bdd152d4d1c4b},
-                {0xbf5cd54678eef0b6, 0xd262d45a78a0635e},
-                {0xef340a98172aace4, 0x86fb897116c87c35},
-                {0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1},
-                {0xbae0a846d2195712, 0x8974836059cca10a},
-                {0xe998d258869facd7, 0x2bd1a438703fc94c},
-                {0x91ff83775423cc06, 0x7b6306a34627ddd0},
-                {0xb67f6455292cbf08, 0x1a3bc84c17b1d543},
-                {0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94},
-                {0x8e938662882af53e, 0x547eb47b7282ee9d},
-                {0xb23867fb2a35b28d, 0xe99e619a4f23aa44},
-                {0xdec681f9f4c31f31, 0x6405fa00e2ec94d5},
-                {0x8b3c113c38f9f37e, 0xde83bc408dd3dd05},
-                {0xae0b158b4738705e, 0x9624ab50b148d446},
-                {0xd98ddaee19068c76, 0x3badd624dd9b0958},
-                {0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7},
-                {0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d},
-                {0xd47487cc8470652b, 0x7647c32000696720},
-                {0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074},
-                {0xa5fb0a17c777cf09, 0xf468107100525891},
-                {0xcf79cc9db955c2cc, 0x7182148d4066eeb5},
-                {0x81ac1fe293d599bf, 0xc6f14cd848405531},
-                {0xa21727db38cb002f, 0xb8ada00e5a506a7d},
-                {0xca9cf1d206fdc03b, 0xa6d90811f0e4851d},
-                {0xfd442e4688bd304a, 0x908f4a166d1da664},
-                {0x9e4a9cec15763e2e, 0x9a598e4e043287ff},
-                {0xc5dd44271ad3cdba, 0x40eff1e1853f29fe},
-                {0xf7549530e188c128, 0xd12bee59e68ef47d},
-                {0x9a94dd3e8cf578b9, 0x82bb74f8301958cf},
-                {0xc13a148e3032d6e7, 0xe36a52363c1faf02},
-                {0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2},
-                {0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba},
-                {0xbcb2b812db11a5de, 0x7415d448f6b6f0e8},
-                {0xebdf661791d60f56, 0x111b495b3464ad22},
-                {0x936b9fcebb25c995, 0xcab10dd900beec35},
-                {0xb84687c269ef3bfb, 0x3d5d514f40eea743},
-                {0xe65829b3046b0afa, 0x0cb4a5a3112a5113},
-                {0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac},
-                {0xb3f4e093db73a093, 0x59ed216765690f57},
-                {0xe0f218b8d25088b8, 0x306869c13ec3532d},
-                {0x8c974f7383725573, 0x1e414218c73a13fc},
-                {0xafbd2350644eeacf, 0xe5d1929ef90898fb},
-                {0xdbac6c247d62a583, 0xdf45f746b74abf3a},
-                {0x894bc396ce5da772, 0x6b8bba8c328eb784},
-                {0xab9eb47c81f5114f, 0x066ea92f3f326565},
-                {0xd686619ba27255a2, 0xc80a537b0efefebe},
-                {0x8613fd0145877585, 0xbd06742ce95f5f37},
-                {0xa798fc4196e952e7, 0x2c48113823b73705},
-                {0xd17f3b51fca3a7a0, 0xf75a15862ca504c6},
-                {0x82ef85133de648c4, 0x9a984d73dbe722fc},
-                {0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb},
-                {0xcc963fee10b7d1b3, 0x318df905079926a9},
-                {0xffbbcfe994e5c61f, 0xfdf17746497f7053},
-                {0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634},
-                {0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1},
-                {0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1},
-                {0x9c1661a651213e2d, 0x06bea10ca65c084f},
-                {0xc31bfa0fe5698db8, 0x486e494fcff30a63},
-                {0xf3e2f893dec3f126, 0x5a89dba3c3efccfb},
-                {0x986ddb5c6b3a76b7, 0xf89629465a75e01d},
-                {0xbe89523386091465, 0xf6bbb397f1135824},
-                {0xee2ba6c0678b597f, 0x746aa07ded582e2d},
-                {0x94db483840b717ef, 0xa8c2a44eb4571cdd},
-                {0xba121a4650e4ddeb, 0x92f34d62616ce414},
-                {0xe896a0d7e51e1566, 0x77b020baf9c81d18},
-                {0x915e2486ef32cd60, 0x0ace1474dc1d122f},
-                {0xb5b5ada8aaff80b8, 0x0d819992132456bb},
-                {0xe3231912d5bf60e6, 0x10e1fff697ed6c6a},
-                {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2},
-                {0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3},
-                {0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf},
-                {0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c},
-                {0xad4ab7112eb3929d, 0x86c16c98d2c953c7},
-                {0xd89d64d57a607744, 0xe871c7bf077ba8b8},
-                {0x87625f056c7c4a8b, 0x11471cd764ad4973},
-                {0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0},
-                {0xd389b47879823479, 0x4aff1d108d4ec2c4},
-                {0x843610cb4bf160cb, 0xcedf722a585139bb},
-                {0xa54394fe1eedb8fe, 0xc2974eb4ee658829},
-                {0xce947a3da6a9273e, 0x733d226229feea33},
-                {0x811ccc668829b887, 0x0806357d5a3f5260},
-                {0xa163ff802a3426a8, 0xca07c2dcb0cf26f8},
-                {0xc9bcff6034c13052, 0xfc89b393dd02f0b6},
-                {0xfc2c3f3841f17c67, 0xbbac2078d443ace3},
-                {0x9d9ba7832936edc0, 0xd54b944b84aa4c0e},
-                {0xc5029163f384a931, 0x0a9e795e65d4df12},
-                {0xf64335bcf065d37d, 0x4d4617b5ff4a16d6},
-                {0x99ea0196163fa42e, 0x504bced1bf8e4e46},
-                {0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7},
-                {0xf07da27a82c37088, 0x5d767327bb4e5a4d},
-                {0x964e858c91ba2655, 0x3a6a07f8d510f870},
-                {0xbbe226efb628afea, 0x890489f70a55368c},
-                {0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f},
-                {0x92c8ae6b464fc96f, 0x3b0b8bc90012929e},
-                {0xb77ada0617e3bbcb, 0x09ce6ebb40173745},
-                {0xe55990879ddcaabd, 0xcc420a6a101d0516},
-                {0x8f57fa54c2a9eab6, 0x9fa946824a12232e},
-                {0xb32df8e9f3546564, 0x47939822dc96abfa},
-                {0xdff9772470297ebd, 0x59787e2b93bc56f8},
-                {0x8bfbea76c619ef36, 0x57eb4edb3c55b65b},
-                {0xaefae51477a06b03, 0xede622920b6b23f2},
-                {0xdab99e59958885c4, 0xe95fab368e45ecee},
-                {0x88b402f7fd75539b, 0x11dbcb0218ebb415},
-                {0xaae103b5fcd2a881, 0xd652bdc29f26a11a},
-                {0xd59944a37c0752a2, 0x4be76d3346f04960},
-                {0x857fcae62d8493a5, 0x6f70a4400c562ddc},
-                {0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953},
-                {0xd097ad07a71f26b2, 0x7e2000a41346a7a8},
-                {0x825ecc24c873782f, 0x8ed400668c0c28c9},
-                {0xa2f67f2dfa90563b, 0x728900802f0f32fb},
-                {0xcbb41ef979346bca, 0x4f2b40a03ad2ffba},
-                {0xfea126b7d78186bc, 0xe2f610c84987bfa9},
-                {0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca},
-                {0xc6ede63fa05d3143, 0x91503d1c79720dbc},
-                {0xf8a95fcf88747d94, 0x75a44c6397ce912b},
-                {0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb},
-                {0xc24452da229b021b, 0xfbe85badce996169},
-                {0xf2d56790ab41c2a2, 0xfae27299423fb9c4},
-                {0x97c560ba6b0919a5, 0xdccd879fc967d41b},
-                {0xbdb6b8e905cb600f, 0x5400e987bbc1c921},
-                {0xed246723473e3813, 0x290123e9aab23b69},
-                {0x9436c0760c86e30b, 0xf9a0b6720aaf6522},
-                {0xb94470938fa89bce, 0xf808e40e8d5b3e6a},
-                {0xe7958cb87392c2c2, 0xb60b1d1230b20e05},
-                {0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3},
-                {0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4},
-                {0xe2280b6c20dd5232, 0x25c6da63c38de1b1},
-                {0x8d590723948a535f, 0x579c487e5a38ad0f},
-                {0xb0af48ec79ace837, 0x2d835a9df0c6d852},
-                {0xdcdb1b2798182244, 0xf8e431456cf88e66},
-                {0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900},
-                {0xac8b2d36eed2dac5, 0xe272467e3d222f40},
-                {0xd7adf884aa879177, 0x5b0ed81dcc6abb10},
-                {0x86ccbb52ea94baea, 0x98e947129fc2b4ea},
-                {0xa87fea27a539e9a5, 0x3f2398d747b36225},
-                {0xd29fe4b18e88640e, 0x8eec7f0d19a03aae},
-                {0x83a3eeeef9153e89, 0x1953cf68300424ad},
-                {0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8},
-                {0xcdb02555653131b6, 0x3792f412cb06794e},
-                {0x808e17555f3ebf11, 0xe2bbd88bbee40bd1},
-                {0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5},
-                {0xc8de047564d20a8b, 0xf245825a5a445276},
-                {0xfb158592be068d2e, 0xeed6e2f0f0d56713},
-                {0x9ced737bb6c4183d, 0x55464dd69685606c},
-                {0xc428d05aa4751e4c, 0xaa97e14c3c26b887},
-                {0xf53304714d9265df, 0xd53dd99f4b3066a9},
-                {0x993fe2c6d07b7fab, 0xe546a8038efe402a},
-                {0xbf8fdb78849a5f96, 0xde98520472bdd034},
-                {0xef73d256a5c0f77c, 0x963e66858f6d4441},
-                {0x95a8637627989aad, 0xdde7001379a44aa9},
-                {0xbb127c53b17ec159, 0x5560c018580d5d53},
-                {0xe9d71b689dde71af, 0xaab8f01e6e10b4a7},
-                {0x9226712162ab070d, 0xcab3961304ca70e9},
-                {0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23},
-                {0xe45c10c42a2b3b05, 0x8cb89a7db77c506b},
-                {0x8eb98a7a9a5b04e3, 0x77f3608e92adb243},
-                {0xb267ed1940f1c61c, 0x55f038b237591ed4},
-                {0xdf01e85f912e37a3, 0x6b6c46dec52f6689},
-                {0x8b61313bbabce2c6, 0x2323ac4b3b3da016},
-                {0xae397d8aa96c1b77, 0xabec975e0a0d081b},
-                {0xd9c7dced53c72255, 0x96e7bd358c904a22},
-                {0x881cea14545c7575, 0x7e50d64177da2e55},
-                {0xaa242499697392d2, 0xdde50bd1d5d0b9ea},
-                {0xd4ad2dbfc3d07787, 0x955e4ec64b44e865},
-                {0x84ec3c97da624ab4, 0xbd5af13bef0b113f},
-                {0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f},
-                {0xcfb11ead453994ba, 0x67de18eda5814af3},
-                {0x81ceb32c4b43fcf4, 0x80eacf948770ced8},
-                {0xa2425ff75e14fc31, 0xa1258379a94d028e},
-                {0xcad2f7f5359a3b3e, 0x096ee45813a04331},
-                {0xfd87b5f28300ca0d, 0x8bca9d6e188853fd},
-                {0x9e74d1b791e07e48, 0x775ea264cf55347e},
-                {0xc612062576589dda, 0x95364afe032a819e},
-                {0xf79687aed3eec551, 0x3a83ddbd83f52205},
-                {0x9abe14cd44753b52, 0xc4926a9672793543},
-                {0xc16d9a0095928a27, 0x75b7053c0f178294},
-                {0xf1c90080baf72cb1, 0x5324c68b12dd6339},
-                {0x971da05074da7bee, 0xd3f6fc16ebca5e04},
-                {0xbce5086492111aea, 0x88f4bb1ca6bcf585},
-                {0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6},
-                {0x9392ee8e921d5d07, 0x3aff322e62439fd0},
-                {0xb877aa3236a4b449, 0x09befeb9fad487c3},
-                {0xe69594bec44de15b, 0x4c2ebe687989a9b4},
-                {0x901d7cf73ab0acd9, 0x0f9d37014bf60a11},
-                {0xb424dc35095cd80f, 0x538484c19ef38c95},
-                {0xe12e13424bb40e13, 0x2865a5f206b06fba},
-                {0x8cbccc096f5088cb, 0xf93f87b7442e45d4},
-                {0xafebff0bcb24aafe, 0xf78f69a51539d749},
-                {0xdbe6fecebdedd5be, 0xb573440e5a884d1c},
-                {0x89705f4136b4a597, 0x31680a88f8953031},
-                {0xabcc77118461cefc, 0xfdc20d2b36ba7c3e},
-                {0xd6bf94d5e57a42bc, 0x3d32907604691b4d},
-                {0x8637bd05af6c69b5, 0xa63f9a49c2c1b110},
-                {0xa7c5ac471b478423, 0x0fcf80dc33721d54},
-                {0xd1b71758e219652b, 0xd3c36113404ea4a9},
-                {0x83126e978d4fdf3b, 0x645a1cac083126ea},
-                {0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4},
-                {0xcccccccccccccccc, 0xcccccccccccccccd},
-                {0x8000000000000000, 0x0000000000000000},
-                {0xa000000000000000, 0x0000000000000000},
-                {0xc800000000000000, 0x0000000000000000},
-                {0xfa00000000000000, 0x0000000000000000},
-                {0x9c40000000000000, 0x0000000000000000},
-                {0xc350000000000000, 0x0000000000000000},
-                {0xf424000000000000, 0x0000000000000000},
-                {0x9896800000000000, 0x0000000000000000},
-                {0xbebc200000000000, 0x0000000000000000},
-                {0xee6b280000000000, 0x0000000000000000},
-                {0x9502f90000000000, 0x0000000000000000},
-                {0xba43b74000000000, 0x0000000000000000},
-                {0xe8d4a51000000000, 0x0000000000000000},
-                {0x9184e72a00000000, 0x0000000000000000},
-                {0xb5e620f480000000, 0x0000000000000000},
-                {0xe35fa931a0000000, 0x0000000000000000},
-                {0x8e1bc9bf04000000, 0x0000000000000000},
-                {0xb1a2bc2ec5000000, 0x0000000000000000},
-                {0xde0b6b3a76400000, 0x0000000000000000},
-                {0x8ac7230489e80000, 0x0000000000000000},
-                {0xad78ebc5ac620000, 0x0000000000000000},
-                {0xd8d726b7177a8000, 0x0000000000000000},
-                {0x878678326eac9000, 0x0000000000000000},
-                {0xa968163f0a57b400, 0x0000000000000000},
-                {0xd3c21bcecceda100, 0x0000000000000000},
-                {0x84595161401484a0, 0x0000000000000000},
-                {0xa56fa5b99019a5c8, 0x0000000000000000},
-                {0xcecb8f27f4200f3a, 0x0000000000000000},
-                {0x813f3978f8940984, 0x4000000000000000},
-                {0xa18f07d736b90be5, 0x5000000000000000},
-                {0xc9f2c9cd04674ede, 0xa400000000000000},
-                {0xfc6f7c4045812296, 0x4d00000000000000},
-                {0x9dc5ada82b70b59d, 0xf020000000000000},
-                {0xc5371912364ce305, 0x6c28000000000000},
-                {0xf684df56c3e01bc6, 0xc732000000000000},
-                {0x9a130b963a6c115c, 0x3c7f400000000000},
-                {0xc097ce7bc90715b3, 0x4b9f100000000000},
-                {0xf0bdc21abb48db20, 0x1e86d40000000000},
-                {0x96769950b50d88f4, 0x1314448000000000},
-                {0xbc143fa4e250eb31, 0x17d955a000000000},
-                {0xeb194f8e1ae525fd, 0x5dcfab0800000000},
-                {0x92efd1b8d0cf37be, 0x5aa1cae500000000},
-                {0xb7abc627050305ad, 0xf14a3d9e40000000},
-                {0xe596b7b0c643c719, 0x6d9ccd05d0000000},
-                {0x8f7e32ce7bea5c6f, 0xe4820023a2000000},
-                {0xb35dbf821ae4f38b, 0xdda2802c8a800000},
-                {0xe0352f62a19e306e, 0xd50b2037ad200000},
-                {0x8c213d9da502de45, 0x4526f422cc340000},
-                {0xaf298d050e4395d6, 0x9670b12b7f410000},
-                {0xdaf3f04651d47b4c, 0x3c0cdd765f114000},
-                {0x88d8762bf324cd0f, 0xa5880a69fb6ac800},
-                {0xab0e93b6efee0053, 0x8eea0d047a457a00},
-                {0xd5d238a4abe98068, 0x72a4904598d6d880},
-                {0x85a36366eb71f041, 0x47a6da2b7f864750},
-                {0xa70c3c40a64e6c51, 0x999090b65f67d924},
-                {0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d},
-                {0x82818f1281ed449f, 0xbff8f10e7a8921a5},
-                {0xa321f2d7226895c7, 0xaff72d52192b6a0e},
-                {0xcbea6f8ceb02bb39, 0x9bf4f8a69f764491},
-                {0xfee50b7025c36a08, 0x02f236d04753d5b5},
-                {0x9f4f2726179a2245, 0x01d762422c946591},
-                {0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef6},
-                {0xf8ebad2b84e0d58b, 0xd2e0898765a7deb3},
-                {0x9b934c3b330c8577, 0x63cc55f49f88eb30},
-                {0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fc},
-                {0xf316271c7fc3908a, 0x8bef464e3945ef7b},
-                {0x97edd871cfda3a56, 0x97758bf0e3cbb5ad},
-                {0xbde94e8e43d0c8ec, 0x3d52eeed1cbea318},
-                {0xed63a231d4c4fb27, 0x4ca7aaa863ee4bde},
-                {0x945e455f24fb1cf8, 0x8fe8caa93e74ef6b},
-                {0xb975d6b6ee39e436, 0xb3e2fd538e122b45},
-                {0xe7d34c64a9c85d44, 0x60dbbca87196b617},
-                {0x90e40fbeea1d3a4a, 0xbc8955e946fe31ce},
-                {0xb51d13aea4a488dd, 0x6babab6398bdbe42},
-                {0xe264589a4dcdab14, 0xc696963c7eed2dd2},
-                {0x8d7eb76070a08aec, 0xfc1e1de5cf543ca3},
-                {0xb0de65388cc8ada8, 0x3b25a55f43294bcc},
-                {0xdd15fe86affad912, 0x49ef0eb713f39ebf},
-                {0x8a2dbf142dfcc7ab, 0x6e3569326c784338},
-                {0xacb92ed9397bf996, 0x49c2c37f07965405},
-                {0xd7e77a8f87daf7fb, 0xdc33745ec97be907},
-                {0x86f0ac99b4e8dafd, 0x69a028bb3ded71a4},
-                {0xa8acd7c0222311bc, 0xc40832ea0d68ce0d},
-                {0xd2d80db02aabd62b, 0xf50a3fa490c30191},
-                {0x83c7088e1aab65db, 0x792667c6da79e0fb},
-                {0xa4b8cab1a1563f52, 0x577001b891185939},
-                {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87},
-                {0x80b05e5ac60b6178, 0x544f8158315b05b5},
-                {0xa0dc75f1778e39d6, 0x696361ae3db1c722},
-                {0xc913936dd571c84c, 0x03bc3a19cd1e38ea},
-                {0xfb5878494ace3a5f, 0x04ab48a04065c724},
-                {0x9d174b2dcec0e47b, 0x62eb0d64283f9c77},
-                {0xc45d1df942711d9a, 0x3ba5d0bd324f8395},
-                {0xf5746577930d6500, 0xca8f44ec7ee3647a},
-                {0x9968bf6abbe85f20, 0x7e998b13cf4e1ecc},
-                {0xbfc2ef456ae276e8, 0x9e3fedd8c321a67f},
-                {0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101f},
-                {0x95d04aee3b80ece5, 0xbba1f1d158724a13},
-                {0xbb445da9ca61281f, 0x2a8a6e45ae8edc98},
-                {0xea1575143cf97226, 0xf52d09d71a3293be},
-                {0x924d692ca61be758, 0x593c2626705f9c57},
-                {0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836d},
-                {0xe498f455c38b997a, 0x0b6dfb9c0f956448},
-                {0x8edf98b59a373fec, 0x4724bd4189bd5ead},
-                {0xb2977ee300c50fe7, 0x58edec91ec2cb658},
-                {0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ee},
-                {0x8b865b215899f46c, 0xbd79e0d20082ee75},
-                {0xae67f1e9aec07187, 0xecd8590680a3aa12},
-                {0xda01ee641a708de9, 0xe80e6f4820cc9496},
-                {0x884134fe908658b2, 0x3109058d147fdcde},
-                {0xaa51823e34a7eede, 0xbd4b46f0599fd416},
-                {0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91b},
-                {0x850fadc09923329e, 0x03e2cf6bc604ddb1},
-                {0xa6539930bf6bff45, 0x84db8346b786151d},
-                {0xcfe87f7cef46ff16, 0xe612641865679a64},
-                {0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07f},
-                {0xa26da3999aef7749, 0xe3be5e330f38f09e},
-                {0xcb090c8001ab551c, 0x5cadf5bfd3072cc6},
-                {0xfdcb4fa002162a63, 0x73d9732fc7c8f7f7},
-                {0x9e9f11c4014dda7e, 0x2867e7fddcdd9afb},
-                {0xc646d63501a1511d, 0xb281e1fd541501b9},
-                {0xf7d88bc24209a565, 0x1f225a7ca91a4227},
-                {0x9ae757596946075f, 0x3375788de9b06959},
-                {0xc1a12d2fc3978937, 0x0052d6b1641c83af},
-                {0xf209787bb47d6b84, 0xc0678c5dbd23a49b},
-                {0x9745eb4d50ce6332, 0xf840b7ba963646e1},
-                {0xbd176620a501fbff, 0xb650e5a93bc3d899},
-                {0xec5d3fa8ce427aff, 0xa3e51f138ab4cebf},
-                {0x93ba47c980e98cdf, 0xc66f336c36b10138},
-                {0xb8a8d9bbe123f017, 0xb80b0047445d4185},
-                {0xe6d3102ad96cec1d, 0xa60dc059157491e6},
-                {0x9043ea1ac7e41392, 0x87c89837ad68db30},
-                {0xb454e4a179dd1877, 0x29babe4598c311fc},
-                {0xe16a1dc9d8545e94, 0xf4296dd6fef3d67b},
-                {0x8ce2529e2734bb1d, 0x1899e4a65f58660d},
-                {0xb01ae745b101e9e4, 0x5ec05dcff72e7f90},
-                {0xdc21a1171d42645d, 0x76707543f4fa1f74},
-                {0x899504ae72497eba, 0x6a06494a791c53a9},
-                {0xabfa45da0edbde69, 0x0487db9d17636893},
-                {0xd6f8d7509292d603, 0x45a9d2845d3c42b7},
-                {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3},
-                {0xa7f26836f282b732, 0x8e6cac7768d7141f},
-                {0xd1ef0244af2364ff, 0x3207d795430cd927},
-                {0x8335616aed761f1f, 0x7f44e6bd49e807b9},
-                {0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a7},
-                {0xcd036837130890a1, 0x36dba887c37a8c10},
-                {0x802221226be55a64, 0xc2494954da2c978a},
-                {0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6d},
-                {0xc83553c5c8965d3d, 0x6f92829494e5acc8},
-                {0xfa42a8b73abbf48c, 0xcb772339ba1f17fa},
-                {0x9c69a97284b578d7, 0xff2a760414536efc},
-                {0xc38413cf25e2d70d, 0xfef5138519684abb},
-                {0xf46518c2ef5b8cd1, 0x7eb258665fc25d6a},
-                {0x98bf2f79d5993802, 0xef2f773ffbd97a62},
-                {0xbeeefb584aff8603, 0xaafb550ffacfd8fb},
-                {0xeeaaba2e5dbf6784, 0x95ba2a53f983cf39},
-                {0x952ab45cfa97a0b2, 0xdd945a747bf26184},
-                {0xba756174393d88df, 0x94f971119aeef9e5},
-                {0xe912b9d1478ceb17, 0x7a37cd5601aab85e},
-                {0x91abb422ccb812ee, 0xac62e055c10ab33b},
-                {0xb616a12b7fe617aa, 0x577b986b314d600a},
-                {0xe39c49765fdf9d94, 0xed5a7e85fda0b80c},
-                {0x8e41ade9fbebc27d, 0x14588f13be847308},
-                {0xb1d219647ae6b31c, 0x596eb2d8ae258fc9},
-                {0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bc},
-                {0x8aec23d680043bee, 0x25de7bb9480d5855},
-                {0xada72ccc20054ae9, 0xaf561aa79a10ae6b},
-                {0xd910f7ff28069da4, 0x1b2ba1518094da05},
-                {0x87aa9aff79042286, 0x90fb44d2f05d0843},
-                {0xa99541bf57452b28, 0x353a1607ac744a54},
-                {0xd3fa922f2d1675f2, 0x42889b8997915ce9},
-                {0x847c9b5d7c2e09b7, 0x69956135febada12},
-                {0xa59bc234db398c25, 0x43fab9837e699096},
-                {0xcf02b2c21207ef2e, 0x94f967e45e03f4bc},
-                {0x8161afb94b44f57d, 0x1d1be0eebac278f6},
-                {0xa1ba1ba79e1632dc, 0x6462d92a69731733},
-                {0xca28a291859bbf93, 0x7d7b8f7503cfdcff},
-                {0xfcb2cb35e702af78, 0x5cda735244c3d43f},
-                {0x9defbf01b061adab, 0x3a0888136afa64a8},
-                {0xc56baec21c7a1916, 0x088aaa1845b8fdd1},
-                {0xf6c69a72a3989f5b, 0x8aad549e57273d46},
-                {0x9a3c2087a63f6399, 0x36ac54e2f678864c},
-                {0xc0cb28a98fcf3c7f, 0x84576a1bb416a7de},
-                {0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d6},
-                {0x969eb7c47859e743, 0x9f644ae5a4b1b326},
-                {0xbc4665b596706114, 0x873d5d9f0dde1fef},
-                {0xeb57ff22fc0c7959, 0xa90cb506d155a7eb},
-                {0x9316ff75dd87cbd8, 0x09a7f12442d588f3},
-                {0xb7dcbf5354e9bece, 0x0c11ed6d538aeb30},
-                {0xe5d3ef282a242e81, 0x8f1668c8a86da5fb},
-                {0x8fa475791a569d10, 0xf96e017d694487bd},
-                {0xb38d92d760ec4455, 0x37c981dcc395a9ad},
-                {0xe070f78d3927556a, 0x85bbe253f47b1418},
-                {0x8c469ab843b89562, 0x93956d7478ccec8f},
-                {0xaf58416654a6babb, 0x387ac8d1970027b3},
-                {0xdb2e51bfe9d0696a, 0x06997b05fcc0319f},
-                {0x88fcf317f22241e2, 0x441fece3bdf81f04},
-                {0xab3c2fddeeaad25a, 0xd527e81cad7626c4},
-                {0xd60b3bd56a5586f1, 0x8a71e223d8d3b075},
-                {0x85c7056562757456, 0xf6872d5667844e4a},
-                {0xa738c6bebb12d16c, 0xb428f8ac016561dc},
-                {0xd106f86e69d785c7, 0xe13336d701beba53},
-                {0x82a45b450226b39c, 0xecc0024661173474},
-                {0xa34d721642b06084, 0x27f002d7f95d0191},
-                {0xcc20ce9bd35c78a5, 0x31ec038df7b441f5},
-                {0xff290242c83396ce, 0x7e67047175a15272},
-                {0x9f79a169bd203e41, 0x0f0062c6e984d387},
-                {0xc75809c42c684dd1, 0x52c07b78a3e60869},
-                {0xf92e0c3537826145, 0xa7709a56ccdf8a83},
-                {0x9bbcc7a142b17ccb, 0x88a66076400bb692},
-                {0xc2abf989935ddbfe, 0x6acff893d00ea436},
-                {0xf356f7ebf83552fe, 0x0583f6b8c4124d44},
-                {0x98165af37b2153de, 0xc3727a337a8b704b},
-                {0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5d},
-                {0xeda2ee1c7064130c, 0x1162def06f79df74},
-                {0x9485d4d1c63e8be7, 0x8addcb5645ac2ba9},
-                {0xb9a74a0637ce2ee1, 0x6d953e2bd7173693},
-                {0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0438},
-                {0x910ab1d4db9914a0, 0x1d9c9892400a22a3},
-                {0xb54d5e4a127f59c8, 0x2503beb6d00cab4c},
-                {0xe2a0b5dc971f303a, 0x2e44ae64840fd61e},
-                {0x8da471a9de737e24, 0x5ceaecfed289e5d3},
-                {0xb10d8e1456105dad, 0x7425a83e872c5f48},
-                {0xdd50f1996b947518, 0xd12f124e28f7771a},
-                {0x8a5296ffe33cc92f, 0x82bd6b70d99aaa70},
-                {0xace73cbfdc0bfb7b, 0x636cc64d1001550c},
-                {0xd8210befd30efa5a, 0x3c47f7e05401aa4f},
-                {0x8714a775e3e95c78, 0x65acfaec34810a72},
-                {0xa8d9d1535ce3b396, 0x7f1839a741a14d0e},
-                {0xd31045a8341ca07c, 0x1ede48111209a051},
-                {0x83ea2b892091e44d, 0x934aed0aab460433},
-                {0xa4e4b66b68b65d60, 0xf81da84d56178540},
-                {0xce1de40642e3f4b9, 0x36251260ab9d668f},
-                {0x80d2ae83e9ce78f3, 0xc1d72b7c6b42601a},
-                {0xa1075a24e4421730, 0xb24cf65b8612f820},
-                {0xc94930ae1d529cfc, 0xdee033f26797b628},
-                {0xfb9b7cd9a4a7443c, 0x169840ef017da3b2},
-                {0x9d412e0806e88aa5, 0x8e1f289560ee864f},
-                {0xc491798a08a2ad4e, 0xf1a6f2bab92a27e3},
-                {0xf5b5d7ec8acb58a2, 0xae10af696774b1dc},
-                {0x9991a6f3d6bf1765, 0xacca6da1e0a8ef2a},
-                {0xbff610b0cc6edd3f, 0x17fd090a58d32af4},
-                {0xeff394dcff8a948e, 0xddfc4b4cef07f5b1},
-                {0x95f83d0a1fb69cd9, 0x4abdaf101564f98f},
-                {0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f2},
-                {0xea53df5fd18d5513, 0x84c86189216dc5ee},
-                {0x92746b9be2f8552c, 0x32fd3cf5b4e49bb5},
-                {0xb7118682dbb66a77, 0x3fbc8c33221dc2a2},
-                {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
-                {0x8f05b1163ba6832d, 0x29cb4d87f2a7400f},
-                {0xb2c71d5bca9023f8, 0x743e20e9ef511013},
-                {0xdf78e4b2bd342cf6, 0x914da9246b255417},
-                {0x8bab8eefb6409c1a, 0x1ad089b6c2f7548f},
-                {0xae9672aba3d0c320, 0xa184ac2473b529b2},
-                {0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741f},
-                {0x8865899617fb1871, 0x7e2fa67c7a658893},
-                {0xaa7eebfb9df9de8d, 0xddbb901b98feeab8},
-                {0xd51ea6fa85785631, 0x552a74227f3ea566},
-                {0x8533285c936b35de, 0xd53a88958f872760},
-                {0xa67ff273b8460356, 0x8a892abaf368f138},
-                {0xd01fef10a657842c, 0x2d2b7569b0432d86},
-                {0x8213f56a67f6b29b, 0x9c3b29620e29fc74},
-                {0xa298f2c501f45f42, 0x8349f3ba91b47b90},
-                {0xcb3f2f7642717713, 0x241c70a936219a74},
-                {0xfe0efb53d30dd4d7, 0xed238cd383aa0111},
-                {0x9ec95d1463e8a506, 0xf4363804324a40ab},
-                {0xc67bb4597ce2ce48, 0xb143c6053edcd0d6},
-                {0xf81aa16fdc1b81da, 0xdd94b7868e94050b},
-                {0x9b10a4e5e9913128, 0xca7cf2b4191c8327},
-                {0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f1},
-                {0xf24a01a73cf2dccf, 0xbc633b39673c8ced},
-                {0x976e41088617ca01, 0xd5be0503e085d814},
-                {0xbd49d14aa79dbc82, 0x4b2d8644d8a74e19},
-                {0xec9c459d51852ba2, 0xddf8e7d60ed1219f},
-                {0x93e1ab8252f33b45, 0xcabb90e5c942b504},
-                {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
-                {0xe7109bfba19c0c9d, 0x0cc512670a783ad5},
-                {0x906a617d450187e2, 0x27fb2b80668b24c6},
-                {0xb484f9dc9641e9da, 0xb1f9f660802dedf7},
-                {0xe1a63853bbd26451, 0x5e7873f8a0396974},
-                {0x8d07e33455637eb2, 0xdb0b487b6423e1e9},
-                {0xb049dc016abc5e5f, 0x91ce1a9a3d2cda63},
-                {0xdc5c5301c56b75f7, 0x7641a140cc7810fc},
-                {0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9e},
-                {0xac2820d9623bf429, 0x546345fa9fbdcd45},
-                {0xd732290fbacaf133, 0xa97c177947ad4096},
-                {0x867f59a9d4bed6c0, 0x49ed8eabcccc485e},
-                {0xa81f301449ee8c70, 0x5c68f256bfff5a75},
-                {0xd226fc195c6a2f8c, 0x73832eec6fff3112},
-                {0x83585d8fd9c25db7, 0xc831fd53c5ff7eac},
-                {0xa42e74f3d032f525, 0xba3e7ca8b77f5e56},
-                {0xcd3a1230c43fb26f, 0x28ce1bd2e55f35ec},
-                {0x80444b5e7aa7cf85, 0x7980d163cf5b81b4},
-                {0xa0555e361951c366, 0xd7e105bcc3326220},
-                {0xc86ab5c39fa63440, 0x8dd9472bf3fefaa8},
-                {0xfa856334878fc150, 0xb14f98f6f0feb952},
-                {0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d4},
-                {0xc3b8358109e84f07, 0x0a862f80ec4700c9},
-                {0xf4a642e14c6262c8, 0xcd27bb612758c0fb},
-                {0x98e7e9cccfbd7dbd, 0x8038d51cb897789d},
-                {0xbf21e44003acdd2c, 0xe0470a63e6bd56c4},
-                {0xeeea5d5004981478, 0x1858ccfce06cac75},
-                {0x95527a5202df0ccb, 0x0f37801e0c43ebc9},
-                {0xbaa718e68396cffd, 0xd30560258f54e6bb},
-                {0xe950df20247c83fd, 0x47c6b82ef32a206a},
-                {0x91d28b7416cdd27e, 0x4cdc331d57fa5442},
-                {0xb6472e511c81471d, 0xe0133fe4adf8e953},
-                {0xe3d8f9e563a198e5, 0x58180fddd97723a7},
-                {0x8e679c2f5e44ff8f, 0x570f09eaa7ea7649},
-                {0xb201833b35d63f73, 0x2cd2cc6551e513db},
-                {0xde81e40a034bcf4f, 0xf8077f7ea65e58d2},
-                {0x8b112e86420f6191, 0xfb04afaf27faf783},
-                {0xadd57a27d29339f6, 0x79c5db9af1f9b564},
-                {0xd94ad8b1c7380874, 0x18375281ae7822bd},
-                {0x87cec76f1c830548, 0x8f2293910d0b15b6},
-                {0xa9c2794ae3a3c69a, 0xb2eb3875504ddb23},
-                {0xd433179d9c8cb841, 0x5fa60692a46151ec},
-                {0x849feec281d7f328, 0xdbc7c41ba6bcd334},
-                {0xa5c7ea73224deff3, 0x12b9b522906c0801},
-                {0xcf39e50feae16bef, 0xd768226b34870a01},
-                {0x81842f29f2cce375, 0xe6a1158300d46641},
-                {0xa1e53af46f801c53, 0x60495ae3c1097fd1},
-                {0xca5e89b18b602368, 0x385bb19cb14bdfc5},
-                {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6},
-                {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2},
-                {0xc5a05277621be293, 0xc7098b7305241886},
-                {0xf70867153aa2db38, 0xb8cbee4fc66d1ea8}
-#else
-                {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
-                {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
-                {0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f},
-                {0x86a8d39ef77164bc, 0xae5dff9c02033198},
-                {0xd98ddaee19068c76, 0x3badd624dd9b0958},
-                {0xafbd2350644eeacf, 0xe5d1929ef90898fb},
-                {0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2},
-                {0xe55990879ddcaabd, 0xcc420a6a101d0516},
-                {0xb94470938fa89bce, 0xf808e40e8d5b3e6a},
-                {0x95a8637627989aad, 0xdde7001379a44aa9},
-                {0xf1c90080baf72cb1, 0x5324c68b12dd6339},
-                {0xc350000000000000, 0x0000000000000000},
-                {0x9dc5ada82b70b59d, 0xf020000000000000},
-                {0xfee50b7025c36a08, 0x02f236d04753d5b5},
-                {0xcde6fd5e09abcf26, 0xed4c0226b55e6f87},
-                {0xa6539930bf6bff45, 0x84db8346b786151d},
-                {0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3},
-                {0xd910f7ff28069da4, 0x1b2ba1518094da05},
-                {0xaf58416654a6babb, 0x387ac8d1970027b3},
-                {0x8da471a9de737e24, 0x5ceaecfed289e5d3},
-                {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
-                {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
-                { 0x95527a5202df0ccb,
-                  0x0f37801e0c43ebc9 }
-#endif
-            };
-
-#if FMT_USE_FULL_CACHE_DRAGONBOX
-            return pow10_significands[k - float_info<double>::min_k];
-#else
-            static constexpr const uint64_t powers_of_5_64[] = {
-                0x0000000000000001, 0x0000000000000005, 0x0000000000000019, 0x000000000000007d, 0x0000000000000271,
-                0x0000000000000c35, 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, 0x00000000001dcd65,
-                0x00000000009502f9, 0x0000000002e90edd, 0x000000000e8d4a51, 0x0000000048c27395, 0x000000016bcc41e9,
-                0x000000071afd498d, 0x0000002386f26fc1, 0x000000b1a2bc2ec5, 0x000003782dace9d9, 0x00001158e460913d,
-                0x000056bc75e2d631, 0x0001b1ae4d6e2ef5, 0x000878678326eac9, 0x002a5a058fc295ed, 0x00d3c21bcecceda1,
-                0x0422ca8b0a00a425, 0x14adf4b7320334b9};
-
-            static const int compression_ratio = 27;
-
-            // Compute base index.
-            int cache_index = (k - float_info<double>::min_k) / compression_ratio;
-            int kb          = cache_index * compression_ratio + float_info<double>::min_k;
-            int offset      = k - kb;
-
-            // Get base cache.
-            uint128_fallback base_cache = pow10_significands[cache_index];
-            if(offset == 0)
-                return base_cache;
-
-            // Compute the required amount of bit-shift.
-            int alpha = floor_log2_pow10(kb + offset) - floor_log2_pow10(kb) - offset;
-            FMT_ASSERT(alpha > 0 && alpha < 64, "shifting error detected");
-
-            // Try to recover the real cache.
-            uint64_t         pow5            = powers_of_5_64[offset];
-            uint128_fallback recovered_cache = umul128(base_cache.high(), pow5);
-            uint128_fallback middle_low      = umul128(base_cache.low(), pow5);
-
-            recovered_cache += middle_low.high();
-
-            uint64_t high_to_middle = recovered_cache.high() << (64 - alpha);
-            uint64_t middle_to_low  = recovered_cache.low() << (64 - alpha);
-
-            recovered_cache = uint128_fallback{
-                (recovered_cache.low() >> alpha) | high_to_middle, ((middle_low.low() >> alpha) | middle_to_low)};
-            FMT_ASSERT(recovered_cache.low() + 1 != 0, "");
-            return {recovered_cache.high(), recovered_cache.low() + 1};
-#endif
-        }
-
-        struct compute_mul_result
-        {
-            carrier_uint result;
-            bool         is_integer;
-        };
-        struct compute_mul_parity_result
-        {
-            bool parity;
-            bool is_integer;
-        };
-
-        static compute_mul_result compute_mul(carrier_uint u, const cache_entry_type &cache) noexcept
-        {
-            auto r = umul192_upper128(u, cache);
-            return {r.high(), r.low() == 0};
-        }
-
-        static uint32_t compute_delta(cache_entry_type const &cache, int beta) noexcept
-        {
-            return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
-        }
-
-        static compute_mul_parity_result
-        compute_mul_parity(carrier_uint two_f, const cache_entry_type &cache, int beta) noexcept
-        {
-            FMT_ASSERT(beta >= 1, "");
-            FMT_ASSERT(beta < 64, "");
-
-            auto r = umul192_lower128(two_f, cache);
-            return {((r.high() >> (64 - beta)) & 1) != 0, ((r.high() << beta) | (r.low() >> (64 - beta))) == 0};
-        }
-
-        static carrier_uint
-        compute_left_endpoint_for_shorter_interval_case(const cache_entry_type &cache, int beta) noexcept
-        {
-            return (cache.high() - (cache.high() >> (num_significand_bits<double>() + 2))) >>
-                   (64 - num_significand_bits<double>() - 1 - beta);
-        }
-
-        static carrier_uint
-        compute_right_endpoint_for_shorter_interval_case(const cache_entry_type &cache, int beta) noexcept
-        {
-            return (cache.high() + (cache.high() >> (num_significand_bits<double>() + 1))) >>
-                   (64 - num_significand_bits<double>() - 1 - beta);
-        }
-
-        static carrier_uint compute_round_up_for_shorter_interval_case(const cache_entry_type &cache, int beta) noexcept
-        {
-            return ((cache.high() >> (64 - num_significand_bits<double>() - 2 - beta)) + 1) / 2;
-        }
-    };
-
-    // Various integer checks
-    template <class T>
-    bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept
-    {
-        const int case_shorter_interval_left_endpoint_lower_threshold = 2;
-        const int case_shorter_interval_left_endpoint_upper_threshold = 3;
-        return exponent >= case_shorter_interval_left_endpoint_lower_threshold &&
-               exponent <= case_shorter_interval_left_endpoint_upper_threshold;
-    }
-
-    // Remove trailing zeros from n and return the number of zeros removed (float)
-    FMT_INLINE int remove_trailing_zeros(uint32_t &n) noexcept
-    {
-        FMT_ASSERT(n != 0, "");
-        const uint32_t mod_inv_5  = 0xcccccccd;
-        const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
-
-        int s = 0;
-        while(true)
-        {
-            auto q = rotr(n * mod_inv_25, 2);
-            if(q > max_value<uint32_t>() / 100)
-                break;
-            n = q;
-            s += 2;
-        }
-        auto q = rotr(n * mod_inv_5, 1);
-        if(q <= max_value<uint32_t>() / 10)
-        {
-            n = q;
-            s |= 1;
-        }
-
-        return s;
-    }
-
-    // Removes trailing zeros and returns the number of zeros removed (double)
-    FMT_INLINE int remove_trailing_zeros(uint64_t &n) noexcept
-    {
-        FMT_ASSERT(n != 0, "");
-
-        // This magic number is ceil(2^90 / 10^8).
-        constexpr uint64_t magic_number = 12379400392853802749ull;
-        auto               nm           = umul128(n, magic_number);
-
-        // Is n is divisible by 10^8?
-        if((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number)
-        {
-            // If yes, work with the quotient.
-            auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
-
-            const uint32_t mod_inv_5  = 0xcccccccd;
-            const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5;
-
-            int s = 8;
-            while(true)
-            {
-                auto q = rotr(n32 * mod_inv_25, 2);
-                if(q > max_value<uint32_t>() / 100)
-                    break;
-                n32 = q;
-                s += 2;
-            }
-            auto q = rotr(n32 * mod_inv_5, 1);
-            if(q <= max_value<uint32_t>() / 10)
-            {
-                n32 = q;
-                s |= 1;
-            }
-
-            n = n32;
-            return s;
-        }
-
-        // If n is not divisible by 10^8, work with n itself.
-        const uint64_t mod_inv_5  = 0xcccccccccccccccd;
-        const uint64_t mod_inv_25 = mod_inv_5 * mod_inv_5;
-
-        int s = 0;
-        while(true)
-        {
-            auto q = rotr(n * mod_inv_25, 2);
-            if(q > max_value<uint64_t>() / 100)
-                break;
-            n = q;
-            s += 2;
-        }
-        auto q = rotr(n * mod_inv_5, 1);
-        if(q <= max_value<uint64_t>() / 10)
-        {
-            n = q;
-            s |= 1;
-        }
-
-        return s;
-    }
-
-    // The main algorithm for shorter interval case
-    template <class T>
-    FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept
-    {
-        decimal_fp<T> ret_value;
-        // Compute k and beta
-        const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
-        const int beta    = exponent + floor_log2_pow10(-minus_k);
-
-        // Compute xi and zi
-        using cache_entry_type       = typename cache_accessor<T>::cache_entry_type;
-        const cache_entry_type cache = cache_accessor<T>::get_cached_power(-minus_k);
-
-        auto xi = cache_accessor<T>::compute_left_endpoint_for_shorter_interval_case(cache, beta);
-        auto zi = cache_accessor<T>::compute_right_endpoint_for_shorter_interval_case(cache, beta);
-
-        // If the left endpoint is not an integer, increase it
-        if(!is_left_endpoint_integer_shorter_interval<T>(exponent))
-            ++xi;
-
-        // Try bigger divisor
-        ret_value.significand = zi / 10;
-
-        // If succeed, remove trailing zeros if necessary and return
-        if(ret_value.significand * 10 >= xi)
-        {
-            ret_value.exponent = minus_k + 1;
-            ret_value.exponent += remove_trailing_zeros(ret_value.significand);
-            return ret_value;
-        }
-
-        // Otherwise, compute the round-up of y
-        ret_value.significand = cache_accessor<T>::compute_round_up_for_shorter_interval_case(cache, beta);
-        ret_value.exponent    = minus_k;
-
-        // When tie occurs, choose one of them according to the rule
-        if(exponent >= float_info<T>::shorter_interval_tie_lower_threshold &&
-           exponent <= float_info<T>::shorter_interval_tie_upper_threshold)
-        {
-            ret_value.significand = ret_value.significand % 2 == 0 ? ret_value.significand : ret_value.significand - 1;
-        }
-        else if(ret_value.significand < xi)
-        {
-            ++ret_value.significand;
-        }
-        return ret_value;
-    }
-
-    template <typename T>
-    decimal_fp<T> to_decimal(T x) noexcept
-    {
-        // Step 1: integer promotion & Schubfach multiplier calculation.
-
-        using carrier_uint     = typename float_info<T>::carrier_uint;
-        using cache_entry_type = typename cache_accessor<T>::cache_entry_type;
-        auto br                = bit_cast<carrier_uint>(x);
-
-        // Extract significand bits and exponent bits.
-        const carrier_uint significand_mask = (static_cast<carrier_uint>(1) << num_significand_bits<T>()) - 1;
-        carrier_uint       significand      = (br & significand_mask);
-        int                exponent         = static_cast<int>((br & exponent_mask<T>()) >> num_significand_bits<T>());
-
-        if(exponent != 0)
-        { // Check if normal.
-            exponent -= exponent_bias<T>() + num_significand_bits<T>();
-
-            // Shorter interval case; proceed like Schubfach.
-            // In fact, when exponent == 1 and significand == 0, the interval is
-            // regular. However, it can be shown that the end-results are anyway same.
-            if(significand == 0)
-                return shorter_interval_case<T>(exponent);
-
-            significand |= (static_cast<carrier_uint>(1) << num_significand_bits<T>());
-        }
-        else
-        {
-            // Subnormal case; the interval is always regular.
-            if(significand == 0)
-                return {0, 0};
-            exponent = std::numeric_limits<T>::min_exponent - num_significand_bits<T>() - 1;
-        }
-
-        const bool include_left_endpoint  = (significand % 2 == 0);
-        const bool include_right_endpoint = include_left_endpoint;
-
-        // Compute k and beta.
-        const int              minus_k = floor_log10_pow2(exponent) - float_info<T>::kappa;
-        const cache_entry_type cache   = cache_accessor<T>::get_cached_power(-minus_k);
-        const int              beta    = exponent + floor_log2_pow10(-minus_k);
-
-        // Compute zi and deltai.
-        // 10^kappa <= deltai < 10^(kappa + 1)
-        const uint32_t     deltai = cache_accessor<T>::compute_delta(cache, beta);
-        const carrier_uint two_fc = significand << 1;
-
-        // For the case of binary32, the result of integer check is not correct for
-        // 29711844 * 2^-82
-        // = 6.1442653300000000008655037797566933477355632930994033813476... * 10^-18
-        // and 29711844 * 2^-81
-        // = 1.2288530660000000001731007559513386695471126586198806762695... * 10^-17,
-        // and they are the unique counterexamples. However, since 29711844 is even,
-        // this does not cause any problem for the endpoints calculations; it can only
-        // cause a problem when we need to perform integer check for the center.
-        // Fortunately, with these inputs, that branch is never executed, so we are
-        // fine.
-        const typename cache_accessor<T>::compute_mul_result z_mul =
-            cache_accessor<T>::compute_mul((two_fc | 1) << beta, cache);
-
-        // Step 2: Try larger divisor; remove trailing zeros if necessary.
-
-        // Using an upper bound on zi, we might be able to optimize the division
-        // better than the compiler; we are computing zi / big_divisor here.
-        decimal_fp<T> ret_value;
-        ret_value.significand = divide_by_10_to_kappa_plus_1(z_mul.result);
-        uint32_t r = static_cast<uint32_t>(z_mul.result - float_info<T>::big_divisor * ret_value.significand);
-
-        if(r < deltai)
-        {
-            // Exclude the right endpoint if necessary.
-            if(r == 0 && (z_mul.is_integer & !include_right_endpoint))
-            {
-                --ret_value.significand;
-                r = float_info<T>::big_divisor;
-                goto small_divisor_case_label;
-            }
-        }
-        else if(r > deltai)
-        {
-            goto small_divisor_case_label;
-        }
-        else
-        {
-            // r == deltai; compare fractional parts.
-            const typename cache_accessor<T>::compute_mul_parity_result x_mul =
-                cache_accessor<T>::compute_mul_parity(two_fc - 1, cache, beta);
-
-            if(!(x_mul.parity | (x_mul.is_integer & include_left_endpoint)))
-                goto small_divisor_case_label;
-        }
-        ret_value.exponent = minus_k + float_info<T>::kappa + 1;
-
-        // We may need to remove trailing zeros.
-        ret_value.exponent += remove_trailing_zeros(ret_value.significand);
-        return ret_value;
-
-        // Step 3: Find the significand with the smaller divisor.
-
-    small_divisor_case_label:
-        ret_value.significand *= 10;
-        ret_value.exponent = minus_k + float_info<T>::kappa;
-
-        uint32_t   dist            = r - (deltai / 2) + (float_info<T>::small_divisor / 2);
-        const bool approx_y_parity = ((dist ^ (float_info<T>::small_divisor / 2)) & 1) != 0;
-
-        // Is dist divisible by 10^kappa?
-        const bool divisible_by_small_divisor = check_divisibility_and_divide_by_pow10<float_info<T>::kappa>(dist);
-
-        // Add dist / 10^kappa to the significand.
-        ret_value.significand += dist;
-
-        if(!divisible_by_small_divisor)
-            return ret_value;
-
-        // Check z^(f) >= epsilon^(f).
-        // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1,
-        // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f).
-        // Since there are only 2 possibilities, we only need to care about the
-        // parity. Also, zi and r should have the same parity since the divisor
-        // is an even number.
-        const auto y_mul = cache_accessor<T>::compute_mul_parity(two_fc, cache, beta);
-
-        // If z^(f) >= epsilon^(f), we might have a tie when z^(f) == epsilon^(f),
-        // or equivalently, when y is an integer.
-        if(y_mul.parity != approx_y_parity)
-            --ret_value.significand;
-        else if(y_mul.is_integer & (ret_value.significand % 2 != 0))
-            --ret_value.significand;
-        return ret_value;
-    }
-} // namespace dragonbox
-
-#ifdef _MSC_VER
-FMT_FUNC auto fmt_snprintf(char *buf, size_t size, const char *fmt, ...) -> int
-{
-    auto args = va_list();
-    va_start(args, fmt);
-    int result = vsnprintf_s(buf, size, _TRUNCATE, fmt, args);
-    va_end(args);
-    return result;
-}
-#endif
-} // namespace detail
-
-template <>
-struct formatter<detail::bigint>
-{
-    FMT_CONSTEXPR auto parse(format_parse_context &ctx) -> format_parse_context::iterator { return ctx.begin(); }
-
-    template <typename FormatContext>
-    auto format(const detail::bigint &n, FormatContext &ctx) const -> typename FormatContext::iterator
-    {
-        auto out   = ctx.out();
-        bool first = true;
-        for(auto i = n.bigits_.size(); i > 0; --i)
-        {
-            auto value = n.bigits_[i - 1u];
-            if(first)
-            {
-                out   = format_to(out, FMT_STRING("{:x}"), value);
-                first = false;
-                continue;
-            }
-            out = format_to(out, FMT_STRING("{:08x}"), value);
-        }
-        if(n.exp_ > 0)
-            out = format_to(out, FMT_STRING("p{}"), n.exp_ * detail::bigint::bigit_bits);
-        return out;
-    }
-};
-
-FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s)
-{
-    for_each_codepoint(
-        s,
-        [this](uint32_t cp, string_view)
-        {
-            if(cp == invalid_code_point)
-                FMT_THROW(std::runtime_error("invalid utf8"));
-            if(cp <= 0xFFFF)
-            {
-                buffer_.push_back(static_cast<wchar_t>(cp));
-            }
-            else
-            {
-                cp -= 0x10000;
-                buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
-                buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
-            }
-            return true;
-        });
-    buffer_.push_back(0);
-}
-
-FMT_FUNC void format_system_error(detail::buffer<char> &out, int error_code, const char *message) noexcept
-{
-    FMT_TRY
-    {
-        auto ec = std::error_code(error_code, std::generic_category());
-        write(std::back_inserter(out), std::system_error(ec, message).what());
-        return;
-    }
-    FMT_CATCH(...) {}
-    format_error_code(out, error_code, message);
-}
-
-FMT_FUNC void report_system_error(int error_code, const char *message) noexcept
-{
-    report_error(format_system_error, error_code, message);
-}
-
-FMT_FUNC std::string vformat(string_view fmt, format_args args)
-{
-    // Don't optimize the "{}" case to keep the binary size small and because it
-    // can be better optimized in fmt::format anyway.
-    auto buffer = memory_buffer();
-    detail::vformat_to(buffer, fmt, args);
-    return to_string(buffer);
-}
-
-namespace detail
-{
-#ifdef _WIN32
-using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
-extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
-    void *,
-    const void *,
-    dword,
-    dword *,
-    void *);
-
-FMT_FUNC bool write_console(std::FILE *f, string_view text)
-{
-    auto fd = _fileno(f);
-    if(_isatty(fd))
-    {
-        detail::utf8_to_utf16 u16(string_view(text.data(), text.size()));
-        auto                  written = detail::dword();
-        if(detail::WriteConsoleW(
-               reinterpret_cast<void *>(_get_osfhandle(fd)),
-               u16.c_str(),
-               static_cast<uint32_t>(u16.size()),
-               &written,
-               nullptr))
-        {
-            return true;
-        }
-    }
-    // We return false if the file descriptor was not TTY, or it was but
-    // SetConsoleW failed which can happen if the output has been redirected to
-    // NUL. In both cases when we return false, we should attempt to do regular
-    // write via fwrite or std::ostream::write.
-    return false;
-}
-#endif
-
-FMT_FUNC void print(std::FILE *f, string_view text)
-{
-#ifdef _WIN32
-    if(write_console(f, text))
-        return;
-#endif
-    detail::fwrite_fully(text.data(), 1, text.size(), f);
-}
-} // namespace detail
-
-FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args)
-{
-    memory_buffer buffer;
-    detail::vformat_to(buffer, format_str, args);
-    detail::print(f, {buffer.data(), buffer.size()});
-}
-
-#ifdef _WIN32
-// Print assuming legacy (non-Unicode) encoding.
-FMT_FUNC void detail::vprint_mojibake(std::FILE *f, string_view format_str, format_args args)
-{
-    memory_buffer buffer;
-    detail::vformat_to(buffer, format_str, basic_format_args<buffer_context<char>>(args));
-    fwrite_fully(buffer.data(), 1, buffer.size(), f);
-}
-#endif
-
-FMT_FUNC void vprint(string_view format_str, format_args args)
-{
-    vprint(stdout, format_str, args);
-}
-
-namespace detail
-{
-
-struct singleton
-{
-    unsigned char upper;
-    unsigned char lower_count;
-};
-
-inline auto is_printable(
-    uint16_t             x,
-    const singleton     *singletons,
-    size_t               singletons_size,
-    const unsigned char *singleton_lowers,
-    const unsigned char *normal,
-    size_t               normal_size) -> bool
-{
-    auto upper       = x >> 8;
-    auto lower_start = 0;
-    for(size_t i = 0; i < singletons_size; ++i)
-    {
-        auto s         = singletons[i];
-        auto lower_end = lower_start + s.lower_count;
-        if(upper < s.upper)
-            break;
-        if(upper == s.upper)
-        {
-            for(auto j = lower_start; j < lower_end; ++j)
-            {
-                if(singleton_lowers[j] == (x & 0xff))
-                    return false;
-            }
-        }
-        lower_start = lower_end;
-    }
-
-    auto xsigned = static_cast<int>(x);
-    auto current = true;
-    for(size_t i = 0; i < normal_size; ++i)
-    {
-        auto v   = static_cast<int>(normal[i]);
-        auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v;
-        xsigned -= len;
-        if(xsigned < 0)
-            break;
-        current = !current;
-    }
-    return current;
-}
-
-// This code is generated by support/printable.py.
-FMT_FUNC auto is_printable(uint32_t cp) -> bool
-{
-    static constexpr singleton singletons0[] = {
-        {0x00, 1},  {0x03, 5},  {0x05, 6},  {0x06, 3}, {0x07, 6}, {0x08, 8},  {0x09, 17}, {0x0a, 28}, {0x0b, 25},
-        {0x0c, 20}, {0x0d, 16}, {0x0e, 13}, {0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9},  {0x16, 1},  {0x17, 5},
-        {0x18, 2},  {0x19, 3},  {0x1a, 7},  {0x1c, 2}, {0x1d, 1}, {0x1f, 22}, {0x20, 3},  {0x2b, 3},  {0x2c, 2},
-        {0x2d, 11}, {0x2e, 1},  {0x30, 3},  {0x31, 2}, {0x32, 1}, {0xa7, 2},  {0xa9, 2},  {0xaa, 4},  {0xab, 8},
-        {0xfa, 2},  {0xfb, 5},  {0xfd, 4},  {0xfe, 3}, {0xff, 9},
-    };
-    static constexpr unsigned char singletons0_lower[] = {
-        0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90, 0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b,
-        0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f, 0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
-        0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04, 0x11, 0x12, 0x29, 0x31, 0x34, 0x37,
-        0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d, 0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
-        0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, 0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e,
-        0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d, 0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
-        0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d, 0x11, 0x45, 0x49, 0x64, 0x65, 0x80,
-        0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, 0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
-        0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, 0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f,
-        0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7, 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
-        0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e, 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e,
-        0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16, 0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
-        0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f, 0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26,
-        0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf, 0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
-        0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, 0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37,
-        0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91, 0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
-        0xfe, 0xff,
-    };
-    static constexpr singleton singletons1[] = {
-        {0x00, 6}, {0x01, 1}, {0x03, 1},  {0x04, 2}, {0x08, 8},  {0x09, 2}, {0x0a, 5},  {0x0b, 2}, {0x0e, 4}, {0x10, 1},
-        {0x11, 2}, {0x12, 5}, {0x13, 17}, {0x14, 1}, {0x15, 2},  {0x17, 2}, {0x19, 13}, {0x1c, 5}, {0x1d, 8}, {0x24, 1},
-        {0x6a, 3}, {0x6b, 2}, {0xbc, 2},  {0xd1, 2}, {0xd4, 12}, {0xd5, 9}, {0xd6, 2},  {0xd7, 2}, {0xda, 1}, {0xe0, 5},
-        {0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2},  {0xf9, 2}, {0xfa, 2},  {0xfb, 1},
-    };
-    static constexpr unsigned char singletons1_lower[] = {
-        0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07, 0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3,
-        0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36, 0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
-        0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, 0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f,
-        0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b, 0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
-        0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66, 0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee,
-        0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27, 0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
-        0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, 0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a,
-        0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6, 0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
-        0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66, 0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a,
-        0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0, 0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
-    };
-    static constexpr unsigned char normal0[] = {
-        0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04, 0x06, 0x11, 0x81, 0xac, 0x0e, 0x80,
-        0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0, 0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
-        0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03, 0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08,
-        0x03, 0x07, 0x03, 0x02, 0x03, 0x03, 0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
-        0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15, 0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d,
-        0x03, 0x01, 0x04, 0x11, 0x06, 0x0f, 0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
-        0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07, 0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c,
-        0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06, 0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
-        0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac, 0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c,
-        0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c, 0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
-        0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c, 0x04, 0x82, 0x97, 0x19, 0x0b, 0x15,
-        0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b, 0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
-        0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03, 0x84, 0x8d, 0x03, 0x37, 0x09, 0x81,
-        0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80, 0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
-        0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c, 0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80,
-        0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17, 0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
-        0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80, 0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b,
-        0x03, 0x0f, 0x0d,
-    };
-    static constexpr unsigned char normal1[] = {
-        0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f, 0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31,
-        0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e, 0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
-        0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09, 0x16, 0x0a, 0x08, 0x80, 0x98, 0x39,
-        0x03, 0x63, 0x08, 0x09, 0x30, 0x16, 0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
-        0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36, 0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04,
-        0x0c, 0x07, 0x50, 0x49, 0x37, 0x33, 0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
-        0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e, 0x19, 0x07, 0x0a, 0x06, 0x48, 0x08,
-        0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41, 0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
-        0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22, 0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13,
-        0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04, 0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
-        0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03, 0x47, 0x49, 0x37, 0x03, 0x0e, 0x08,
-        0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81, 0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
-        0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1, 0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04,
-        0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a, 0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
-        0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09, 0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03,
-        0x11, 0x04, 0x08, 0x81, 0x8c, 0x89, 0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
-        0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09, 0x19, 0x80, 0x87, 0x81, 0x47, 0x03,
-        0x85, 0x42, 0x0f, 0x15, 0x85, 0x50, 0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
-        0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83, 0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2,
-        0x3c, 0x06, 0x01, 0x04, 0x55, 0x05, 0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
-        0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80, 0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03,
-        0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07, 0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
-        0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07, 0x09, 0x03, 0x0d, 0x07, 0x29, 0x80,
-        0xcb, 0x25, 0x0a, 0x84, 0x06,
-    };
-    auto lower = static_cast<uint16_t>(cp);
-    if(cp < 0x10000)
-    {
-        return is_printable(
-            lower,
-            singletons0,
-            sizeof(singletons0) / sizeof(*singletons0),
-            singletons0_lower,
-            normal0,
-            sizeof(normal0));
-    }
-    if(cp < 0x20000)
-    {
-        return is_printable(
-            lower,
-            singletons1,
-            sizeof(singletons1) / sizeof(*singletons1),
-            singletons1_lower,
-            normal1,
-            sizeof(normal1));
-    }
-    if(0x2a6de <= cp && cp < 0x2a700)
-        return false;
-    if(0x2b735 <= cp && cp < 0x2b740)
-        return false;
-    if(0x2b81e <= cp && cp < 0x2b820)
-        return false;
-    if(0x2cea2 <= cp && cp < 0x2ceb0)
-        return false;
-    if(0x2ebe1 <= cp && cp < 0x2f800)
-        return false;
-    if(0x2fa1e <= cp && cp < 0x30000)
-        return false;
-    if(0x3134b <= cp && cp < 0xe0100)
-        return false;
-    if(0xe01f0 <= cp && cp < 0x110000)
-        return false;
-    return cp < 0x110000;
-}
-
-} // namespace detail
-
-FMT_END_NAMESPACE
-
-#endif // FMT_FORMAT_INL_H_
diff --git a/include/spdlog/fmt/bundled/format.h b/include/spdlog/fmt/bundled/format.h
deleted file mode 100644
index 6d296484b..000000000
--- a/include/spdlog/fmt/bundled/format.h
+++ /dev/null
@@ -1,4767 +0,0 @@
-/*
-  Formatting library for C++
-
-  Copyright (c) 2012 - present, Victor Zverovich
-
-  Permission is hereby granted, free of charge, to any person obtaining
-  a copy of this software and associated documentation files (the
-  "Software"), to deal in the Software without restriction, including
-  without limitation the rights to use, copy, modify, merge, publish,
-  distribute, sublicense, and/or sell copies of the Software, and to
-  permit persons to whom the Software is furnished to do so, subject to
-  the following conditions:
-
-  The above copyright notice and this permission notice shall be
-  included in all copies or substantial portions of the Software.
-
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-  --- Optional exception to the license ---
-
-  As an exception, if, as a result of your compiling your source code, portions
-  of this Software are embedded into a machine-executable object form of such
-  source code, you may redistribute such embedded portions in such object form
-  without including the above copyright and permission notices.
- */
-
-#ifndef FMT_FORMAT_H_
-#define FMT_FORMAT_H_
-
-#include <cmath>        // std::signbit
-#include <cstdint>      // uint32_t
-#include <cstring>      // std::memcpy
-#include <limits>       // std::numeric_limits
-#include <memory>       // std::uninitialized_copy
-#include <stdexcept>    // std::runtime_error
-#include <system_error> // std::system_error
-
-#ifdef __cpp_lib_bit_cast
-#include <bit> // std::bitcast
-#endif
-
-#include "core.h"
-
-#if FMT_GCC_VERSION
-#define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden")))
-#else
-#define FMT_GCC_VISIBILITY_HIDDEN
-#endif
-
-#ifdef __NVCC__
-#define FMT_CUDA_VERSION (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__)
-#else
-#define FMT_CUDA_VERSION 0
-#endif
-
-#ifdef __has_builtin
-#define FMT_HAS_BUILTIN(x) __has_builtin(x)
-#else
-#define FMT_HAS_BUILTIN(x) 0
-#endif
-
-#if FMT_GCC_VERSION || FMT_CLANG_VERSION
-#define FMT_NOINLINE __attribute__((noinline))
-#else
-#define FMT_NOINLINE
-#endif
-
-#if FMT_MSC_VERSION
-#define FMT_MSC_DEFAULT = default
-#else
-#define FMT_MSC_DEFAULT
-#endif
-
-#ifndef FMT_THROW
-#if FMT_EXCEPTIONS
-#if FMT_MSC_VERSION || defined(__NVCC__)
-FMT_BEGIN_NAMESPACE
-namespace detail
-{
-template <typename Exception>
-inline void do_throw(const Exception &x)
-{
-    // Silence unreachable code warnings in MSVC and NVCC because these
-    // are nearly impossible to fix in a generic code.
-    volatile bool b = true;
-    if(b)
-        throw x;
-}
-} // namespace detail
-FMT_END_NAMESPACE
-#define FMT_THROW(x) detail::do_throw(x)
-#else
-#define FMT_THROW(x) throw x
-#endif
-#else
-#define FMT_THROW(x)                                                                                                   \
-    do                                                                                                                 \
-    {                                                                                                                  \
-        FMT_ASSERT(false, (x).what());                                                                                 \
-    } while(false)
-#endif
-#endif
-
-#if FMT_EXCEPTIONS
-#define FMT_TRY      try
-#define FMT_CATCH(x) catch(x)
-#else
-#define FMT_TRY      if(true)
-#define FMT_CATCH(x) if(false)
-#endif
-
-#ifndef FMT_MAYBE_UNUSED
-#if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
-#define FMT_MAYBE_UNUSED [[maybe_unused]]
-#else
-#define FMT_MAYBE_UNUSED
-#endif
-#endif
-
-#ifndef FMT_USE_USER_DEFINED_LITERALS
-// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs.
-#if(FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || FMT_MSC_VERSION >= 1900) &&                        \
-    (!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480)
-#define FMT_USE_USER_DEFINED_LITERALS 1
-#else
-#define FMT_USE_USER_DEFINED_LITERALS 0
-#endif
-#endif
-
-// Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of
-// integer formatter template instantiations to just one by only using the
-// largest integer type. This results in a reduction in binary size but will
-// cause a decrease in integer formatting performance.
-#if !defined(FMT_REDUCE_INT_INSTANTIATIONS)
-#define FMT_REDUCE_INT_INSTANTIATIONS 0
-#endif
-
-// __builtin_clz is broken in clang with Microsoft CodeGen:
-// https://github.com/fmtlib/fmt/issues/519.
-#if !FMT_MSC_VERSION
-#if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION
-#define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
-#endif
-#if FMT_HAS_BUILTIN(__builtin_clzll) || FMT_GCC_VERSION || FMT_ICC_VERSION
-#define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
-#endif
-#endif
-
-// __builtin_ctz is broken in Intel Compiler Classic on Windows:
-// https://github.com/fmtlib/fmt/issues/2510.
-#ifndef __ICL
-#if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION || defined(__NVCOMPILER)
-#define FMT_BUILTIN_CTZ(n) __builtin_ctz(n)
-#endif
-#if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || FMT_ICC_VERSION || defined(__NVCOMPILER)
-#define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n)
-#endif
-#endif
-
-#if FMT_MSC_VERSION
-#include <intrin.h> // _BitScanReverse[64], _BitScanForward[64], _umul128
-#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.
-#if FMT_MSC_VERSION && !defined(FMT_BUILTIN_CLZLL) && !defined(FMT_BUILTIN_CTZLL)
-FMT_BEGIN_NAMESPACE
-namespace detail
-{
-// Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning.
-#if !defined(__clang__)
-#pragma intrinsic(_BitScanForward)
-#pragma intrinsic(_BitScanReverse)
-#if defined(_WIN64)
-#pragma intrinsic(_BitScanForward64)
-#pragma intrinsic(_BitScanReverse64)
-#endif
-#endif
-
-inline auto clz(uint32_t x) -> int
-{
-    unsigned long r = 0;
-    _BitScanReverse(&r, x);
-    FMT_ASSERT(x != 0, "");
-    // Static analysis complains about using uninitialized data
-    // "r", but the only way that can happen is if "x" is 0,
-    // which the callers guarantee to not happen.
-    FMT_MSC_WARNING(suppress : 6102)
-    return 31 ^ static_cast<int>(r);
-}
-#define FMT_BUILTIN_CLZ(n) detail::clz(n)
-
-inline auto clzll(uint64_t x) -> int
-{
-    unsigned long r = 0;
-#ifdef _WIN64
-    _BitScanReverse64(&r, x);
-#else
-    // Scan the high 32 bits.
-    if(_BitScanReverse(&r, static_cast<uint32_t>(x >> 32)))
-        return 63 ^ (r + 32);
-    // Scan the low 32 bits.
-    _BitScanReverse(&r, static_cast<uint32_t>(x));
-#endif
-    FMT_ASSERT(x != 0, "");
-    FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
-    return 63 ^ static_cast<int>(r);
-}
-#define FMT_BUILTIN_CLZLL(n) detail::clzll(n)
-
-inline auto ctz(uint32_t x) -> int
-{
-    unsigned long r = 0;
-    _BitScanForward(&r, x);
-    FMT_ASSERT(x != 0, "");
-    FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
-    return static_cast<int>(r);
-}
-#define FMT_BUILTIN_CTZ(n) detail::ctz(n)
-
-inline auto ctzll(uint64_t x) -> int
-{
-    unsigned long r = 0;
-    FMT_ASSERT(x != 0, "");
-    FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
-#ifdef _WIN64
-    _BitScanForward64(&r, x);
-#else
-    // Scan the low 32 bits.
-    if(_BitScanForward(&r, static_cast<uint32_t>(x)))
-        return static_cast<int>(r);
-    // Scan the high 32 bits.
-    _BitScanForward(&r, static_cast<uint32_t>(x >> 32));
-    r += 32;
-#endif
-    return static_cast<int>(r);
-}
-#define FMT_BUILTIN_CTZLL(n) detail::ctzll(n)
-} // namespace detail
-FMT_END_NAMESPACE
-#endif
-
-FMT_BEGIN_NAMESPACE
-namespace detail
-{
-
-FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition)
-{
-    ignore_unused(condition);
-#ifdef FMT_FUZZ
-    if(condition)
-        throw std::runtime_error("fuzzing limit reached");
-#endif
-}
-
-template <typename CharT, CharT... C>
-struct string_literal
-{
-    static constexpr CharT value[sizeof...(C)] = {C...};
-    constexpr              operator basic_string_view<CharT>() const { return {value, sizeof...(C)}; }
-};
-
-#if FMT_CPLUSPLUS < 201703L
-template <typename CharT, CharT... C>
-constexpr CharT string_literal<CharT, C...>::value[sizeof...(C)];
-#endif
-
-template <typename Streambuf>
-class formatbuf : public Streambuf
-{
-private:
-    using char_type   = typename Streambuf::char_type;
-    using streamsize  = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
-    using int_type    = typename Streambuf::int_type;
-    using traits_type = typename Streambuf::traits_type;
-
-    buffer<char_type> &buffer_;
-
-public:
-    explicit formatbuf(buffer<char_type> &buf) : buffer_(buf) {}
-
-protected:
-    // The put area is always empty. This makes the implementation simpler and has
-    // the advantage that the streambuf and the buffer are always in sync and
-    // sputc never writes into uninitialized memory. A disadvantage is that each
-    // call to sputc always results in a (virtual) call to overflow. There is no
-    // disadvantage here for sputn since this always results in a call to xsputn.
-
-    auto overflow(int_type ch) -> int_type override
-    {
-        if(!traits_type::eq_int_type(ch, traits_type::eof()))
-            buffer_.push_back(static_cast<char_type>(ch));
-        return ch;
-    }
-
-    auto xsputn(const char_type *s, streamsize count) -> streamsize override
-    {
-        buffer_.append(s, s + count);
-        return count;
-    }
-};
-
-// Implementation of std::bit_cast for pre-C++20.
-template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) == sizeof(From))>
-FMT_CONSTEXPR20 auto bit_cast(const From &from) -> To
-{
-#ifdef __cpp_lib_bit_cast
-    if(is_constant_evaluated())
-        return std::bit_cast<To>(from);
-#endif
-    auto to = To();
-    // The cast suppresses a bogus -Wclass-memaccess on GCC.
-    std::memcpy(static_cast<void *>(&to), &from, sizeof(to));
-    return to;
-}
-
-inline auto is_big_endian() -> bool
-{
-#ifdef _WIN32
-    return false;
-#elif defined(__BIG_ENDIAN__)
-    return true;
-#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
-    return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__;
-#else
-    struct bytes
-    {
-        char data[sizeof(int)];
-    };
-    return bit_cast<bytes>(1).data[0] == 0;
-#endif
-}
-
-class uint128_fallback
-{
-private:
-    uint64_t lo_, hi_;
-
-    friend uint128_fallback umul128(uint64_t x, uint64_t y) noexcept;
-
-public:
-    constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
-    constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {}
-
-    constexpr uint64_t high() const noexcept { return hi_; }
-    constexpr uint64_t low() const noexcept { return lo_; }
-
-    template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-    constexpr explicit operator T() const
-    {
-        return static_cast<T>(lo_);
-    }
-
-    friend constexpr auto operator==(const uint128_fallback &lhs, const uint128_fallback &rhs) -> bool
-    {
-        return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_;
-    }
-    friend constexpr auto operator!=(const uint128_fallback &lhs, const uint128_fallback &rhs) -> bool
-    {
-        return !(lhs == rhs);
-    }
-    friend constexpr auto operator>(const uint128_fallback &lhs, const uint128_fallback &rhs) -> bool
-    {
-        return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_;
-    }
-    friend constexpr auto operator|(const uint128_fallback &lhs, const uint128_fallback &rhs) -> uint128_fallback
-    {
-        return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_};
-    }
-    friend constexpr auto operator&(const uint128_fallback &lhs, const uint128_fallback &rhs) -> uint128_fallback
-    {
-        return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_};
-    }
-    friend auto operator+(const uint128_fallback &lhs, const uint128_fallback &rhs) -> uint128_fallback
-    {
-        auto result = uint128_fallback(lhs);
-        result += rhs;
-        return result;
-    }
-    friend auto operator*(const uint128_fallback &lhs, uint32_t rhs) -> uint128_fallback
-    {
-        FMT_ASSERT(lhs.hi_ == 0, "");
-        uint64_t hi     = (lhs.lo_ >> 32) * rhs;
-        uint64_t lo     = (lhs.lo_ & ~uint32_t()) * rhs;
-        uint64_t new_lo = (hi << 32) + lo;
-        return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo};
-    }
-    friend auto operator-(const uint128_fallback &lhs, uint64_t rhs) -> uint128_fallback
-    {
-        return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs};
-    }
-    FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback
-    {
-        if(shift == 64)
-            return {0, hi_};
-        if(shift > 64)
-            return uint128_fallback(0, hi_) >> (shift - 64);
-        return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)};
-    }
-    FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback
-    {
-        if(shift == 64)
-            return {lo_, 0};
-        if(shift > 64)
-            return uint128_fallback(lo_, 0) << (shift - 64);
-        return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)};
-    }
-    FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback & { return *this = *this >> shift; }
-    FMT_CONSTEXPR void operator+=(uint128_fallback n)
-    {
-        uint64_t new_lo = lo_ + n.lo_;
-        uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0);
-        FMT_ASSERT(new_hi >= hi_, "");
-        lo_ = new_lo;
-        hi_ = new_hi;
-    }
-
-    FMT_CONSTEXPR20 uint128_fallback &operator+=(uint64_t n) noexcept
-    {
-        if(is_constant_evaluated())
-        {
-            lo_ += n;
-            hi_ += (lo_ < n ? 1 : 0);
-            return *this;
-        }
-#if FMT_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__)
-        unsigned long long carry;
-        lo_ = __builtin_addcll(lo_, n, 0, &carry);
-        hi_ += carry;
-#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__)
-        unsigned long long result;
-        auto               carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result);
-        lo_                      = result;
-        hi_ += carry;
-#elif defined(_MSC_VER) && defined(_M_X64)
-        auto carry = _addcarry_u64(0, lo_, n, &lo_);
-        _addcarry_u64(carry, hi_, 0, &hi_);
-#else
-        lo_ += n;
-        hi_ += (lo_ < n ? 1 : 0);
-#endif
-        return *this;
-    }
-};
-
-using uint128_t = conditional_t<FMT_USE_INT128, uint128_opt, uint128_fallback>;
-
-#ifdef UINTPTR_MAX
-using uintptr_t = ::uintptr_t;
-#else
-using uintptr_t = uint128_t;
-#endif
-
-// Returns the largest possible value for type T. Same as
-// std::numeric_limits<T>::max() but shorter and not affected by the max macro.
-template <typename T>
-constexpr auto max_value() -> T
-{
-    return (std::numeric_limits<T>::max)();
-}
-template <typename T>
-constexpr auto num_bits() -> int
-{
-    return std::numeric_limits<T>::digits;
-}
-// std::numeric_limits<T>::digits may return 0 for 128-bit ints.
-template <>
-constexpr auto num_bits<int128_opt>() -> int
-{
-    return 128;
-}
-template <>
-constexpr auto num_bits<uint128_t>() -> int
-{
-    return 128;
-}
-
-// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t
-// and 128-bit pointers to uint128_fallback.
-template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) > sizeof(From))>
-inline auto bit_cast(const From &from) -> To
-{
-    constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned));
-    struct data_t
-    {
-        unsigned value[static_cast<unsigned>(size)];
-    } data      = bit_cast<data_t>(from);
-    auto result = To();
-    if(const_check(is_big_endian()))
-    {
-        for(int i = 0; i < size; ++i)
-            result = (result << num_bits<unsigned>()) | data.value[i];
-    }
-    else
-    {
-        for(int i = size - 1; i >= 0; --i)
-            result = (result << num_bits<unsigned>()) | data.value[i];
-    }
-    return result;
-}
-
-FMT_INLINE void assume(bool condition)
-{
-    (void) condition;
-#if FMT_HAS_BUILTIN(__builtin_assume) && !FMT_ICC_VERSION
-    __builtin_assume(condition);
-#endif
-}
-
-// An approximation of iterator_t for pre-C++20 systems.
-template <typename T>
-using iterator_t = decltype(std::begin(std::declval<T &>()));
-template <typename T>
-using sentinel_t = decltype(std::end(std::declval<T &>()));
-
-// A workaround for std::string not having mutable data() until C++17.
-template <typename Char>
-inline auto get_data(std::basic_string<Char> &s) -> Char *
-{
-    return &s[0];
-}
-template <typename Container>
-inline auto get_data(Container &c) -> typename Container::value_type *
-{
-    return c.data();
-}
-
-#if defined(_SECURE_SCL) && _SECURE_SCL
-// Make a checked iterator to avoid MSVC warnings.
-template <typename T>
-using checked_ptr = stdext::checked_array_iterator<T *>;
-template <typename T>
-constexpr auto make_checked(T *p, size_t size) -> checked_ptr<T>
-{
-    return {p, size};
-}
-#else
-template <typename T>
-using checked_ptr = T *;
-template <typename T>
-constexpr auto make_checked(T *p, size_t) -> T *
-{
-    return p;
-}
-#endif
-
-// Attempts to reserve space for n extra characters in the output range.
-// Returns a pointer to the reserved range or a reference to it.
-template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
-#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION
-__attribute__((no_sanitize("undefined")))
-#endif
-inline auto
-reserve(std::back_insert_iterator<Container> it, size_t n) -> checked_ptr<typename Container::value_type>
-{
-    Container &c    = get_container(it);
-    size_t     size = c.size();
-    c.resize(size + n);
-    return make_checked(get_data(c) + size, n);
-}
-
-template <typename T>
-inline auto reserve(buffer_appender<T> it, size_t n) -> buffer_appender<T>
-{
-    buffer<T> &buf = get_container(it);
-    buf.try_reserve(buf.size() + n);
-    return it;
-}
-
-template <typename Iterator>
-constexpr auto reserve(Iterator &it, size_t) -> Iterator &
-{
-    return it;
-}
-
-template <typename OutputIt>
-using reserve_iterator = remove_reference_t<decltype(reserve(std::declval<OutputIt &>(), 0))>;
-
-template <typename T, typename OutputIt>
-constexpr auto to_pointer(OutputIt, size_t) -> T *
-{
-    return nullptr;
-}
-template <typename T>
-auto to_pointer(buffer_appender<T> it, size_t n) -> T *
-{
-    buffer<T> &buf  = get_container(it);
-    auto       size = buf.size();
-    if(buf.capacity() < size + n)
-        return nullptr;
-    buf.try_resize(size + n);
-    return buf.data() + size;
-}
-
-template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
-inline auto base_iterator(std::back_insert_iterator<Container> &it, checked_ptr<typename Container::value_type>)
-    -> std::back_insert_iterator<Container>
-{
-    return it;
-}
-
-template <typename Iterator>
-constexpr auto base_iterator(Iterator, Iterator it) -> Iterator
-{
-    return it;
-}
-
-// <algorithm> is spectacularly slow to compile in C++20 so use a simple fill_n
-// instead (#1998).
-template <typename OutputIt, typename Size, typename T>
-FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T &value) -> OutputIt
-{
-    for(Size i = 0; i < count; ++i)
-        *out++ = value;
-    return out;
-}
-template <typename T, typename Size>
-FMT_CONSTEXPR20 auto fill_n(T *out, Size count, char value) -> T *
-{
-    if(is_constant_evaluated())
-    {
-        return fill_n<T *, Size, T>(out, count, value);
-    }
-    std::memset(out, value, to_unsigned(count));
-    return out + count;
-}
-
-#ifdef __cpp_char8_t
-using char8_type = char8_t;
-#else
-enum char8_type : unsigned char
-{
-};
-#endif
-
-template <typename OutChar, typename InputIt, typename OutputIt>
-FMT_CONSTEXPR FMT_NOINLINE auto copy_str_noinline(InputIt begin, InputIt end, OutputIt out) -> OutputIt
-{
-    return copy_str<OutChar>(begin, end, out);
-}
-
-// A public domain branchless UTF-8 decoder by Christopher Wellons:
-// https://github.com/skeeto/branchless-utf8
-/* Decode the next character, c, from s, reporting errors in e.
- *
- * Since this is a branchless decoder, four bytes will be read from the
- * buffer regardless of the actual length of the next character. This
- * means the buffer _must_ have at least three bytes of zero padding
- * following the end of the data stream.
- *
- * Errors are reported in e, which will be non-zero if the parsed
- * character was somehow invalid: invalid byte sequence, non-canonical
- * encoding, or a surrogate half.
- *
- * The function returns a pointer to the next character. When an error
- * occurs, this pointer will be a guess that depends on the particular
- * error, but it will always advance at least one byte.
- */
-FMT_CONSTEXPR inline auto utf8_decode(const char *s, uint32_t *c, int *e) -> const char *
-{
-    constexpr const int      masks[]  = {0x00, 0x7f, 0x1f, 0x0f, 0x07};
-    constexpr const uint32_t mins[]   = {4194304, 0, 128, 2048, 65536};
-    constexpr const int      shiftc[] = {0, 18, 12, 6, 0};
-    constexpr const int      shifte[] = {0, 6, 4, 2, 0};
-
-    int len = code_point_length_impl(*s);
-    // Compute the pointer to the next character early so that the next
-    // iteration can start working on the next character. Neither Clang
-    // nor GCC figure out this reordering on their own.
-    const char *next = s + len + !len;
-
-    using uchar = unsigned char;
-
-    // Assume a four-byte character and load four bytes. Unused bits are
-    // shifted out.
-    *c = uint32_t(uchar(s[0]) & masks[len]) << 18;
-    *c |= uint32_t(uchar(s[1]) & 0x3f) << 12;
-    *c |= uint32_t(uchar(s[2]) & 0x3f) << 6;
-    *c |= uint32_t(uchar(s[3]) & 0x3f) << 0;
-    *c >>= shiftc[len];
-
-    // Accumulate the various error conditions.
-    *e = (*c < mins[len]) << 6;      // non-canonical encoding
-    *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half?
-    *e |= (*c > 0x10FFFF) << 8;      // out of range?
-    *e |= (uchar(s[1]) & 0xc0) >> 2;
-    *e |= (uchar(s[2]) & 0xc0) >> 4;
-    *e |= uchar(s[3]) >> 6;
-    *e ^= 0x2a; // top two bits of each tail byte correct?
-    *e >>= shifte[len];
-
-    return next;
-}
-
-constexpr uint32_t invalid_code_point = ~uint32_t();
-
-// Invokes f(cp, sv) for every code point cp in s with sv being the string view
-// corresponding to the code point. cp is invalid_code_point on error.
-template <typename F>
-FMT_CONSTEXPR void for_each_codepoint(string_view s, F f)
-{
-    auto decode = [f](const char *buf_ptr, const char *ptr)
-    {
-        auto cp     = uint32_t();
-        auto error  = 0;
-        auto end    = utf8_decode(buf_ptr, &cp, &error);
-        bool result = f(error ? invalid_code_point : cp, string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr)));
-        return result ? (error ? buf_ptr + 1 : end) : nullptr;
-    };
-    auto         p          = s.data();
-    const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars.
-    if(s.size() >= block_size)
-    {
-        for(auto end = p + s.size() - block_size + 1; p < end;)
-        {
-            p = decode(p, p);
-            if(!p)
-                return;
-        }
-    }
-    if(auto num_chars_left = s.data() + s.size() - p)
-    {
-        char buf[2 * block_size - 1] = {};
-        copy_str<char>(p, p + num_chars_left, buf);
-        const char *buf_ptr = buf;
-        do
-        {
-            auto end = decode(buf_ptr, p);
-            if(!end)
-                return;
-            p += end - buf_ptr;
-            buf_ptr = end;
-        } while(buf_ptr - buf < num_chars_left);
-    }
-}
-
-template <typename Char>
-inline auto compute_width(basic_string_view<Char> s) -> size_t
-{
-    return s.size();
-}
-
-// Computes approximate display width of a UTF-8 string.
-FMT_CONSTEXPR inline size_t compute_width(string_view s)
-{
-    size_t num_code_points = 0;
-    // It is not a lambda for compatibility with C++14.
-    struct count_code_points
-    {
-        size_t            *count;
-        FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool
-        {
-            *count += detail::to_unsigned(
-                1 + (cp >= 0x1100 && (cp <= 0x115f || // Hangul Jamo init. consonants
-                                      cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET
-                                      cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET
-                                      // CJK ... Yi except IDEOGRAPHIC HALF FILL SPACE:
-                                      (cp >= 0x2e80 && cp <= 0xa4cf && cp != 0x303f) ||
-                                      (cp >= 0xac00 && cp <= 0xd7a3) ||   // Hangul Syllables
-                                      (cp >= 0xf900 && cp <= 0xfaff) ||   // CJK Compatibility Ideographs
-                                      (cp >= 0xfe10 && cp <= 0xfe19) ||   // Vertical Forms
-                                      (cp >= 0xfe30 && cp <= 0xfe6f) ||   // CJK Compatibility Forms
-                                      (cp >= 0xff00 && cp <= 0xff60) ||   // Fullwidth Forms
-                                      (cp >= 0xffe0 && cp <= 0xffe6) ||   // Fullwidth Forms
-                                      (cp >= 0x20000 && cp <= 0x2fffd) || // CJK
-                                      (cp >= 0x30000 && cp <= 0x3fffd) ||
-                                      // Miscellaneous Symbols and Pictographs + Emoticons:
-                                      (cp >= 0x1f300 && cp <= 0x1f64f) ||
-                                      // Supplemental Symbols and Pictographs:
-                                      (cp >= 0x1f900 && cp <= 0x1f9ff))));
-            return true;
-        }
-    };
-    for_each_codepoint(s, count_code_points{&num_code_points});
-    return num_code_points;
-}
-
-inline auto compute_width(basic_string_view<char8_type> s) -> size_t
-{
-    return compute_width(string_view(reinterpret_cast<const char *>(s.data()), s.size()));
-}
-
-template <typename Char>
-inline auto code_point_index(basic_string_view<Char> s, size_t n) -> size_t
-{
-    size_t size = s.size();
-    return n < size ? n : size;
-}
-
-// Calculates the index of the nth code point in a UTF-8 string.
-inline auto code_point_index(string_view s, size_t n) -> size_t
-{
-    const char *data            = s.data();
-    size_t      num_code_points = 0;
-    for(size_t i = 0, size = s.size(); i != size; ++i)
-    {
-        if((data[i] & 0xc0) != 0x80 && ++num_code_points > n)
-            return i;
-    }
-    return s.size();
-}
-
-inline auto code_point_index(basic_string_view<char8_type> s, size_t n) -> size_t
-{
-    return code_point_index(string_view(reinterpret_cast<const char *>(s.data()), s.size()), n);
-}
-
-#ifndef FMT_USE_FLOAT128
-#ifdef __SIZEOF_FLOAT128__
-#define FMT_USE_FLOAT128 1
-#else
-#define FMT_USE_FLOAT128 0
-#endif
-#endif
-#if FMT_USE_FLOAT128
-using float128 = __float128;
-#else
-using float128 = void;
-#endif
-template <typename T>
-using is_float128 = std::is_same<T, float128>;
-
-template <typename T>
-using is_floating_point = bool_constant<std::is_floating_point<T>::value || is_float128<T>::value>;
-
-template <typename T, bool = std::is_floating_point<T>::value>
-struct is_fast_float : bool_constant<std::numeric_limits<T>::is_iec559 && sizeof(T) <= sizeof(double)>
-{
-};
-template <typename T>
-struct is_fast_float<T, false> : std::false_type
-{
-};
-
-template <typename T>
-using is_double_double = bool_constant<std::numeric_limits<T>::digits == 106>;
-
-#ifndef FMT_USE_FULL_CACHE_DRAGONBOX
-#define FMT_USE_FULL_CACHE_DRAGONBOX 0
-#endif
-
-template <typename T>
-template <typename U>
-void buffer<T>::append(const U *begin, const U *end)
-{
-    while(begin != end)
-    {
-        auto count = to_unsigned(end - begin);
-        try_reserve(size_ + count);
-        auto free_cap = capacity_ - size_;
-        if(free_cap < count)
-            count = free_cap;
-        std::uninitialized_copy_n(begin, count, make_checked(ptr_ + size_, count));
-        size_ += count;
-        begin += count;
-    }
-}
-
-template <typename T, typename Enable = void>
-struct is_locale : std::false_type
-{
-};
-template <typename T>
-struct is_locale<T, void_t<decltype(T::classic())>> : std::true_type
-{
-};
-} // namespace detail
-
-FMT_MODULE_EXPORT_BEGIN
-
-// The number of characters to store in the basic_memory_buffer object itself
-// to avoid dynamic memory allocation.
-enum
-{
-    inline_buffer_size = 500
-};
-
-/**
-  \rst
-  A dynamically growing memory buffer for trivially copyable/constructible types
-  with the first ``SIZE`` elements stored in the object itself.
-
-  You can use the ``memory_buffer`` type alias for ``char`` instead.
-
-  **Example**::
-
-     auto out = fmt::memory_buffer();
-     format_to(std::back_inserter(out), "The answer is {}.", 42);
-
-  This will append the following output to the ``out`` object:
-
-  .. code-block:: none
-
-     The answer is 42.
-
-  The output can be converted to an ``std::string`` with ``to_string(out)``.
-  \endrst
- */
-template <typename T, size_t SIZE = inline_buffer_size, typename Allocator = std::allocator<T>>
-class basic_memory_buffer final : public detail::buffer<T>
-{
-private:
-    T store_[SIZE];
-
-    // Don't inherit from Allocator avoid generating type_info for it.
-    Allocator alloc_;
-
-    // Deallocate memory allocated by the buffer.
-    FMT_CONSTEXPR20 void deallocate()
-    {
-        T *data = this->data();
-        if(data != store_)
-            alloc_.deallocate(data, this->capacity());
-    }
-
-protected:
-    FMT_CONSTEXPR20 void grow(size_t size) override;
-
-public:
-    using value_type      = T;
-    using const_reference = const T &;
-
-    FMT_CONSTEXPR20 explicit basic_memory_buffer(const Allocator &alloc = Allocator()) : alloc_(alloc)
-    {
-        this->set(store_, SIZE);
-        if(detail::is_constant_evaluated())
-            detail::fill_n(store_, SIZE, T());
-    }
-    FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); }
-
-private:
-    // Move data from other to this buffer.
-    FMT_CONSTEXPR20 void move(basic_memory_buffer &other)
-    {
-        alloc_      = std::move(other.alloc_);
-        T     *data = other.data();
-        size_t size = other.size(), capacity = other.capacity();
-        if(data == other.store_)
-        {
-            this->set(store_, capacity);
-            detail::copy_str<T>(other.store_, other.store_ + size, detail::make_checked(store_, capacity));
-        }
-        else
-        {
-            this->set(data, capacity);
-            // Set pointer to the inline array so that delete is not called
-            // when deallocating.
-            other.set(other.store_, 0);
-            other.clear();
-        }
-        this->resize(size);
-    }
-
-public:
-    /**
-      \rst
-      Constructs a :class:`fmt::basic_memory_buffer` object moving the content
-      of the other object to it.
-      \endrst
-     */
-    FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer &&other) noexcept { move(other); }
-
-    /**
-      \rst
-      Moves the content of the other ``basic_memory_buffer`` object to this one.
-      \endrst
-     */
-    auto operator=(basic_memory_buffer &&other) noexcept -> basic_memory_buffer &
-    {
-        FMT_ASSERT(this != &other, "");
-        deallocate();
-        move(other);
-        return *this;
-    }
-
-    // Returns a copy of the allocator associated with this buffer.
-    auto get_allocator() const -> Allocator { return alloc_; }
-
-    /**
-      Resizes the buffer to contain *count* elements. If T is a POD type new
-      elements may not be initialized.
-     */
-    FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); }
-
-    /** Increases the buffer capacity to *new_capacity*. */
-    void reserve(size_t new_capacity) { this->try_reserve(new_capacity); }
-
-    // Directly append data into the buffer
-    using detail::buffer<T>::append;
-    template <typename ContiguousRange>
-    void append(const ContiguousRange &range)
-    {
-        append(range.data(), range.data() + range.size());
-    }
-};
-
-template <typename T, size_t SIZE, typename Allocator>
-FMT_CONSTEXPR20 void basic_memory_buffer<T, SIZE, Allocator>::grow(size_t size)
-{
-    detail::abort_fuzzing_if(size > 5000);
-    const size_t max_size     = std::allocator_traits<Allocator>::max_size(alloc_);
-    size_t       old_capacity = this->capacity();
-    size_t       new_capacity = old_capacity + old_capacity / 2;
-    if(size > new_capacity)
-        new_capacity = size;
-    else if(new_capacity > max_size)
-        new_capacity = size > max_size ? size : max_size;
-    T *old_data = this->data();
-    T *new_data = std::allocator_traits<Allocator>::allocate(alloc_, new_capacity);
-    // The following code doesn't throw, so the raw pointer above doesn't leak.
-    std::uninitialized_copy(old_data, old_data + this->size(), detail::make_checked(new_data, new_capacity));
-    this->set(new_data, new_capacity);
-    // deallocate must not throw according to the standard, but even if it does,
-    // the buffer already uses the new storage and will deallocate it in
-    // destructor.
-    if(old_data != store_)
-        alloc_.deallocate(old_data, old_capacity);
-}
-
-using memory_buffer = basic_memory_buffer<char>;
-
-template <typename T, size_t SIZE, typename Allocator>
-struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type
-{
-};
-
-namespace detail
-{
-#ifdef _WIN32
-FMT_API bool write_console(std::FILE *f, string_view text);
-#endif
-FMT_API void print(std::FILE *, string_view);
-} // namespace detail
-
-/** A formatting error such as invalid format string. */
-FMT_CLASS_API
-class FMT_API format_error : public std::runtime_error
-{
-public:
-    explicit format_error(const char *message) : std::runtime_error(message) {}
-    explicit format_error(const std::string &message) : std::runtime_error(message) {}
-    format_error(const format_error &)            = default;
-    format_error &operator=(const format_error &) = default;
-    format_error(format_error &&)                 = default;
-    format_error &operator=(format_error &&)      = default;
-    ~format_error() noexcept override FMT_MSC_DEFAULT;
-};
-
-namespace detail_exported
-{
-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
-template <typename Char, size_t N>
-struct fixed_string
-{
-    constexpr fixed_string(const Char (&str)[N])
-    {
-        detail::copy_str<Char, const Char *, Char *>(static_cast<const Char *>(str), str + N, data);
-    }
-    Char data[N] = {};
-};
-#endif
-
-// Converts a compile-time string to basic_string_view.
-template <typename Char, size_t N>
-constexpr auto compile_string_to_view(const Char (&s)[N]) -> basic_string_view<Char>
-{
-    // Remove trailing NUL character if needed. Won't be present if this is used
-    // with a raw character array (i.e. not defined as a string).
-    return {s, N - (std::char_traits<Char>::to_int_type(s[N - 1]) == 0 ? 1 : 0)};
-}
-template <typename Char>
-constexpr auto compile_string_to_view(detail::std_string_view<Char> s) -> basic_string_view<Char>
-{
-    return {s.data(), s.size()};
-}
-} // namespace detail_exported
-
-FMT_BEGIN_DETAIL_NAMESPACE
-
-template <typename T>
-struct is_integral : std::is_integral<T>
-{
-};
-template <>
-struct is_integral<int128_opt> : std::true_type
-{
-};
-template <>
-struct is_integral<uint128_t> : std::true_type
-{
-};
-
-template <typename T>
-using is_signed = std::integral_constant<bool, std::numeric_limits<T>::is_signed || std::is_same<T, int128_opt>::value>;
-
-// Returns true if value is negative, false otherwise.
-// Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
-template <typename T, FMT_ENABLE_IF(is_signed<T>::value)>
-constexpr auto is_negative(T value) -> bool
-{
-    return value < 0;
-}
-template <typename T, FMT_ENABLE_IF(!is_signed<T>::value)>
-constexpr auto is_negative(T) -> bool
-{
-    return false;
-}
-
-template <typename T>
-FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool
-{
-    if(std::is_same<T, float>())
-        return FMT_USE_FLOAT;
-    if(std::is_same<T, double>())
-        return FMT_USE_DOUBLE;
-    if(std::is_same<T, long double>())
-        return FMT_USE_LONG_DOUBLE;
-    return true;
-}
-
-// Smallest of uint32_t, uint64_t, uint128_t that is large enough to
-// represent all values of an integral type T.
-template <typename T>
-using uint32_or_64_or_128_t = conditional_t<
-    num_bits<T>() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS,
-    uint32_t,
-    conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;
-template <typename T>
-using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>;
-
-#define FMT_POWERS_OF_10(factor)                                                                                       \
-    factor * 10, (factor) *100, (factor) *1000, (factor) *10000, (factor) *100000, (factor) *1000000,                  \
-        (factor) *10000000, (factor) *100000000, (factor) *1000000000
-
-// Converts value in the range [0, 100) to a string.
-constexpr const char *digits2(size_t value)
-{
-    // GCC generates slightly better code when value is pointer-size.
-    return &"0001020304050607080910111213141516171819"
-            "2021222324252627282930313233343536373839"
-            "4041424344454647484950515253545556575859"
-            "6061626364656667686970717273747576777879"
-            "8081828384858687888990919293949596979899"[value * 2];
-}
-
-// Sign is a template parameter to workaround a bug in gcc 4.8.
-template <typename Char, typename Sign>
-constexpr Char sign(Sign s)
-{
-#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604
-    static_assert(std::is_same<Sign, sign_t>::value, "");
-#endif
-    return static_cast<Char>("\0-+ "[s]);
-}
-
-template <typename T>
-FMT_CONSTEXPR auto count_digits_fallback(T n) -> int
-{
-    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
-        // "Three Optimization Tips for C++". See speed-test for a comparison.
-        if(n < 10)
-            return count;
-        if(n < 100)
-            return count + 1;
-        if(n < 1000)
-            return count + 2;
-        if(n < 10000)
-            return count + 3;
-        n /= 10000u;
-        count += 4;
-    }
-}
-#if FMT_USE_INT128
-FMT_CONSTEXPR inline auto count_digits(uint128_opt n) -> int
-{
-    return count_digits_fallback(n);
-}
-#endif
-
-#ifdef FMT_BUILTIN_CLZLL
-// It is a separate function rather than a part of count_digits to workaround
-// the lack of static constexpr in constexpr functions.
-inline auto do_count_digits(uint64_t n) -> int
-{
-    // This has comparable performance to the version by Kendall Willets
-    // (https://github.com/fmtlib/format-benchmark/blob/master/digits10)
-    // but uses smaller tables.
-    // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)).
-    static constexpr uint8_t        bsr2log10[] = {1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4,  4,  5,  5,  5,
-                                                   6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  9,  9,  9,  10, 10, 10,
-                                                   10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
-                                                   15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20};
-    auto                            t           = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63];
-    static constexpr const uint64_t zero_or_powers_of_10[] = {
-        0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), 10000000000000000000ULL};
-    return t - (n < zero_or_powers_of_10[t]);
-}
-#endif
-
-// Returns the number of decimal digits in n. Leading zeros are not counted
-// except for n == 0 in which case count_digits returns 1.
-FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int
-{
-#ifdef FMT_BUILTIN_CLZLL
-    if(!is_constant_evaluated())
-    {
-        return do_count_digits(n);
-    }
-#endif
-    return count_digits_fallback(n);
-}
-
-// Counts the number of digits in n. BITS = log2(radix).
-template <int BITS, typename UInt>
-FMT_CONSTEXPR auto count_digits(UInt n) -> int
-{
-#ifdef FMT_BUILTIN_CLZ
-    if(!is_constant_evaluated() && num_bits<UInt>() == 32)
-        return (FMT_BUILTIN_CLZ(static_cast<uint32_t>(n) | 1) ^ 31) / BITS + 1;
-#endif
-    // Lambda avoids unreachable code warnings from NVHPC.
-    return [](UInt m)
-    {
-        int num_digits = 0;
-        do
-        {
-            ++num_digits;
-        } while((m >>= BITS) != 0);
-        return num_digits;
-    }(n);
-}
-
-#ifdef FMT_BUILTIN_CLZ
-// It is a separate function rather than a part of count_digits to workaround
-// the lack of static constexpr in constexpr functions.
-FMT_INLINE auto do_count_digits(uint32_t n) -> int
-{
-// An optimization by Kendall Willets from https://bit.ly/3uOIQrB.
-// This increments the upper 32 bits (log10(T) - 1) when >= T is added.
-#define FMT_INC(T) (((sizeof(#T) - 1ull) << 32) - T)
-    static constexpr uint64_t table[] = {
-        FMT_INC(0),          FMT_INC(0),          FMT_INC(0),          // 8
-        FMT_INC(10),         FMT_INC(10),         FMT_INC(10),         // 64
-        FMT_INC(100),        FMT_INC(100),        FMT_INC(100),        // 512
-        FMT_INC(1000),       FMT_INC(1000),       FMT_INC(1000),       // 4096
-        FMT_INC(10000),      FMT_INC(10000),      FMT_INC(10000),      // 32k
-        FMT_INC(100000),     FMT_INC(100000),     FMT_INC(100000),     // 256k
-        FMT_INC(1000000),    FMT_INC(1000000),    FMT_INC(1000000),    // 2048k
-        FMT_INC(10000000),   FMT_INC(10000000),   FMT_INC(10000000),   // 16M
-        FMT_INC(100000000),  FMT_INC(100000000),  FMT_INC(100000000),  // 128M
-        FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000), // 1024M
-        FMT_INC(1000000000), FMT_INC(1000000000)                       // 4B
-    };
-    auto inc = table[FMT_BUILTIN_CLZ(n | 1) ^ 31];
-    return static_cast<int>((n + inc) >> 32);
-}
-#endif
-
-// Optional version of count_digits for better performance on 32-bit platforms.
-FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int
-{
-#ifdef FMT_BUILTIN_CLZ
-    if(!is_constant_evaluated())
-    {
-        return do_count_digits(n);
-    }
-#endif
-    return count_digits_fallback(n);
-}
-
-template <typename Int>
-constexpr auto digits10() noexcept -> int
-{
-    return std::numeric_limits<Int>::digits10;
-}
-template <>
-constexpr auto digits10<int128_opt>() noexcept -> int
-{
-    return 38;
-}
-template <>
-constexpr auto digits10<uint128_t>() noexcept -> int
-{
-    return 38;
-}
-
-template <typename Char>
-struct thousands_sep_result
-{
-    std::string grouping;
-    Char        thousands_sep;
-};
-
-template <typename Char>
-FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char>;
-template <typename Char>
-inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<Char>
-{
-    auto result = thousands_sep_impl<char>(loc);
-    return {result.grouping, Char(result.thousands_sep)};
-}
-template <>
-inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<wchar_t>
-{
-    return thousands_sep_impl<wchar_t>(loc);
-}
-
-template <typename Char>
-FMT_API auto decimal_point_impl(locale_ref loc) -> Char;
-template <typename Char>
-inline auto decimal_point(locale_ref loc) -> Char
-{
-    return Char(decimal_point_impl<char>(loc));
-}
-template <>
-inline auto decimal_point(locale_ref loc) -> wchar_t
-{
-    return decimal_point_impl<wchar_t>(loc);
-}
-
-// Compares two characters for equality.
-template <typename Char>
-auto equal2(const Char *lhs, const char *rhs) -> bool
-{
-    return lhs[0] == Char(rhs[0]) && lhs[1] == Char(rhs[1]);
-}
-inline auto equal2(const char *lhs, const char *rhs) -> bool
-{
-    return memcmp(lhs, rhs, 2) == 0;
-}
-
-// Copies two characters from src to dst.
-template <typename Char>
-FMT_CONSTEXPR20 FMT_INLINE void copy2(Char *dst, const char *src)
-{
-    if(!is_constant_evaluated() && sizeof(Char) == sizeof(char))
-    {
-        memcpy(dst, src, 2);
-        return;
-    }
-    *dst++ = static_cast<Char>(*src++);
-    *dst   = static_cast<Char>(*src);
-}
-
-template <typename Iterator>
-struct format_decimal_result
-{
-    Iterator begin;
-    Iterator end;
-};
-
-// Formats a decimal unsigned integer value writing into out pointing to a
-// buffer of specified size. The caller must ensure that the buffer is large
-// enough.
-template <typename Char, typename UInt>
-FMT_CONSTEXPR20 auto format_decimal(Char *out, UInt value, int size) -> format_decimal_result<Char *>
-{
-    FMT_ASSERT(size >= count_digits(value), "invalid digit count");
-    out += size;
-    Char *end = out;
-    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
-        // "Three Optimization Tips for C++". See speed-test for a comparison.
-        out -= 2;
-        copy2(out, digits2(static_cast<size_t>(value % 100)));
-        value /= 100;
-    }
-    if(value < 10)
-    {
-        *--out = static_cast<Char>('0' + value);
-        return {out, end};
-    }
-    out -= 2;
-    copy2(out, digits2(static_cast<size_t>(value)));
-    return {out, end};
-}
-
-template <
-    typename Char,
-    typename UInt,
-    typename Iterator,
-    FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)>
-FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size) -> format_decimal_result<Iterator>
-{
-    // Buffer is large enough to hold all digits (digits10 + 1).
-    Char buffer[digits10<UInt>() + 1];
-    auto end = format_decimal(buffer, value, size).end;
-    return {out, detail::copy_str_noinline<Char>(buffer, end, out)};
-}
-
-template <unsigned BASE_BITS, typename Char, typename UInt>
-FMT_CONSTEXPR auto format_uint(Char *buffer, UInt value, int num_digits, bool upper = false) -> Char *
-{
-    buffer += num_digits;
-    Char *end = buffer;
-    do
-    {
-        const char *digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
-        unsigned    digit  = static_cast<unsigned>(value & ((1 << BASE_BITS) - 1));
-        *--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 Char, typename It, typename UInt>
-inline auto format_uint(It out, UInt value, int num_digits, bool upper = false) -> It
-{
-    if(auto ptr = to_pointer<Char>(out, to_unsigned(num_digits)))
-    {
-        format_uint<BASE_BITS>(ptr, value, num_digits, upper);
-        return out;
-    }
-    // Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
-    char buffer[num_bits<UInt>() / BASE_BITS + 1];
-    format_uint<BASE_BITS>(buffer, value, num_digits, upper);
-    return detail::copy_str_noinline<Char>(buffer, buffer + num_digits, out);
-}
-
-// A converter from UTF-8 to UTF-16.
-class utf8_to_utf16
-{
-private:
-    basic_memory_buffer<wchar_t> buffer_;
-
-public:
-    FMT_API explicit utf8_to_utf16(string_view s);
-         operator basic_string_view<wchar_t>() const { return {&buffer_[0], size()}; }
-    auto size() const -> size_t { return buffer_.size() - 1; }
-    auto c_str() const -> const wchar_t * { return &buffer_[0]; }
-    auto str() const -> std::wstring { return {&buffer_[0], size()}; }
-};
-
-namespace dragonbox
-{
-
-// Type-specific information that Dragonbox uses.
-template <typename T, typename Enable = void>
-struct float_info;
-
-template <>
-struct float_info<float>
-{
-    using carrier_uint                                    = uint32_t;
-    static const int exponent_bits                        = 8;
-    static const int kappa                                = 1;
-    static const int big_divisor                          = 100;
-    static const int small_divisor                        = 10;
-    static const int min_k                                = -31;
-    static const int max_k                                = 46;
-    static const int shorter_interval_tie_lower_threshold = -35;
-    static const int shorter_interval_tie_upper_threshold = -35;
-};
-
-template <>
-struct float_info<double>
-{
-    using carrier_uint                                    = uint64_t;
-    static const int exponent_bits                        = 11;
-    static const int kappa                                = 2;
-    static const int big_divisor                          = 1000;
-    static const int small_divisor                        = 100;
-    static const int min_k                                = -292;
-    static const int max_k                                = 326;
-    static const int shorter_interval_tie_lower_threshold = -77;
-    static const int shorter_interval_tie_upper_threshold = -77;
-};
-
-// An 80- or 128-bit floating point number.
-template <typename T>
-struct float_info<
-    T,
-    enable_if_t<std::numeric_limits<T>::digits == 64 || std::numeric_limits<T>::digits == 113 || is_float128<T>::value>>
-{
-    using carrier_uint             = detail::uint128_t;
-    static const int exponent_bits = 15;
-};
-
-// A double-double floating point number.
-template <typename T>
-struct float_info<T, enable_if_t<is_double_double<T>::value>>
-{
-    using carrier_uint = detail::uint128_t;
-};
-
-template <typename T>
-struct decimal_fp
-{
-    using significand_type = typename float_info<T>::carrier_uint;
-    significand_type significand;
-    int              exponent;
-};
-
-template <typename T>
-FMT_API auto to_decimal(T x) noexcept -> decimal_fp<T>;
-} // namespace dragonbox
-
-// Returns true iff Float has the implicit bit which is not stored.
-template <typename Float>
-constexpr bool has_implicit_bit()
-{
-    // An 80-bit FP number has a 64-bit significand an no implicit bit.
-    return std::numeric_limits<Float>::digits != 64;
-}
-
-// Returns the number of significand bits stored in Float. The implicit bit is
-// not counted since it is not stored.
-template <typename Float>
-constexpr int num_significand_bits()
-{
-    // std::numeric_limits may not support __float128.
-    return is_float128<Float>() ? 112 : (std::numeric_limits<Float>::digits - (has_implicit_bit<Float>() ? 1 : 0));
-}
-
-template <typename Float>
-constexpr auto exponent_mask() -> typename dragonbox::float_info<Float>::carrier_uint
-{
-    using uint = typename dragonbox::float_info<Float>::carrier_uint;
-    return ((uint(1) << dragonbox::float_info<Float>::exponent_bits) - 1) << num_significand_bits<Float>();
-}
-template <typename Float>
-constexpr auto exponent_bias() -> int
-{
-    // std::numeric_limits may not support __float128.
-    return is_float128<Float>() ? 16383 : std::numeric_limits<Float>::max_exponent - 1;
-}
-
-// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
-template <typename Char, typename It>
-FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It
-{
-    FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range");
-    if(exp < 0)
-    {
-        *it++ = static_cast<Char>('-');
-        exp   = -exp;
-    }
-    else
-    {
-        *it++ = static_cast<Char>('+');
-    }
-    if(exp >= 100)
-    {
-        const char *top = digits2(to_unsigned(exp / 100));
-        if(exp >= 1000)
-            *it++ = static_cast<Char>(top[0]);
-        *it++ = static_cast<Char>(top[1]);
-        exp %= 100;
-    }
-    const char *d = digits2(to_unsigned(exp));
-    *it++         = static_cast<Char>(d[0]);
-    *it++         = static_cast<Char>(d[1]);
-    return it;
-}
-
-// A floating-point number f * pow(2, e) where F is an unsigned type.
-template <typename F>
-struct basic_fp
-{
-    F   f;
-    int e;
-
-    static constexpr const int num_significand_bits = static_cast<int>(sizeof(F) * num_bits<unsigned char>());
-
-    constexpr basic_fp() : f(0), e(0) {}
-    constexpr basic_fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {}
-
-    // Constructs fp from an IEEE754 floating-point number.
-    template <typename Float>
-    FMT_CONSTEXPR basic_fp(Float n)
-    {
-        assign(n);
-    }
-
-    // Assigns n to this and return true iff predecessor is closer than successor.
-    template <typename Float, FMT_ENABLE_IF(!is_double_double<Float>::value)>
-    FMT_CONSTEXPR auto assign(Float n) -> bool
-    {
-        static_assert(std::numeric_limits<Float>::digits <= 113, "unsupported FP");
-        // Assume Float is in the format [sign][exponent][significand].
-        using carrier_uint                    = typename dragonbox::float_info<Float>::carrier_uint;
-        const auto num_float_significand_bits = detail::num_significand_bits<Float>();
-        const auto implicit_bit               = carrier_uint(1) << num_float_significand_bits;
-        const auto significand_mask           = implicit_bit - 1;
-        auto       u                          = bit_cast<carrier_uint>(n);
-        f                                     = static_cast<F>(u & significand_mask);
-        auto biased_e = static_cast<int>((u & exponent_mask<Float>()) >> num_float_significand_bits);
-        // The predecessor is closer if n is a normalized power of 2 (f == 0)
-        // other than the smallest normalized number (biased_e > 1).
-        auto is_predecessor_closer = f == 0 && biased_e > 1;
-        if(biased_e == 0)
-            biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
-        else if(has_implicit_bit<Float>())
-            f += static_cast<F>(implicit_bit);
-        e = biased_e - exponent_bias<Float>() - num_float_significand_bits;
-        if(!has_implicit_bit<Float>())
-            ++e;
-        return is_predecessor_closer;
-    }
-
-    template <typename Float, FMT_ENABLE_IF(is_double_double<Float>::value)>
-    FMT_CONSTEXPR auto assign(Float n) -> bool
-    {
-        static_assert(std::numeric_limits<double>::is_iec559, "unsupported FP");
-        return assign(static_cast<double>(n));
-    }
-};
-
-using fp = basic_fp<unsigned long long>;
-
-// Normalizes the value converted from double and multiplied by (1 << SHIFT).
-template <int SHIFT = 0, typename F>
-FMT_CONSTEXPR basic_fp<F> normalize(basic_fp<F> value)
-{
-    // Handle subnormals.
-    const auto implicit_bit         = F(1) << num_significand_bits<double>();
-    const auto shifted_implicit_bit = implicit_bit << SHIFT;
-    while((value.f & shifted_implicit_bit) == 0)
-    {
-        value.f <<= 1;
-        --value.e;
-    }
-    // Subtract 1 to account for hidden bit.
-    const auto offset = basic_fp<F>::num_significand_bits - num_significand_bits<double>() - SHIFT - 1;
-    value.f <<= offset;
-    value.e -= offset;
-    return value;
-}
-
-// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
-FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs)
-{
-#if FMT_USE_INT128
-    auto product = static_cast<__uint128_t>(lhs) * rhs;
-    auto f       = static_cast<uint64_t>(product >> 64);
-    return (static_cast<uint64_t>(product) & (1ULL << 63)) != 0 ? f + 1 : f;
-#else
-    // Multiply 32-bit parts of significands.
-    uint64_t mask = (1ULL << 32) - 1;
-    uint64_t a = lhs >> 32, b = lhs & mask;
-    uint64_t c = rhs >> 32, d = rhs & mask;
-    uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
-    // Compute mid 64-bit of result and round.
-    uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
-    return ac + (ad >> 32) + (bc >> 32) + (mid >> 32);
-#endif
-}
-
-FMT_CONSTEXPR inline fp operator*(fp x, fp y)
-{
-    return {multiply(x.f, y.f), x.e + y.e + 64};
-}
-
-template <typename T = void>
-struct basic_data
-{
-    // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
-    // These are generated by support/compute-powers.py.
-    static constexpr uint64_t pow10_significands[87] = {
-        0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d,
-        0xe61acf033d1a45df, 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, 0x8dd01fad907ffc3c,
-        0xd3515c2831559a83, 0x9d71ac8fada6c9b5, 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
-        0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, 0xa086cfcd97bf97f4, 0xef340a98172aace5,
-        0xb23867fb2a35b28e, 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, 0xdbac6c247d62a584,
-        0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
-        0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, 0xf8a95fcf88747d94, 0xb94470938fa89bcf,
-        0x8a08f0f8bf0f156b, 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, 0xaa242499697392d3,
-        0xfd87b5f28300ca0e, 0xbce5086492111aeb, 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
-        0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70,
-        0xd5d238a4abe98068, 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, 0x83c7088e1aab65db,
-        0xc45d1df942711d9a, 0x924d692ca61be758, 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
-        0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3,
-        0xa59bc234db398c25, 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, 0xcc20ce9bd35c78a5,
-        0x98165af37b2153df, 0xe2a0b5dc971f303a, 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
-        0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, 0xe7109bfba19c0c9d, 0xac2820d9623bf429,
-        0x80444b5e7aa7cf85, 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, 0x9e19db92b4e31ba9,
-        0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
-    };
-
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wnarrowing"
-#endif
-    // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
-    // to significands above.
-    static constexpr int16_t pow10_exponents[87] = {
-        -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, -927, -901, -874, -847,
-        -821,  -794,  -768,  -741,  -715,  -688,  -661,  -635,  -608,  -582, -555, -529, -502, -475, -449,
-        -422,  -396,  -369,  -343,  -316,  -289,  -263,  -236,  -210,  -183, -157, -130, -103, -77,  -50,
-        -24,   3,     30,    56,    83,    109,   136,   162,   189,   216,  242,  269,  295,  322,  348,
-        375,   402,   428,   455,   481,   508,   534,   561,   588,   614,  641,  667,  694,  720,  747,
-        774,   800,   827,   853,   880,   907,   933,   960,   986,   1013, 1039, 1066};
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
-#pragma GCC diagnostic pop
-#endif
-
-    static constexpr uint64_t power_of_10_64[20] =
-        {1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL), 10000000000000000000ULL};
-};
-
-#if FMT_CPLUSPLUS < 201703L
-template <typename T>
-constexpr uint64_t basic_data<T>::pow10_significands[];
-template <typename T>
-constexpr int16_t basic_data<T>::pow10_exponents[];
-template <typename T>
-constexpr uint64_t basic_data<T>::power_of_10_64[];
-#endif
-
-// This is a struct rather than an alias to avoid shadowing warnings in gcc.
-struct data : basic_data<>
-{
-};
-
-// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
-// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
-FMT_CONSTEXPR inline fp get_cached_power(int min_exponent, int &pow10_exponent)
-{
-    const int shift = 32;
-    // log10(2) = 0x0.4d104d427de7fbcc...
-    const int64_t significand = 0x4d104d427de7fbcc;
-    int           index       = static_cast<int>(
-        ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) + ((int64_t(1) << shift) - 1)) // ceil
-        >> 32 // arithmetic shift
-    );
-    // Decimal exponent of the first (smallest) cached power of 10.
-    const int first_dec_exp = -348;
-    // Difference between 2 consecutive decimal exponents in cached powers of 10.
-    const int dec_exp_step = 8;
-    index                  = (index - first_dec_exp - 1) / dec_exp_step + 1;
-    pow10_exponent         = first_dec_exp + index * dec_exp_step;
-    // Using *(x + index) instead of x[index] avoids an issue with some compilers
-    // using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode).
-    return {*(data::pow10_significands + index), *(data::pow10_exponents + index)};
-}
-
-#ifndef _MSC_VER
-#define FMT_SNPRINTF snprintf
-#else
-FMT_API auto fmt_snprintf(char *buf, size_t size, const char *fmt, ...) -> int;
-#define FMT_SNPRINTF fmt_snprintf
-#endif // _MSC_VER
-
-// Formats a floating-point number with snprintf using the hexfloat format.
-template <typename T>
-auto snprintf_float(T value, int precision, float_specs specs, buffer<char> &buf) -> int
-{
-    // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
-    FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
-    FMT_ASSERT(specs.format == float_format::hex, "");
-    static_assert(!std::is_same<T, float>::value, "");
-
-    // Build the format string.
-    char  format[7]; // The longest format is "%#.*Le".
-    char *format_ptr = format;
-    *format_ptr++    = '%';
-    if(specs.showpoint)
-        *format_ptr++ = '#';
-    if(precision >= 0)
-    {
-        *format_ptr++ = '.';
-        *format_ptr++ = '*';
-    }
-    if(std::is_same<T, long double>())
-        *format_ptr++ = 'L';
-    *format_ptr++ = specs.upper ? 'A' : 'a';
-    *format_ptr   = '\0';
-
-    // Format using snprintf.
-    auto offset = buf.size();
-    for(;;)
-    {
-        auto begin    = buf.data() + offset;
-        auto capacity = buf.capacity() - offset;
-        abort_fuzzing_if(precision > 100000);
-        // Suppress the warning about a nonliteral format string.
-        // Cannot use auto because of a bug in MinGW (#1532).
-        int (*snprintf_ptr)(char *, size_t, const char *, ...) = FMT_SNPRINTF;
-        int result = precision >= 0 ? snprintf_ptr(begin, capacity, format, precision, value) :
-                                      snprintf_ptr(begin, capacity, format, value);
-        if(result < 0)
-        {
-            // The buffer will grow exponentially.
-            buf.try_reserve(buf.capacity() + 1);
-            continue;
-        }
-        auto size = to_unsigned(result);
-        // Size equal to capacity means that the last character was truncated.
-        if(size < capacity)
-        {
-            buf.try_resize(size + offset);
-            return 0;
-        }
-        buf.try_reserve(size + offset + 1); // Add 1 for the terminating '\0'.
-    }
-}
-
-template <typename T>
-using convert_float_result = conditional_t<std::is_same<T, float>::value || sizeof(T) == sizeof(double), double, T>;
-
-template <typename T>
-constexpr auto convert_float(T value) -> convert_float_result<T>
-{
-    return static_cast<convert_float_result<T>>(value);
-}
-
-template <typename OutputIt, typename Char>
-FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, const fill_t<Char> &fill) -> OutputIt
-{
-    auto fill_size = fill.size();
-    if(fill_size == 1)
-        return detail::fill_n(it, n, fill[0]);
-    auto data = fill.data();
-    for(size_t i = 0; i < n; ++i)
-        it = copy_str<Char>(data, data + fill_size, it);
-    return it;
-}
-
-// Writes the output of f, padded according to format specifications in specs.
-// size: output size in code units.
-// width: output display width in (terminal) column positions.
-template <align::type align = align::left, typename OutputIt, typename Char, typename F>
-FMT_CONSTEXPR auto write_padded(OutputIt out, const basic_format_specs<Char> &specs, size_t size, size_t width, F &&f)
-    -> OutputIt
-{
-    static_assert(align == align::left || align == align::right, "");
-    unsigned spec_width = to_unsigned(specs.width);
-    size_t   padding    = spec_width > width ? spec_width - width : 0;
-    // Shifts are encoded as string literals because static constexpr is not
-    // supported in constexpr functions.
-    auto  *shifts        = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01";
-    size_t left_padding  = padding >> shifts[specs.align];
-    size_t right_padding = padding - left_padding;
-    auto   it            = reserve(out, size + padding * specs.fill.size());
-    if(left_padding != 0)
-        it = fill(it, left_padding, specs.fill);
-    it = f(it);
-    if(right_padding != 0)
-        it = fill(it, right_padding, specs.fill);
-    return base_iterator(out, it);
-}
-
-template <align::type align = align::left, typename OutputIt, typename Char, typename F>
-constexpr auto write_padded(OutputIt out, const basic_format_specs<Char> &specs, size_t size, F &&f) -> OutputIt
-{
-    return write_padded<align>(out, specs, size, size, f);
-}
-
-template <align::type align = align::left, typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, const basic_format_specs<Char> &specs) -> OutputIt
-{
-    return write_padded<align>(
-        out,
-        specs,
-        bytes.size(),
-        [bytes](reserve_iterator<OutputIt> it)
-        {
-            const char *data = bytes.data();
-            return copy_str<Char>(data, data + bytes.size(), it);
-        });
-}
-
-template <typename Char, typename OutputIt, typename UIntPtr>
-auto write_ptr(OutputIt out, UIntPtr value, const basic_format_specs<Char> *specs) -> OutputIt
-{
-    int  num_digits = count_digits<4>(value);
-    auto size       = to_unsigned(num_digits) + size_t(2);
-    auto write      = [=](reserve_iterator<OutputIt> it)
-    {
-        *it++ = static_cast<Char>('0');
-        *it++ = static_cast<Char>('x');
-        return format_uint<4, Char>(it, value, num_digits);
-    };
-    return specs ? write_padded<align::right>(out, *specs, size, write) : base_iterator(out, write(reserve(out, size)));
-}
-
-// Returns true iff the code point cp is printable.
-FMT_API auto is_printable(uint32_t cp) -> bool;
-
-inline auto needs_escape(uint32_t cp) -> bool
-{
-    return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' || !is_printable(cp);
-}
-
-template <typename Char>
-struct find_escape_result
-{
-    const Char *begin;
-    const Char *end;
-    uint32_t    cp;
-};
-
-template <typename Char>
-using make_unsigned_char =
-    typename conditional_t<std::is_integral<Char>::value, std::make_unsigned<Char>, type_identity<uint32_t>>::type;
-
-template <typename Char>
-auto find_escape(const Char *begin, const Char *end) -> find_escape_result<Char>
-{
-    for(; begin != end; ++begin)
-    {
-        uint32_t cp = static_cast<make_unsigned_char<Char>>(*begin);
-        if(const_check(sizeof(Char) == 1) && cp >= 0x80)
-            continue;
-        if(needs_escape(cp))
-            return {begin, begin + 1, cp};
-    }
-    return {begin, nullptr, 0};
-}
-
-inline auto find_escape(const char *begin, const char *end) -> find_escape_result<char>
-{
-    if(!is_utf8())
-        return find_escape<char>(begin, end);
-    auto result = find_escape_result<char>{end, nullptr, 0};
-    for_each_codepoint(
-        string_view(begin, to_unsigned(end - begin)),
-        [&](uint32_t cp, string_view sv)
-        {
-            if(needs_escape(cp))
-            {
-                result = {sv.begin(), sv.end(), cp};
-                return false;
-            }
-            return true;
-        });
-    return result;
-}
-
-#define FMT_STRING_IMPL(s, base, explicit)                                                                             \
-    []                                                                                                                 \
-    {                                                                                                                  \
-        /* Use the hidden visibility as a workaround for a GCC bug (#1973). */                                         \
-        /* Use a macro-like name to avoid shadowing warnings. */                                                       \
-        struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base                                                     \
-        {                                                                                                              \
-            using char_type                         FMT_MAYBE_UNUSED = fmt::remove_cvref_t<decltype(s[0])>;            \
-            FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit operator fmt::basic_string_view<char_type>() const                 \
-            {                                                                                                          \
-                return fmt::detail_exported::compile_string_to_view<char_type>(s);                                     \
-            }                                                                                                          \
-        };                                                                                                             \
-        return FMT_COMPILE_STRING();                                                                                   \
-    }()
-
-/**
-  \rst
-  Constructs a compile-time format string from a string literal *s*.
-
-  **Example**::
-
-    // A compile-time error because 'd' is an invalid specifier for strings.
-    std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
-  \endrst
- */
-#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, )
-
-template <size_t width, typename Char, typename OutputIt>
-auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt
-{
-    *out++ = static_cast<Char>('\\');
-    *out++ = static_cast<Char>(prefix);
-    Char buf[width];
-    fill_n(buf, width, static_cast<Char>('0'));
-    format_uint<4>(buf, cp, width);
-    return copy_str<Char>(buf, buf + width, out);
-}
-
-template <typename OutputIt, typename Char>
-auto write_escaped_cp(OutputIt out, const find_escape_result<Char> &escape) -> OutputIt
-{
-    auto c = static_cast<Char>(escape.cp);
-    switch(escape.cp)
-    {
-        case '\n':
-            *out++ = static_cast<Char>('\\');
-            c      = static_cast<Char>('n');
-            break;
-        case '\r':
-            *out++ = static_cast<Char>('\\');
-            c      = static_cast<Char>('r');
-            break;
-        case '\t':
-            *out++ = static_cast<Char>('\\');
-            c      = static_cast<Char>('t');
-            break;
-        case '"':
-            FMT_FALLTHROUGH;
-        case '\'':
-            FMT_FALLTHROUGH;
-        case '\\':
-            *out++ = static_cast<Char>('\\');
-            break;
-        default:
-            if(is_utf8())
-            {
-                if(escape.cp < 0x100)
-                {
-                    return write_codepoint<2, Char>(out, 'x', escape.cp);
-                }
-                if(escape.cp < 0x10000)
-                {
-                    return write_codepoint<4, Char>(out, 'u', escape.cp);
-                }
-                if(escape.cp < 0x110000)
-                {
-                    return write_codepoint<8, Char>(out, 'U', escape.cp);
-                }
-            }
-            for(Char escape_char : basic_string_view<Char>(escape.begin, to_unsigned(escape.end - escape.begin)))
-            {
-                out = write_codepoint<2, Char>(out, 'x', static_cast<uint32_t>(escape_char) & 0xFF);
-            }
-            return out;
-    }
-    *out++ = c;
-    return out;
-}
-
-template <typename Char, typename OutputIt>
-auto write_escaped_string(OutputIt out, basic_string_view<Char> str) -> OutputIt
-{
-    *out++     = static_cast<Char>('"');
-    auto begin = str.begin(), end = str.end();
-    do
-    {
-        auto escape = find_escape(begin, end);
-        out         = copy_str<Char>(begin, escape.begin, out);
-        begin       = escape.end;
-        if(!begin)
-            break;
-        out = write_escaped_cp<OutputIt, Char>(out, escape);
-    } while(begin != end);
-    *out++ = static_cast<Char>('"');
-    return out;
-}
-
-template <typename Char, typename OutputIt>
-auto write_escaped_char(OutputIt out, Char v) -> OutputIt
-{
-    *out++ = static_cast<Char>('\'');
-    if((needs_escape(static_cast<uint32_t>(v)) && v != static_cast<Char>('"')) || v == static_cast<Char>('\''))
-    {
-        out = write_escaped_cp(out, find_escape_result<Char>{&v, &v + 1, static_cast<uint32_t>(v)});
-    }
-    else
-    {
-        *out++ = v;
-    }
-    *out++ = static_cast<Char>('\'');
-    return out;
-}
-
-template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write_char(OutputIt out, Char value, const basic_format_specs<Char> &specs) -> OutputIt
-{
-    bool is_debug = specs.type == presentation_type::debug;
-    return write_padded(
-        out,
-        specs,
-        1,
-        [=](reserve_iterator<OutputIt> it)
-        {
-            if(is_debug)
-                return write_escaped_char(it, value);
-            *it++ = value;
-            return it;
-        });
-}
-template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write(OutputIt out, Char value, const basic_format_specs<Char> &specs, locale_ref loc = {})
-    -> OutputIt
-{
-    return check_char_specs(specs) ? write_char(out, value, specs) : write(out, static_cast<int>(value), specs, loc);
-}
-
-// Data for write_int that doesn't depend on output iterator type. It is used to
-// avoid template code bloat.
-template <typename Char>
-struct write_int_data
-{
-    size_t size;
-    size_t padding;
-
-    FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix, const basic_format_specs<Char> &specs) :
-        size((prefix >> 24) + to_unsigned(num_digits)), padding(0)
-    {
-        if(specs.align == align::numeric)
-        {
-            auto width = to_unsigned(specs.width);
-            if(width > size)
-            {
-                padding = width - size;
-                size    = width;
-            }
-        }
-        else if(specs.precision > num_digits)
-        {
-            size    = (prefix >> 24) + to_unsigned(specs.precision);
-            padding = to_unsigned(specs.precision - num_digits);
-        }
-    }
-};
-
-// Writes an integer in the format
-//   <left-padding><prefix><numeric-padding><digits><right-padding>
-// where <digits> are written by write_digits(it).
-// prefix contains chars in three lower bytes and the size in the fourth byte.
-template <typename OutputIt, typename Char, typename W>
-FMT_CONSTEXPR FMT_INLINE auto
-write_int(OutputIt out, int num_digits, unsigned prefix, const basic_format_specs<Char> &specs, W write_digits)
-    -> OutputIt
-{
-    // Slightly faster check for specs.width == 0 && specs.precision == -1.
-    if((specs.width | (specs.precision + 1)) == 0)
-    {
-        auto it = reserve(out, to_unsigned(num_digits) + (prefix >> 24));
-        if(prefix != 0)
-        {
-            for(unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
-                *it++ = static_cast<Char>(p & 0xff);
-        }
-        return base_iterator(out, write_digits(it));
-    }
-    auto data = write_int_data<Char>(num_digits, prefix, specs);
-    return write_padded<align::right>(
-        out,
-        specs,
-        data.size,
-        [=](reserve_iterator<OutputIt> it)
-        {
-            for(unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
-                *it++ = static_cast<Char>(p & 0xff);
-            it = detail::fill_n(it, data.padding, static_cast<Char>('0'));
-            return write_digits(it);
-        });
-}
-
-template <typename Char>
-class digit_grouping
-{
-private:
-    thousands_sep_result<Char> sep_;
-
-    struct next_state
-    {
-        std::string::const_iterator group;
-        int                         pos;
-    };
-    next_state initial_state() const { return {sep_.grouping.begin(), 0}; }
-
-    // Returns the next digit group separator position.
-    int next(next_state &state) const
-    {
-        if(!sep_.thousands_sep)
-            return max_value<int>();
-        if(state.group == sep_.grouping.end())
-            return state.pos += sep_.grouping.back();
-        if(*state.group <= 0 || *state.group == max_value<char>())
-            return max_value<int>();
-        state.pos += *state.group++;
-        return state.pos;
-    }
-
-public:
-    explicit digit_grouping(locale_ref loc, bool localized = true)
-    {
-        if(localized)
-            sep_ = thousands_sep<Char>(loc);
-        else
-            sep_.thousands_sep = Char();
-    }
-    explicit digit_grouping(thousands_sep_result<Char> sep) : sep_(sep) {}
-
-    Char separator() const { return sep_.thousands_sep; }
-
-    int count_separators(int num_digits) const
-    {
-        int  count = 0;
-        auto state = initial_state();
-        while(num_digits > next(state))
-            ++count;
-        return count;
-    }
-
-    // Applies grouping to digits and write the output to out.
-    template <typename Out, typename C>
-    Out apply(Out out, basic_string_view<C> digits) const
-    {
-        auto num_digits = static_cast<int>(digits.size());
-        auto separators = basic_memory_buffer<int>();
-        separators.push_back(0);
-        auto state = initial_state();
-        while(int i = next(state))
-        {
-            if(i >= num_digits)
-                break;
-            separators.push_back(i);
-        }
-        for(int i = 0, sep_index = static_cast<int>(separators.size() - 1); i < num_digits; ++i)
-        {
-            if(num_digits - i == separators[sep_index])
-            {
-                *out++ = separator();
-                --sep_index;
-            }
-            *out++ = static_cast<Char>(digits[to_unsigned(i)]);
-        }
-        return out;
-    }
-};
-
-template <typename OutputIt, typename UInt, typename Char>
-auto write_int_localized(
-    OutputIt                        out,
-    UInt                            value,
-    unsigned                        prefix,
-    const basic_format_specs<Char> &specs,
-    const digit_grouping<Char>     &grouping) -> OutputIt
-{
-    static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
-    int  num_digits = count_digits(value);
-    char digits[40];
-    format_decimal(digits, value, num_digits);
-    unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits + grouping.count_separators(num_digits));
-    return write_padded<align::right>(
-        out,
-        specs,
-        size,
-        size,
-        [&](reserve_iterator<OutputIt> it)
-        {
-            if(prefix != 0)
-            {
-                char sign = static_cast<char>(prefix);
-                *it++     = static_cast<Char>(sign);
-            }
-            return grouping.apply(it, string_view(digits, to_unsigned(num_digits)));
-        });
-}
-
-template <typename OutputIt, typename UInt, typename Char>
-auto write_int_localized(
-    OutputIt                       &out,
-    UInt                            value,
-    unsigned                        prefix,
-    const basic_format_specs<Char> &specs,
-    locale_ref                      loc) -> bool
-{
-    auto grouping = digit_grouping<Char>(loc);
-    out           = write_int_localized(out, value, prefix, specs, grouping);
-    return true;
-}
-
-FMT_CONSTEXPR inline void prefix_append(unsigned &prefix, unsigned value)
-{
-    prefix |= prefix != 0 ? value << 8 : value;
-    prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
-}
-
-template <typename UInt>
-struct write_int_arg
-{
-    UInt     abs_value;
-    unsigned prefix;
-};
-
-template <typename T>
-FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) -> write_int_arg<uint32_or_64_or_128_t<T>>
-{
-    auto prefix    = 0u;
-    auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
-    if(is_negative(value))
-    {
-        prefix    = 0x01000000 | '-';
-        abs_value = 0 - abs_value;
-    }
-    else
-    {
-        constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', 0x1000000u | ' '};
-        prefix                               = prefixes[sign];
-    }
-    return {abs_value, prefix};
-}
-
-template <typename Char, typename OutputIt, typename T>
-FMT_CONSTEXPR FMT_INLINE auto
-write_int(OutputIt out, write_int_arg<T> arg, const basic_format_specs<Char> &specs, locale_ref loc) -> OutputIt
-{
-    static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
-    auto abs_value = arg.abs_value;
-    auto prefix    = arg.prefix;
-    switch(specs.type)
-    {
-        case presentation_type::none:
-        case presentation_type::dec:
-        {
-            if(specs.localized &&
-               write_int_localized(out, static_cast<uint64_or_128_t<T>>(abs_value), prefix, specs, loc))
-            {
-                return out;
-            }
-            auto num_digits = count_digits(abs_value);
-            return write_int(
-                out,
-                num_digits,
-                prefix,
-                specs,
-                [=](reserve_iterator<OutputIt> it) { return format_decimal<Char>(it, abs_value, num_digits).end; });
-        }
-        case presentation_type::hex_lower:
-        case presentation_type::hex_upper:
-        {
-            bool upper = specs.type == presentation_type::hex_upper;
-            if(specs.alt)
-                prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0');
-            int num_digits = count_digits<4>(abs_value);
-            return write_int(
-                out,
-                num_digits,
-                prefix,
-                specs,
-                [=](reserve_iterator<OutputIt> it) { return format_uint<4, Char>(it, abs_value, num_digits, upper); });
-        }
-        case presentation_type::bin_lower:
-        case presentation_type::bin_upper:
-        {
-            bool upper = specs.type == presentation_type::bin_upper;
-            if(specs.alt)
-                prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0');
-            int num_digits = count_digits<1>(abs_value);
-            return write_int(
-                out,
-                num_digits,
-                prefix,
-                specs,
-                [=](reserve_iterator<OutputIt> it) { return format_uint<1, Char>(it, abs_value, num_digits); });
-        }
-        case presentation_type::oct:
-        {
-            int num_digits = count_digits<3>(abs_value);
-            // Octal prefix '0' is counted as a digit, so only add it if precision
-            // is not greater than the number of digits.
-            if(specs.alt && specs.precision <= num_digits && abs_value != 0)
-                prefix_append(prefix, '0');
-            return write_int(
-                out,
-                num_digits,
-                prefix,
-                specs,
-                [=](reserve_iterator<OutputIt> it) { return format_uint<3, Char>(it, abs_value, num_digits); });
-        }
-        case presentation_type::chr:
-            return write_char(out, static_cast<Char>(abs_value), specs);
-        default:
-            throw_format_error("invalid type specifier");
-    }
-    return out;
-}
-template <typename Char, typename OutputIt, typename T>
-FMT_CONSTEXPR FMT_NOINLINE auto
-write_int_noinline(OutputIt out, write_int_arg<T> arg, const basic_format_specs<Char> &specs, locale_ref loc)
-    -> OutputIt
-{
-    return write_int(out, arg, specs, loc);
-}
-template <
-    typename Char,
-    typename OutputIt,
-    typename T,
-    FMT_ENABLE_IF(
-        is_integral<T>::value && !std::is_same<T, bool>::value && std::is_same<OutputIt, buffer_appender<Char>>::value)>
-FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, const basic_format_specs<Char> &specs, locale_ref loc)
-    -> OutputIt
-{
-    return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs, loc);
-}
-// An inlined version of write used in format string compilation.
-template <
-    typename Char,
-    typename OutputIt,
-    typename T,
-    FMT_ENABLE_IF(
-        is_integral<T>::value && !std::is_same<T, bool>::value &&
-        !std::is_same<OutputIt, buffer_appender<Char>>::value)>
-FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, const basic_format_specs<Char> &specs, locale_ref loc)
-    -> OutputIt
-{
-    return write_int(out, make_write_int_arg(value, specs.sign), specs, loc);
-}
-
-// An output iterator that counts the number of objects written to it and
-// discards them.
-class counting_iterator
-{
-private:
-    size_t count_;
-
-public:
-    using iterator_category = std::output_iterator_tag;
-    using difference_type   = std::ptrdiff_t;
-    using pointer           = void;
-    using reference         = void;
-    FMT_UNCHECKED_ITERATOR(counting_iterator);
-
-    struct value_type
-    {
-        template <typename T>
-        FMT_CONSTEXPR void operator=(const T &)
-        {
-        }
-    };
-
-    FMT_CONSTEXPR counting_iterator() : count_(0) {}
-
-    FMT_CONSTEXPR size_t count() const { return count_; }
-
-    FMT_CONSTEXPR counting_iterator &operator++()
-    {
-        ++count_;
-        return *this;
-    }
-    FMT_CONSTEXPR counting_iterator operator++(int)
-    {
-        auto it = *this;
-        ++*this;
-        return it;
-    }
-
-    FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it, difference_type n)
-    {
-        it.count_ += static_cast<size_t>(n);
-        return it;
-    }
-
-    FMT_CONSTEXPR value_type operator*() const { return {}; }
-};
-
-template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s, const basic_format_specs<Char> &specs) -> OutputIt
-{
-    auto data = s.data();
-    auto size = s.size();
-    if(specs.precision >= 0 && to_unsigned(specs.precision) < size)
-        size = code_point_index(s, to_unsigned(specs.precision));
-    bool   is_debug = specs.type == presentation_type::debug;
-    size_t width    = 0;
-    if(specs.width != 0)
-    {
-        if(is_debug)
-            width = write_escaped_string(counting_iterator{}, s).count();
-        else
-            width = compute_width(basic_string_view<Char>(data, size));
-    }
-    return write_padded(
-        out,
-        specs,
-        size,
-        width,
-        [=](reserve_iterator<OutputIt> it)
-        {
-            if(is_debug)
-                return write_escaped_string(it, s);
-            return copy_str<Char>(data, data + size, it);
-        });
-}
-template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto
-write(OutputIt out, basic_string_view<type_identity_t<Char>> s, const basic_format_specs<Char> &specs, locale_ref)
-    -> OutputIt
-{
-    check_string_type_spec(specs.type);
-    return write(out, s, specs);
-}
-template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write(OutputIt out, const Char *s, const basic_format_specs<Char> &specs, locale_ref) -> OutputIt
-{
-    return check_cstring_type_spec(specs.type) ? write(out, basic_string_view<Char>(s), specs, {}) :
-                                                 write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
-}
-
-template <
-    typename Char,
-    typename OutputIt,
-    typename T,
-    FMT_ENABLE_IF(is_integral<T>::value && !std::is_same<T, bool>::value && !std::is_same<T, Char>::value)>
-FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt
-{
-    auto abs_value = static_cast<uint32_or_64_or_128_t<T>>(value);
-    bool negative  = is_negative(value);
-    // Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
-    if(negative)
-        abs_value = ~abs_value + 1;
-    int  num_digits = count_digits(abs_value);
-    auto size       = (negative ? 1 : 0) + static_cast<size_t>(num_digits);
-    auto it         = reserve(out, size);
-    if(auto ptr = to_pointer<Char>(it, size))
-    {
-        if(negative)
-            *ptr++ = static_cast<Char>('-');
-        format_decimal<Char>(ptr, abs_value, num_digits);
-        return out;
-    }
-    if(negative)
-        *it++ = static_cast<Char>('-');
-    it = format_decimal<Char>(it, abs_value, num_digits).end;
-    return base_iterator(out, it);
-}
-
-template <typename Char, typename OutputIt>
-FMT_CONSTEXPR20 auto
-write_nonfinite(OutputIt out, bool isnan, basic_format_specs<Char> specs, const float_specs &fspecs) -> OutputIt
-{
-    auto             str      = isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf");
-    constexpr size_t str_size = 3;
-    auto             sign     = fspecs.sign;
-    auto             size     = str_size + (sign ? 1 : 0);
-    // Replace '0'-padding with space for non-finite values.
-    const bool is_zero_fill = specs.fill.size() == 1 && *specs.fill.data() == static_cast<Char>('0');
-    if(is_zero_fill)
-        specs.fill[0] = static_cast<Char>(' ');
-    return write_padded(
-        out,
-        specs,
-        size,
-        [=](reserve_iterator<OutputIt> it)
-        {
-            if(sign)
-                *it++ = detail::sign<Char>(sign);
-            return copy_str<Char>(str, str + str_size, it);
-        });
-}
-
-// A decimal floating-point number significand * pow(10, exp).
-struct big_decimal_fp
-{
-    const char *significand;
-    int         significand_size;
-    int         exponent;
-};
-
-constexpr auto get_significand_size(const big_decimal_fp &f) -> int
-{
-    return f.significand_size;
-}
-template <typename T>
-inline auto get_significand_size(const dragonbox::decimal_fp<T> &f) -> int
-{
-    return count_digits(f.significand);
-}
-
-template <typename Char, typename OutputIt>
-constexpr auto write_significand(OutputIt out, const char *significand, int significand_size) -> OutputIt
-{
-    return copy_str<Char>(significand, significand + significand_size, out);
-}
-template <typename Char, typename OutputIt, typename UInt>
-inline auto write_significand(OutputIt out, UInt significand, int significand_size) -> OutputIt
-{
-    return format_decimal<Char>(out, significand, significand_size).end;
-}
-template <typename Char, typename OutputIt, typename T, typename Grouping>
-FMT_CONSTEXPR20 auto
-write_significand(OutputIt out, T significand, int significand_size, int exponent, const Grouping &grouping) -> OutputIt
-{
-    if(!grouping.separator())
-    {
-        out = write_significand<Char>(out, significand, significand_size);
-        return detail::fill_n(out, exponent, static_cast<Char>('0'));
-    }
-    auto buffer = memory_buffer();
-    write_significand<char>(appender(buffer), significand, significand_size);
-    detail::fill_n(appender(buffer), exponent, '0');
-    return grouping.apply(out, string_view(buffer.data(), buffer.size()));
-}
-
-template <typename Char, typename UInt, FMT_ENABLE_IF(std::is_integral<UInt>::value)>
-inline auto write_significand(Char *out, UInt significand, int significand_size, int integral_size, Char decimal_point)
-    -> Char *
-{
-    if(!decimal_point)
-        return format_decimal(out, significand, significand_size).end;
-    out += significand_size + 1;
-    Char *end           = out;
-    int   floating_size = significand_size - integral_size;
-    for(int i = floating_size / 2; i > 0; --i)
-    {
-        out -= 2;
-        copy2(out, digits2(static_cast<std::size_t>(significand % 100)));
-        significand /= 100;
-    }
-    if(floating_size % 2 != 0)
-    {
-        *--out = static_cast<Char>('0' + significand % 10);
-        significand /= 10;
-    }
-    *--out = decimal_point;
-    format_decimal(out - integral_size, significand, integral_size);
-    return end;
-}
-
-template <
-    typename OutputIt,
-    typename UInt,
-    typename Char,
-    FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<OutputIt>>::value)>
-inline auto
-write_significand(OutputIt out, UInt significand, int significand_size, int integral_size, Char decimal_point)
-    -> OutputIt
-{
-    // Buffer is large enough to hold digits (digits10 + 1) and a decimal point.
-    Char buffer[digits10<UInt>() + 2];
-    auto end = write_significand(buffer, significand, significand_size, integral_size, decimal_point);
-    return detail::copy_str_noinline<Char>(buffer, end, out);
-}
-
-template <typename OutputIt, typename Char>
-FMT_CONSTEXPR auto
-write_significand(OutputIt out, const char *significand, int significand_size, int integral_size, Char decimal_point)
-    -> OutputIt
-{
-    out = detail::copy_str_noinline<Char>(significand, significand + integral_size, out);
-    if(!decimal_point)
-        return out;
-    *out++ = decimal_point;
-    return detail::copy_str_noinline<Char>(significand + integral_size, significand + significand_size, out);
-}
-
-template <typename OutputIt, typename Char, typename T, typename Grouping>
-FMT_CONSTEXPR20 auto write_significand(
-    OutputIt        out,
-    T               significand,
-    int             significand_size,
-    int             integral_size,
-    Char            decimal_point,
-    const Grouping &grouping) -> OutputIt
-{
-    if(!grouping.separator())
-    {
-        return write_significand(out, significand, significand_size, integral_size, decimal_point);
-    }
-    auto buffer = basic_memory_buffer<Char>();
-    write_significand(buffer_appender<Char>(buffer), significand, significand_size, integral_size, decimal_point);
-    grouping.apply(out, basic_string_view<Char>(buffer.data(), to_unsigned(integral_size)));
-    return detail::copy_str_noinline<Char>(buffer.data() + integral_size, buffer.end(), out);
-}
-
-template <typename OutputIt, typename DecimalFP, typename Char, typename Grouping = digit_grouping<Char>>
-FMT_CONSTEXPR20 auto do_write_float(
-    OutputIt                        out,
-    const DecimalFP                &f,
-    const basic_format_specs<Char> &specs,
-    float_specs                     fspecs,
-    locale_ref                      loc) -> OutputIt
-{
-    auto       significand      = f.significand;
-    int        significand_size = get_significand_size(f);
-    const Char zero             = static_cast<Char>('0');
-    auto       sign             = fspecs.sign;
-    size_t     size             = to_unsigned(significand_size) + (sign ? 1 : 0);
-    using iterator              = reserve_iterator<OutputIt>;
-
-    Char decimal_point = fspecs.locale ? detail::decimal_point<Char>(loc) : static_cast<Char>('.');
-
-    int  output_exp     = f.exponent + significand_size - 1;
-    auto use_exp_format = [=]()
-    {
-        if(fspecs.format == float_format::exp)
-            return true;
-        if(fspecs.format != float_format::general)
-            return false;
-        // Use the fixed notation if the exponent is in [exp_lower, exp_upper),
-        // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation.
-        const int exp_lower = -4, exp_upper = 16;
-        return output_exp < exp_lower || output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper);
-    };
-    if(use_exp_format())
-    {
-        int num_zeros = 0;
-        if(fspecs.showpoint)
-        {
-            num_zeros = fspecs.precision - significand_size;
-            if(num_zeros < 0)
-                num_zeros = 0;
-            size += to_unsigned(num_zeros);
-        }
-        else if(significand_size == 1)
-        {
-            decimal_point = Char();
-        }
-        auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp;
-        int  exp_digits     = 2;
-        if(abs_output_exp >= 100)
-            exp_digits = abs_output_exp >= 1000 ? 4 : 3;
-
-        size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits);
-        char exp_char = fspecs.upper ? 'E' : 'e';
-        auto write    = [=](iterator it)
-        {
-            if(sign)
-                *it++ = detail::sign<Char>(sign);
-            // Insert a decimal point after the first digit and add an exponent.
-            it = write_significand(it, significand, significand_size, 1, decimal_point);
-            if(num_zeros > 0)
-                it = detail::fill_n(it, num_zeros, zero);
-            *it++ = static_cast<Char>(exp_char);
-            return write_exponent<Char>(output_exp, it);
-        };
-        return specs.width > 0 ? write_padded<align::right>(out, specs, size, write) :
-                                 base_iterator(out, write(reserve(out, size)));
-    }
-
-    int exp = f.exponent + significand_size;
-    if(f.exponent >= 0)
-    {
-        // 1234e5 -> 123400000[.0+]
-        size += to_unsigned(f.exponent);
-        int num_zeros = fspecs.precision - exp;
-        abort_fuzzing_if(num_zeros > 5000);
-        if(fspecs.showpoint)
-        {
-            ++size;
-            if(num_zeros <= 0 && fspecs.format != float_format::fixed)
-                num_zeros = 1;
-            if(num_zeros > 0)
-                size += to_unsigned(num_zeros);
-        }
-        auto grouping = Grouping(loc, fspecs.locale);
-        size += to_unsigned(grouping.count_separators(exp));
-        return write_padded<align::right>(
-            out,
-            specs,
-            size,
-            [&](iterator it)
-            {
-                if(sign)
-                    *it++ = detail::sign<Char>(sign);
-                it = write_significand<Char>(it, significand, significand_size, f.exponent, grouping);
-                if(!fspecs.showpoint)
-                    return it;
-                *it++ = decimal_point;
-                return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
-            });
-    }
-    else if(exp > 0)
-    {
-        // 1234e-2 -> 12.34[0+]
-        int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0;
-        size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0);
-        auto grouping = Grouping(loc, fspecs.locale);
-        size += to_unsigned(grouping.count_separators(significand_size));
-        return write_padded<align::right>(
-            out,
-            specs,
-            size,
-            [&](iterator it)
-            {
-                if(sign)
-                    *it++ = detail::sign<Char>(sign);
-                it = write_significand(it, significand, significand_size, exp, decimal_point, grouping);
-                return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
-            });
-    }
-    // 1234e-6 -> 0.001234
-    int num_zeros = -exp;
-    if(significand_size == 0 && fspecs.precision >= 0 && fspecs.precision < num_zeros)
-    {
-        num_zeros = fspecs.precision;
-    }
-    bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint;
-    size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros);
-    return write_padded<align::right>(
-        out,
-        specs,
-        size,
-        [&](iterator it)
-        {
-            if(sign)
-                *it++ = detail::sign<Char>(sign);
-            *it++ = zero;
-            if(!pointy)
-                return it;
-            *it++ = decimal_point;
-            it    = detail::fill_n(it, num_zeros, zero);
-            return write_significand<Char>(it, significand, significand_size);
-        });
-}
-
-template <typename Char>
-class fallback_digit_grouping
-{
-public:
-    constexpr fallback_digit_grouping(locale_ref, bool) {}
-
-    constexpr Char separator() const { return Char(); }
-
-    constexpr int count_separators(int) const { return 0; }
-
-    template <typename Out, typename C>
-    constexpr Out apply(Out out, basic_string_view<C>) const
-    {
-        return out;
-    }
-};
-
-template <typename OutputIt, typename DecimalFP, typename Char>
-FMT_CONSTEXPR20 auto
-write_float(OutputIt out, const DecimalFP &f, const basic_format_specs<Char> &specs, float_specs fspecs, locale_ref loc)
-    -> OutputIt
-{
-    if(is_constant_evaluated())
-    {
-        return do_write_float<OutputIt, DecimalFP, Char, fallback_digit_grouping<Char>>(out, f, specs, fspecs, loc);
-    }
-    else
-    {
-        return do_write_float(out, f, specs, fspecs, loc);
-    }
-}
-
-template <typename T>
-constexpr bool isnan(T value)
-{
-    return !(value >= value); // std::isnan doesn't support __float128.
-}
-
-template <typename T, typename Enable = void>
-struct has_isfinite : std::false_type
-{
-};
-
-template <typename T>
-struct has_isfinite<T, enable_if_t<sizeof(std::isfinite(T())) != 0>> : std::true_type
-{
-};
-
-template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value &&has_isfinite<T>::value)>
-FMT_CONSTEXPR20 bool isfinite(T value)
-{
-    constexpr T inf = T(std::numeric_limits<double>::infinity());
-    if(is_constant_evaluated())
-        return !detail::isnan(value) && value != inf && value != -inf;
-    return std::isfinite(value);
-}
-template <typename T, FMT_ENABLE_IF(!has_isfinite<T>::value)>
-FMT_CONSTEXPR bool isfinite(T value)
-{
-    T inf = T(std::numeric_limits<double>::infinity());
-    // std::isfinite doesn't support __float128.
-    return !detail::isnan(value) && value != inf && value != -inf;
-}
-
-template <typename T, FMT_ENABLE_IF(is_floating_point<T>::value)>
-FMT_INLINE FMT_CONSTEXPR bool signbit(T value)
-{
-    if(is_constant_evaluated())
-    {
-#ifdef __cpp_if_constexpr
-        if constexpr(std::numeric_limits<double>::is_iec559)
-        {
-            auto bits = detail::bit_cast<uint64_t>(static_cast<double>(value));
-            return (bits >> (num_bits<uint64_t>() - 1)) != 0;
-        }
-#endif
-    }
-    return std::signbit(static_cast<double>(value));
-}
-
-enum class round_direction
-{
-    unknown,
-    up,
-    down
-};
-
-// Given the divisor (normally a power of 10), the remainder = v % divisor for
-// some number v and the error, returns whether v should be rounded up, down, or
-// whether the rounding direction can't be determined due to error.
-// error should be less than divisor / 2.
-FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder, uint64_t error)
-{
-    FMT_ASSERT(remainder < divisor, "");     // divisor - remainder won't overflow.
-    FMT_ASSERT(error < divisor, "");         // divisor - error won't overflow.
-    FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
-    // Round down if (remainder + error) * 2 <= divisor.
-    if(remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
-        return round_direction::down;
-    // Round up if (remainder - error) * 2 >= divisor.
-    if(remainder >= error && remainder - error >= divisor - (remainder - error))
-    {
-        return round_direction::up;
-    }
-    return round_direction::unknown;
-}
-
-namespace digits
-{
-enum result
-{
-    more, // Generate more digits.
-    done, // Done generating digits.
-    error // Digit generation cancelled due to an error.
-};
-}
-
-struct gen_digits_handler
-{
-    char *buf;
-    int   size;
-    int   precision;
-    int   exp10;
-    bool  fixed;
-
-    FMT_CONSTEXPR digits::result
-                  on_digit(char digit, uint64_t divisor, uint64_t remainder, uint64_t error, bool integral)
-    {
-        FMT_ASSERT(remainder < divisor, "");
-        buf[size++] = digit;
-        if(!integral && error >= remainder)
-            return digits::error;
-        if(size < precision)
-            return digits::more;
-        if(!integral)
-        {
-            // Check if error * 2 < divisor with overflow prevention.
-            // The check is not needed for the integral part because error = 1
-            // and divisor > (1 << 32) there.
-            if(error >= divisor || error >= divisor - error)
-                return digits::error;
-        }
-        else
-        {
-            FMT_ASSERT(error == 1 && divisor > 2, "");
-        }
-        auto dir = get_round_direction(divisor, remainder, error);
-        if(dir != round_direction::up)
-            return dir == round_direction::down ? digits::done : digits::error;
-        ++buf[size - 1];
-        for(int i = size - 1; i > 0 && buf[i] > '9'; --i)
-        {
-            buf[i] = '0';
-            ++buf[i - 1];
-        }
-        if(buf[0] > '9')
-        {
-            buf[0] = '1';
-            if(fixed)
-                buf[size++] = '0';
-            else
-                ++exp10;
-        }
-        return digits::done;
-    }
-};
-
-inline FMT_CONSTEXPR20 void adjust_precision(int &precision, int exp10)
-{
-    // Adjust fixed precision by exponent because it is relative to decimal
-    // point.
-    if(exp10 > 0 && precision > max_value<int>() - exp10)
-        FMT_THROW(format_error("number is too big"));
-    precision += exp10;
-}
-
-// Generates output using the Grisu digit-gen algorithm.
-// error: the size of the region (lower, upper) outside of which numbers
-// definitely do not round to value (Delta in Grisu3).
-FMT_INLINE FMT_CONSTEXPR20 auto grisu_gen_digits(fp value, uint64_t error, int &exp, gen_digits_handler &handler)
-    -> digits::result
-{
-    const fp one(1ULL << -value.e, value.e);
-    // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
-    // zero because it contains a product of two 64-bit numbers with MSB set (due
-    // to normalization) - 1, shifted right by at most 60 bits.
-    auto integral = static_cast<uint32_t>(value.f >> -one.e);
-    FMT_ASSERT(integral != 0, "");
-    FMT_ASSERT(integral == value.f >> -one.e, "");
-    // The fractional part of scaled value (p2 in Grisu) c = value % one.
-    uint64_t fractional = value.f & (one.f - 1);
-    exp                 = count_digits(integral); // kappa in Grisu.
-    // Non-fixed formats require at least one digit and no precision adjustment.
-    if(handler.fixed)
-    {
-        adjust_precision(handler.precision, exp + handler.exp10);
-        // Check if precision is satisfied just by leading zeros, e.g.
-        // format("{:.2f}", 0.001) gives "0.00" without generating any digits.
-        if(handler.precision <= 0)
-        {
-            if(handler.precision < 0)
-                return digits::done;
-            // Divide by 10 to prevent overflow.
-            uint64_t divisor = data::power_of_10_64[exp - 1] << -one.e;
-            auto     dir     = get_round_direction(divisor, value.f / 10, error * 10);
-            if(dir == round_direction::unknown)
-                return digits::error;
-            handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0';
-            return digits::done;
-        }
-    }
-    // Generate digits for the integral part. This can produce up to 10 digits.
-    do
-    {
-        uint32_t digit           = 0;
-        auto     divmod_integral = [&](uint32_t divisor)
-        {
-            digit = integral / divisor;
-            integral %= divisor;
-        };
-        // This optimization by Milo Yip reduces the number of integer divisions by
-        // one per iteration.
-        switch(exp)
-        {
-            case 10:
-                divmod_integral(1000000000);
-                break;
-            case 9:
-                divmod_integral(100000000);
-                break;
-            case 8:
-                divmod_integral(10000000);
-                break;
-            case 7:
-                divmod_integral(1000000);
-                break;
-            case 6:
-                divmod_integral(100000);
-                break;
-            case 5:
-                divmod_integral(10000);
-                break;
-            case 4:
-                divmod_integral(1000);
-                break;
-            case 3:
-                divmod_integral(100);
-                break;
-            case 2:
-                divmod_integral(10);
-                break;
-            case 1:
-                digit    = integral;
-                integral = 0;
-                break;
-            default:
-                FMT_ASSERT(false, "invalid number of digits");
-        }
-        --exp;
-        auto remainder = (static_cast<uint64_t>(integral) << -one.e) + fractional;
-        auto result    = handler.on_digit(
-            static_cast<char>('0' + digit), data::power_of_10_64[exp] << -one.e, remainder, error, true);
-        if(result != digits::more)
-            return result;
-    } while(exp > 0);
-    // Generate digits for the fractional part.
-    for(;;)
-    {
-        fractional *= 10;
-        error *= 10;
-        char digit = static_cast<char>('0' + (fractional >> -one.e));
-        fractional &= one.f - 1;
-        --exp;
-        auto result = handler.on_digit(digit, one.f, fractional, error, false);
-        if(result != digits::more)
-            return result;
-    }
-}
-
-class bigint
-{
-private:
-    // A bigint is stored as an array of bigits (big digits), with bigit at index
-    // 0 being the least significant one.
-    using bigit        = uint32_t;
-    using double_bigit = uint64_t;
-    enum
-    {
-        bigits_capacity = 32
-    };
-    basic_memory_buffer<bigit, bigits_capacity> bigits_;
-    int                                         exp_;
-
-    FMT_CONSTEXPR20 bigit  operator[](int index) const { return bigits_[to_unsigned(index)]; }
-    FMT_CONSTEXPR20 bigit &operator[](int index) { return bigits_[to_unsigned(index)]; }
-
-    static constexpr const int bigit_bits = num_bits<bigit>();
-
-    friend struct formatter<bigint>;
-
-    FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit &borrow)
-    {
-        auto result    = static_cast<double_bigit>((*this)[index]) - other - borrow;
-        (*this)[index] = static_cast<bigit>(result);
-        borrow         = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
-    }
-
-    FMT_CONSTEXPR20 void remove_leading_zeros()
-    {
-        int num_bigits = static_cast<int>(bigits_.size()) - 1;
-        while(num_bigits > 0 && (*this)[num_bigits] == 0)
-            --num_bigits;
-        bigits_.resize(to_unsigned(num_bigits + 1));
-    }
-
-    // Computes *this -= other assuming aligned bigints and *this >= other.
-    FMT_CONSTEXPR20 void subtract_aligned(const bigint &other)
-    {
-        FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints");
-        FMT_ASSERT(compare(*this, other) >= 0, "");
-        bigit borrow = 0;
-        int   i      = other.exp_ - exp_;
-        for(size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j)
-            subtract_bigits(i, other.bigits_[j], borrow);
-        while(borrow > 0)
-            subtract_bigits(i, 0, borrow);
-        remove_leading_zeros();
-    }
-
-    FMT_CONSTEXPR20 void multiply(uint32_t value)
-    {
-        const double_bigit wide_value = value;
-        bigit              carry      = 0;
-        for(size_t i = 0, n = bigits_.size(); i < n; ++i)
-        {
-            double_bigit result = bigits_[i] * wide_value + carry;
-            bigits_[i]          = static_cast<bigit>(result);
-            carry               = static_cast<bigit>(result >> bigit_bits);
-        }
-        if(carry != 0)
-            bigits_.push_back(carry);
-    }
-
-    template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value || std::is_same<UInt, uint128_t>::value)>
-    FMT_CONSTEXPR20 void multiply(UInt value)
-    {
-        using half_uint  = conditional_t<std::is_same<UInt, uint128_t>::value, uint64_t, uint32_t>;
-        const int  shift = num_bits<half_uint>() - bigit_bits;
-        const UInt lower = static_cast<half_uint>(value);
-        const UInt upper = value >> num_bits<half_uint>();
-        UInt       carry = 0;
-        for(size_t i = 0, n = bigits_.size(); i < n; ++i)
-        {
-            UInt result = lower * bigits_[i] + static_cast<bigit>(carry);
-            carry       = (upper * bigits_[i] << shift) + (result >> bigit_bits) + (carry >> bigit_bits);
-            bigits_[i]  = static_cast<bigit>(result);
-        }
-        while(carry != 0)
-        {
-            bigits_.push_back(static_cast<bigit>(carry));
-            carry >>= bigit_bits;
-        }
-    }
-
-    template <typename UInt, FMT_ENABLE_IF(std::is_same<UInt, uint64_t>::value || std::is_same<UInt, uint128_t>::value)>
-    FMT_CONSTEXPR20 void assign(UInt n)
-    {
-        size_t num_bigits = 0;
-        do
-        {
-            bigits_[num_bigits++] = static_cast<bigit>(n);
-            n >>= bigit_bits;
-        } while(n != 0);
-        bigits_.resize(num_bigits);
-        exp_ = 0;
-    }
-
-public:
-    FMT_CONSTEXPR20 bigint() : exp_(0) {}
-    explicit bigint(uint64_t n) { assign(n); }
-
-    bigint(const bigint &)         = delete;
-    void operator=(const bigint &) = delete;
-
-    FMT_CONSTEXPR20 void assign(const bigint &other)
-    {
-        auto size = other.bigits_.size();
-        bigits_.resize(size);
-        auto data = other.bigits_.data();
-        std::copy(data, data + size, make_checked(bigits_.data(), size));
-        exp_ = other.exp_;
-    }
-
-    template <typename Int>
-    FMT_CONSTEXPR20 void operator=(Int n)
-    {
-        FMT_ASSERT(n > 0, "");
-        assign(uint64_or_128_t<Int>(n));
-    }
-
-    FMT_CONSTEXPR20 int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
-
-    FMT_NOINLINE FMT_CONSTEXPR20 bigint &operator<<=(int shift)
-    {
-        FMT_ASSERT(shift >= 0, "");
-        exp_ += shift / bigit_bits;
-        shift %= bigit_bits;
-        if(shift == 0)
-            return *this;
-        bigit carry = 0;
-        for(size_t i = 0, n = bigits_.size(); i < n; ++i)
-        {
-            bigit c    = bigits_[i] >> (bigit_bits - shift);
-            bigits_[i] = (bigits_[i] << shift) + carry;
-            carry      = c;
-        }
-        if(carry != 0)
-            bigits_.push_back(carry);
-        return *this;
-    }
-
-    template <typename Int>
-    FMT_CONSTEXPR20 bigint &operator*=(Int value)
-    {
-        FMT_ASSERT(value > 0, "");
-        multiply(uint32_or_64_or_128_t<Int>(value));
-        return *this;
-    }
-
-    friend FMT_CONSTEXPR20 int compare(const bigint &lhs, const bigint &rhs)
-    {
-        int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
-        if(num_lhs_bigits != num_rhs_bigits)
-            return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
-        int i   = static_cast<int>(lhs.bigits_.size()) - 1;
-        int j   = static_cast<int>(rhs.bigits_.size()) - 1;
-        int end = i - j;
-        if(end < 0)
-            end = 0;
-        for(; i >= end; --i, --j)
-        {
-            bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
-            if(lhs_bigit != rhs_bigit)
-                return lhs_bigit > rhs_bigit ? 1 : -1;
-        }
-        if(i != j)
-            return i > j ? 1 : -1;
-        return 0;
-    }
-
-    // Returns compare(lhs1 + lhs2, rhs).
-    friend FMT_CONSTEXPR20 int add_compare(const bigint &lhs1, const bigint &lhs2, const bigint &rhs)
-    {
-        auto minimum        = [](int a, int b) { return a < b ? a : b; };
-        auto maximum        = [](int a, int b) { return a > b ? a : b; };
-        int  max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits());
-        int  num_rhs_bigits = rhs.num_bigits();
-        if(max_lhs_bigits + 1 < num_rhs_bigits)
-            return -1;
-        if(max_lhs_bigits > num_rhs_bigits)
-            return 1;
-        auto get_bigit = [](const bigint &n, int i) -> bigit
-        { return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0; };
-        double_bigit borrow  = 0;
-        int          min_exp = minimum(minimum(lhs1.exp_, lhs2.exp_), rhs.exp_);
-        for(int i = num_rhs_bigits - 1; i >= min_exp; --i)
-        {
-            double_bigit sum       = static_cast<double_bigit>(get_bigit(lhs1, i)) + get_bigit(lhs2, i);
-            bigit        rhs_bigit = get_bigit(rhs, i);
-            if(sum > rhs_bigit + borrow)
-                return 1;
-            borrow = rhs_bigit + borrow - sum;
-            if(borrow > 1)
-                return -1;
-            borrow <<= bigit_bits;
-        }
-        return borrow != 0 ? -1 : 0;
-    }
-
-    // Assigns pow(10, exp) to this bigint.
-    FMT_CONSTEXPR20 void assign_pow10(int exp)
-    {
-        FMT_ASSERT(exp >= 0, "");
-        if(exp == 0)
-            return *this = 1;
-        // Find the top bit.
-        int bitmask = 1;
-        while(exp >= bitmask)
-            bitmask <<= 1;
-        bitmask >>= 1;
-        // pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
-        // repeated squaring and multiplication.
-        *this = 5;
-        bitmask >>= 1;
-        while(bitmask != 0)
-        {
-            square();
-            if((exp & bitmask) != 0)
-                *this *= 5;
-            bitmask >>= 1;
-        }
-        *this <<= exp; // Multiply by pow(2, exp) by shifting.
-    }
-
-    FMT_CONSTEXPR20 void square()
-    {
-        int                                         num_bigits        = static_cast<int>(bigits_.size());
-        int                                         num_result_bigits = 2 * num_bigits;
-        basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
-        bigits_.resize(to_unsigned(num_result_bigits));
-        auto sum = uint128_t();
-        for(int bigit_index = 0; bigit_index < num_bigits; ++bigit_index)
-        {
-            // Compute bigit at position bigit_index of the result by adding
-            // cross-product terms n[i] * n[j] such that i + j == bigit_index.
-            for(int i = 0, j = bigit_index; j >= 0; ++i, --j)
-            {
-                // Most terms are multiplied twice which can be optimized in the future.
-                sum += static_cast<double_bigit>(n[i]) * n[j];
-            }
-            (*this)[bigit_index] = static_cast<bigit>(sum);
-            sum >>= num_bits<bigit>(); // Compute the carry.
-        }
-        // Do the same for the top half.
-        for(int bigit_index = num_bigits; bigit_index < num_result_bigits; ++bigit_index)
-        {
-            for(int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
-                sum += static_cast<double_bigit>(n[i++]) * n[j--];
-            (*this)[bigit_index] = static_cast<bigit>(sum);
-            sum >>= num_bits<bigit>();
-        }
-        remove_leading_zeros();
-        exp_ *= 2;
-    }
-
-    // If this bigint has a bigger exponent than other, adds trailing zero to make
-    // exponents equal. This simplifies some operations such as subtraction.
-    FMT_CONSTEXPR20 void align(const bigint &other)
-    {
-        int exp_difference = exp_ - other.exp_;
-        if(exp_difference <= 0)
-            return;
-        int num_bigits = static_cast<int>(bigits_.size());
-        bigits_.resize(to_unsigned(num_bigits + exp_difference));
-        for(int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
-            bigits_[j] = bigits_[i];
-        std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
-        exp_ -= exp_difference;
-    }
-
-    // Divides this bignum by divisor, assigning the remainder to this and
-    // returning the quotient.
-    FMT_CONSTEXPR20 int divmod_assign(const bigint &divisor)
-    {
-        FMT_ASSERT(this != &divisor, "");
-        if(compare(*this, divisor) < 0)
-            return 0;
-        FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
-        align(divisor);
-        int quotient = 0;
-        do
-        {
-            subtract_aligned(divisor);
-            ++quotient;
-        } while(compare(*this, divisor) >= 0);
-        return quotient;
-    }
-};
-
-// format_dragon flags.
-enum dragon
-{
-    predecessor_closer = 1,
-    fixup              = 2, // Run fixup to correct exp10 which can be off by one.
-    fixed              = 4,
-};
-
-// Formats a floating-point number using a variation of the Fixed-Precision
-// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
-// https://fmt.dev/papers/p372-steele.pdf.
-FMT_CONSTEXPR20 inline void
-format_dragon(basic_fp<uint128_t> value, unsigned flags, int num_digits, buffer<char> &buf, int &exp10)
-{
-    bigint numerator;   // 2 * R in (FPP)^2.
-    bigint denominator; // 2 * S in (FPP)^2.
-    // lower and upper are differences between value and corresponding boundaries.
-    bigint  lower;           // (M^- in (FPP)^2).
-    bigint  upper_store;     // upper's value if different from lower.
-    bigint *upper = nullptr; // (M^+ in (FPP)^2).
-    // Shift numerator and denominator by an extra bit or two (if lower boundary
-    // is closer) to make lower and upper integers. This eliminates multiplication
-    // by 2 during later computations.
-    bool is_predecessor_closer = (flags & dragon::predecessor_closer) != 0;
-    int  shift                 = is_predecessor_closer ? 2 : 1;
-    if(value.e >= 0)
-    {
-        numerator = value.f;
-        numerator <<= value.e + shift;
-        lower = 1;
-        lower <<= value.e;
-        if(is_predecessor_closer)
-        {
-            upper_store = 1;
-            upper_store <<= value.e + 1;
-            upper = &upper_store;
-        }
-        denominator.assign_pow10(exp10);
-        denominator <<= shift;
-    }
-    else if(exp10 < 0)
-    {
-        numerator.assign_pow10(-exp10);
-        lower.assign(numerator);
-        if(is_predecessor_closer)
-        {
-            upper_store.assign(numerator);
-            upper_store <<= 1;
-            upper = &upper_store;
-        }
-        numerator *= value.f;
-        numerator <<= shift;
-        denominator = 1;
-        denominator <<= shift - value.e;
-    }
-    else
-    {
-        numerator = value.f;
-        numerator <<= shift;
-        denominator.assign_pow10(exp10);
-        denominator <<= shift - value.e;
-        lower = 1;
-        if(is_predecessor_closer)
-        {
-            upper_store = 1ULL << 1;
-            upper       = &upper_store;
-        }
-    }
-    int even = static_cast<int>((value.f & 1) == 0);
-    if(!upper)
-        upper = &lower;
-    if((flags & dragon::fixup) != 0)
-    {
-        if(add_compare(numerator, *upper, denominator) + even <= 0)
-        {
-            --exp10;
-            numerator *= 10;
-            if(num_digits < 0)
-            {
-                lower *= 10;
-                if(upper != &lower)
-                    *upper *= 10;
-            }
-        }
-        if((flags & dragon::fixed) != 0)
-            adjust_precision(num_digits, exp10 + 1);
-    }
-    // Invariant: value == (numerator / denominator) * pow(10, exp10).
-    if(num_digits < 0)
-    {
-        // Generate the shortest representation.
-        num_digits = 0;
-        char *data = buf.data();
-        for(;;)
-        {
-            int  digit = numerator.divmod_assign(denominator);
-            bool low   = compare(numerator, lower) - even < 0; // numerator <[=] lower.
-            // numerator + upper >[=] pow10:
-            bool high          = add_compare(numerator, *upper, denominator) + even > 0;
-            data[num_digits++] = static_cast<char>('0' + digit);
-            if(low || high)
-            {
-                if(!low)
-                {
-                    ++data[num_digits - 1];
-                }
-                else if(high)
-                {
-                    int result = add_compare(numerator, numerator, denominator);
-                    // Round half to even.
-                    if(result > 0 || (result == 0 && (digit % 2) != 0))
-                        ++data[num_digits - 1];
-                }
-                buf.try_resize(to_unsigned(num_digits));
-                exp10 -= num_digits - 1;
-                return;
-            }
-            numerator *= 10;
-            lower *= 10;
-            if(upper != &lower)
-                *upper *= 10;
-        }
-    }
-    // Generate the given number of digits.
-    exp10 -= num_digits - 1;
-    if(num_digits == 0)
-    {
-        denominator *= 10;
-        auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
-        buf.push_back(digit);
-        return;
-    }
-    buf.try_resize(to_unsigned(num_digits));
-    for(int i = 0; i < num_digits - 1; ++i)
-    {
-        int digit = numerator.divmod_assign(denominator);
-        buf[i]    = static_cast<char>('0' + digit);
-        numerator *= 10;
-    }
-    int  digit  = numerator.divmod_assign(denominator);
-    auto result = add_compare(numerator, numerator, denominator);
-    if(result > 0 || (result == 0 && (digit % 2) != 0))
-    {
-        if(digit == 9)
-        {
-            const auto overflow = '0' + 10;
-            buf[num_digits - 1] = overflow;
-            // Propagate the carry.
-            for(int i = num_digits - 1; i > 0 && buf[i] == overflow; --i)
-            {
-                buf[i] = '0';
-                ++buf[i - 1];
-            }
-            if(buf[0] == overflow)
-            {
-                buf[0] = '1';
-                ++exp10;
-            }
-            return;
-        }
-        ++digit;
-    }
-    buf[num_digits - 1] = static_cast<char>('0' + digit);
-}
-
-template <typename Float>
-FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, buffer<char> &buf) -> int
-{
-    // float is passed as double to reduce the number of instantiations.
-    static_assert(!std::is_same<Float, float>::value, "");
-    FMT_ASSERT(value >= 0, "value is negative");
-    auto converted_value = convert_float(value);
-
-    const bool fixed = specs.format == float_format::fixed;
-    if(value <= 0)
-    { // <= instead of == to silence a warning.
-        if(precision <= 0 || !fixed)
-        {
-            buf.push_back('0');
-            return 0;
-        }
-        buf.try_resize(to_unsigned(precision));
-        fill_n(buf.data(), precision, '0');
-        return -precision;
-    }
-
-    int      exp          = 0;
-    bool     use_dragon   = true;
-    unsigned dragon_flags = 0;
-    if(!is_fast_float<Float>())
-    {
-        const auto inv_log2_10 = 0.3010299956639812; // 1 / log2(10)
-        using info             = dragonbox::float_info<decltype(converted_value)>;
-        const auto f           = basic_fp<typename info::carrier_uint>(converted_value);
-        // Compute exp, an approximate power of 10, such that
-        //   10^(exp - 1) <= value < 10^exp or 10^exp <= value < 10^(exp + 1).
-        // This is based on log10(value) == log2(value) / log2(10) and approximation
-        // of log2(value) by e + num_fraction_bits idea from double-conversion.
-        exp          = static_cast<int>(std::ceil((f.e + count_digits<1>(f.f) - 1) * inv_log2_10 - 1e-10));
-        dragon_flags = dragon::fixup;
-    }
-    else if(!is_constant_evaluated() && precision < 0)
-    {
-        // Use Dragonbox for the shortest format.
-        if(specs.binary32)
-        {
-            auto dec = dragonbox::to_decimal(static_cast<float>(value));
-            write<char>(buffer_appender<char>(buf), dec.significand);
-            return dec.exponent;
-        }
-        auto dec = dragonbox::to_decimal(static_cast<double>(value));
-        write<char>(buffer_appender<char>(buf), dec.significand);
-        return dec.exponent;
-    }
-    else
-    {
-        // Use Grisu + Dragon4 for the given precision:
-        // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf.
-        const int  min_exp      = -60; // alpha in Grisu.
-        int        cached_exp10 = 0;   // K in Grisu.
-        fp         normalized   = normalize(fp(converted_value));
-        const auto cached_pow   = get_cached_power(min_exp - (normalized.e + fp::num_significand_bits), cached_exp10);
-        normalized              = normalized * cached_pow;
-        gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
-        if(grisu_gen_digits(normalized, 1, exp, handler) != digits::error && !is_constant_evaluated())
-        {
-            exp += handler.exp10;
-            buf.try_resize(to_unsigned(handler.size));
-            use_dragon = false;
-        }
-        else
-        {
-            exp += handler.size - cached_exp10 - 1;
-            precision = handler.precision;
-        }
-    }
-    if(use_dragon)
-    {
-        auto f                     = basic_fp<uint128_t>();
-        bool is_predecessor_closer = specs.binary32 ? f.assign(static_cast<float>(value)) : f.assign(converted_value);
-        if(is_predecessor_closer)
-            dragon_flags |= dragon::predecessor_closer;
-        if(fixed)
-            dragon_flags |= dragon::fixed;
-        // Limit precision to the maximum possible number of significant digits in
-        // an IEEE754 double because we don't need to generate zeros.
-        const int max_double_digits = 767;
-        if(precision > max_double_digits)
-            precision = max_double_digits;
-        format_dragon(f, dragon_flags, precision, buf, exp);
-    }
-    if(!fixed && !specs.showpoint)
-    {
-        // Remove trailing zeros.
-        auto num_digits = buf.size();
-        while(num_digits > 0 && buf[num_digits - 1] == '0')
-        {
-            --num_digits;
-            ++exp;
-        }
-        buf.try_resize(num_digits);
-    }
-    return exp;
-}
-
-template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(is_floating_point<T>::value)>
-FMT_CONSTEXPR20 auto write(OutputIt out, T value, basic_format_specs<Char> specs, locale_ref loc = {}) -> OutputIt
-{
-    if(const_check(!is_supported_floating_point(value)))
-        return out;
-    float_specs fspecs = parse_float_type_spec(specs);
-    fspecs.sign        = specs.sign;
-    if(detail::signbit(value))
-    { // value < 0 is false for NaN so use signbit.
-        fspecs.sign = sign::minus;
-        value       = -value;
-    }
-    else if(fspecs.sign == sign::minus)
-    {
-        fspecs.sign = sign::none;
-    }
-
-    if(!detail::isfinite(value))
-        return write_nonfinite(out, detail::isnan(value), specs, fspecs);
-
-    if(specs.align == align::numeric && fspecs.sign)
-    {
-        auto it     = reserve(out, 1);
-        *it++       = detail::sign<Char>(fspecs.sign);
-        out         = base_iterator(out, it);
-        fspecs.sign = sign::none;
-        if(specs.width != 0)
-            --specs.width;
-    }
-
-    memory_buffer buffer;
-    if(fspecs.format == float_format::hex)
-    {
-        if(fspecs.sign)
-            buffer.push_back(detail::sign<char>(fspecs.sign));
-        snprintf_float(convert_float(value), specs.precision, fspecs, buffer);
-        return write_bytes<align::right>(out, {buffer.data(), buffer.size()}, specs);
-    }
-    int precision = specs.precision >= 0 || specs.type == presentation_type::none ? specs.precision : 6;
-    if(fspecs.format == float_format::exp)
-    {
-        if(precision == max_value<int>())
-            throw_format_error("number is too big");
-        else
-            ++precision;
-    }
-    else if(fspecs.format != float_format::fixed && precision == 0)
-    {
-        precision = 1;
-    }
-    if(const_check(std::is_same<T, float>()))
-        fspecs.binary32 = true;
-    int exp          = format_float(convert_float(value), precision, fspecs, buffer);
-    fspecs.precision = precision;
-    auto f           = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
-    return write_float(out, f, specs, fspecs, loc);
-}
-
-template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(is_fast_float<T>::value)>
-FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt
-{
-    if(is_constant_evaluated())
-        return write(out, value, basic_format_specs<Char>());
-    if(const_check(!is_supported_floating_point(value)))
-        return out;
-
-    auto fspecs = float_specs();
-    if(detail::signbit(value))
-    {
-        fspecs.sign = sign::minus;
-        value       = -value;
-    }
-
-    constexpr auto specs = basic_format_specs<Char>();
-    using floaty         = conditional_t<std::is_same<T, long double>::value, double, T>;
-    using uint           = typename dragonbox::float_info<floaty>::carrier_uint;
-    uint mask            = exponent_mask<floaty>();
-    if((bit_cast<uint>(value) & mask) == mask)
-        return write_nonfinite(out, std::isnan(value), specs, fspecs);
-
-    auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
-    return write_float(out, dec, specs, fspecs, {});
-}
-
-template <
-    typename Char,
-    typename OutputIt,
-    typename T,
-    FMT_ENABLE_IF(is_floating_point<T>::value && !is_fast_float<T>::value)>
-inline auto write(OutputIt out, T value) -> OutputIt
-{
-    return write(out, value, basic_format_specs<Char>());
-}
-
-template <typename Char, typename OutputIt>
-auto write(OutputIt out, monostate, basic_format_specs<Char> = {}, locale_ref = {}) -> OutputIt
-{
-    FMT_ASSERT(false, "");
-    return out;
-}
-
-template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> value) -> OutputIt
-{
-    auto it = reserve(out, value.size());
-    it      = copy_str_noinline<Char>(value.begin(), value.end(), it);
-    return base_iterator(out, it);
-}
-
-template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(is_string<T>::value)>
-constexpr auto write(OutputIt out, const T &value) -> OutputIt
-{
-    return write<Char>(out, to_string_view(value));
-}
-
-// FMT_ENABLE_IF() condition separated to workaround an MSVC bug.
-template <
-    typename Char,
-    typename OutputIt,
-    typename T,
-    bool check = std::is_enum<T>::value && !std::is_same<T, Char>::value &&
-                 mapped_type_constant<T, basic_format_context<OutputIt, Char>>::value != type::custom_type,
-    FMT_ENABLE_IF(check)>
-FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt
-{
-    return write<Char>(out, static_cast<underlying_t<T>>(value));
-}
-
-template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(std::is_same<T, bool>::value)>
-FMT_CONSTEXPR auto write(OutputIt out, T value, const basic_format_specs<Char> &specs = {}, locale_ref = {}) -> OutputIt
-{
-    return specs.type != presentation_type::none && specs.type != presentation_type::string ?
-               write(out, value ? 1 : 0, specs, {}) :
-               write_bytes(out, value ? "true" : "false", specs);
-}
-
-template <typename Char, typename OutputIt>
-FMT_CONSTEXPR auto write(OutputIt out, Char value) -> OutputIt
-{
-    auto it = reserve(out, 1);
-    *it++   = value;
-    return base_iterator(out, it);
-}
-
-template <typename Char, typename OutputIt>
-FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char *value) -> OutputIt
-{
-    if(!value)
-    {
-        throw_format_error("string pointer is null");
-    }
-    else
-    {
-        out = write(out, basic_string_view<Char>(value));
-    }
-    return out;
-}
-
-template <typename Char, typename OutputIt, typename T, FMT_ENABLE_IF(std::is_same<T, void>::value)>
-auto write(OutputIt out, const T *value, const basic_format_specs<Char> &specs = {}, locale_ref = {}) -> OutputIt
-{
-    check_pointer_type_spec(specs.type, error_handler());
-    return write_ptr<Char>(out, bit_cast<uintptr_t>(value), &specs);
-}
-
-// A write overload that handles implicit conversions.
-template <typename Char, typename OutputIt, typename T, typename Context = basic_format_context<OutputIt, Char>>
-FMT_CONSTEXPR auto write(OutputIt out, const T &value) -> enable_if_t<
-    std::is_class<T>::value && !is_string<T>::value && !is_floating_point<T>::value && !std::is_same<T, Char>::value &&
-        !std::is_same<const T &, decltype(arg_mapper<Context>().map(value))>::value,
-    OutputIt>
-{
-    return write<Char>(out, arg_mapper<Context>().map(value));
-}
-
-template <typename Char, typename OutputIt, typename T, typename Context = basic_format_context<OutputIt, Char>>
-FMT_CONSTEXPR auto write(OutputIt out, const T &value)
-    -> enable_if_t<mapped_type_constant<T, Context>::value == type::custom_type, OutputIt>
-{
-    using formatter_type = conditional_t<
-        has_formatter<T, Context>::value,
-        typename Context::template formatter_type<T>,
-        fallback_formatter<T, Char>>;
-    auto ctx = Context(out, {}, {});
-    return formatter_type().format(value, ctx);
-}
-
-// An argument visitor that formats the argument and writes it via the output
-// iterator. It's a class and not a generic lambda for compatibility with C++11.
-template <typename Char>
-struct default_arg_formatter
-{
-    using iterator = buffer_appender<Char>;
-    using context  = buffer_context<Char>;
-
-    iterator                   out;
-    basic_format_args<context> args;
-    locale_ref                 loc;
-
-    template <typename T>
-    auto operator()(T value) -> iterator
-    {
-        return write<Char>(out, value);
-    }
-    auto operator()(typename basic_format_arg<context>::handle h) -> iterator
-    {
-        basic_format_parse_context<Char> parse_ctx({});
-        context                          format_ctx(out, args, loc);
-        h.format(parse_ctx, format_ctx);
-        return format_ctx.out();
-    }
-};
-
-template <typename Char>
-struct arg_formatter
-{
-    using iterator = buffer_appender<Char>;
-    using context  = buffer_context<Char>;
-
-    iterator                        out;
-    const basic_format_specs<Char> &specs;
-    locale_ref                      locale;
-
-    template <typename T>
-    FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator
-    {
-        return detail::write(out, value, specs, locale);
-    }
-    auto operator()(typename basic_format_arg<context>::handle) -> iterator
-    {
-        // User-defined types are handled separately because they require access
-        // to the parse context.
-        return out;
-    }
-};
-
-template <typename Char>
-struct custom_formatter
-{
-    basic_format_parse_context<Char> &parse_ctx;
-    buffer_context<Char>             &ctx;
-
-    void operator()(typename basic_format_arg<buffer_context<Char>>::handle h) const { h.format(parse_ctx, ctx); }
-    template <typename T>
-    void operator()(T) const
-    {
-    }
-};
-
-template <typename T>
-using is_integer = bool_constant<
-    is_integral<T>::value && !std::is_same<T, bool>::value && !std::is_same<T, char>::value &&
-    !std::is_same<T, wchar_t>::value>;
-
-template <typename ErrorHandler>
-class width_checker
-{
-public:
-    explicit FMT_CONSTEXPR width_checker(ErrorHandler &eh) : handler_(eh) {}
-
-    template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
-    FMT_CONSTEXPR auto operator()(T value) -> unsigned long long
-    {
-        if(is_negative(value))
-            handler_.on_error("negative width");
-        return static_cast<unsigned long long>(value);
-    }
-
-    template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
-    FMT_CONSTEXPR auto operator()(T) -> unsigned long long
-    {
-        handler_.on_error("width is not integer");
-        return 0;
-    }
-
-private:
-    ErrorHandler &handler_;
-};
-
-template <typename ErrorHandler>
-class precision_checker
-{
-public:
-    explicit FMT_CONSTEXPR precision_checker(ErrorHandler &eh) : handler_(eh) {}
-
-    template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
-    FMT_CONSTEXPR auto operator()(T value) -> unsigned long long
-    {
-        if(is_negative(value))
-            handler_.on_error("negative precision");
-        return static_cast<unsigned long long>(value);
-    }
-
-    template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
-    FMT_CONSTEXPR auto operator()(T) -> unsigned long long
-    {
-        handler_.on_error("precision is not integer");
-        return 0;
-    }
-
-private:
-    ErrorHandler &handler_;
-};
-
-template <template <typename> class Handler, typename FormatArg, typename ErrorHandler>
-FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg, ErrorHandler eh) -> int
-{
-    unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
-    if(value > to_unsigned(max_value<int>()))
-        eh.on_error("number is too big");
-    return static_cast<int>(value);
-}
-
-template <typename Context, typename ID>
-FMT_CONSTEXPR auto get_arg(Context &ctx, ID id) -> typename Context::format_arg
-{
-    auto arg = ctx.arg(id);
-    if(!arg)
-        ctx.on_error("argument not found");
-    return arg;
-}
-
-// The standard format specifier handler with checking.
-template <typename Char>
-class specs_handler : public specs_setter<Char>
-{
-private:
-    basic_format_parse_context<Char> &parse_context_;
-    buffer_context<Char>             &context_;
-
-    // This is only needed for compatibility with gcc 4.4.
-    using format_arg = basic_format_arg<buffer_context<Char>>;
-
-    FMT_CONSTEXPR auto get_arg(auto_id) -> format_arg
-    {
-        return detail::get_arg(context_, parse_context_.next_arg_id());
-    }
-
-    FMT_CONSTEXPR auto get_arg(int arg_id) -> format_arg
-    {
-        parse_context_.check_arg_id(arg_id);
-        return detail::get_arg(context_, arg_id);
-    }
-
-    FMT_CONSTEXPR auto get_arg(basic_string_view<Char> arg_id) -> format_arg
-    {
-        parse_context_.check_arg_id(arg_id);
-        return detail::get_arg(context_, arg_id);
-    }
-
-public:
-    FMT_CONSTEXPR specs_handler(
-        basic_format_specs<Char>         &specs,
-        basic_format_parse_context<Char> &parse_ctx,
-        buffer_context<Char>             &ctx) :
-        specs_setter<Char>(specs), parse_context_(parse_ctx), context_(ctx)
-    {
-    }
-
-    template <typename Id>
-    FMT_CONSTEXPR void on_dynamic_width(Id arg_id)
-    {
-        this->specs_.width = get_dynamic_spec<width_checker>(get_arg(arg_id), context_.error_handler());
-    }
-
-    template <typename Id>
-    FMT_CONSTEXPR void on_dynamic_precision(Id arg_id)
-    {
-        this->specs_.precision = get_dynamic_spec<precision_checker>(get_arg(arg_id), context_.error_handler());
-    }
-
-    void on_error(const char *message) { context_.on_error(message); }
-};
-
-template <template <typename> class Handler, typename Context>
-FMT_CONSTEXPR void handle_dynamic_spec(int &value, arg_ref<typename Context::char_type> ref, Context &ctx)
-{
-    switch(ref.kind)
-    {
-        case arg_id_kind::none:
-            break;
-        case arg_id_kind::index:
-            value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.index), ctx.error_handler());
-            break;
-        case arg_id_kind::name:
-            value = detail::get_dynamic_spec<Handler>(ctx.arg(ref.val.name), ctx.error_handler());
-            break;
-    }
-}
-
-#if FMT_USE_USER_DEFINED_LITERALS
-template <typename Char>
-struct udl_formatter
-{
-    basic_string_view<Char> str;
-
-    template <typename... T>
-    auto operator()(T &&...args) const -> std::basic_string<Char>
-    {
-        return vformat(str, fmt::make_format_args<buffer_context<Char>>(args...));
-    }
-};
-
-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
-template <typename T, typename Char, size_t N, fmt::detail_exported::fixed_string<Char, N> Str>
-struct statically_named_arg : view
-{
-    static constexpr auto name = Str.data;
-
-    const T &value;
-    statically_named_arg(const T &v) : value(v) {}
-};
-
-template <typename T, typename Char, size_t N, fmt::detail_exported::fixed_string<Char, N> Str>
-struct is_named_arg<statically_named_arg<T, Char, N, Str>> : std::true_type
-{
-};
-
-template <typename T, typename Char, size_t N, fmt::detail_exported::fixed_string<Char, N> Str>
-struct is_statically_named_arg<statically_named_arg<T, Char, N, Str>> : std::true_type
-{
-};
-
-template <typename Char, size_t N, fmt::detail_exported::fixed_string<Char, N> Str>
-struct udl_arg
-{
-    template <typename T>
-    auto operator=(T &&value) const
-    {
-        return statically_named_arg<T, Char, N, Str>(std::forward<T>(value));
-    }
-};
-#else
-template <typename Char>
-struct udl_arg
-{
-    const Char *str;
-
-    template <typename T>
-    auto operator=(T &&value) const -> named_arg<Char, T>
-    {
-        return {str, std::forward<T>(value)};
-    }
-};
-#endif
-#endif // FMT_USE_USER_DEFINED_LITERALS
-
-template <typename Locale, typename Char>
-auto vformat(
-    const Locale                                            &loc,
-    basic_string_view<Char>                                  format_str,
-    basic_format_args<buffer_context<type_identity_t<Char>>> args) -> std::basic_string<Char>
-{
-    basic_memory_buffer<Char> buffer;
-    detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
-    return {buffer.data(), buffer.size()};
-}
-
-using format_func = void (*)(detail::buffer<char> &, int, const char *);
-
-FMT_API void format_error_code(buffer<char> &out, int error_code, string_view message) noexcept;
-
-FMT_API void report_error(format_func func, int error_code, const char *message) noexcept;
-FMT_END_DETAIL_NAMESPACE
-
-FMT_API auto vsystem_error(int error_code, string_view format_str, format_args args) -> std::system_error;
-
-/**
- \rst
- Constructs :class:`std::system_error` with a message formatted with
- ``fmt::format(fmt, args...)``.
-  *error_code* is a system error code as given by ``errno``.
-
- **Example**::
-
-   // This throws std::system_error with the description
-   //   cannot open file 'madeup': No such file or directory
-   // or similar (system message may vary).
-   const char* filename = "madeup";
-   std::FILE* file = std::fopen(filename, "r");
-   if (!file)
-     throw fmt::system_error(errno, "cannot open file '{}'", filename);
- \endrst
-*/
-template <typename... T>
-auto system_error(int error_code, format_string<T...> fmt, T &&...args) -> std::system_error
-{
-    return vsystem_error(error_code, fmt, fmt::make_format_args(args...));
-}
-
-/**
-  \rst
-  Formats an error message for an error returned by an operating system or a
-  language runtime, for example a file opening error, and writes it to *out*.
-  The format is the same as the one used by ``std::system_error(ec, message)``
-  where ``ec`` is ``std::error_code(error_code, std::generic_category()})``.
-  It is implementation-defined but normally looks like:
-
-  .. parsed-literal::
-     *<message>*: *<system-message>*
-
-  where *<message>* is the passed message and *<system-message>* is the system
-  message corresponding to the error code.
-  *error_code* is a system error code as given by ``errno``.
-  \endrst
- */
-FMT_API void format_system_error(detail::buffer<char> &out, int error_code, const char *message) noexcept;
-
-// Reports a system error without throwing an exception.
-// Can be used to report errors from destructors.
-FMT_API void report_system_error(int error_code, const char *message) noexcept;
-
-/** Fast integer formatter. */
-class format_int
-{
-private:
-    // Buffer should be large enough to hold all digits (digits10 + 1),
-    // a sign and a null character.
-    enum
-    {
-        buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3
-    };
-    mutable char buffer_[buffer_size];
-    char        *str_;
-
-    template <typename UInt>
-    auto format_unsigned(UInt value) -> char *
-    {
-        auto n = static_cast<detail::uint32_or_64_or_128_t<UInt>>(value);
-        return detail::format_decimal(buffer_, n, buffer_size - 1).begin;
-    }
-
-    template <typename Int>
-    auto format_signed(Int value) -> char *
-    {
-        auto abs_value = static_cast<detail::uint32_or_64_or_128_t<Int>>(value);
-        bool negative  = value < 0;
-        if(negative)
-            abs_value = 0 - abs_value;
-        auto begin = format_unsigned(abs_value);
-        if(negative)
-            *--begin = '-';
-        return begin;
-    }
-
-public:
-    explicit format_int(int value) : str_(format_signed(value)) {}
-    explicit format_int(long value) : str_(format_signed(value)) {}
-    explicit format_int(long long value) : str_(format_signed(value)) {}
-    explicit format_int(unsigned value) : str_(format_unsigned(value)) {}
-    explicit format_int(unsigned long value) : str_(format_unsigned(value)) {}
-    explicit format_int(unsigned long long value) : str_(format_unsigned(value)) {}
-
-    /** Returns the number of characters written to the output buffer. */
-    auto size() const -> size_t { return detail::to_unsigned(buffer_ - str_ + buffer_size - 1); }
-
-    /**
-      Returns a pointer to the output buffer content. No terminating null
-      character is appended.
-     */
-    auto data() const -> const char * { return str_; }
-
-    /**
-      Returns a pointer to the output buffer content with terminating null
-      character appended.
-     */
-    auto c_str() const -> const char *
-    {
-        buffer_[buffer_size - 1] = '\0';
-        return str_;
-    }
-
-    /**
-      \rst
-      Returns the content of the output buffer as an ``std::string``.
-      \endrst
-     */
-    auto str() const -> std::string { return std::string(str_, size()); }
-};
-
-template <typename T, typename Char>
-template <typename FormatContext>
-FMT_CONSTEXPR FMT_INLINE auto
-formatter<T, Char, enable_if_t<detail::type_constant<T, Char>::value != detail::type::custom_type>>::format(
-    const T       &val,
-    FormatContext &ctx) const -> decltype(ctx.out())
-{
-    if(specs_.width_ref.kind != detail::arg_id_kind::none || specs_.precision_ref.kind != detail::arg_id_kind::none)
-    {
-        auto specs = specs_;
-        detail::handle_dynamic_spec<detail::width_checker>(specs.width, specs.width_ref, ctx);
-        detail::handle_dynamic_spec<detail::precision_checker>(specs.precision, specs.precision_ref, ctx);
-        return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
-    }
-    return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
-}
-
-template <typename Char>
-struct formatter<void *, Char> : formatter<const void *, Char>
-{
-    template <typename FormatContext>
-    auto format(void *val, FormatContext &ctx) const -> decltype(ctx.out())
-    {
-        return formatter<const void *, Char>::format(val, ctx);
-    }
-};
-
-template <typename Char, size_t N>
-struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char>
-{
-    template <typename FormatContext>
-    FMT_CONSTEXPR auto format(const Char *val, FormatContext &ctx) const -> decltype(ctx.out())
-    {
-        return formatter<basic_string_view<Char>, Char>::format(val, ctx);
-    }
-};
-
-// A formatter for types known only at run time such as variant alternatives.
-//
-// Usage:
-//   using variant = std::variant<int, std::string>;
-//   template <>
-//   struct formatter<variant>: dynamic_formatter<> {
-//     auto format(const variant& v, format_context& ctx) {
-//       return visit([&](const auto& val) {
-//           return dynamic_formatter<>::format(val, ctx);
-//       }, v);
-//     }
-//   };
-template <typename Char = char>
-class dynamic_formatter
-{
-private:
-    detail::dynamic_format_specs<Char> specs_;
-    const Char                        *format_str_;
-
-    struct null_handler : detail::error_handler
-    {
-        void on_align(align_t) {}
-        void on_sign(sign_t) {}
-        void on_hash() {}
-    };
-
-    template <typename Context>
-    void handle_specs(Context &ctx)
-    {
-        detail::handle_dynamic_spec<detail::width_checker>(specs_.width, specs_.width_ref, ctx);
-        detail::handle_dynamic_spec<detail::precision_checker>(specs_.precision, specs_.precision_ref, ctx);
-    }
-
-public:
-    template <typename ParseContext>
-    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
-    {
-        format_str_ = ctx.begin();
-        // Checks are deferred to formatting time when the argument type is known.
-        detail::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
-        return detail::parse_format_specs(ctx.begin(), ctx.end(), handler);
-    }
-
-    template <typename T, typename FormatContext>
-    auto format(const T &val, FormatContext &ctx) -> decltype(ctx.out())
-    {
-        handle_specs(ctx);
-        detail::specs_checker<null_handler> checker(
-            null_handler(), detail::mapped_type_constant<T, FormatContext>::value);
-        checker.on_align(specs_.align);
-        if(specs_.sign != sign::none)
-            checker.on_sign(specs_.sign);
-        if(specs_.alt)
-            checker.on_hash();
-        if(specs_.precision >= 0)
-            checker.end_precision();
-        return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
-    }
-};
-
-/**
-  \rst
-  Converts ``p`` to ``const void*`` for pointer formatting.
-
-  **Example**::
-
-    auto s = fmt::format("{}", fmt::ptr(p));
-  \endrst
- */
-template <typename T>
-auto ptr(T p) -> const void *
-{
-    static_assert(std::is_pointer<T>::value, "");
-    return detail::bit_cast<const void *>(p);
-}
-template <typename T>
-auto ptr(const std::unique_ptr<T> &p) -> const void *
-{
-    return p.get();
-}
-template <typename T>
-auto ptr(const std::shared_ptr<T> &p) -> const void *
-{
-    return p.get();
-}
-
-/**
-  \rst
-  Converts ``e`` to the underlying type.
-
-  **Example**::
-
-    enum class color { red, green, blue };
-    auto s = fmt::format("{}", fmt::underlying(color::red));
-  \endrst
- */
-template <typename Enum>
-constexpr auto underlying(Enum e) noexcept -> underlying_t<Enum>
-{
-    return static_cast<underlying_t<Enum>>(e);
-}
-
-namespace enums
-{
-template <typename Enum, FMT_ENABLE_IF(std::is_enum<Enum>::value)>
-constexpr auto format_as(Enum e) noexcept -> underlying_t<Enum>
-{
-    return static_cast<underlying_t<Enum>>(e);
-}
-} // namespace enums
-
-class bytes
-{
-private:
-    string_view data_;
-    friend struct formatter<bytes>;
-
-public:
-    explicit bytes(string_view data) : data_(data) {}
-};
-
-template <>
-struct formatter<bytes>
-{
-private:
-    detail::dynamic_format_specs<char> specs_;
-
-public:
-    template <typename ParseContext>
-    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
-    {
-        using handler_type = detail::dynamic_specs_handler<ParseContext>;
-        detail::specs_checker<handler_type> handler(handler_type(specs_, ctx), detail::type::string_type);
-        auto                                it = parse_format_specs(ctx.begin(), ctx.end(), handler);
-        detail::check_string_type_spec(specs_.type, ctx.error_handler());
-        return it;
-    }
-
-    template <typename FormatContext>
-    auto format(bytes b, FormatContext &ctx) -> decltype(ctx.out())
-    {
-        detail::handle_dynamic_spec<detail::width_checker>(specs_.width, specs_.width_ref, ctx);
-        detail::handle_dynamic_spec<detail::precision_checker>(specs_.precision, specs_.precision_ref, ctx);
-        return detail::write_bytes(ctx.out(), b.data_, specs_);
-    }
-};
-
-// group_digits_view is not derived from view because it copies the argument.
-template <typename T>
-struct group_digits_view
-{
-    T value;
-};
-
-/**
-  \rst
-  Returns a view that formats an integer value using ',' as a locale-independent
-  thousands separator.
-
-  **Example**::
-
-    fmt::print("{}", fmt::group_digits(12345));
-    // Output: "12,345"
-  \endrst
- */
-template <typename T>
-auto group_digits(T value) -> group_digits_view<T>
-{
-    return {value};
-}
-
-template <typename T>
-struct formatter<group_digits_view<T>> : formatter<T>
-{
-private:
-    detail::dynamic_format_specs<char> specs_;
-
-public:
-    template <typename ParseContext>
-    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
-    {
-        using handler_type = detail::dynamic_specs_handler<ParseContext>;
-        detail::specs_checker<handler_type> handler(handler_type(specs_, ctx), detail::type::int_type);
-        auto                                it = parse_format_specs(ctx.begin(), ctx.end(), handler);
-        detail::check_string_type_spec(specs_.type, ctx.error_handler());
-        return it;
-    }
-
-    template <typename FormatContext>
-    auto format(group_digits_view<T> t, FormatContext &ctx) -> decltype(ctx.out())
-    {
-        detail::handle_dynamic_spec<detail::width_checker>(specs_.width, specs_.width_ref, ctx);
-        detail::handle_dynamic_spec<detail::precision_checker>(specs_.precision, specs_.precision_ref, ctx);
-        return detail::write_int_localized(
-            ctx.out(),
-            static_cast<detail::uint64_or_128_t<T>>(t.value),
-            0,
-            specs_,
-            detail::digit_grouping<char>({"\3", ','}));
-    }
-};
-
-template <typename It, typename Sentinel, typename Char = char>
-struct join_view : detail::view
-{
-    It                      begin;
-    Sentinel                end;
-    basic_string_view<Char> sep;
-
-    join_view(It b, Sentinel e, basic_string_view<Char> s) : begin(b), end(e), sep(s) {}
-};
-
-template <typename It, typename Sentinel, typename Char>
-struct formatter<join_view<It, Sentinel, Char>, Char>
-{
-private:
-    using value_type =
-#ifdef __cpp_lib_ranges
-        std::iter_value_t<It>;
-#else
-        typename std::iterator_traits<It>::value_type;
-#endif
-    using context = buffer_context<Char>;
-    using mapper  = detail::arg_mapper<context>;
-
-    template <typename T, FMT_ENABLE_IF(has_formatter<T, context>::value)>
-    static auto map(const T &value) -> const T &
-    {
-        return value;
-    }
-    template <typename T, FMT_ENABLE_IF(!has_formatter<T, context>::value)>
-    static auto map(const T &value) -> decltype(mapper().map(value))
-    {
-        return mapper().map(value);
-    }
-
-    using formatter_type = conditional_t<
-        is_formattable<value_type, Char>::value,
-        formatter<remove_cvref_t<decltype(map(std::declval<const value_type &>()))>, Char>,
-        detail::fallback_formatter<value_type, Char>>;
-
-    formatter_type value_formatter_;
-
-public:
-    template <typename ParseContext>
-    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
-    {
-        return value_formatter_.parse(ctx);
-    }
-
-    template <typename FormatContext>
-    auto format(const join_view<It, Sentinel, Char> &value, FormatContext &ctx) const -> decltype(ctx.out())
-    {
-        auto it  = value.begin;
-        auto out = ctx.out();
-        if(it != value.end)
-        {
-            out = value_formatter_.format(map(*it), ctx);
-            ++it;
-            while(it != value.end)
-            {
-                out = detail::copy_str<Char>(value.sep.begin(), value.sep.end(), out);
-                ctx.advance_to(out);
-                out = value_formatter_.format(map(*it), ctx);
-                ++it;
-            }
-        }
-        return out;
-    }
-};
-
-/**
-  Returns a view that formats the iterator range `[begin, end)` with elements
-  separated by `sep`.
- */
-template <typename It, typename Sentinel>
-auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel>
-{
-    return {begin, end, sep};
-}
-
-/**
-  \rst
-  Returns a view that formats `range` with elements separated by `sep`.
-
-  **Example**::
-
-    std::vector<int> v = {1, 2, 3};
-    fmt::print("{}", fmt::join(v, ", "));
-    // Output: "1, 2, 3"
-
-  ``fmt::join`` applies passed format specifiers to the range elements::
-
-    fmt::print("{:02}", fmt::join(v, ", "));
-    // Output: "01, 02, 03"
-  \endrst
- */
-template <typename Range>
-auto join(Range &&range, string_view sep) -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>>
-{
-    return join(std::begin(range), std::end(range), sep);
-}
-
-/**
-  \rst
-  Converts *value* to ``std::string`` using the default format for type *T*.
-
-  **Example**::
-
-    #include <fmt/format.h>
-
-    std::string answer = fmt::to_string(42);
-  \endrst
- */
-template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-inline auto to_string(const T &value) -> std::string
-{
-    auto result = std::string();
-    detail::write<char>(std::back_inserter(result), value);
-    return result;
-}
-
-template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-FMT_NODISCARD inline auto to_string(T value) -> std::string
-{
-    // The buffer should be large enough to store the number including the sign
-    // or "false" for bool.
-    constexpr int max_size = detail::digits10<T>() + 2;
-    char          buffer[max_size > 5 ? static_cast<unsigned>(max_size) : 5];
-    char         *begin = buffer;
-    return std::string(begin, detail::write<char>(begin, value));
-}
-
-template <typename Char, size_t SIZE>
-FMT_NODISCARD auto to_string(const basic_memory_buffer<Char, SIZE> &buf) -> std::basic_string<Char>
-{
-    auto size = buf.size();
-    detail::assume(size < std::basic_string<Char>().max_size());
-    return std::basic_string<Char>(buf.data(), size);
-}
-
-FMT_BEGIN_DETAIL_NAMESPACE
-
-template <typename Char>
-void vformat_to(
-    buffer<Char>                                                &buf,
-    basic_string_view<Char>                                      fmt,
-    basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
-    locale_ref                                                   loc)
-{
-    // workaround for msvc bug regarding name-lookup in module
-    // link names into function scope
-    using detail::arg_formatter;
-    using detail::buffer_appender;
-    using detail::custom_formatter;
-    using detail::default_arg_formatter;
-    using detail::get_arg;
-    using detail::locale_ref;
-    using detail::parse_format_specs;
-    using detail::specs_checker;
-    using detail::specs_handler;
-    using detail::to_unsigned;
-    using detail::type;
-    using detail::write;
-    auto out = buffer_appender<Char>(buf);
-    if(fmt.size() == 2 && equal2(fmt.data(), "{}"))
-    {
-        auto arg = args.get(0);
-        if(!arg)
-            error_handler().on_error("argument not found");
-        visit_format_arg(default_arg_formatter<Char>{out, args, loc}, arg);
-        return;
-    }
-
-    struct format_handler : error_handler
-    {
-        basic_format_parse_context<Char> parse_context;
-        buffer_context<Char>             context;
-
-        format_handler(
-            buffer_appender<Char>                   p_out,
-            basic_string_view<Char>                 str,
-            basic_format_args<buffer_context<Char>> p_args,
-            locale_ref                              p_loc) :
-            parse_context(str), context(p_out, p_args, p_loc)
-        {
-        }
-
-        void on_text(const Char *begin, const Char *end)
-        {
-            auto text = basic_string_view<Char>(begin, to_unsigned(end - begin));
-            context.advance_to(write<Char>(context.out(), text));
-        }
-
-        FMT_CONSTEXPR auto on_arg_id() -> int { return parse_context.next_arg_id(); }
-        FMT_CONSTEXPR auto on_arg_id(int id) -> int { return parse_context.check_arg_id(id), id; }
-        FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int
-        {
-            int arg_id = context.arg_id(id);
-            if(arg_id < 0)
-                on_error("argument not found");
-            return arg_id;
-        }
-
-        FMT_INLINE void on_replacement_field(int id, const Char *)
-        {
-            auto arg = get_arg(context, id);
-            context.advance_to(
-                visit_format_arg(default_arg_formatter<Char>{context.out(), context.args(), context.locale()}, arg));
-        }
-
-        auto on_format_specs(int id, const Char *begin, const Char *end) -> const Char *
-        {
-            auto arg = get_arg(context, id);
-            if(arg.type() == type::custom_type)
-            {
-                parse_context.advance_to(parse_context.begin() + (begin - &*parse_context.begin()));
-                visit_format_arg(custom_formatter<Char>{parse_context, context}, arg);
-                return parse_context.begin();
-            }
-            auto                               specs = basic_format_specs<Char>();
-            specs_checker<specs_handler<Char>> handler(specs_handler<Char>(specs, parse_context, context), arg.type());
-            begin = parse_format_specs(begin, end, handler);
-            if(begin == end || *begin != '}')
-                on_error("missing '}' in format string");
-            auto f = arg_formatter<Char>{context.out(), specs, context.locale()};
-            context.advance_to(visit_format_arg(f, arg));
-            return begin;
-        }
-    };
-    detail::parse_format_string<false>(fmt, format_handler(out, fmt, args, loc));
-}
-
-#ifndef FMT_HEADER_ONLY
-extern template FMT_API auto thousands_sep_impl<char>(locale_ref) -> thousands_sep_result<char>;
-extern template FMT_API auto thousands_sep_impl<wchar_t>(locale_ref) -> thousands_sep_result<wchar_t>;
-extern template FMT_API auto decimal_point_impl(locale_ref) -> char;
-extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
-#endif // FMT_HEADER_ONLY
-
-FMT_END_DETAIL_NAMESPACE
-
-#if FMT_USE_USER_DEFINED_LITERALS
-inline namespace literals
-{
-/**
-  \rst
-  User-defined literal equivalent of :func:`fmt::arg`.
-
-  **Example**::
-
-    using namespace fmt::literals;
-    fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23);
-  \endrst
- */
-#if FMT_USE_NONTYPE_TEMPLATE_ARGS
-template <detail_exported::fixed_string Str>
-constexpr auto operator""_a()
-{
-    using char_t = remove_cvref_t<decltype(Str.data[0])>;
-    return detail::udl_arg<char_t, sizeof(Str.data) / sizeof(char_t), Str>();
-}
-#else
-constexpr auto operator"" _a(const char *s, size_t) -> detail::udl_arg<char>
-{
-    return {s};
-}
-#endif
-} // namespace literals
-#endif // FMT_USE_USER_DEFINED_LITERALS
-
-template <typename Locale, FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
-inline auto vformat(const Locale &loc, string_view fmt, format_args args) -> std::string
-{
-    return detail::vformat(loc, fmt, args);
-}
-
-template <typename Locale, typename... T, FMT_ENABLE_IF(detail::is_locale<Locale>::value)>
-inline auto format(const Locale &loc, format_string<T...> fmt, T &&...args) -> std::string
-{
-    return vformat(loc, string_view(fmt), fmt::make_format_args(args...));
-}
-
-template <
-    typename OutputIt,
-    typename Locale,
-    FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value &&detail::is_locale<Locale>::value)>
-auto vformat_to(OutputIt out, const Locale &loc, string_view fmt, format_args args) -> OutputIt
-{
-    using detail::get_buffer;
-    auto &&buf = get_buffer<char>(out);
-    detail::vformat_to(buf, fmt, args, detail::locale_ref(loc));
-    return detail::get_iterator(buf);
-}
-
-template <
-    typename OutputIt,
-    typename Locale,
-    typename... T,
-    FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value &&detail::is_locale<Locale>::value)>
-FMT_INLINE auto format_to(OutputIt out, const Locale &loc, format_string<T...> fmt, T &&...args) -> OutputIt
-{
-    return vformat_to(out, loc, fmt, fmt::make_format_args(args...));
-}
-
-FMT_MODULE_EXPORT_END
-FMT_END_NAMESPACE
-
-#ifdef FMT_HEADER_ONLY
-#define FMT_FUNC inline
-#include "format-inl.h"
-#else
-#define FMT_FUNC
-#endif
-
-#endif // FMT_FORMAT_H_
diff --git a/include/spdlog/fmt/bundled/locale.h b/include/spdlog/fmt/bundled/locale.h
deleted file mode 100644
index 7571b5261..000000000
--- a/include/spdlog/fmt/bundled/locale.h
+++ /dev/null
@@ -1,2 +0,0 @@
-#include "xchar.h"
-#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead
diff --git a/include/spdlog/fmt/bundled/os.h b/include/spdlog/fmt/bundled/os.h
deleted file mode 100644
index 417c09e35..000000000
--- a/include/spdlog/fmt/bundled/os.h
+++ /dev/null
@@ -1,495 +0,0 @@
-// Formatting library for C++ - optional OS-specific functionality
-//
-// Copyright (c) 2012 - present, Victor Zverovich
-// All rights reserved.
-//
-// For the license information refer to format.h.
-
-#ifndef FMT_OS_H_
-#define FMT_OS_H_
-
-#include <cerrno>
-#include <cstddef>
-#include <cstdio>
-#include <system_error> // std::system_error
-
-#if defined __APPLE__ || defined(__FreeBSD__)
-#include <xlocale.h> // for LC_NUMERIC_MASK on OS X
-#endif
-
-#include "format.h"
-
-#ifndef FMT_USE_FCNTL
-// UWP doesn't provide _pipe.
-#if FMT_HAS_INCLUDE("winapifamily.h")
-#include <winapifamily.h>
-#endif
-#if(FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || defined(__linux__)) &&                                         \
-    (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
-#include <fcntl.h> // for O_RDONLY
-#define FMT_USE_FCNTL 1
-#else
-#define FMT_USE_FCNTL 0
-#endif
-#endif
-
-#ifndef FMT_POSIX
-#if defined(_WIN32) && !defined(__MINGW32__)
-// Fix warnings about deprecated symbols.
-#define FMT_POSIX(call) _##call
-#else
-#define FMT_POSIX(call) call
-#endif
-#endif
-
-// Calls to system functions are wrapped in FMT_SYSTEM for testability.
-#ifdef FMT_SYSTEM
-#define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
-#else
-#define FMT_SYSTEM(call) ::call
-#ifdef _WIN32
-// Fix warnings about deprecated symbols.
-#define FMT_POSIX_CALL(call) ::_##call
-#else
-#define FMT_POSIX_CALL(call) ::call
-#endif
-#endif
-
-// Retries the expression while it evaluates to error_result and errno
-// equals to EINTR.
-#ifndef _WIN32
-#define FMT_RETRY_VAL(result, expression, error_result)                                                                \
-    do                                                                                                                 \
-    {                                                                                                                  \
-        (result) = (expression);                                                                                       \
-    } while((result) == (error_result) && errno == EINTR)
-#else
-#define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
-#endif
-
-#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
-
-FMT_BEGIN_NAMESPACE
-FMT_MODULE_EXPORT_BEGIN
-
-/**
-  \rst
-  A reference to a null-terminated string. It can be constructed from a C
-  string or ``std::string``.
-
-  You can use one of the following type aliases for common character types:
-
-  +---------------+-----------------------------+
-  | Type          | Definition                  |
-  +===============+=============================+
-  | cstring_view  | basic_cstring_view<char>    |
-  +---------------+-----------------------------+
-  | wcstring_view | basic_cstring_view<wchar_t> |
-  +---------------+-----------------------------+
-
-  This class is most useful as a parameter type to allow passing
-  different types of strings to a function, for example::
-
-    template <typename... Args>
-    std::string format(cstring_view format_str, const Args & ... args);
-
-    format("{}", 42);
-    format(std::string("{}"), 42);
-  \endrst
- */
-template <typename Char>
-class basic_cstring_view
-{
-private:
-    const Char *data_;
-
-public:
-    /** Constructs a string reference object from a C string. */
-    basic_cstring_view(const Char *s) : data_(s) {}
-
-    /**
-      \rst
-      Constructs a string reference from an ``std::string`` object.
-      \endrst
-     */
-    basic_cstring_view(const std::basic_string<Char> &s) : data_(s.c_str()) {}
-
-    /** Returns the pointer to a C string. */
-    const Char *c_str() const { return data_; }
-};
-
-using cstring_view  = basic_cstring_view<char>;
-using wcstring_view = basic_cstring_view<wchar_t>;
-
-template <typename Char>
-struct formatter<std::error_code, Char>
-{
-    template <typename ParseContext>
-    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
-    {
-        return ctx.begin();
-    }
-
-    template <typename FormatContext>
-    FMT_CONSTEXPR auto format(const std::error_code &ec, FormatContext &ctx) const -> decltype(ctx.out())
-    {
-        auto out = ctx.out();
-        out      = detail::write_bytes(out, ec.category().name(), basic_format_specs<Char>());
-        out      = detail::write<Char>(out, Char(':'));
-        out      = detail::write<Char>(out, ec.value());
-        return out;
-    }
-};
-
-#ifdef _WIN32
-FMT_API const std::error_category &system_category() noexcept;
-
-FMT_BEGIN_DETAIL_NAMESPACE
-// A converter from UTF-16 to UTF-8.
-// It is only provided for Windows since other systems support UTF-8 natively.
-class utf16_to_utf8
-{
-private:
-    memory_buffer buffer_;
-
-public:
-    utf16_to_utf8() {}
-    FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
-                operator string_view() const { return string_view(&buffer_[0], size()); }
-    size_t      size() const { return buffer_.size() - 1; }
-    const char *c_str() const { return &buffer_[0]; }
-    std::string str() const { return std::string(&buffer_[0], size()); }
-
-    // Performs conversion returning a system error code instead of
-    // throwing exception on conversion error. This method may still throw
-    // in case of memory allocation error.
-    FMT_API int convert(basic_string_view<wchar_t> s);
-};
-
-FMT_API void format_windows_error(buffer<char> &out, int error_code, const char *message) noexcept;
-FMT_END_DETAIL_NAMESPACE
-
-FMT_API std::system_error vwindows_error(int error_code, string_view format_str, format_args args);
-
-/**
- \rst
- Constructs a :class:`std::system_error` object with the description
- of the form
-
- .. parsed-literal::
-   *<message>*: *<system-message>*
-
- where *<message>* is the formatted message and *<system-message>* is the
- system message corresponding to the error code.
- *error_code* is a Windows error code as given by ``GetLastError``.
- If *error_code* is not a valid error code such as -1, the system message
- will look like "error -1".
-
- **Example**::
-
-   // This throws a system_error with the description
-   //   cannot open file 'madeup': The system cannot find the file specified.
-   // or similar (system message may vary).
-   const char *filename = "madeup";
-   LPOFSTRUCT of = LPOFSTRUCT();
-   HFILE file = OpenFile(filename, &of, OF_READ);
-   if (file == HFILE_ERROR) {
-     throw fmt::windows_error(GetLastError(),
-                              "cannot open file '{}'", filename);
-   }
- \endrst
-*/
-template <typename... Args>
-std::system_error windows_error(int error_code, string_view message, const Args &...args)
-{
-    return vwindows_error(error_code, message, fmt::make_format_args(args...));
-}
-
-// Reports a Windows error without throwing an exception.
-// Can be used to report errors from destructors.
-FMT_API void report_windows_error(int error_code, const char *message) noexcept;
-#else
-inline const std::error_category &system_category() noexcept
-{
-    return std::system_category();
-}
-#endif // _WIN32
-
-// std::system is not available on some platforms such as iOS (#2248).
-#ifdef __OSX__
-template <typename S, typename... Args, typename Char = char_t<S>>
-void say(const S &format_str, Args &&...args)
-{
-    std::system(format("say \"{}\"", format(format_str, args...)).c_str());
-}
-#endif
-
-// A buffered file.
-class buffered_file
-{
-private:
-    FILE *file_;
-
-    friend class file;
-
-    explicit buffered_file(FILE *f) : file_(f) {}
-
-public:
-    buffered_file(const buffered_file &)  = delete;
-    void operator=(const buffered_file &) = delete;
-
-    // Constructs a buffered_file object which doesn't represent any file.
-    buffered_file() noexcept : file_(nullptr) {}
-
-    // Destroys the object closing the file it represents if any.
-    FMT_API ~buffered_file() noexcept;
-
-public:
-    buffered_file(buffered_file &&other) noexcept : file_(other.file_) { other.file_ = nullptr; }
-
-    buffered_file &operator=(buffered_file &&other)
-    {
-        close();
-        file_       = other.file_;
-        other.file_ = nullptr;
-        return *this;
-    }
-
-    // Opens a file.
-    FMT_API buffered_file(cstring_view filename, cstring_view mode);
-
-    // Closes the file.
-    FMT_API void close();
-
-    // Returns the pointer to a FILE object representing this file.
-    FILE *get() const noexcept { return file_; }
-
-    FMT_API int descriptor() const;
-
-    void vprint(string_view format_str, format_args args) { fmt::vprint(file_, format_str, args); }
-
-    template <typename... Args>
-    inline void print(string_view format_str, const Args &...args)
-    {
-        vprint(format_str, fmt::make_format_args(args...));
-    }
-};
-
-#if FMT_USE_FCNTL
-// A file. Closed file is represented by a file object with descriptor -1.
-// Methods that are not declared with noexcept may throw
-// fmt::system_error in case of failure. Note that some errors such as
-// closing the file multiple times will cause a crash on Windows rather
-// than an exception. You can get standard behavior by overriding the
-// invalid parameter handler with _set_invalid_parameter_handler.
-class FMT_API file
-{
-private:
-    int fd_; // File descriptor.
-
-    // Constructs a file object with a given descriptor.
-    explicit file(int fd) : fd_(fd) {}
-
-public:
-    // Possible values for the oflag argument to the constructor.
-    enum
-    {
-        RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
-        WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
-        RDWR   = FMT_POSIX(O_RDWR),   // Open for reading and writing.
-        CREATE = FMT_POSIX(O_CREAT),  // Create if the file doesn't exist.
-        APPEND = FMT_POSIX(O_APPEND), // Open in append mode.
-        TRUNC  = FMT_POSIX(O_TRUNC)   // Truncate the content of the file.
-    };
-
-    // Constructs a file object which doesn't represent any file.
-    file() noexcept : fd_(-1) {}
-
-    // Opens a file and constructs a file object representing this file.
-    file(cstring_view path, int oflag);
-
-public:
-    file(const file &)           = delete;
-    void operator=(const file &) = delete;
-
-    file(file &&other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
-
-    // Move assignment is not noexcept because close may throw.
-    file &operator=(file &&other)
-    {
-        close();
-        fd_       = other.fd_;
-        other.fd_ = -1;
-        return *this;
-    }
-
-    // Destroys the object closing the file it represents if any.
-    ~file() noexcept;
-
-    // Returns the file descriptor.
-    int descriptor() const noexcept { return fd_; }
-
-    // Closes the file.
-    void close();
-
-    // Returns the file size. The size has signed type for consistency with
-    // stat::st_size.
-    long long size() const;
-
-    // Attempts to read count bytes from the file into the specified buffer.
-    size_t read(void *buffer, size_t count);
-
-    // Attempts to write count bytes from the specified buffer to the file.
-    size_t write(const void *buffer, size_t count);
-
-    // Duplicates a file descriptor with the dup function and returns
-    // the duplicate as a file object.
-    static file dup(int fd);
-
-    // Makes fd be the copy of this file descriptor, closing fd first if
-    // necessary.
-    void dup2(int fd);
-
-    // Makes fd be the copy of this file descriptor, closing fd first if
-    // necessary.
-    void dup2(int fd, std::error_code &ec) noexcept;
-
-    // Creates a pipe setting up read_end and write_end file objects for reading
-    // and writing respectively.
-    static void pipe(file &read_end, file &write_end);
-
-    // Creates a buffered_file object associated with this file and detaches
-    // this file object from the file.
-    buffered_file fdopen(const char *mode);
-};
-
-// Returns the memory page size.
-long getpagesize();
-
-FMT_BEGIN_DETAIL_NAMESPACE
-
-struct buffer_size
-{
-    buffer_size()     = default;
-    size_t      value = 0;
-    buffer_size operator=(size_t val) const
-    {
-        auto bs  = buffer_size();
-        bs.value = val;
-        return bs;
-    }
-};
-
-struct ostream_params
-{
-    int    oflag       = file::WRONLY | file::CREATE | file::TRUNC;
-    size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
-
-    ostream_params() {}
-
-    template <typename... T>
-    ostream_params(T... params, int new_oflag) : ostream_params(params...)
-    {
-        oflag = new_oflag;
-    }
-
-    template <typename... T>
-    ostream_params(T... params, detail::buffer_size bs) : ostream_params(params...)
-    {
-        this->buffer_size = bs.value;
-    }
-
-// Intel has a bug that results in failure to deduce a constructor
-// for empty parameter packs.
-#if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
-    ostream_params(int new_oflag) : oflag(new_oflag) {}
-    ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
-#endif
-};
-
-FMT_END_DETAIL_NAMESPACE
-
-// Added {} below to work around default constructor error known to
-// occur in Xcode versions 7.2.1 and 8.2.1.
-constexpr detail::buffer_size buffer_size{};
-
-/** A fast output stream which is not thread-safe. */
-class FMT_API ostream final : private detail::buffer<char>
-{
-private:
-    file file_;
-
-    void grow(size_t) override;
-
-    ostream(cstring_view path, const detail::ostream_params &params) : file_(path, params.oflag)
-    {
-        set(new char[params.buffer_size], params.buffer_size);
-    }
-
-public:
-    ostream(ostream &&other) :
-        detail::buffer<char>(other.data(), other.size(), other.capacity()), file_(std::move(other.file_))
-    {
-        other.clear();
-        other.set(nullptr, 0);
-    }
-    ~ostream()
-    {
-        flush();
-        delete[] data();
-    }
-
-    void flush()
-    {
-        if(size() == 0)
-            return;
-        file_.write(data(), size());
-        clear();
-    }
-
-    template <typename... T>
-    friend ostream output_file(cstring_view path, T... params);
-
-    void close()
-    {
-        flush();
-        file_.close();
-    }
-
-    /**
-      Formats ``args`` according to specifications in ``fmt`` and writes the
-      output to the file.
-     */
-    template <typename... T>
-    void print(format_string<T...> fmt, T &&...args)
-    {
-        vformat_to(detail::buffer_appender<char>(*this), fmt, fmt::make_format_args(args...));
-    }
-};
-
-/**
-  \rst
-  Opens a file for writing. Supported parameters passed in *params*:
-
-  * ``<integer>``: Flags passed to `open
-    <https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
-    (``file::WRONLY | file::CREATE | file::TRUNC`` by default)
-  * ``buffer_size=<integer>``: Output buffer size
-
-  **Example**::
-
-    auto out = fmt::output_file("guide.txt");
-    out.print("Don't {}", "Panic");
-  \endrst
- */
-template <typename... T>
-inline ostream output_file(cstring_view path, T... params)
-{
-    return {path, detail::ostream_params(params...)};
-}
-#endif // FMT_USE_FCNTL
-
-FMT_MODULE_EXPORT_END
-FMT_END_NAMESPACE
-
-#endif // FMT_OS_H_
diff --git a/include/spdlog/fmt/bundled/ostream.h b/include/spdlog/fmt/bundled/ostream.h
deleted file mode 100644
index 64a3b6cd2..000000000
--- a/include/spdlog/fmt/bundled/ostream.h
+++ /dev/null
@@ -1,259 +0,0 @@
-// Formatting library for C++ - std::ostream support
-//
-// Copyright (c) 2012 - present, Victor Zverovich
-// All rights reserved.
-//
-// For the license information refer to format.h.
-
-#ifndef FMT_OSTREAM_H_
-#define FMT_OSTREAM_H_
-
-#include <fstream>
-#include <ostream>
-#if defined(_WIN32) && defined(__GLIBCXX__)
-#include <ext/stdio_filebuf.h>
-#include <ext/stdio_sync_filebuf.h>
-#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
-#include <__std_stream>
-#endif
-
-#include "format.h"
-
-FMT_BEGIN_NAMESPACE
-
-template <typename OutputIt, typename Char>
-class basic_printf_context;
-
-namespace detail
-{
-
-// Checks if T has a user-defined operator<<.
-template <typename T, typename Char, typename Enable = void>
-class is_streamable
-{
-private:
-    template <typename U>
-    static auto test(int)
-        -> bool_constant<sizeof(std::declval<std::basic_ostream<Char> &>() << std::declval<U>()) != 0>;
-
-    template <typename>
-    static auto test(...) -> std::false_type;
-
-    using result = decltype(test<T>(0));
-
-public:
-    is_streamable() = default;
-
-    static const bool value = result::value;
-};
-
-// Formatting of built-in types and arrays is intentionally disabled because
-// it's handled by standard (non-ostream) formatters.
-template <typename T, typename Char>
-struct is_streamable<
-    T,
-    Char,
-    enable_if_t<
-        std::is_arithmetic<T>::value || std::is_array<T>::value || std::is_pointer<T>::value ||
-        std::is_same<T, char8_type>::value || std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
-        std::is_same<T, std_string_view<Char>>::value ||
-        (std::is_convertible<T, int>::value && !std::is_enum<T>::value)>> : std::false_type
-{
-};
-
-// Generate a unique explicit instantion in every translation unit using a tag
-// type in an anonymous namespace.
-namespace
-{
-    struct file_access_tag
-    {
-    };
-} // namespace
-template <class Tag, class BufType, FILE *BufType::*FileMemberPtr>
-class file_access
-{
-    friend auto get_file(BufType &obj) -> FILE * { return obj.*FileMemberPtr; }
-};
-
-#if FMT_MSC_VERSION
-template class file_access<file_access_tag, std::filebuf, &std::filebuf::_Myfile>;
-auto get_file(std::filebuf &) -> FILE *;
-#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
-template class file_access<file_access_tag, std::__stdoutbuf<char>, &std::__stdoutbuf<char>::__file_>;
-auto get_file(std::__stdoutbuf<char> &) -> FILE *;
-#endif
-
-inline bool write_ostream_unicode(std::ostream &os, fmt::string_view data)
-{
-#if FMT_MSC_VERSION
-    if(auto *buf = dynamic_cast<std::filebuf *>(os.rdbuf()))
-        if(FILE *f = get_file(*buf))
-            return write_console(f, data);
-#elif defined(_WIN32) && defined(__GLIBCXX__)
-    auto *rdbuf = os.rdbuf();
-    FILE *c_file;
-    if(auto *fbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char> *>(rdbuf))
-        c_file = fbuf->file();
-    else if(auto *fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char> *>(rdbuf))
-        c_file = fbuf->file();
-    else
-        return false;
-    if(c_file)
-        return write_console(c_file, data);
-#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
-    if(auto *buf = dynamic_cast<std::__stdoutbuf<char> *>(os.rdbuf()))
-        if(FILE *f = get_file(*buf))
-            return write_console(f, data);
-#else
-    ignore_unused(os, data);
-#endif
-    return false;
-}
-inline bool write_ostream_unicode(std::wostream &, fmt::basic_string_view<wchar_t>)
-{
-    return false;
-}
-
-// Write the content of buf to os.
-// It is a separate function rather than a part of vprint to simplify testing.
-template <typename Char>
-void write_buffer(std::basic_ostream<Char> &os, buffer<Char> &buf)
-{
-    const Char *buf_data         = buf.data();
-    using unsigned_streamsize    = std::make_unsigned<std::streamsize>::type;
-    unsigned_streamsize size     = buf.size();
-    unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
-    do
-    {
-        unsigned_streamsize n = size <= max_size ? size : max_size;
-        os.write(buf_data, static_cast<std::streamsize>(n));
-        buf_data += n;
-        size -= n;
-    } while(size != 0);
-}
-
-template <typename Char, typename T>
-void format_value(buffer<Char> &buf, const T &value, locale_ref loc = locale_ref())
-{
-    auto &&format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
-    auto &&output     = std::basic_ostream<Char>(&format_buf);
-#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
-    if(loc)
-        output.imbue(loc.get<std::locale>());
-#endif
-    output << value;
-    output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
-}
-
-template <typename T>
-struct streamed_view
-{
-    const T &value;
-};
-
-} // namespace detail
-
-// Formats an object of type T that has an overloaded ostream operator<<.
-template <typename Char>
-struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char>
-{
-    void set_debug_format() = delete;
-
-    template <typename T, typename OutputIt>
-    auto format(const T &value, basic_format_context<OutputIt, Char> &ctx) const -> OutputIt
-    {
-        auto buffer = basic_memory_buffer<Char>();
-        format_value(buffer, value, ctx.locale());
-        return formatter<basic_string_view<Char>, Char>::format({buffer.data(), buffer.size()}, ctx);
-    }
-};
-
-using ostream_formatter = basic_ostream_formatter<char>;
-
-template <typename T, typename Char>
-struct formatter<detail::streamed_view<T>, Char> : basic_ostream_formatter<Char>
-{
-    template <typename OutputIt>
-    auto format(detail::streamed_view<T> view, basic_format_context<OutputIt, Char> &ctx) const -> OutputIt
-    {
-        return basic_ostream_formatter<Char>::format(view.value, ctx);
-    }
-};
-
-/**
-  \rst
-  Returns a view that formats `value` via an ostream ``operator<<``.
-
-  **Example**::
-
-    fmt::print("Current thread id: {}\n",
-               fmt::streamed(std::this_thread::get_id()));
-  \endrst
- */
-template <typename T>
-auto streamed(const T &value) -> detail::streamed_view<T>
-{
-    return {value};
-}
-
-namespace detail
-{
-
-// Formats an object of type T that has an overloaded ostream operator<<.
-template <typename T, typename Char>
-struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> : basic_ostream_formatter<Char>
-{
-    using basic_ostream_formatter<Char>::format;
-};
-
-inline void vprint_directly(std::ostream &os, string_view format_str, format_args args)
-{
-    auto buffer = memory_buffer();
-    detail::vformat_to(buffer, format_str, args);
-    detail::write_buffer(os, buffer);
-}
-
-} // namespace detail
-
-FMT_MODULE_EXPORT template <typename Char>
-void vprint(
-    std::basic_ostream<Char>                                &os,
-    basic_string_view<type_identity_t<Char>>                 format_str,
-    basic_format_args<buffer_context<type_identity_t<Char>>> args)
-{
-    auto buffer = basic_memory_buffer<Char>();
-    detail::vformat_to(buffer, format_str, args);
-    if(detail::write_ostream_unicode(os, {buffer.data(), buffer.size()}))
-        return;
-    detail::write_buffer(os, buffer);
-}
-
-/**
-  \rst
-  Prints formatted data to the stream *os*.
-
-  **Example**::
-
-    fmt::print(cerr, "Don't {}!", "panic");
-  \endrst
- */
-FMT_MODULE_EXPORT template <typename... T>
-void print(std::ostream &os, format_string<T...> fmt, T &&...args)
-{
-    const auto &vargs = fmt::make_format_args(args...);
-    if(detail::is_utf8())
-        vprint(os, fmt, vargs);
-    else
-        detail::vprint_directly(os, fmt, vargs);
-}
-
-FMT_MODULE_EXPORT
-template <typename... Args>
-void print(std::wostream &os, basic_format_string<wchar_t, type_identity_t<Args>...> fmt, Args &&...args)
-{
-    vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
-}
-
-FMT_END_NAMESPACE
-
-#endif // FMT_OSTREAM_H_
diff --git a/include/spdlog/fmt/bundled/printf.h b/include/spdlog/fmt/bundled/printf.h
deleted file mode 100644
index 3c2e6d16a..000000000
--- a/include/spdlog/fmt/bundled/printf.h
+++ /dev/null
@@ -1,718 +0,0 @@
-// Formatting library for C++ - legacy printf implementation
-//
-// Copyright (c) 2012 - 2016, Victor Zverovich
-// All rights reserved.
-//
-// For the license information refer to format.h.
-
-#ifndef FMT_PRINTF_H_
-#define FMT_PRINTF_H_
-
-#include "format.h"
-
-#include <algorithm> // std::max
-#include <limits>    // std::numeric_limits
-
-FMT_BEGIN_NAMESPACE
-FMT_MODULE_EXPORT_BEGIN
-
-template <typename T>
-struct printf_formatter
-{
-    printf_formatter() = delete;
-};
-
-template <typename Char>
-class basic_printf_parse_context : public basic_format_parse_context<Char>
-{
-    using basic_format_parse_context<Char>::basic_format_parse_context;
-};
-
-template <typename OutputIt, typename Char>
-class basic_printf_context
-{
-private:
-    OutputIt                                out_;
-    basic_format_args<basic_printf_context> args_;
-
-public:
-    using char_type          = Char;
-    using format_arg         = basic_format_arg<basic_printf_context>;
-    using parse_context_type = basic_printf_parse_context<Char>;
-    template <typename T>
-    using formatter_type = printf_formatter<T>;
-
-    /**
-      \rst
-      Constructs a ``printf_context`` object. References to the arguments are
-      stored in the context object so make sure they have appropriate lifetimes.
-      \endrst
-     */
-    basic_printf_context(OutputIt out, basic_format_args<basic_printf_context> args) : out_(out), args_(args) {}
-
-    OutputIt out() { return out_; }
-    void     advance_to(OutputIt it) { out_ = it; }
-
-    detail::locale_ref locale() { return {}; }
-
-    format_arg arg(int id) const { return args_.get(id); }
-
-    FMT_CONSTEXPR void on_error(const char *message) { detail::error_handler().on_error(message); }
-};
-
-FMT_BEGIN_DETAIL_NAMESPACE
-
-// Checks if a value fits in int - used to avoid warnings about comparing
-// signed and unsigned integers.
-template <bool IsSigned>
-struct int_checker
-{
-    template <typename T>
-    static bool fits_in_int(T value)
-    {
-        unsigned max = max_value<int>();
-        return value <= max;
-    }
-    static bool fits_in_int(bool) { return true; }
-};
-
-template <>
-struct int_checker<true>
-{
-    template <typename T>
-    static bool fits_in_int(T value)
-    {
-        return value >= (std::numeric_limits<int>::min)() && value <= max_value<int>();
-    }
-    static bool fits_in_int(int) { return true; }
-};
-
-class printf_precision_handler
-{
-public:
-    template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-    int operator()(T value)
-    {
-        if(!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
-            FMT_THROW(format_error("number is too big"));
-        return (std::max)(static_cast<int>(value), 0);
-    }
-
-    template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-    int operator()(T)
-    {
-        FMT_THROW(format_error("precision is not integer"));
-        return 0;
-    }
-};
-
-// An argument visitor that returns true iff arg is a zero integer.
-class is_zero_int
-{
-public:
-    template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-    bool operator()(T value)
-    {
-        return value == 0;
-    }
-
-    template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-    bool operator()(T)
-    {
-        return false;
-    }
-};
-
-template <typename T>
-struct make_unsigned_or_bool : std::make_unsigned<T>
-{
-};
-
-template <>
-struct make_unsigned_or_bool<bool>
-{
-    using type = bool;
-};
-
-template <typename T, typename Context>
-class arg_converter
-{
-private:
-    using char_type = typename Context::char_type;
-
-    basic_format_arg<Context> &arg_;
-    char_type                  type_;
-
-public:
-    arg_converter(basic_format_arg<Context> &arg, char_type type) : arg_(arg), type_(type) {}
-
-    void operator()(bool value)
-    {
-        if(type_ != 's')
-            operator()<bool>(value);
-    }
-
-    template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
-    void operator()(U value)
-    {
-        bool is_signed    = type_ == 'd' || type_ == 'i';
-        using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
-        if(const_check(sizeof(target_type) <= sizeof(int)))
-        {
-            // Extra casts are used to silence warnings.
-            if(is_signed)
-            {
-                arg_ = detail::make_arg<Context>(static_cast<int>(static_cast<target_type>(value)));
-            }
-            else
-            {
-                using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
-                arg_ = detail::make_arg<Context>(static_cast<unsigned>(static_cast<unsigned_type>(value)));
-            }
-        }
-        else
-        {
-            if(is_signed)
-            {
-                // glibc's printf doesn't sign extend arguments of smaller types:
-                //   std::printf("%lld", -42);  // prints "4294967254"
-                // but we don't have to do the same because it's a UB.
-                arg_ = detail::make_arg<Context>(static_cast<long long>(value));
-            }
-            else
-            {
-                arg_ = detail::make_arg<Context>(static_cast<typename make_unsigned_or_bool<U>::type>(value));
-            }
-        }
-    }
-
-    template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
-    void operator()(U)
-    {
-    } // No conversion needed for non-integral types.
-};
-
-// Converts an integer argument to T for printf, if T is an integral type.
-// If T is void, the argument is converted to corresponding signed or unsigned
-// type depending on the type specifier: 'd' and 'i' - signed, other -
-// unsigned).
-template <typename T, typename Context, typename Char>
-void convert_arg(basic_format_arg<Context> &arg, Char type)
-{
-    visit_format_arg(arg_converter<T, Context>(arg, type), arg);
-}
-
-// Converts an integer argument to char for printf.
-template <typename Context>
-class char_converter
-{
-private:
-    basic_format_arg<Context> &arg_;
-
-public:
-    explicit char_converter(basic_format_arg<Context> &arg) : arg_(arg) {}
-
-    template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-    void operator()(T value)
-    {
-        arg_ = detail::make_arg<Context>(static_cast<typename Context::char_type>(value));
-    }
-
-    template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-    void operator()(T)
-    {
-    } // No conversion needed for non-integral types.
-};
-
-// An argument visitor that return a pointer to a C string if argument is a
-// string or null otherwise.
-template <typename Char>
-struct get_cstring
-{
-    template <typename T>
-    const Char *operator()(T)
-    {
-        return nullptr;
-    }
-    const Char *operator()(const Char *s) { return s; }
-};
-
-// Checks if an argument is a valid printf width specifier and sets
-// left alignment if it is negative.
-template <typename Char>
-class printf_width_handler
-{
-private:
-    using format_specs = basic_format_specs<Char>;
-
-    format_specs &specs_;
-
-public:
-    explicit printf_width_handler(format_specs &specs) : specs_(specs) {}
-
-    template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-    unsigned operator()(T value)
-    {
-        auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
-        if(detail::is_negative(value))
-        {
-            specs_.align = align::left;
-            width        = 0 - width;
-        }
-        unsigned int_max = max_value<int>();
-        if(width > int_max)
-            FMT_THROW(format_error("number is too big"));
-        return static_cast<unsigned>(width);
-    }
-
-    template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-    unsigned operator()(T)
-    {
-        FMT_THROW(format_error("width is not integer"));
-        return 0;
-    }
-};
-
-// The ``printf`` argument formatter.
-template <typename OutputIt, typename Char>
-class printf_arg_formatter : public arg_formatter<Char>
-{
-private:
-    using base         = arg_formatter<Char>;
-    using context_type = basic_printf_context<OutputIt, Char>;
-    using format_specs = basic_format_specs<Char>;
-
-    context_type &context_;
-
-    OutputIt write_null_pointer(bool is_string = false)
-    {
-        auto s = this->specs;
-        s.type = presentation_type::none;
-        return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
-    }
-
-public:
-    printf_arg_formatter(OutputIt iter, format_specs &s, context_type &ctx) : base{iter, s, locale_ref()}, context_(ctx)
-    {
-    }
-
-    OutputIt operator()(monostate value) { return base::operator()(value); }
-
-    template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
-    OutputIt operator()(T value)
-    {
-        // MSVC2013 fails to compile separate overloads for bool and Char so use
-        // std::is_same instead.
-        if(std::is_same<T, Char>::value)
-        {
-            format_specs fmt_specs = this->specs;
-            if(fmt_specs.type != presentation_type::none && fmt_specs.type != presentation_type::chr)
-            {
-                return (*this)(static_cast<int>(value));
-            }
-            fmt_specs.sign    = sign::none;
-            fmt_specs.alt     = false;
-            fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
-            // align::numeric needs to be overwritten here since the '0' flag is
-            // ignored for non-numeric types
-            if(fmt_specs.align == align::none || fmt_specs.align == align::numeric)
-                fmt_specs.align = align::right;
-            return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
-        }
-        return base::operator()(value);
-    }
-
-    template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
-    OutputIt operator()(T value)
-    {
-        return base::operator()(value);
-    }
-
-    /** Formats a null-terminated C string. */
-    OutputIt operator()(const char *value)
-    {
-        if(value)
-            return base::operator()(value);
-        return write_null_pointer(this->specs.type != presentation_type::pointer);
-    }
-
-    /** Formats a null-terminated wide C string. */
-    OutputIt operator()(const wchar_t *value)
-    {
-        if(value)
-            return base::operator()(value);
-        return write_null_pointer(this->specs.type != presentation_type::pointer);
-    }
-
-    OutputIt operator()(basic_string_view<Char> value) { return base::operator()(value); }
-
-    /** Formats a pointer. */
-    OutputIt operator()(const void *value) { return value ? base::operator()(value) : write_null_pointer(); }
-
-    /** Formats an argument of a custom (user-defined) type. */
-    OutputIt operator()(typename basic_format_arg<context_type>::handle handle)
-    {
-        auto parse_ctx = basic_printf_parse_context<Char>(basic_string_view<Char>());
-        handle.format(parse_ctx, context_);
-        return this->out;
-    }
-};
-
-template <typename Char>
-void parse_flags(basic_format_specs<Char> &specs, const Char *&it, const Char *end)
-{
-    for(; it != end; ++it)
-    {
-        switch(*it)
-        {
-            case '-':
-                specs.align = align::left;
-                break;
-            case '+':
-                specs.sign = sign::plus;
-                break;
-            case '0':
-                specs.fill[0] = '0';
-                break;
-            case ' ':
-                if(specs.sign != sign::plus)
-                {
-                    specs.sign = sign::space;
-                }
-                break;
-            case '#':
-                specs.alt = true;
-                break;
-            default:
-                return;
-        }
-    }
-}
-
-template <typename Char, typename GetArg>
-int parse_header(const Char *&it, const Char *end, basic_format_specs<Char> &specs, GetArg get_arg)
-{
-    int  arg_index = -1;
-    Char c         = *it;
-    if(c >= '0' && c <= '9')
-    {
-        // Parse an argument index (if followed by '$') or a width possibly
-        // preceded with '0' flag(s).
-        int value = parse_nonnegative_int(it, end, -1);
-        if(it != end && *it == '$')
-        { // value is an argument index
-            ++it;
-            arg_index = value != -1 ? value : max_value<int>();
-        }
-        else
-        {
-            if(c == '0')
-                specs.fill[0] = '0';
-            if(value != 0)
-            {
-                // Nonzero value means that we parsed width and don't need to
-                // parse it or flags again, so return now.
-                if(value == -1)
-                    FMT_THROW(format_error("number is too big"));
-                specs.width = value;
-                return arg_index;
-            }
-        }
-    }
-    parse_flags(specs, it, end);
-    // Parse width.
-    if(it != end)
-    {
-        if(*it >= '0' && *it <= '9')
-        {
-            specs.width = parse_nonnegative_int(it, end, -1);
-            if(specs.width == -1)
-                FMT_THROW(format_error("number is too big"));
-        }
-        else if(*it == '*')
-        {
-            ++it;
-            specs.width = static_cast<int>(visit_format_arg(detail::printf_width_handler<Char>(specs), get_arg(-1)));
-        }
-    }
-    return arg_index;
-}
-
-template <typename Char, typename Context>
-void vprintf(buffer<Char> &buf, basic_string_view<Char> format, basic_format_args<Context> args)
-{
-    using OutputIt = buffer_appender<Char>;
-    auto out       = OutputIt(buf);
-    auto context   = basic_printf_context<OutputIt, Char>(out, args);
-    auto parse_ctx = basic_printf_parse_context<Char>(format);
-
-    // Returns the argument with specified index or, if arg_index is -1, the next
-    // argument.
-    auto get_arg = [&](int arg_index)
-    {
-        if(arg_index < 0)
-            arg_index = parse_ctx.next_arg_id();
-        else
-            parse_ctx.check_arg_id(--arg_index);
-        return detail::get_arg(context, arg_index);
-    };
-
-    const Char *start = parse_ctx.begin();
-    const Char *end   = parse_ctx.end();
-    auto        it    = start;
-    while(it != end)
-    {
-        if(!detail::find<false, Char>(it, end, '%', it))
-        {
-            it = end; // detail::find leaves it == nullptr if it doesn't find '%'
-            break;
-        }
-        Char c = *it++;
-        if(it != end && *it == c)
-        {
-            out   = detail::write(out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
-            start = ++it;
-            continue;
-        }
-        out = detail::write(out, basic_string_view<Char>(start, detail::to_unsigned(it - 1 - start)));
-
-        basic_format_specs<Char> specs;
-        specs.align = align::right;
-
-        // Parse argument index, flags and width.
-        int arg_index = parse_header(it, end, specs, get_arg);
-        if(arg_index == 0)
-            parse_ctx.on_error("argument not found");
-
-        // Parse precision.
-        if(it != end && *it == '.')
-        {
-            ++it;
-            c = it != end ? *it : 0;
-            if('0' <= c && c <= '9')
-            {
-                specs.precision = parse_nonnegative_int(it, end, 0);
-            }
-            else if(c == '*')
-            {
-                ++it;
-                specs.precision = static_cast<int>(visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
-            }
-            else
-            {
-                specs.precision = 0;
-            }
-        }
-
-        auto arg = get_arg(arg_index);
-        // For d, i, o, u, x, and X conversion specifiers, if a precision is
-        // specified, the '0' flag is ignored
-        if(specs.precision >= 0 && arg.is_integral())
-            specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-' present.
-        if(specs.precision >= 0 && arg.type() == detail::type::cstring_type)
-        {
-            auto str     = visit_format_arg(detail::get_cstring<Char>(), arg);
-            auto str_end = str + specs.precision;
-            auto nul     = std::find(str, str_end, Char());
-            arg          = detail::make_arg<basic_printf_context<OutputIt, Char>>(
-                basic_string_view<Char>(str, detail::to_unsigned(nul != str_end ? nul - str : specs.precision)));
-        }
-        if(specs.alt && visit_format_arg(detail::is_zero_int(), arg))
-            specs.alt = false;
-        if(specs.fill[0] == '0')
-        {
-            if(arg.is_arithmetic() && specs.align != align::left)
-                specs.align = align::numeric;
-            else
-                specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
-                                     // flag is also present.
-        }
-
-        // Parse length and convert the argument to the required type.
-        c      = it != end ? *it++ : 0;
-        Char t = it != end ? *it : 0;
-        using detail::convert_arg;
-        switch(c)
-        {
-            case 'h':
-                if(t == 'h')
-                {
-                    ++it;
-                    t = it != end ? *it : 0;
-                    convert_arg<signed char>(arg, t);
-                }
-                else
-                {
-                    convert_arg<short>(arg, t);
-                }
-                break;
-            case 'l':
-                if(t == 'l')
-                {
-                    ++it;
-                    t = it != end ? *it : 0;
-                    convert_arg<long long>(arg, t);
-                }
-                else
-                {
-                    convert_arg<long>(arg, t);
-                }
-                break;
-            case 'j':
-                convert_arg<intmax_t>(arg, t);
-                break;
-            case 'z':
-                convert_arg<size_t>(arg, t);
-                break;
-            case 't':
-                convert_arg<std::ptrdiff_t>(arg, t);
-                break;
-            case 'L':
-                // printf produces garbage when 'L' is omitted for long double, no
-                // need to do the same.
-                break;
-            default:
-                --it;
-                convert_arg<void>(arg, c);
-        }
-
-        // Parse type.
-        if(it == end)
-            FMT_THROW(format_error("invalid format string"));
-        char type = static_cast<char>(*it++);
-        if(arg.is_integral())
-        {
-            // Normalize type.
-            switch(type)
-            {
-                case 'i':
-                case 'u':
-                    type = 'd';
-                    break;
-                case 'c':
-                    visit_format_arg(detail::char_converter<basic_printf_context<OutputIt, Char>>(arg), arg);
-                    break;
-            }
-        }
-        specs.type = parse_presentation_type(type);
-        if(specs.type == presentation_type::none)
-            parse_ctx.on_error("invalid type specifier");
-
-        start = it;
-
-        // Format argument.
-        out = visit_format_arg(detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
-    }
-    detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
-}
-FMT_END_DETAIL_NAMESPACE
-
-template <typename Char>
-using basic_printf_context_t = basic_printf_context<detail::buffer_appender<Char>, Char>;
-
-using printf_context  = basic_printf_context_t<char>;
-using wprintf_context = basic_printf_context_t<wchar_t>;
-
-using printf_args  = basic_format_args<printf_context>;
-using wprintf_args = basic_format_args<wprintf_context>;
-
-/**
-  \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... T>
-inline auto make_printf_args(const T &...args) -> format_arg_store<printf_context, T...>
-{
-    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... T>
-inline auto make_wprintf_args(const T &...args) -> format_arg_store<wprintf_context, T...>
-{
-    return {args...};
-}
-
-template <typename S, typename Char = char_t<S>>
-inline auto vsprintf(const S &fmt, basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-    -> std::basic_string<Char>
-{
-    basic_memory_buffer<Char> buffer;
-    vprintf(buffer, detail::to_string_view(fmt), args);
-    return to_string(buffer);
-}
-
-/**
-  \rst
-  Formats arguments and returns the result as a string.
-
-  **Example**::
-
-    std::string message = fmt::sprintf("The answer is %d", 42);
-  \endrst
-*/
-template <typename S, typename... T, typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
-inline auto sprintf(const S &fmt, const T &...args) -> std::basic_string<Char>
-{
-    using context = basic_printf_context_t<Char>;
-    return vsprintf(detail::to_string_view(fmt), fmt::make_format_args<context>(args...));
-}
-
-template <typename S, typename Char = char_t<S>>
-inline auto vfprintf(std::FILE *f, const S &fmt, basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-    -> int
-{
-    basic_memory_buffer<Char> buffer;
-    vprintf(buffer, detail::to_string_view(fmt), args);
-    size_t size = buffer.size();
-    return std::fwrite(buffer.data(), sizeof(Char), size, f) < size ? -1 : static_cast<int>(size);
-}
-
-/**
-  \rst
-  Prints formatted data to the file *f*.
-
-  **Example**::
-
-    fmt::fprintf(stderr, "Don't %s!", "panic");
-  \endrst
- */
-template <typename S, typename... T, typename Char = char_t<S>>
-inline auto fprintf(std::FILE *f, const S &fmt, const T &...args) -> int
-{
-    using context = basic_printf_context_t<Char>;
-    return vfprintf(f, detail::to_string_view(fmt), fmt::make_format_args<context>(args...));
-}
-
-template <typename S, typename Char = char_t<S>>
-inline auto vprintf(const S &fmt, basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) -> int
-{
-    return vfprintf(stdout, detail::to_string_view(fmt), args);
-}
-
-/**
-  \rst
-  Prints formatted data to ``stdout``.
-
-  **Example**::
-
-    fmt::printf("Elapsed time: %.2f seconds", 1.23);
-  \endrst
- */
-template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
-inline auto printf(const S &fmt, const T &...args) -> int
-{
-    return vprintf(detail::to_string_view(fmt), fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
-}
-
-FMT_MODULE_EXPORT_END
-FMT_END_NAMESPACE
-
-#endif // FMT_PRINTF_H_
diff --git a/include/spdlog/fmt/bundled/ranges.h b/include/spdlog/fmt/bundled/ranges.h
deleted file mode 100644
index 73cdbbbc7..000000000
--- a/include/spdlog/fmt/bundled/ranges.h
+++ /dev/null
@@ -1,806 +0,0 @@
-// Formatting library for C++ - experimental range support
-//
-// 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 ranges, containers and types tuple interface.
-
-#ifndef FMT_RANGES_H_
-#define FMT_RANGES_H_
-
-#include "format.h"
-
-#include <initializer_list>
-#include <tuple>
-#include <type_traits>
-
-FMT_BEGIN_NAMESPACE
-
-namespace detail
-{
-
-template <typename RangeT, typename OutputIterator>
-OutputIterator copy(const RangeT &range, OutputIterator out)
-{
-    for(auto it = range.begin(), end = range.end(); it != end; ++it)
-        *out++ = *it;
-    return out;
-}
-
-template <typename OutputIterator>
-OutputIterator copy(const char *str, OutputIterator out)
-{
-    while(*str)
-        *out++ = *str++;
-    return out;
-}
-
-template <typename OutputIterator>
-OutputIterator copy(char ch, OutputIterator out)
-{
-    *out++ = ch;
-    return out;
-}
-
-template <typename OutputIterator>
-OutputIterator copy(wchar_t ch, OutputIterator out)
-{
-    *out++ = ch;
-    return out;
-}
-
-// Returns true if T has a std::string-like interface, like std::string_view.
-template <typename T>
-class is_std_string_like
-{
-    template <typename U>
-    static auto check(U *p) -> decltype((void) p->find('a'), p->length(), (void) p->data(), int());
-    template <typename>
-    static void check(...);
-
-public:
-    static constexpr const bool value = is_string<T>::value || std::is_convertible<T, std_string_view<char>>::value ||
-                                        !std::is_void<decltype(check<T>(nullptr))>::value;
-};
-
-template <typename Char>
-struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type
-{
-};
-
-template <typename T>
-class is_map
-{
-    template <typename U>
-    static auto check(U *) -> typename U::mapped_type;
-    template <typename>
-    static void check(...);
-
-public:
-#ifdef FMT_FORMAT_MAP_AS_LIST
-    static constexpr const bool value = false;
-#else
-    static constexpr const bool value = !std::is_void<decltype(check<T>(nullptr))>::value;
-#endif
-};
-
-template <typename T>
-class is_set
-{
-    template <typename U>
-    static auto check(U *) -> typename U::key_type;
-    template <typename>
-    static void check(...);
-
-public:
-#ifdef FMT_FORMAT_SET_AS_LIST
-    static constexpr const bool value = false;
-#else
-    static constexpr const bool value = !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
-#endif
-};
-
-template <typename... Ts>
-struct conditional_helper
-{
-};
-
-template <typename T, typename _ = void>
-struct is_range_ : std::false_type
-{
-};
-
-#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
-
-#define FMT_DECLTYPE_RETURN(val)                                                                                       \
-    ->decltype(val)                                                                                                    \
-    {                                                                                                                  \
-        return val;                                                                                                    \
-    }                                                                                                                  \
-    static_assert(true, "") // This makes it so that a semicolon is required after the
-                            // macro, which helps clang-format handle the formatting.
-
-// C array overload
-template <typename T, std::size_t N>
-auto range_begin(const T (&arr)[N]) -> const T *
-{
-    return arr;
-}
-template <typename T, std::size_t N>
-auto range_end(const T (&arr)[N]) -> const T *
-{
-    return arr + N;
-}
-
-template <typename T, typename Enable = void>
-struct has_member_fn_begin_end_t : std::false_type
-{
-};
-
-template <typename T>
-struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()), decltype(std::declval<T>().end())>>
-    : std::true_type
-{
-};
-
-// Member function overload
-template <typename T>
-auto range_begin(T &&rng) FMT_DECLTYPE_RETURN(static_cast<T &&>(rng).begin());
-template <typename T>
-auto range_end(T &&rng) FMT_DECLTYPE_RETURN(static_cast<T &&>(rng).end());
-
-// ADL overload. Only participates in overload resolution if member functions
-// are not found.
-template <typename T>
-auto range_begin(T &&rng)
-    -> enable_if_t<!has_member_fn_begin_end_t<T &&>::value, decltype(begin(static_cast<T &&>(rng)))>
-{
-    return begin(static_cast<T &&>(rng));
-}
-template <typename T>
-auto range_end(T &&rng) -> enable_if_t<!has_member_fn_begin_end_t<T &&>::value, decltype(end(static_cast<T &&>(rng)))>
-{
-    return end(static_cast<T &&>(rng));
-}
-
-template <typename T, typename Enable = void>
-struct has_const_begin_end : std::false_type
-{
-};
-template <typename T, typename Enable = void>
-struct has_mutable_begin_end : std::false_type
-{
-};
-
-template <typename T>
-struct has_const_begin_end<
-    T,
-    void_t<
-        decltype(detail::range_begin(std::declval<const remove_cvref_t<T> &>())),
-        decltype(detail::range_end(std::declval<const remove_cvref_t<T> &>()))>> : std::true_type
-{
-};
-
-template <typename T>
-struct has_mutable_begin_end<
-    T,
-    void_t<
-        decltype(detail::range_begin(std::declval<T>())),
-        decltype(detail::range_end(std::declval<T>())),
-        enable_if_t<std::is_copy_constructible<T>::value>>> : std::true_type
-{
-};
-
-template <typename T>
-struct is_range_<T, void>
-    : std::integral_constant<bool, (has_const_begin_end<T>::value || has_mutable_begin_end<T>::value)>
-{
-};
-#undef FMT_DECLTYPE_RETURN
-#endif
-
-// tuple_size and tuple_element check.
-template <typename T>
-class is_tuple_like_
-{
-    template <typename U>
-    static auto check(U *p) -> decltype(std::tuple_size<U>::value, int());
-    template <typename>
-    static void check(...);
-
-public:
-    static constexpr const bool value = !std::is_void<decltype(check<T>(nullptr))>::value;
-};
-
-// Check for integer_sequence
-#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
-template <typename T, T... N>
-using integer_sequence = std::integer_sequence<T, N...>;
-template <size_t... N>
-using index_sequence = std::index_sequence<N...>;
-template <size_t N>
-using make_index_sequence = std::make_index_sequence<N>;
-#else
-template <typename T, T... N>
-struct integer_sequence
-{
-    using value_type = T;
-
-    static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
-};
-
-template <size_t... N>
-using index_sequence = integer_sequence<size_t, N...>;
-
-template <typename T, size_t N, T... Ns>
-struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...>
-{
-};
-template <typename T, T... Ns>
-struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...>
-{
-};
-
-template <size_t N>
-using make_index_sequence = make_integer_sequence<size_t, N>;
-#endif
-
-template <typename T>
-using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
-
-template <typename T, typename C, bool = is_tuple_like_<T>::value>
-class is_tuple_formattable_
-{
-public:
-    static constexpr const bool value = false;
-};
-template <typename T, typename C>
-class is_tuple_formattable_<T, C, true>
-{
-    template <std::size_t... I>
-    static std::true_type  check2(index_sequence<I...>, integer_sequence<bool, (I == I)...>);
-    static std::false_type check2(...);
-    template <std::size_t... I>
-    static decltype(check2(
-        index_sequence<I...>{},
-        integer_sequence<bool, (is_formattable<typename std::tuple_element<I, T>::type, C>::value)...>{}))
-        check(index_sequence<I...>);
-
-public:
-    static constexpr const bool value = decltype(check(tuple_index_sequence<T>{}))::value;
-};
-
-template <class Tuple, class F, size_t... Is>
-void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) noexcept
-{
-    using std::get;
-    // using free function get<I>(T) now.
-    const int _[] = {0, ((void) f(get<Is>(tup)), 0)...};
-    (void) _; // blocks warnings
-}
-
-template <class T>
-FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(T const &)
-{
-    return {};
-}
-
-template <class Tuple, class F>
-void for_each(Tuple &&tup, F &&f)
-{
-    const auto indexes = get_indexes(tup);
-    for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
-}
-
-#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
-// Older MSVC doesn't get the reference type correctly for arrays.
-template <typename R>
-struct range_reference_type_impl
-{
-    using type = decltype(*detail::range_begin(std::declval<R &>()));
-};
-
-template <typename T, std::size_t N>
-struct range_reference_type_impl<T[N]>
-{
-    using type = T &;
-};
-
-template <typename T>
-using range_reference_type = typename range_reference_type_impl<T>::type;
-#else
-template <typename Range>
-using range_reference_type = decltype(*detail::range_begin(std::declval<Range &>()));
-#endif
-
-// We don't use the Range's value_type for anything, but we do need the Range's
-// reference type, with cv-ref stripped.
-template <typename Range>
-using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
-
-template <typename Range>
-using uncvref_first_type = remove_cvref_t<decltype(std::declval<range_reference_type<Range>>().first)>;
-
-template <typename Range>
-using uncvref_second_type = remove_cvref_t<decltype(std::declval<range_reference_type<Range>>().second)>;
-
-template <typename OutputIt>
-OutputIt write_delimiter(OutputIt out)
-{
-    *out++ = ',';
-    *out++ = ' ';
-    return out;
-}
-
-template <typename Char, typename OutputIt>
-auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt
-{
-    return write_escaped_string(out, str);
-}
-
-template <
-    typename Char,
-    typename OutputIt,
-    typename T,
-    FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
-inline auto write_range_entry(OutputIt out, const T &str) -> OutputIt
-{
-    auto sv = std_string_view<Char>(str);
-    return write_range_entry<Char>(out, basic_string_view<Char>(sv));
-}
-
-template <typename Char, typename OutputIt, typename Arg, FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
-OutputIt write_range_entry(OutputIt out, const Arg v)
-{
-    return write_escaped_char(out, v);
-}
-
-template <
-    typename Char,
-    typename OutputIt,
-    typename Arg,
-    FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value && !std::is_same<Arg, Char>::value)>
-OutputIt write_range_entry(OutputIt out, const Arg &v)
-{
-    return write<Char>(out, v);
-}
-
-} // namespace detail
-
-template <typename T>
-struct is_tuple_like
-{
-    static constexpr const bool value = detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
-};
-
-template <typename T, typename C>
-struct is_tuple_formattable
-{
-    static constexpr const bool value = detail::is_tuple_formattable_<T, C>::value;
-};
-
-template <typename TupleT, typename Char>
-struct formatter<
-    TupleT,
-    Char,
-    enable_if_t<fmt::is_tuple_like<TupleT>::value && fmt::is_tuple_formattable<TupleT, Char>::value>>
-{
-private:
-    basic_string_view<Char> separator_       = detail::string_literal<Char, ',', ' '>{};
-    basic_string_view<Char> opening_bracket_ = detail::string_literal<Char, '('>{};
-    basic_string_view<Char> closing_bracket_ = detail::string_literal<Char, ')'>{};
-
-    // C++11 generic lambda for format().
-    template <typename FormatContext>
-    struct format_each
-    {
-        template <typename T>
-        void operator()(const T &v)
-        {
-            if(i > 0)
-                out = detail::copy_str<Char>(separator, out);
-            out = detail::write_range_entry<Char>(out, v);
-            ++i;
-        }
-        int                               i;
-        typename FormatContext::iterator &out;
-        basic_string_view<Char>           separator;
-    };
-
-public:
-    FMT_CONSTEXPR formatter() {}
-
-    FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { separator_ = sep; }
-
-    FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, basic_string_view<Char> close)
-    {
-        opening_bracket_ = open;
-        closing_bracket_ = close;
-    }
-
-    template <typename ParseContext>
-    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
-    {
-        return ctx.begin();
-    }
-
-    template <typename FormatContext = format_context>
-    auto format(const TupleT &values, FormatContext &ctx) const -> decltype(ctx.out())
-    {
-        auto out = ctx.out();
-        out      = detail::copy_str<Char>(opening_bracket_, out);
-        detail::for_each(values, format_each<FormatContext>{0, out, separator_});
-        out = detail::copy_str<Char>(closing_bracket_, out);
-        return out;
-    }
-};
-
-template <typename T, typename Char>
-struct is_range
-{
-    static constexpr const bool value = detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
-                                        !std::is_convertible<T, std::basic_string<Char>>::value &&
-                                        !std::is_convertible<T, detail::std_string_view<Char>>::value;
-};
-
-namespace detail
-{
-template <typename Context>
-struct range_mapper
-{
-    using mapper = arg_mapper<Context>;
-
-    template <typename T, FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
-    static auto map(T &&value) -> T &&
-    {
-        return static_cast<T &&>(value);
-    }
-    template <typename T, FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
-    static auto map(T &&value) -> decltype(mapper().map(static_cast<T &&>(value)))
-    {
-        return mapper().map(static_cast<T &&>(value));
-    }
-};
-
-template <typename Char, typename Element>
-using range_formatter_type = conditional_t<
-    is_formattable<Element, Char>::value,
-    formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(std::declval<Element>()))>, Char>,
-    fallback_formatter<Element, Char>>;
-
-template <typename R>
-using maybe_const_range = conditional_t<has_const_begin_end<R>::value, const R, R>;
-
-// Workaround a bug in MSVC 2015 and earlier.
-#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
-template <typename R, typename Char>
-struct is_formattable_delayed : disjunction<
-                                    is_formattable<uncvref_type<maybe_const_range<R>>, Char>,
-                                    has_fallback_formatter<uncvref_type<maybe_const_range<R>>, Char>>
-{
-};
-#endif
-
-} // namespace detail
-
-template <typename T, typename Char, typename Enable = void>
-struct range_formatter;
-
-template <typename T, typename Char>
-struct range_formatter<
-    T,
-    Char,
-    enable_if_t<conjunction<
-        std::is_same<T, remove_cvref_t<T>>,
-        disjunction<is_formattable<T, Char>, detail::has_fallback_formatter<T, Char>>>::value>>
-{
-private:
-    detail::range_formatter_type<Char, T> underlying_;
-    bool                                  custom_specs_    = false;
-    basic_string_view<Char>               separator_       = detail::string_literal<Char, ',', ' '>{};
-    basic_string_view<Char>               opening_bracket_ = detail::string_literal<Char, '['>{};
-    basic_string_view<Char>               closing_bracket_ = detail::string_literal<Char, ']'>{};
-
-    template <class U>
-    FMT_CONSTEXPR static auto maybe_set_debug_format(U &u, int) -> decltype(u.set_debug_format())
-    {
-        u.set_debug_format();
-    }
-
-    template <class U>
-    FMT_CONSTEXPR static void maybe_set_debug_format(U &, ...)
-    {
-    }
-
-    FMT_CONSTEXPR void maybe_set_debug_format() { maybe_set_debug_format(underlying_, 0); }
-
-public:
-    FMT_CONSTEXPR range_formatter() {}
-
-    FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T> & { return underlying_; }
-
-    FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) { separator_ = sep; }
-
-    FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open, basic_string_view<Char> close)
-    {
-        opening_bracket_ = open;
-        closing_bracket_ = close;
-    }
-
-    template <typename ParseContext>
-    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
-    {
-        auto it  = ctx.begin();
-        auto end = ctx.end();
-        if(it == end || *it == '}')
-        {
-            maybe_set_debug_format();
-            return it;
-        }
-
-        if(*it == 'n')
-        {
-            set_brackets({}, {});
-            ++it;
-        }
-
-        if(*it == '}')
-        {
-            maybe_set_debug_format();
-            return it;
-        }
-
-        if(*it != ':')
-            FMT_THROW(format_error("no other top-level range formatters supported"));
-
-        custom_specs_ = true;
-        ++it;
-        ctx.advance_to(it);
-        return underlying_.parse(ctx);
-    }
-
-    template <typename R, class FormatContext>
-    auto format(R &&range, FormatContext &ctx) const -> decltype(ctx.out())
-    {
-        detail::range_mapper<buffer_context<Char>> mapper;
-        auto                                       out = ctx.out();
-        out                                            = detail::copy_str<Char>(opening_bracket_, out);
-        int  i                                         = 0;
-        auto it                                        = detail::range_begin(range);
-        auto end                                       = detail::range_end(range);
-        for(; it != end; ++it)
-        {
-            if(i > 0)
-                out = detail::copy_str<Char>(separator_, out);
-            ;
-            ctx.advance_to(out);
-            out = underlying_.format(mapper.map(*it), ctx);
-            ++i;
-        }
-        out = detail::copy_str<Char>(closing_bracket_, out);
-        return out;
-    }
-};
-
-enum class range_format
-{
-    disabled,
-    map,
-    set,
-    sequence,
-    string,
-    debug_string
-};
-
-namespace detail
-{
-template <typename T>
-struct range_format_kind_
-{
-    static constexpr auto value = std::is_same<range_reference_type<T>, T>::value ? range_format::disabled :
-                                  is_map<T>::value                                ? range_format::map :
-                                  is_set<T>::value                                ? range_format::set :
-                                                                                    range_format::sequence;
-};
-
-template <range_format K, typename R, typename Char, typename Enable = void>
-struct range_default_formatter;
-
-template <range_format K>
-using range_format_constant = std::integral_constant<range_format, K>;
-
-template <range_format K, typename R, typename Char>
-struct range_default_formatter<
-    K,
-    R,
-    Char,
-    enable_if_t<(K == range_format::sequence || K == range_format::map || K == range_format::set)>>
-{
-    using range_type = detail::maybe_const_range<R>;
-    range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
-
-    FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
-
-    FMT_CONSTEXPR void init(range_format_constant<range_format::set>)
-    {
-        underlying_.set_brackets(detail::string_literal<Char, '{'>{}, detail::string_literal<Char, '}'>{});
-    }
-
-    FMT_CONSTEXPR void init(range_format_constant<range_format::map>)
-    {
-        underlying_.set_brackets(detail::string_literal<Char, '{'>{}, detail::string_literal<Char, '}'>{});
-        underlying_.underlying().set_brackets({}, {});
-        underlying_.underlying().set_separator(detail::string_literal<Char, ':', ' '>{});
-    }
-
-    FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
-
-    template <typename ParseContext>
-    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
-    {
-        return underlying_.parse(ctx);
-    }
-
-    template <typename FormatContext>
-    auto format(range_type &range, FormatContext &ctx) const -> decltype(ctx.out())
-    {
-        return underlying_.format(range, ctx);
-    }
-};
-} // namespace detail
-
-template <typename T, typename Char, typename Enable = void>
-struct range_format_kind : conditional_t<
-                               is_range<T, Char>::value,
-                               detail::range_format_kind_<T>,
-                               std::integral_constant<range_format, range_format::disabled>>
-{
-};
-
-template <typename R, typename Char>
-struct formatter<
-    R,
-    Char,
-    enable_if_t<conjunction<
-        bool_constant<range_format_kind<R, Char>::value != range_format::disabled>
-// Workaround a bug in MSVC 2015 and earlier.
-#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
-        ,
-        detail::is_formattable_delayed<R, Char>
-#endif
-        >::value>> : detail::range_default_formatter<range_format_kind<R, Char>::value, R, Char>
-{
-};
-
-template <typename Char, typename... T>
-struct tuple_join_view : detail::view
-{
-    const std::tuple<T...> &tuple;
-    basic_string_view<Char> sep;
-
-    tuple_join_view(const std::tuple<T...> &t, basic_string_view<Char> s) : tuple(t), sep{s} {}
-};
-
-template <typename Char, typename... T>
-using tuple_arg_join = tuple_join_view<Char, T...>;
-
-// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
-// support in tuple_join. It is disabled by default because of issues with
-// the dynamic width and precision.
-#ifndef FMT_TUPLE_JOIN_SPECIFIERS
-#define FMT_TUPLE_JOIN_SPECIFIERS 0
-#endif
-
-template <typename Char, typename... T>
-struct formatter<tuple_join_view<Char, T...>, Char>
-{
-    template <typename ParseContext>
-    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
-    {
-        return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
-    }
-
-    template <typename FormatContext>
-    auto format(const tuple_join_view<Char, T...> &value, FormatContext &ctx) const -> typename FormatContext::iterator
-    {
-        return do_format(value, ctx, std::integral_constant<size_t, sizeof...(T)>());
-    }
-
-private:
-    std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
-
-    template <typename ParseContext>
-    FMT_CONSTEXPR auto do_parse(ParseContext &ctx, std::integral_constant<size_t, 0>) -> decltype(ctx.begin())
-    {
-        return ctx.begin();
-    }
-
-    template <typename ParseContext, size_t N>
-    FMT_CONSTEXPR auto do_parse(ParseContext &ctx, std::integral_constant<size_t, N>) -> decltype(ctx.begin())
-    {
-        auto end = ctx.begin();
-#if FMT_TUPLE_JOIN_SPECIFIERS
-        end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
-        if(N > 1)
-        {
-            auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
-            if(end != end1)
-                FMT_THROW(format_error("incompatible format specs for tuple elements"));
-        }
-#endif
-        return end;
-    }
-
-    template <typename FormatContext>
-    auto do_format(const tuple_join_view<Char, T...> &, FormatContext &ctx, std::integral_constant<size_t, 0>) const ->
-        typename FormatContext::iterator
-    {
-        return ctx.out();
-    }
-
-    template <typename FormatContext, size_t N>
-    auto
-    do_format(const tuple_join_view<Char, T...> &value, FormatContext &ctx, std::integral_constant<size_t, N>) const ->
-        typename FormatContext::iterator
-    {
-        auto out = std::get<sizeof...(T) - N>(formatters_).format(std::get<sizeof...(T) - N>(value.tuple), ctx);
-        if(N > 1)
-        {
-            out = std::copy(value.sep.begin(), value.sep.end(), out);
-            ctx.advance_to(out);
-            return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
-        }
-        return out;
-    }
-};
-
-FMT_MODULE_EXPORT_BEGIN
-
-/**
-  \rst
-  Returns an object that formats `tuple` with elements separated by `sep`.
-
-  **Example**::
-
-    std::tuple<int, char> t = {1, 'a'};
-    fmt::print("{}", fmt::join(t, ", "));
-    // Output: "1, a"
-  \endrst
- */
-template <typename... T>
-FMT_CONSTEXPR auto join(const std::tuple<T...> &tuple, string_view sep) -> tuple_join_view<char, T...>
-{
-    return {tuple, sep};
-}
-
-template <typename... T>
-FMT_CONSTEXPR auto join(const std::tuple<T...> &tuple, basic_string_view<wchar_t> sep) -> tuple_join_view<wchar_t, T...>
-{
-    return {tuple, sep};
-}
-
-/**
-  \rst
-  Returns an object that formats `initializer_list` with elements separated by
-  `sep`.
-
-  **Example**::
-
-    fmt::print("{}", fmt::join({1, 2, 3}, ", "));
-    // Output: "1, 2, 3"
-  \endrst
- */
-template <typename T>
-auto join(std::initializer_list<T> list, string_view sep) -> join_view<const T *, const T *>
-{
-    return join(std::begin(list), std::end(list), sep);
-}
-
-FMT_MODULE_EXPORT_END
-FMT_END_NAMESPACE
-
-#endif // FMT_RANGES_H_
diff --git a/include/spdlog/fmt/bundled/std.h b/include/spdlog/fmt/bundled/std.h
deleted file mode 100644
index 436731489..000000000
--- a/include/spdlog/fmt/bundled/std.h
+++ /dev/null
@@ -1,179 +0,0 @@
-// Formatting library for C++ - formatters for standard library types
-//
-// Copyright (c) 2012 - present, Victor Zverovich
-// All rights reserved.
-//
-// For the license information refer to format.h.
-
-#ifndef FMT_STD_H_
-#define FMT_STD_H_
-
-#include "ostream.h"
-
-#include <thread>
-#include <type_traits>
-#include <utility>
-
-#if FMT_HAS_INCLUDE(<version>)
-#include <version>
-#endif
-// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
-#if FMT_CPLUSPLUS >= 201703L
-#if FMT_HAS_INCLUDE(<filesystem>)
-#include <filesystem>
-#endif
-#if FMT_HAS_INCLUDE(<variant>)
-#include <variant>
-#endif
-#endif
-
-#ifdef __cpp_lib_filesystem
-FMT_BEGIN_NAMESPACE
-
-namespace detail
-{
-
-template <typename Char>
-void write_escaped_path(basic_memory_buffer<Char> &quoted, const std::filesystem::path &p)
-{
-    write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
-}
-#ifdef _WIN32
-template <>
-inline void write_escaped_path<char>(basic_memory_buffer<char> &quoted, const std::filesystem::path &p)
-{
-    auto s = p.u8string();
-    write_escaped_string<char>(
-        std::back_inserter(quoted), string_view(reinterpret_cast<const char *>(s.c_str()), s.size()));
-}
-#endif
-template <>
-inline void write_escaped_path<std::filesystem::path::value_type>(
-    basic_memory_buffer<std::filesystem::path::value_type> &quoted,
-    const std::filesystem::path                            &p)
-{
-    write_escaped_string<std::filesystem::path::value_type>(std::back_inserter(quoted), p.native());
-}
-
-} // namespace detail
-
-template <typename Char>
-struct formatter<std::filesystem::path, Char> : formatter<basic_string_view<Char>>
-{
-    template <typename FormatContext>
-    auto format(const std::filesystem::path &p, FormatContext &ctx) const -> typename FormatContext::iterator
-    {
-        basic_memory_buffer<Char> quoted;
-        detail::write_escaped_path(quoted, p);
-        return formatter<basic_string_view<Char>>::format(basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
-    }
-};
-FMT_END_NAMESPACE
-#endif
-
-FMT_BEGIN_NAMESPACE
-template <typename Char>
-struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char>
-{
-};
-FMT_END_NAMESPACE
-
-#ifdef __cpp_lib_variant
-FMT_BEGIN_NAMESPACE
-template <typename Char>
-struct formatter<std::monostate, Char>
-{
-    template <typename ParseContext>
-    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
-    {
-        return ctx.begin();
-    }
-
-    template <typename FormatContext>
-    auto format(const std::monostate &, FormatContext &ctx) const -> decltype(ctx.out())
-    {
-        auto out = ctx.out();
-        out      = detail::write<Char>(out, "monostate");
-        return out;
-    }
-};
-
-namespace detail
-{
-
-template <typename T>
-using variant_index_sequence = std::make_index_sequence<std::variant_size<T>::value>;
-
-// variant_size and variant_alternative check.
-template <typename T, typename U = void>
-struct is_variant_like_ : std::false_type
-{
-};
-template <typename T>
-struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>> : std::true_type
-{
-};
-
-// formattable element check
-template <typename T, typename C>
-class is_variant_formattable_
-{
-    template <std::size_t... I>
-    static std::conjunction<is_formattable<std::variant_alternative_t<I, T>, C>...> check(std::index_sequence<I...>);
-
-public:
-    static constexpr const bool value = decltype(check(variant_index_sequence<T>{}))::value;
-};
-
-template <typename Char, typename OutputIt, typename T>
-auto write_variant_alternative(OutputIt out, const T &v) -> OutputIt
-{
-    if constexpr(is_string<T>::value)
-        return write_escaped_string<Char>(out, detail::to_string_view(v));
-    else if constexpr(std::is_same_v<T, Char>)
-        return write_escaped_char(out, v);
-    else
-        return write<Char>(out, v);
-}
-
-} // namespace detail
-
-template <typename T>
-struct is_variant_like
-{
-    static constexpr const bool value = detail::is_variant_like_<T>::value;
-};
-
-template <typename T, typename C>
-struct is_variant_formattable
-{
-    static constexpr const bool value = detail::is_variant_formattable_<T, C>::value;
-};
-
-template <typename Variant, typename Char>
-struct formatter<
-    Variant,
-    Char,
-    std::enable_if_t<std::conjunction_v<is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>>
-{
-    template <typename ParseContext>
-    FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
-    {
-        return ctx.begin();
-    }
-
-    template <typename FormatContext>
-    auto format(const Variant &value, FormatContext &ctx) const -> decltype(ctx.out())
-    {
-        auto out = ctx.out();
-
-        out = detail::write<Char>(out, "variant(");
-        std::visit([&](const auto &v) { out = detail::write_variant_alternative<Char>(out, v); }, value);
-        *out++ = ')';
-        return out;
-    }
-};
-FMT_END_NAMESPACE
-#endif
-
-#endif // FMT_STD_H_
diff --git a/include/spdlog/fmt/bundled/xchar.h b/include/spdlog/fmt/bundled/xchar.h
deleted file mode 100644
index 265ceffe9..000000000
--- a/include/spdlog/fmt/bundled/xchar.h
+++ /dev/null
@@ -1,278 +0,0 @@
-// Formatting library for C++ - optional wchar_t and exotic character support
-//
-// Copyright (c) 2012 - present, Victor Zverovich
-// All rights reserved.
-//
-// For the license information refer to format.h.
-
-#ifndef FMT_XCHAR_H_
-#define FMT_XCHAR_H_
-
-#include "format.h"
-
-#include <cwchar>
-
-FMT_BEGIN_NAMESPACE
-namespace detail
-{
-template <typename T>
-using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
-}
-
-FMT_MODULE_EXPORT_BEGIN
-
-using wstring_view          = basic_string_view<wchar_t>;
-using wformat_parse_context = basic_format_parse_context<wchar_t>;
-using wformat_context       = buffer_context<wchar_t>;
-using wformat_args          = basic_format_args<wformat_context>;
-using wmemory_buffer        = basic_memory_buffer<wchar_t>;
-
-#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
-// Workaround broken conversion on older gcc.
-template <typename... Args>
-using wformat_string = wstring_view;
-inline auto runtime(wstring_view s) -> wstring_view
-{
-    return s;
-}
-#else
-template <typename... Args>
-using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
-inline auto runtime(wstring_view s) -> basic_runtime<wchar_t>
-{
-    return {{s}};
-}
-#endif
-
-template <>
-struct is_char<wchar_t> : std::true_type
-{
-};
-template <>
-struct is_char<detail::char8_type> : std::true_type
-{
-};
-template <>
-struct is_char<char16_t> : std::true_type
-{
-};
-template <>
-struct is_char<char32_t> : std::true_type
-{
-};
-
-template <typename... Args>
-constexpr format_arg_store<wformat_context, Args...> make_wformat_args(const Args &...args)
-{
-    return {args...};
-}
-
-inline namespace literals
-{
-#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
-constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t *s, size_t)
-{
-    return {s};
-}
-#endif
-} // namespace literals
-
-template <typename It, typename Sentinel>
-auto join(It begin, Sentinel end, wstring_view sep) -> join_view<It, Sentinel, wchar_t>
-{
-    return {begin, end, sep};
-}
-
-template <typename Range>
-auto join(Range &&range, wstring_view sep) -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>, wchar_t>
-{
-    return join(std::begin(range), std::end(range), sep);
-}
-
-template <typename T>
-auto join(std::initializer_list<T> list, wstring_view sep) -> join_view<const T *, const T *, wchar_t>
-{
-    return join(std::begin(list), std::end(list), sep);
-}
-
-template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
-auto vformat(basic_string_view<Char> format_str, basic_format_args<buffer_context<type_identity_t<Char>>> args)
-    -> std::basic_string<Char>
-{
-    basic_memory_buffer<Char> buffer;
-    detail::vformat_to(buffer, format_str, args);
-    return to_string(buffer);
-}
-
-template <typename... T>
-auto format(wformat_string<T...> fmt, T &&...args) -> std::wstring
-{
-    return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
-}
-
-// Pass char_t as a default template parameter instead of using
-// std::basic_string<char_t<S>> to reduce the symbol size.
-template <
-    typename S,
-    typename... Args,
-    typename Char = char_t<S>,
-    FMT_ENABLE_IF(!std::is_same<Char, char>::value && !std::is_same<Char, wchar_t>::value)>
-auto format(const S &format_str, Args &&...args) -> std::basic_string<Char>
-{
-    return vformat(detail::to_string_view(format_str), fmt::make_format_args<buffer_context<Char>>(args...));
-}
-
-template <
-    typename Locale,
-    typename S,
-    typename Char = char_t<S>,
-    FMT_ENABLE_IF(detail::is_locale<Locale>::value &&detail::is_exotic_char<Char>::value)>
-inline auto
-vformat(const Locale &loc, const S &format_str, basic_format_args<buffer_context<type_identity_t<Char>>> args)
-    -> std::basic_string<Char>
-{
-    return detail::vformat(loc, detail::to_string_view(format_str), args);
-}
-
-template <
-    typename Locale,
-    typename S,
-    typename... Args,
-    typename Char = char_t<S>,
-    FMT_ENABLE_IF(detail::is_locale<Locale>::value &&detail::is_exotic_char<Char>::value)>
-inline auto format(const Locale &loc, const S &format_str, Args &&...args) -> std::basic_string<Char>
-{
-    return detail::vformat(
-        loc, detail::to_string_view(format_str), fmt::make_format_args<buffer_context<Char>>(args...));
-}
-
-template <
-    typename OutputIt,
-    typename S,
-    typename Char = char_t<S>,
-    FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&detail::is_exotic_char<Char>::value)>
-auto vformat_to(OutputIt out, const S &format_str, basic_format_args<buffer_context<type_identity_t<Char>>> args)
-    -> OutputIt
-{
-    auto &&buf = detail::get_buffer<Char>(out);
-    detail::vformat_to(buf, detail::to_string_view(format_str), args);
-    return detail::get_iterator(buf);
-}
-
-template <
-    typename OutputIt,
-    typename S,
-    typename... Args,
-    typename Char = char_t<S>,
-    FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&detail::is_exotic_char<Char>::value)>
-inline auto format_to(OutputIt out, const S &fmt, Args &&...args) -> OutputIt
-{
-    return vformat_to(out, detail::to_string_view(fmt), fmt::make_format_args<buffer_context<Char>>(args...));
-}
-
-template <
-    typename Locale,
-    typename S,
-    typename OutputIt,
-    typename... Args,
-    typename Char = char_t<S>,
-    FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&detail::is_locale<Locale>::value
-                                                                    &&detail::is_exotic_char<Char>::value)>
-inline auto vformat_to(
-    OutputIt                                                 out,
-    const Locale                                            &loc,
-    const S                                                 &format_str,
-    basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt
-{
-    auto &&buf = detail::get_buffer<Char>(out);
-    vformat_to(buf, detail::to_string_view(format_str), args, detail::locale_ref(loc));
-    return detail::get_iterator(buf);
-}
-
-template <
-    typename OutputIt,
-    typename Locale,
-    typename S,
-    typename... Args,
-    typename Char = char_t<S>,
-    bool enable   = detail::is_output_iterator<OutputIt, Char>::value &&detail::is_locale<Locale>::value
-                                                                    &&detail::is_exotic_char<Char>::value>
-inline auto format_to(OutputIt out, const Locale &loc, const S &format_str, Args &&...args) ->
-    typename std::enable_if<enable, OutputIt>::type
-{
-    return vformat_to(out, loc, to_string_view(format_str), fmt::make_format_args<buffer_context<Char>>(args...));
-}
-
-template <
-    typename OutputIt,
-    typename Char,
-    typename... Args,
-    FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&detail::is_exotic_char<Char>::value)>
-inline auto vformat_to_n(
-    OutputIt                                                 out,
-    size_t                                                   n,
-    basic_string_view<Char>                                  format_str,
-    basic_format_args<buffer_context<type_identity_t<Char>>> args) -> format_to_n_result<OutputIt>
-{
-    detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out, n);
-    detail::vformat_to(buf, format_str, args);
-    return {buf.out(), buf.count()};
-}
-
-template <
-    typename OutputIt,
-    typename S,
-    typename... Args,
-    typename Char = char_t<S>,
-    FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&detail::is_exotic_char<Char>::value)>
-inline auto format_to_n(OutputIt out, size_t n, const S &fmt, const Args &...args) -> format_to_n_result<OutputIt>
-{
-    return vformat_to_n(out, n, detail::to_string_view(fmt), fmt::make_format_args<buffer_context<Char>>(args...));
-}
-
-template <typename S, typename... Args, typename Char = char_t<S>, FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
-inline auto formatted_size(const S &fmt, Args &&...args) -> size_t
-{
-    detail::counting_buffer<Char> buf;
-    detail::vformat_to(buf, detail::to_string_view(fmt), fmt::make_format_args<buffer_context<Char>>(args...));
-    return buf.count();
-}
-
-inline void vprint(std::FILE *f, wstring_view fmt, wformat_args args)
-{
-    wmemory_buffer buffer;
-    detail::vformat_to(buffer, fmt, args);
-    buffer.push_back(L'\0');
-    if(std::fputws(buffer.data(), f) == -1)
-        FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
-}
-
-inline void vprint(wstring_view fmt, wformat_args args)
-{
-    vprint(stdout, fmt, args);
-}
-
-template <typename... T>
-void print(std::FILE *f, wformat_string<T...> fmt, T &&...args)
-{
-    return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
-}
-
-template <typename... T>
-void print(wformat_string<T...> fmt, T &&...args)
-{
-    return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
-}
-
-/**
-  Converts *value* to ``std::wstring`` using the default format for type *T*.
- */
-template <typename T>
-inline auto to_wstring(const T &value) -> std::wstring
-{
-    return format(FMT_STRING(L"{}"), value);
-}
-FMT_MODULE_EXPORT_END
-FMT_END_NAMESPACE
-
-#endif // FMT_XCHAR_H_
diff --git a/include/spdlog/fmt/chrono.h b/include/spdlog/fmt/chrono.h
deleted file mode 100644
index a24c199b2..000000000
--- a/include/spdlog/fmt/chrono.h
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-// Copyright(c) 2016 Gabi Melman.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-//
-
-#pragma once
-//
-// include bundled or external copy of fmtlib's chrono support
-//
-
-#if !defined(SPDLOG_USE_STD_FORMAT)
-#if !defined(SPDLOG_FMT_EXTERNAL)
-#ifdef SPDLOG_HEADER_ONLY
-#ifndef FMT_HEADER_ONLY
-#define FMT_HEADER_ONLY
-#endif
-#endif
-#include <spdlog/fmt/bundled/chrono.h>
-#else
-#include <fmt/chrono.h>
-#endif
-#endif
diff --git a/include/spdlog/fmt/compile.h b/include/spdlog/fmt/compile.h
deleted file mode 100644
index 498872f5e..000000000
--- a/include/spdlog/fmt/compile.h
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-// Copyright(c) 2016 Gabi Melman.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-//
-
-#pragma once
-//
-// include bundled or external copy of fmtlib's compile-time support
-//
-
-#if !defined(SPDLOG_USE_STD_FORMAT)
-#if !defined(SPDLOG_FMT_EXTERNAL)
-#ifdef SPDLOG_HEADER_ONLY
-#ifndef FMT_HEADER_ONLY
-#define FMT_HEADER_ONLY
-#endif
-#endif
-#include <spdlog/fmt/bundled/compile.h>
-#else
-#include <fmt/compile.h>
-#endif
-#endif
diff --git a/include/spdlog/fmt/fmt.h b/include/spdlog/fmt/fmt.h
deleted file mode 100644
index 5e7441d48..000000000
--- a/include/spdlog/fmt/fmt.h
+++ /dev/null
@@ -1,33 +0,0 @@
-//
-// Copyright(c) 2016-2018 Gabi Melman.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-//
-
-#pragma once
-
-//
-// Include a bundled header-only copy of fmtlib or an external one.
-// By default spdlog include its own copy.
-//
-
-#if defined(SPDLOG_USE_STD_FORMAT) // SPDLOG_USE_STD_FORMAT is defined - use std::format
-#include <format>
-#elif !defined(SPDLOG_FMT_EXTERNAL)
-#if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY)
-#define FMT_HEADER_ONLY
-#endif
-#ifndef FMT_USE_WINDOWS_H
-#define FMT_USE_WINDOWS_H 0
-#endif
-// enable the 'n' flag in for backward compatibility with fmt 6.x
-#define FMT_DEPRECATED_N_SPECIFIER
-// enable ostream formatting for backward compatibility with fmt 8.x
-#define FMT_DEPRECATED_OSTREAM
-
-#include <spdlog/fmt/bundled/core.h>
-#include <spdlog/fmt/bundled/format.h>
-
-#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
-#include <fmt/core.h>
-#include <fmt/format.h>
-#endif
diff --git a/include/spdlog/fmt/ostr.h b/include/spdlog/fmt/ostr.h
deleted file mode 100644
index 9e17d21b1..000000000
--- a/include/spdlog/fmt/ostr.h
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-// Copyright(c) 2016 Gabi Melman.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-//
-
-#pragma once
-//
-// include bundled or external copy of fmtlib's ostream support
-//
-
-#if !defined(SPDLOG_USE_STD_FORMAT)
-#if !defined(SPDLOG_FMT_EXTERNAL)
-#ifdef SPDLOG_HEADER_ONLY
-#ifndef FMT_HEADER_ONLY
-#define FMT_HEADER_ONLY
-#endif
-#endif
-#include <spdlog/fmt/bundled/ostream.h>
-#else
-#include <fmt/ostream.h>
-#endif
-#endif
diff --git a/include/spdlog/fmt/ranges.h b/include/spdlog/fmt/ranges.h
deleted file mode 100644
index 5b0c77ab5..000000000
--- a/include/spdlog/fmt/ranges.h
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-// Copyright(c) 2016 Gabi Melman.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-//
-
-#pragma once
-//
-// include bundled or external copy of fmtlib's ranges support
-//
-
-#if !defined(SPDLOG_USE_STD_FORMAT)
-#if !defined(SPDLOG_FMT_EXTERNAL)
-#ifdef SPDLOG_HEADER_ONLY
-#ifndef FMT_HEADER_ONLY
-#define FMT_HEADER_ONLY
-#endif
-#endif
-#include <spdlog/fmt/bundled/ranges.h>
-#else
-#include <fmt/ranges.h>
-#endif
-#endif
diff --git a/include/spdlog/fmt/std.h b/include/spdlog/fmt/std.h
deleted file mode 100644
index 260c64ef3..000000000
--- a/include/spdlog/fmt/std.h
+++ /dev/null
@@ -1,23 +0,0 @@
-//
-// Copyright(c) 2016 Gabi Melman.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-//
-
-#pragma once
-//
-// include bundled or external copy of fmtlib's std support (for formatting e.g. std::filesystem::path, std::thread::id,
-// std::monostate, std::variant, ...)
-//
-
-#if !defined(SPDLOG_USE_STD_FORMAT)
-#if !defined(SPDLOG_FMT_EXTERNAL)
-#ifdef SPDLOG_HEADER_ONLY
-#ifndef FMT_HEADER_ONLY
-#define FMT_HEADER_ONLY
-#endif
-#endif
-#include <spdlog/fmt/bundled/std.h>
-#else
-#include <fmt/std.h>
-#endif
-#endif
diff --git a/include/spdlog/fmt/xchar.h b/include/spdlog/fmt/xchar.h
deleted file mode 100644
index 6d5328469..000000000
--- a/include/spdlog/fmt/xchar.h
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-// Copyright(c) 2016 Gabi Melman.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-//
-
-#pragma once
-//
-// include bundled or external copy of fmtlib's xchar support
-//
-
-#if !defined(SPDLOG_USE_STD_FORMAT)
-#if !defined(SPDLOG_FMT_EXTERNAL)
-#ifdef SPDLOG_HEADER_ONLY
-#ifndef FMT_HEADER_ONLY
-#define FMT_HEADER_ONLY
-#endif
-#endif
-#include <spdlog/fmt/bundled/xchar.h>
-#else
-#include <fmt/xchar.h>
-#endif
-#endif
diff --git a/include/spdlog/formatter.h b/include/spdlog/formatter.h
deleted file mode 100644
index 51afa4fd5..000000000
--- a/include/spdlog/formatter.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <spdlog/details/log_msg.h>
-#include <spdlog/fmt/fmt.h>
-
-namespace spdlog
-{
-
-class formatter
-{
-public:
-    virtual ~formatter()                                                                       = default;
-    virtual void                       format(const details::log_msg &msg, memory_buf_t &dest) = 0;
-    virtual std::unique_ptr<formatter> clone() const                                           = 0;
-};
-} // namespace spdlog
diff --git a/include/spdlog/fwd.h b/include/spdlog/fwd.h
deleted file mode 100644
index bdefd39fe..000000000
--- a/include/spdlog/fwd.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-namespace spdlog
-{
-class logger;
-class formatter;
-
-namespace sinks
-{
-    class sink;
-}
-
-namespace level
-{
-    enum level_enum : int;
-}
-
-} // namespace spdlog
diff --git a/include/spdlog/logger-inl.h b/include/spdlog/logger-inl.h
deleted file mode 100644
index 5c4c058af..000000000
--- a/include/spdlog/logger-inl.h
+++ /dev/null
@@ -1,261 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/logger.h>
-#endif
-
-#include <cstdio>
-#include <spdlog/details/backtracer.h>
-#include <spdlog/pattern_formatter.h>
-#include <spdlog/sinks/sink.h>
-
-namespace spdlog
-{
-
-// public methods
-SPDLOG_INLINE logger::logger(const logger &other) :
-    name_(other.name_),
-    sinks_(other.sinks_),
-    level_(other.level_.load(std::memory_order_relaxed)),
-    flush_level_(other.flush_level_.load(std::memory_order_relaxed)),
-    custom_err_handler_(other.custom_err_handler_),
-    tracer_(other.tracer_)
-{
-}
-
-SPDLOG_INLINE logger::logger(logger &&other) SPDLOG_NOEXCEPT
-    : name_(std::move(other.name_)),
-      sinks_(std::move(other.sinks_)),
-      level_(other.level_.load(std::memory_order_relaxed)),
-      flush_level_(other.flush_level_.load(std::memory_order_relaxed)),
-      custom_err_handler_(std::move(other.custom_err_handler_)),
-      tracer_(std::move(other.tracer_))
-
-{
-}
-
-SPDLOG_INLINE logger &logger::operator=(logger other) SPDLOG_NOEXCEPT
-{
-    this->swap(other);
-    return *this;
-}
-
-SPDLOG_INLINE void logger::swap(spdlog::logger &other) SPDLOG_NOEXCEPT
-{
-    name_.swap(other.name_);
-    sinks_.swap(other.sinks_);
-
-    // swap level_
-    auto other_level = other.level_.load();
-    auto my_level    = level_.exchange(other_level);
-    other.level_.store(my_level);
-
-    // swap flush level_
-    other_level = other.flush_level_.load();
-    my_level    = flush_level_.exchange(other_level);
-    other.flush_level_.store(my_level);
-
-    custom_err_handler_.swap(other.custom_err_handler_);
-    std::swap(tracer_, other.tracer_);
-}
-
-SPDLOG_INLINE void swap(logger &a, logger &b)
-{
-    a.swap(b);
-}
-
-SPDLOG_INLINE void logger::set_level(level::level_enum log_level)
-{
-    level_.store(log_level);
-}
-
-SPDLOG_INLINE level::level_enum logger::level() const
-{
-    return static_cast<level::level_enum>(level_.load(std::memory_order_relaxed));
-}
-
-SPDLOG_INLINE const std::string &logger::name() const
-{
-    return name_;
-}
-
-// set formatting for the sinks in this logger.
-// each sink will get a separate instance of the formatter object.
-SPDLOG_INLINE void logger::set_formatter(std::unique_ptr<formatter> f)
-{
-    for(auto it = sinks_.begin(); it != sinks_.end(); ++it)
-    {
-        if(std::next(it) == sinks_.end())
-        {
-            // last element - we can be move it.
-            (*it)->set_formatter(std::move(f));
-            break; // to prevent clang-tidy warning
-        }
-        else
-        {
-            (*it)->set_formatter(f->clone());
-        }
-    }
-}
-
-SPDLOG_INLINE void logger::set_pattern(std::string pattern, pattern_time_type time_type)
-{
-    auto new_formatter = details::make_unique<pattern_formatter>(std::move(pattern), time_type);
-    set_formatter(std::move(new_formatter));
-}
-
-// create new backtrace sink and move to it all our child sinks
-SPDLOG_INLINE void logger::enable_backtrace(size_t n_messages)
-{
-    tracer_.enable(n_messages);
-}
-
-// restore orig sinks and level and delete the backtrace sink
-SPDLOG_INLINE void logger::disable_backtrace()
-{
-    tracer_.disable();
-}
-
-SPDLOG_INLINE void logger::dump_backtrace()
-{
-    dump_backtrace_();
-}
-
-// flush functions
-SPDLOG_INLINE void logger::flush()
-{
-    flush_();
-}
-
-SPDLOG_INLINE void logger::flush_on(level::level_enum log_level)
-{
-    flush_level_.store(log_level);
-}
-
-SPDLOG_INLINE level::level_enum logger::flush_level() const
-{
-    return static_cast<level::level_enum>(flush_level_.load(std::memory_order_relaxed));
-}
-
-// sinks
-SPDLOG_INLINE const std::vector<sink_ptr> &logger::sinks() const
-{
-    return sinks_;
-}
-
-SPDLOG_INLINE std::vector<sink_ptr> &logger::sinks()
-{
-    return sinks_;
-}
-
-// error handler
-SPDLOG_INLINE void logger::set_error_handler(err_handler handler)
-{
-    custom_err_handler_ = std::move(handler);
-}
-
-// create new logger with same sinks and configuration.
-SPDLOG_INLINE std::shared_ptr<logger> logger::clone(std::string logger_name)
-{
-    auto cloned   = std::make_shared<logger>(*this);
-    cloned->name_ = std::move(logger_name);
-    return cloned;
-}
-
-// protected methods
-SPDLOG_INLINE void logger::log_it_(const spdlog::details::log_msg &log_msg, bool log_enabled, bool traceback_enabled)
-{
-    if(log_enabled)
-    {
-        sink_it_(log_msg);
-    }
-    if(traceback_enabled)
-    {
-        tracer_.push_back(log_msg);
-    }
-}
-
-SPDLOG_INLINE void logger::sink_it_(const details::log_msg &msg)
-{
-    for(auto &sink : sinks_)
-    {
-        if(sink->should_log(msg.level))
-        {
-            SPDLOG_TRY
-            {
-                sink->log(msg);
-            }
-            SPDLOG_LOGGER_CATCH(msg.source)
-        }
-    }
-
-    if(should_flush_(msg))
-    {
-        flush_();
-    }
-}
-
-SPDLOG_INLINE void logger::flush_()
-{
-    for(auto &sink : sinks_)
-    {
-        SPDLOG_TRY
-        {
-            sink->flush();
-        }
-        SPDLOG_LOGGER_CATCH(source_loc())
-    }
-}
-
-SPDLOG_INLINE void logger::dump_backtrace_()
-{
-    using details::log_msg;
-    if(tracer_.enabled())
-    {
-        sink_it_(log_msg{name(), level::info, "****************** Backtrace Start ******************"});
-        tracer_.foreach_pop([this](const log_msg &msg) { this->sink_it_(msg); });
-        sink_it_(log_msg{name(), level::info, "****************** Backtrace End ********************"});
-    }
-}
-
-SPDLOG_INLINE bool logger::should_flush_(const details::log_msg &msg)
-{
-    auto flush_level = flush_level_.load(std::memory_order_relaxed);
-    return (msg.level >= flush_level) && (msg.level != level::off);
-}
-
-SPDLOG_INLINE void logger::err_handler_(const std::string &msg)
-{
-    if(custom_err_handler_)
-    {
-        custom_err_handler_(msg);
-    }
-    else
-    {
-        using std::chrono::system_clock;
-        static std::mutex                            mutex;
-        static std::chrono::system_clock::time_point last_report_time;
-        static size_t                                err_counter = 0;
-        std::lock_guard<std::mutex>                  lk{mutex};
-        auto                                         now = system_clock::now();
-        err_counter++;
-        if(now - last_report_time < std::chrono::seconds(1))
-        {
-            return;
-        }
-        last_report_time = now;
-        auto tm_time     = details::os::localtime(system_clock::to_time_t(now));
-        char date_buf[64];
-        std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
-#if defined(USING_R) && defined(R_R_H) // if in R environment
-        REprintf("[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
-#else
-        std::fprintf(
-            stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
-#endif
-    }
-}
-} // namespace spdlog
diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h
deleted file mode 100644
index f3bb2a85d..000000000
--- a/include/spdlog/logger.h
+++ /dev/null
@@ -1,421 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-// Thread safe logger (except for set_error_handler())
-// Has name, log level, vector of std::shared sink pointers and formatter
-// Upon each log write the logger:
-// 1. Checks if its log level is enough to log the message and if yes:
-// 2. Call the underlying sinks to do the job.
-// 3. Each sink use its own private copy of a formatter to format the message
-// and send to its destination.
-//
-// The use of private formatter per sink provides the opportunity to cache some
-// formatted data, and support for different format per sink.
-
-#include <spdlog/common.h>
-#include <spdlog/details/backtracer.h>
-#include <spdlog/details/log_msg.h>
-
-#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
-#ifndef _WIN32
-#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
-#endif
-#include <spdlog/details/os.h>
-#endif
-
-#include <vector>
-
-#ifndef SPDLOG_NO_EXCEPTIONS
-#define SPDLOG_LOGGER_CATCH(location)                                                                                  \
-    catch(const std::exception &ex)                                                                                    \
-    {                                                                                                                  \
-        if(location.filename)                                                                                          \
-        {                                                                                                              \
-            err_handler_(                                                                                              \
-                fmt_lib::format(SPDLOG_FMT_STRING("{} [{}({})]"), ex.what(), location.filename, location.line));       \
-        }                                                                                                              \
-        else                                                                                                           \
-        {                                                                                                              \
-            err_handler_(ex.what());                                                                                   \
-        }                                                                                                              \
-    }                                                                                                                  \
-    catch(...)                                                                                                         \
-    {                                                                                                                  \
-        err_handler_("Rethrowing unknown exception in logger");                                                        \
-        throw;                                                                                                         \
-    }
-#else
-#define SPDLOG_LOGGER_CATCH(location)
-#endif
-
-namespace spdlog
-{
-
-class SPDLOG_API logger
-{
-public:
-    // Empty logger
-    explicit logger(std::string name) : name_(std::move(name)), sinks_() {}
-
-    // Logger with range on sinks
-    template <typename It>
-    logger(std::string name, It begin, It end) : name_(std::move(name)), sinks_(begin, end)
-    {
-    }
-
-    // Logger with single sink
-    logger(std::string name, sink_ptr single_sink) : logger(std::move(name), {std::move(single_sink)}) {}
-
-    // Logger with sinks init list
-    logger(std::string name, sinks_init_list sinks) : logger(std::move(name), sinks.begin(), sinks.end()) {}
-
-    virtual ~logger() = default;
-
-    logger(const logger &other);
-    logger(logger &&other) SPDLOG_NOEXCEPT;
-    logger &operator=(logger other) SPDLOG_NOEXCEPT;
-    void    swap(spdlog::logger &other) SPDLOG_NOEXCEPT;
-
-    template <typename... Args>
-    void log(source_loc loc, level::level_enum lvl, format_string_t<Args...> fmt, Args &&...args)
-    {
-        log_(loc, lvl, details::to_string_view(fmt), std::forward<Args>(args)...);
-    }
-
-    template <typename... Args>
-    void log(level::level_enum lvl, format_string_t<Args...> fmt, Args &&...args)
-    {
-        log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
-    }
-
-    template <typename T>
-    void log(level::level_enum lvl, const T &msg)
-    {
-        log(source_loc{}, lvl, msg);
-    }
-
-    // T cannot be statically converted to format string (including string_view/wstring_view)
-    template <class T, typename std::enable_if<!is_convertible_to_any_format_string<const T &>::value, int>::type = 0>
-    void log(source_loc loc, level::level_enum lvl, const T &msg)
-    {
-        log(loc, lvl, "{}", msg);
-    }
-
-    void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, string_view_t msg)
-    {
-        bool log_enabled       = should_log(lvl);
-        bool traceback_enabled = tracer_.enabled();
-        if(!log_enabled && !traceback_enabled)
-        {
-            return;
-        }
-
-        details::log_msg log_msg(log_time, loc, name_, lvl, msg);
-        log_it_(log_msg, log_enabled, traceback_enabled);
-    }
-
-    void log(source_loc loc, level::level_enum lvl, string_view_t msg)
-    {
-        bool log_enabled       = should_log(lvl);
-        bool traceback_enabled = tracer_.enabled();
-        if(!log_enabled && !traceback_enabled)
-        {
-            return;
-        }
-
-        details::log_msg log_msg(loc, name_, lvl, msg);
-        log_it_(log_msg, log_enabled, traceback_enabled);
-    }
-
-    void log(level::level_enum lvl, string_view_t msg) { log(source_loc{}, lvl, msg); }
-
-    template <typename... Args>
-    void trace(format_string_t<Args...> fmt, Args &&...args)
-    {
-        log(level::trace, fmt, std::forward<Args>(args)...);
-    }
-
-    template <typename... Args>
-    void debug(format_string_t<Args...> fmt, Args &&...args)
-    {
-        log(level::debug, fmt, std::forward<Args>(args)...);
-    }
-
-    template <typename... Args>
-    void info(format_string_t<Args...> fmt, Args &&...args)
-    {
-        log(level::info, fmt, std::forward<Args>(args)...);
-    }
-
-    template <typename... Args>
-    void warn(format_string_t<Args...> fmt, Args &&...args)
-    {
-        log(level::warn, fmt, std::forward<Args>(args)...);
-    }
-
-    template <typename... Args>
-    void error(format_string_t<Args...> fmt, Args &&...args)
-    {
-        log(level::err, fmt, std::forward<Args>(args)...);
-    }
-
-    template <typename... Args>
-    void critical(format_string_t<Args...> fmt, Args &&...args)
-    {
-        log(level::critical, fmt, std::forward<Args>(args)...);
-    }
-
-#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
-    template <typename... Args>
-    void log(source_loc loc, level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&...args)
-    {
-        log_(loc, lvl, details::to_string_view(fmt), std::forward<Args>(args)...);
-    }
-
-    template <typename... Args>
-    void log(level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&...args)
-    {
-        log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
-    }
-
-    void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, wstring_view_t msg)
-    {
-        bool log_enabled       = should_log(lvl);
-        bool traceback_enabled = tracer_.enabled();
-        if(!log_enabled && !traceback_enabled)
-        {
-            return;
-        }
-
-        memory_buf_t buf;
-        details::os::wstr_to_utf8buf(wstring_view_t(msg.data(), msg.size()), buf);
-        details::log_msg log_msg(log_time, loc, name_, lvl, string_view_t(buf.data(), buf.size()));
-        log_it_(log_msg, log_enabled, traceback_enabled);
-    }
-
-    void log(source_loc loc, level::level_enum lvl, wstring_view_t msg)
-    {
-        bool log_enabled       = should_log(lvl);
-        bool traceback_enabled = tracer_.enabled();
-        if(!log_enabled && !traceback_enabled)
-        {
-            return;
-        }
-
-        memory_buf_t buf;
-        details::os::wstr_to_utf8buf(wstring_view_t(msg.data(), msg.size()), buf);
-        details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
-        log_it_(log_msg, log_enabled, traceback_enabled);
-    }
-
-    void log(level::level_enum lvl, wstring_view_t msg)
-    {
-        log(source_loc{}, lvl, msg);
-    }
-
-    template <typename... Args>
-    void trace(wformat_string_t<Args...> fmt, Args &&...args)
-    {
-        log(level::trace, fmt, std::forward<Args>(args)...);
-    }
-
-    template <typename... Args>
-    void debug(wformat_string_t<Args...> fmt, Args &&...args)
-    {
-        log(level::debug, fmt, std::forward<Args>(args)...);
-    }
-
-    template <typename... Args>
-    void info(wformat_string_t<Args...> fmt, Args &&...args)
-    {
-        log(level::info, fmt, std::forward<Args>(args)...);
-    }
-
-    template <typename... Args>
-    void warn(wformat_string_t<Args...> fmt, Args &&...args)
-    {
-        log(level::warn, fmt, std::forward<Args>(args)...);
-    }
-
-    template <typename... Args>
-    void error(wformat_string_t<Args...> fmt, Args &&...args)
-    {
-        log(level::err, fmt, std::forward<Args>(args)...);
-    }
-
-    template <typename... Args>
-    void critical(wformat_string_t<Args...> fmt, Args &&...args)
-    {
-        log(level::critical, fmt, std::forward<Args>(args)...);
-    }
-#endif
-
-    template <typename T>
-    void trace(const T &msg)
-    {
-        log(level::trace, msg);
-    }
-
-    template <typename T>
-    void debug(const T &msg)
-    {
-        log(level::debug, msg);
-    }
-
-    template <typename T>
-    void info(const T &msg)
-    {
-        log(level::info, msg);
-    }
-
-    template <typename T>
-    void warn(const T &msg)
-    {
-        log(level::warn, msg);
-    }
-
-    template <typename T>
-    void error(const T &msg)
-    {
-        log(level::err, msg);
-    }
-
-    template <typename T>
-    void critical(const T &msg)
-    {
-        log(level::critical, msg);
-    }
-
-    // return true logging is enabled for the given level.
-    bool should_log(level::level_enum msg_level) const
-    {
-        return msg_level >= level_.load(std::memory_order_relaxed);
-    }
-
-    // return true if backtrace logging is enabled.
-    bool should_backtrace() const
-    {
-        return tracer_.enabled();
-    }
-
-    void set_level(level::level_enum log_level);
-
-    level::level_enum level() const;
-
-    const std::string &name() const;
-
-    // set formatting for the sinks in this logger.
-    // each sink will get a separate instance of the formatter object.
-    void set_formatter(std::unique_ptr<formatter> f);
-
-    // set formatting for the sinks in this logger.
-    // equivalent to
-    //     set_formatter(make_unique<pattern_formatter>(pattern, time_type))
-    // Note: each sink will get a new instance of a formatter object, replacing the old one.
-    void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
-
-    // backtrace support.
-    // efficiently store all debug/trace messages in a circular buffer until needed for debugging.
-    void enable_backtrace(size_t n_messages);
-    void disable_backtrace();
-    void dump_backtrace();
-
-    // flush functions
-    void              flush();
-    void              flush_on(level::level_enum log_level);
-    level::level_enum flush_level() const;
-
-    // sinks
-    const std::vector<sink_ptr> &sinks() const;
-
-    std::vector<sink_ptr> &sinks();
-
-    // error handler
-    void set_error_handler(err_handler);
-
-    // create new logger with same sinks and configuration.
-    virtual std::shared_ptr<logger> clone(std::string logger_name);
-
-protected:
-    std::string           name_;
-    std::vector<sink_ptr> sinks_;
-    spdlog::level_t       level_{level::info};
-    spdlog::level_t       flush_level_{level::off};
-    err_handler           custom_err_handler_{nullptr};
-    details::backtracer   tracer_;
-
-    // common implementation for after templated public api has been resolved
-    template <typename... Args>
-    void log_(source_loc loc, level::level_enum lvl, string_view_t fmt, Args &&...args)
-    {
-        bool log_enabled       = should_log(lvl);
-        bool traceback_enabled = tracer_.enabled();
-        if(!log_enabled && !traceback_enabled)
-        {
-            return;
-        }
-        SPDLOG_TRY
-        {
-            memory_buf_t buf;
-#ifdef SPDLOG_USE_STD_FORMAT
-            fmt_lib::vformat_to(std::back_inserter(buf), fmt, fmt_lib::make_format_args(std::forward<Args>(args)...));
-#else
-            fmt::vformat_to(fmt::appender(buf), fmt, fmt::make_format_args(std::forward<Args>(args)...));
-#endif
-
-            details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
-            log_it_(log_msg, log_enabled, traceback_enabled);
-        }
-        SPDLOG_LOGGER_CATCH(loc)
-    }
-
-#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
-    template <typename... Args>
-    void log_(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args &&...args)
-    {
-        bool log_enabled       = should_log(lvl);
-        bool traceback_enabled = tracer_.enabled();
-        if(!log_enabled && !traceback_enabled)
-        {
-            return;
-        }
-        SPDLOG_TRY
-        {
-            // format to wmemory_buffer and convert to utf8
-            wmemory_buf_t wbuf;
-            fmt_lib::vformat_to(
-                std::back_inserter(wbuf),
-                fmt,
-                fmt_lib::make_format_args<fmt_lib::wformat_context>(std::forward<Args>(args)...));
-
-            memory_buf_t buf;
-            details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);
-            details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
-            log_it_(log_msg, log_enabled, traceback_enabled);
-        }
-        SPDLOG_LOGGER_CATCH(loc)
-    }
-#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
-
-    // log the given message (if the given log level is high enough),
-    // and save backtrace (if backtrace is enabled).
-    void         log_it_(const details::log_msg &log_msg, bool log_enabled, bool traceback_enabled);
-    virtual void sink_it_(const details::log_msg &msg);
-    virtual void flush_();
-    void         dump_backtrace_();
-    bool         should_flush_(const details::log_msg &msg);
-
-    // handle errors during logging.
-    // default handler prints the error to stderr at max rate of 1 message/sec.
-    void err_handler_(const std::string &msg);
-};
-
-void swap(logger &a, logger &b);
-
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "logger-inl.h"
-#endif
diff --git a/include/spdlog/pattern_formatter-inl.h b/include/spdlog/pattern_formatter-inl.h
deleted file mode 100644
index 0462f9a82..000000000
--- a/include/spdlog/pattern_formatter-inl.h
+++ /dev/null
@@ -1,1374 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/pattern_formatter.h>
-#endif
-
-#include <algorithm>
-#include <array>
-#include <cctype>
-#include <chrono>
-#include <cstring>
-#include <ctime>
-#include <iterator>
-#include <memory>
-#include <mutex>
-#include <spdlog/details/fmt_helper.h>
-#include <spdlog/details/log_msg.h>
-#include <spdlog/details/os.h>
-#include <spdlog/fmt/fmt.h>
-#include <spdlog/formatter.h>
-#include <string>
-#include <thread>
-#include <utility>
-#include <vector>
-
-namespace spdlog
-{
-namespace details
-{
-
-    ///////////////////////////////////////////////////////////////////////
-    // name & level pattern appender
-    ///////////////////////////////////////////////////////////////////////
-
-    class scoped_padder
-    {
-    public:
-        scoped_padder(size_t wrapped_size, const padding_info &padinfo, memory_buf_t &dest) :
-            padinfo_(padinfo), dest_(dest)
-        {
-            remaining_pad_ = static_cast<long>(padinfo.width_) - static_cast<long>(wrapped_size);
-            if(remaining_pad_ <= 0)
-            {
-                return;
-            }
-
-            if(padinfo_.side_ == padding_info::pad_side::left)
-            {
-                pad_it(remaining_pad_);
-                remaining_pad_ = 0;
-            }
-            else if(padinfo_.side_ == padding_info::pad_side::center)
-            {
-                auto half_pad = remaining_pad_ / 2;
-                auto reminder = remaining_pad_ & 1;
-                pad_it(half_pad);
-                remaining_pad_ = half_pad + reminder; // for the right side
-            }
-        }
-
-        template <typename T>
-        static unsigned int count_digits(T n)
-        {
-            return fmt_helper::count_digits(n);
-        }
-
-        ~scoped_padder()
-        {
-            if(remaining_pad_ >= 0)
-            {
-                pad_it(remaining_pad_);
-            }
-            else if(padinfo_.truncate_)
-            {
-                long new_size = static_cast<long>(dest_.size()) + remaining_pad_;
-                dest_.resize(static_cast<size_t>(new_size));
-            }
-        }
-
-    private:
-        void pad_it(long count)
-        {
-            fmt_helper::append_string_view(string_view_t(spaces_.data(), static_cast<size_t>(count)), dest_);
-        }
-
-        const padding_info &padinfo_;
-        memory_buf_t       &dest_;
-        long                remaining_pad_;
-        string_view_t       spaces_{"                                                                ", 64};
-    };
-
-    struct null_scoped_padder
-    {
-        null_scoped_padder(size_t /*wrapped_size*/, const padding_info & /*padinfo*/, memory_buf_t & /*dest*/) {}
-
-        template <typename T>
-        static unsigned int count_digits(T /* number */)
-        {
-            return 0;
-        }
-    };
-
-    template <typename ScopedPadder>
-    class name_formatter final : public flag_formatter
-    {
-    public:
-        explicit name_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
-
-        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
-        {
-            ScopedPadder p(msg.logger_name.size(), padinfo_, dest);
-            fmt_helper::append_string_view(msg.logger_name, dest);
-        }
-    };
-
-    // log level appender
-    template <typename ScopedPadder>
-    class level_formatter final : public flag_formatter
-    {
-    public:
-        explicit level_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
-
-        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
-        {
-            const string_view_t &level_name = level::to_string_view(msg.level);
-            ScopedPadder         p(level_name.size(), padinfo_, dest);
-            fmt_helper::append_string_view(level_name, dest);
-        }
-    };
-
-    // short log level appender
-    template <typename ScopedPadder>
-    class short_level_formatter final : public flag_formatter
-    {
-    public:
-        explicit short_level_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
-
-        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
-        {
-            string_view_t level_name{level::to_short_c_str(msg.level)};
-            ScopedPadder  p(level_name.size(), padinfo_, dest);
-            fmt_helper::append_string_view(level_name, dest);
-        }
-    };
-
-    ///////////////////////////////////////////////////////////////////////
-    // Date time pattern appenders
-    ///////////////////////////////////////////////////////////////////////
-
-    static const char *ampm(const tm &t)
-    {
-        return t.tm_hour >= 12 ? "PM" : "AM";
-    }
-
-    static int to12h(const tm &t)
-    {
-        return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
-    }
-
-    // Abbreviated weekday name
-    static std::array<const char *, 7> days{{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}};
-
-    template <typename ScopedPadder>
-    class a_formatter final : public flag_formatter
-    {
-    public:
-        explicit a_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
-
-        void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
-        {
-            string_view_t field_value{days[static_cast<size_t>(tm_time.tm_wday)]};
-            ScopedPadder  p(field_value.size(), padinfo_, dest);
-            fmt_helper::append_string_view(field_value, dest);
-        }
-    };
-
-    // Full weekday name
-    static std::array<const char *, 7> full_days{
-        {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}};
-
-    template <typename ScopedPadder>
-    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, memory_buf_t &dest) override
-        {
-            string_view_t field_value{full_days[static_cast<size_t>(tm_time.tm_wday)]};
-            ScopedPadder  p(field_value.size(), padinfo_, dest);
-            fmt_helper::append_string_view(field_value, dest);
-        }
-    };
-
-    // Abbreviated month
-    static const std::array<const char *, 12> months{
-        {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}};
-
-    template <typename ScopedPadder>
-    class b_formatter final : public flag_formatter
-    {
-    public:
-        explicit b_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
-
-        void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
-        {
-            string_view_t field_value{months[static_cast<size_t>(tm_time.tm_mon)]};
-            ScopedPadder  p(field_value.size(), padinfo_, dest);
-            fmt_helper::append_string_view(field_value, dest);
-        }
-    };
-
-    // Full month name
-    static const std::array<const char *, 12> full_months{
-        {"January",
-         "February",
-         "March",
-         "April",
-         "May",
-         "June",
-         "July",
-         "August",
-         "September",
-         "October",
-         "November",
-         "December"}};
-
-    template <typename ScopedPadder>
-    class B_formatter final : public flag_formatter
-    {
-    public:
-        explicit B_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
-
-        void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override
-        {
-            string_view_t field_value{full_months[static_cast<size_t>(tm_time.tm_mon)]};
-            ScopedPadder  p(field_value.size(), padinfo_, dest);
-            fmt_helper::append_string_view(field_value, dest);
-        }
-    };
-
-    // Date and time representation (Thu Aug 23 15:35:46 2014)
-    template <typename ScopedPadder>
-    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, memory_buf_t &dest) override
-        {
-            const size_t field_size = 24;
-            ScopedPadder p(field_size, padinfo_, dest);
-
-            fmt_helper::append_string_view(days[static_cast<size_t>(tm_time.tm_wday)], dest);
-            dest.push_back(' ');
-            fmt_helper::append_string_view(months[static_cast<size_t>(tm_time.tm_mon)], dest);
-            dest.push_back(' ');
-            fmt_helper::append_int(tm_time.tm_mday, dest);
-            dest.push_back(' ');
-            // time
-
-            fmt_helper::pad2(tm_time.tm_hour, 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_int(tm_time.tm_year + 1900, dest);
-        }
-    };
-
-    // year - 2 digit
-    template <typename ScopedPadder>
-    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, memory_buf_t &dest) override
-        {
-            const size_t field_size = 2;
-            ScopedPadder p(field_size, padinfo_, dest);
-            fmt_helper::pad2(tm_time.tm_year % 100, dest);
-        }
-    };
-
-    // Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
-    template <typename ScopedPadder>
-    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, memory_buf_t &dest) override
-        {
-            const size_t field_size = 10;
-            ScopedPadder 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);
-            dest.push_back('/');
-            fmt_helper::pad2(tm_time.tm_year % 100, dest);
-        }
-    };
-
-    // year - 4 digit
-    template <typename ScopedPadder>
-    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, memory_buf_t &dest) override
-        {
-            const size_t field_size = 4;
-            ScopedPadder p(field_size, padinfo_, dest);
-            fmt_helper::append_int(tm_time.tm_year + 1900, dest);
-        }
-    };
-
-    // month 1-12
-    template <typename ScopedPadder>
-    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, memory_buf_t &dest) override
-        {
-            const size_t field_size = 2;
-            ScopedPadder p(field_size, padinfo_, dest);
-            fmt_helper::pad2(tm_time.tm_mon + 1, dest);
-        }
-    };
-
-    // day of month 1-31
-    template <typename ScopedPadder>
-    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, memory_buf_t &dest) override
-        {
-            const size_t field_size = 2;
-            ScopedPadder p(field_size, padinfo_, dest);
-            fmt_helper::pad2(tm_time.tm_mday, dest);
-        }
-    };
-
-    // hours in 24 format 0-23
-    template <typename ScopedPadder>
-    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, memory_buf_t &dest) override
-        {
-            const size_t field_size = 2;
-            ScopedPadder p(field_size, padinfo_, dest);
-            fmt_helper::pad2(tm_time.tm_hour, dest);
-        }
-    };
-
-    // hours in 12 format 1-12
-    template <typename ScopedPadder>
-    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, memory_buf_t &dest) override
-        {
-            const size_t field_size = 2;
-            ScopedPadder p(field_size, padinfo_, dest);
-            fmt_helper::pad2(to12h(tm_time), dest);
-        }
-    };
-
-    // minutes 0-59
-    template <typename ScopedPadder>
-    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, memory_buf_t &dest) override
-        {
-            const size_t field_size = 2;
-            ScopedPadder p(field_size, padinfo_, dest);
-            fmt_helper::pad2(tm_time.tm_min, dest);
-        }
-    };
-
-    // seconds 0-59
-    template <typename ScopedPadder>
-    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, memory_buf_t &dest) override
-        {
-            const size_t field_size = 2;
-            ScopedPadder p(field_size, padinfo_, dest);
-            fmt_helper::pad2(tm_time.tm_sec, dest);
-        }
-    };
-
-    // milliseconds
-    template <typename ScopedPadder>
-    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 &, memory_buf_t &dest) override
-        {
-            auto         millis     = fmt_helper::time_fraction<std::chrono::milliseconds>(msg.time);
-            const size_t field_size = 3;
-            ScopedPadder p(field_size, padinfo_, dest);
-            fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest);
-        }
-    };
-
-    // microseconds
-    template <typename ScopedPadder>
-    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 &, memory_buf_t &dest) override
-        {
-            auto micros = fmt_helper::time_fraction<std::chrono::microseconds>(msg.time);
-
-            const size_t field_size = 6;
-            ScopedPadder p(field_size, padinfo_, dest);
-            fmt_helper::pad6(static_cast<size_t>(micros.count()), dest);
-        }
-    };
-
-    // nanoseconds
-    template <typename ScopedPadder>
-    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 &, memory_buf_t &dest) override
-        {
-            auto         ns         = fmt_helper::time_fraction<std::chrono::nanoseconds>(msg.time);
-            const size_t field_size = 9;
-            ScopedPadder p(field_size, padinfo_, dest);
-            fmt_helper::pad9(static_cast<size_t>(ns.count()), dest);
-        }
-    };
-
-    // seconds since epoch
-    template <typename ScopedPadder>
-    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 &, memory_buf_t &dest) override
-        {
-            const size_t field_size = 10;
-            ScopedPadder 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);
-        }
-    };
-
-    // AM/PM
-    template <typename ScopedPadder>
-    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, memory_buf_t &dest) override
-        {
-            const size_t field_size = 2;
-            ScopedPadder p(field_size, padinfo_, dest);
-            fmt_helper::append_string_view(ampm(tm_time), dest);
-        }
-    };
-
-    // 12 hour clock 02:55:02 pm
-    template <typename ScopedPadder>
-    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, memory_buf_t &dest) override
-        {
-            const size_t field_size = 11;
-            ScopedPadder 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_string_view(ampm(tm_time), dest);
-        }
-    };
-
-    // 24-hour HH:MM time, equivalent to %H:%M
-    template <typename ScopedPadder>
-    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, memory_buf_t &dest) override
-        {
-            const size_t field_size = 5;
-            ScopedPadder p(field_size, padinfo_, dest);
-
-            fmt_helper::pad2(tm_time.tm_hour, dest);
-            dest.push_back(':');
-            fmt_helper::pad2(tm_time.tm_min, dest);
-        }
-    };
-
-    // ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
-    template <typename ScopedPadder>
-    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, memory_buf_t &dest) override
-        {
-            const size_t field_size = 8;
-            ScopedPadder p(field_size, padinfo_, dest);
-
-            fmt_helper::pad2(tm_time.tm_hour, dest);
-            dest.push_back(':');
-            fmt_helper::pad2(tm_time.tm_min, dest);
-            dest.push_back(':');
-            fmt_helper::pad2(tm_time.tm_sec, dest);
-        }
-    };
-
-    // ISO 8601 offset from UTC in timezone (+-HH:MM)
-    template <typename ScopedPadder>
-    class z_formatter final : public flag_formatter
-    {
-    public:
-        explicit z_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
-
-        z_formatter()                               = default;
-        z_formatter(const z_formatter &)            = delete;
-        z_formatter &operator=(const z_formatter &) = delete;
-
-        void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) override
-        {
-            const size_t field_size = 6;
-            ScopedPadder p(field_size, padinfo_, dest);
-
-            auto total_minutes = get_cached_offset(msg, tm_time);
-            bool is_negative   = total_minutes < 0;
-            if(is_negative)
-            {
-                total_minutes = -total_minutes;
-                dest.push_back('-');
-            }
-            else
-            {
-                dest.push_back('+');
-            }
-
-            fmt_helper::pad2(total_minutes / 60, dest); // hours
-            dest.push_back(':');
-            fmt_helper::pad2(total_minutes % 60, dest); // minutes
-        }
-
-    private:
-        log_clock::time_point last_update_{std::chrono::seconds(0)};
-        int                   offset_minutes_{0};
-
-        int get_cached_offset(const log_msg &msg, const std::tm &tm_time)
-        {
-            // refresh every 10 seconds
-            if(msg.time - last_update_ >= std::chrono::seconds(10))
-            {
-                offset_minutes_ = os::utc_minutes_offset(tm_time);
-                last_update_    = msg.time;
-            }
-            return offset_minutes_;
-        }
-    };
-
-    // Thread id
-    template <typename ScopedPadder>
-    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 &, memory_buf_t &dest) override
-        {
-            const auto   field_size = ScopedPadder::count_digits(msg.thread_id);
-            ScopedPadder p(field_size, padinfo_, dest);
-            fmt_helper::append_int(msg.thread_id, dest);
-        }
-    };
-
-    // Current pid
-    template <typename ScopedPadder>
-    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 &, memory_buf_t &dest) override
-        {
-            const auto   pid        = static_cast<uint32_t>(details::os::pid());
-            auto         field_size = ScopedPadder::count_digits(pid);
-            ScopedPadder p(field_size, padinfo_, dest);
-            fmt_helper::append_int(pid, dest);
-        }
-    };
-
-    template <typename ScopedPadder>
-    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 &, memory_buf_t &dest) override
-        {
-            ScopedPadder p(msg.payload.size(), padinfo_, dest);
-            fmt_helper::append_string_view(msg.payload, dest);
-        }
-    };
-
-    class ch_formatter final : public flag_formatter
-    {
-    public:
-        explicit ch_formatter(char ch) : ch_(ch) {}
-
-        void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override { dest.push_back(ch_); }
-
-    private:
-        char ch_;
-    };
-
-    // aggregate user chars to display as is
-    class aggregate_formatter final : public flag_formatter
-    {
-    public:
-        aggregate_formatter() = default;
-
-        void add_ch(char ch) { str_ += ch; }
-        void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override
-        {
-            fmt_helper::append_string_view(str_, dest);
-        }
-
-    private:
-        std::string str_;
-    };
-
-    // 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 &, memory_buf_t &dest) override
-        {
-            msg.color_range_start = dest.size();
-        }
-    };
-
-    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 &, memory_buf_t &dest) override
-        {
-            msg.color_range_end = dest.size();
-        }
-    };
-
-    // print source location
-    template <typename ScopedPadder>
-    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 &, memory_buf_t &dest) override
-        {
-            if(msg.source.empty())
-            {
-                ScopedPadder p(0, padinfo_, dest);
-                return;
-            }
-
-            size_t text_size;
-            if(padinfo_.enabled())
-            {
-                // calc text size for padding based on "filename:line"
-                text_size = std::char_traits<char>::length(msg.source.filename) +
-                            ScopedPadder::count_digits(msg.source.line) + 1;
-            }
-            else
-            {
-                text_size = 0;
-            }
-
-            ScopedPadder 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);
-        }
-    };
-
-    // print source filename
-    template <typename ScopedPadder>
-    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 &, memory_buf_t &dest) override
-        {
-            if(msg.source.empty())
-            {
-                ScopedPadder p(0, padinfo_, dest);
-                return;
-            }
-            size_t       text_size = padinfo_.enabled() ? std::char_traits<char>::length(msg.source.filename) : 0;
-            ScopedPadder p(text_size, padinfo_, dest);
-            fmt_helper::append_string_view(msg.source.filename, dest);
-        }
-    };
-
-    template <typename ScopedPadder>
-    class short_filename_formatter final : public flag_formatter
-    {
-    public:
-        explicit short_filename_formatter(padding_info padinfo) : flag_formatter(padinfo) {}
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable : 4127) // consider using 'if constexpr' instead
-#endif                          // _MSC_VER
-        static const char *basename(const char *filename)
-        {
-            // if the size is 2 (1 character + null terminator) we can use the more efficient strrchr
-            // the branch will be elided by optimizations
-            if(sizeof(os::folder_seps) == 2)
-            {
-                const char *rv = std::strrchr(filename, os::folder_seps[0]);
-                return rv != nullptr ? rv + 1 : filename;
-            }
-            else
-            {
-                const std::reverse_iterator<const char *> begin(filename + std::strlen(filename));
-                const std::reverse_iterator<const char *> end(filename);
-
-                const auto it =
-                    std::find_first_of(begin, end, std::begin(os::folder_seps), std::end(os::folder_seps) - 1);
-                return it != end ? it.base() : filename;
-            }
-        }
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif // _MSC_VER
-
-        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
-        {
-            if(msg.source.empty())
-            {
-                ScopedPadder p(0, padinfo_, dest);
-                return;
-            }
-            auto         filename  = basename(msg.source.filename);
-            size_t       text_size = padinfo_.enabled() ? std::char_traits<char>::length(filename) : 0;
-            ScopedPadder p(text_size, padinfo_, dest);
-            fmt_helper::append_string_view(filename, dest);
-        }
-    };
-
-    template <typename ScopedPadder>
-    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 &, memory_buf_t &dest) override
-        {
-            if(msg.source.empty())
-            {
-                ScopedPadder p(0, padinfo_, dest);
-                return;
-            }
-
-            auto         field_size = ScopedPadder::count_digits(msg.source.line);
-            ScopedPadder p(field_size, padinfo_, dest);
-            fmt_helper::append_int(msg.source.line, dest);
-        }
-    };
-
-    // print source funcname
-    template <typename ScopedPadder>
-    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 &, memory_buf_t &dest) override
-        {
-            if(msg.source.empty())
-            {
-                ScopedPadder p(0, padinfo_, dest);
-                return;
-            }
-            size_t       text_size = padinfo_.enabled() ? std::char_traits<char>::length(msg.source.funcname) : 0;
-            ScopedPadder p(text_size, padinfo_, dest);
-            fmt_helper::append_string_view(msg.source.funcname, dest);
-        }
-    };
-
-    // print elapsed time since last message
-    template <typename ScopedPadder, typename Units>
-    class elapsed_formatter final : public flag_formatter
-    {
-    public:
-        using DurationUnits = Units;
-
-        explicit elapsed_formatter(padding_info padinfo) : flag_formatter(padinfo), last_message_time_(log_clock::now())
-        {
-        }
-
-        void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
-        {
-            auto delta               = (std::max)(msg.time - last_message_time_, log_clock::duration::zero());
-            auto delta_units         = std::chrono::duration_cast<DurationUnits>(delta);
-            last_message_time_       = msg.time;
-            auto         delta_count = static_cast<size_t>(delta_units.count());
-            auto         n_digits    = static_cast<size_t>(ScopedPadder::count_digits(delta_count));
-            ScopedPadder p(n_digits, padinfo_, dest);
-            fmt_helper::append_int(delta_count, dest);
-        }
-
-    private:
-        log_clock::time_point last_message_time_;
-    };
-
-    // Full info formatter
-    // pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] [%s:%#] %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, memory_buf_t &dest) override
-        {
-            using std::chrono::duration_cast;
-            using std::chrono::milliseconds;
-            using std::chrono::seconds;
-
-            // cache the date/time part for the next second.
-            auto duration = msg.time.time_since_epoch();
-            auto secs     = duration_cast<seconds>(duration);
-
-            if(cache_timestamp_ != secs || cached_datetime_.size() == 0)
-            {
-                cached_datetime_.clear();
-                cached_datetime_.push_back('[');
-                fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_);
-                cached_datetime_.push_back('-');
-
-                fmt_helper::pad2(tm_time.tm_mon + 1, cached_datetime_);
-                cached_datetime_.push_back('-');
-
-                fmt_helper::pad2(tm_time.tm_mday, cached_datetime_);
-                cached_datetime_.push_back(' ');
-
-                fmt_helper::pad2(tm_time.tm_hour, cached_datetime_);
-                cached_datetime_.push_back(':');
-
-                fmt_helper::pad2(tm_time.tm_min, cached_datetime_);
-                cached_datetime_.push_back(':');
-
-                fmt_helper::pad2(tm_time.tm_sec, cached_datetime_);
-                cached_datetime_.push_back('.');
-
-                cache_timestamp_ = secs;
-            }
-            dest.append(cached_datetime_.begin(), cached_datetime_.end());
-
-            auto millis = fmt_helper::time_fraction<milliseconds>(msg.time);
-            fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest);
-            dest.push_back(']');
-            dest.push_back(' ');
-
-            // append logger name if exists
-            if(msg.logger_name.size() > 0)
-            {
-                dest.push_back('[');
-                fmt_helper::append_string_view(msg.logger_name, dest);
-                dest.push_back(']');
-                dest.push_back(' ');
-            }
-
-            dest.push_back('[');
-            // wrap the level name with color
-            msg.color_range_start = dest.size();
-            // 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(' ');
-
-            // add source location if present
-            if(!msg.source.empty())
-            {
-                dest.push_back('[');
-                const char *filename =
-                    details::short_filename_formatter<details::null_scoped_padder>::basename(msg.source.filename);
-                fmt_helper::append_string_view(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:
-        std::chrono::seconds cache_timestamp_{0};
-        memory_buf_t         cached_datetime_;
-    };
-
-} // namespace details
-
-SPDLOG_INLINE pattern_formatter::pattern_formatter(
-    std::string       pattern,
-    pattern_time_type time_type,
-    std::string       eol,
-    custom_flags      custom_user_flags) :
-    pattern_(std::move(pattern)),
-    eol_(std::move(eol)),
-    pattern_time_type_(time_type),
-    need_localtime_(false),
-    last_log_secs_(0),
-    custom_handlers_(std::move(custom_user_flags))
-{
-    std::memset(&cached_tm_, 0, sizeof(cached_tm_));
-    compile_pattern_(pattern_);
-}
-
-// use by default full formatter for if pattern is not given
-SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type, std::string eol) :
-    pattern_("%+"), eol_(std::move(eol)), pattern_time_type_(time_type), need_localtime_(true), last_log_secs_(0)
-{
-    std::memset(&cached_tm_, 0, sizeof(cached_tm_));
-    formatters_.push_back(details::make_unique<details::full_formatter>(details::padding_info{}));
-}
-
-SPDLOG_INLINE std::unique_ptr<formatter> pattern_formatter::clone() const
-{
-    custom_flags cloned_custom_formatters;
-    for(auto &it : custom_handlers_)
-    {
-        cloned_custom_formatters[it.first] = it.second->clone();
-    }
-    auto cloned = details::make_unique<pattern_formatter>(
-        pattern_, pattern_time_type_, eol_, std::move(cloned_custom_formatters));
-    cloned->need_localtime(need_localtime_);
-#if defined(__GNUC__) && __GNUC__ < 5
-    return std::move(cloned);
-#else
-    return cloned;
-#endif
-}
-
-SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory_buf_t &dest)
-{
-    if(need_localtime_)
-    {
-        const auto secs = std::chrono::duration_cast<std::chrono::seconds>(msg.time.time_since_epoch());
-        if(secs != last_log_secs_)
-        {
-            cached_tm_     = get_time_(msg);
-            last_log_secs_ = secs;
-        }
-    }
-
-    for(auto &f : formatters_)
-    {
-        f->format(msg, cached_tm_, dest);
-    }
-    // write eol
-    details::fmt_helper::append_string_view(eol_, dest);
-}
-
-SPDLOG_INLINE void pattern_formatter::set_pattern(std::string pattern)
-{
-    pattern_        = std::move(pattern);
-    need_localtime_ = false;
-    compile_pattern_(pattern_);
-}
-
-SPDLOG_INLINE void pattern_formatter::need_localtime(bool need)
-{
-    need_localtime_ = need;
-}
-
-SPDLOG_INLINE std::tm pattern_formatter::get_time_(const details::log_msg &msg)
-{
-    if(pattern_time_type_ == pattern_time_type::local)
-    {
-        return details::os::localtime(log_clock::to_time_t(msg.time));
-    }
-    return details::os::gmtime(log_clock::to_time_t(msg.time));
-}
-
-template <typename Padder>
-SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_info padding)
-{
-    // process custom flags
-    auto it = custom_handlers_.find(flag);
-    if(it != custom_handlers_.end())
-    {
-        auto custom_handler = it->second->clone();
-        custom_handler->set_padding_info(padding);
-        formatters_.push_back(std::move(custom_handler));
-        return;
-    }
-
-    // process built-in flags
-    switch(flag)
-    {
-        case('+'): // default formatter
-            formatters_.push_back(details::make_unique<details::full_formatter>(padding));
-            need_localtime_ = true;
-            break;
-
-        case 'n': // logger name
-            formatters_.push_back(details::make_unique<details::name_formatter<Padder>>(padding));
-            break;
-
-        case 'l': // level
-            formatters_.push_back(details::make_unique<details::level_formatter<Padder>>(padding));
-            break;
-
-        case 'L': // short level
-            formatters_.push_back(details::make_unique<details::short_level_formatter<Padder>>(padding));
-            break;
-
-        case('t'): // thread id
-            formatters_.push_back(details::make_unique<details::t_formatter<Padder>>(padding));
-            break;
-
-        case('v'): // the message text
-            formatters_.push_back(details::make_unique<details::v_formatter<Padder>>(padding));
-            break;
-
-        case('a'): // weekday
-            formatters_.push_back(details::make_unique<details::a_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('A'): // short weekday
-            formatters_.push_back(details::make_unique<details::A_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('b'):
-        case('h'): // month
-            formatters_.push_back(details::make_unique<details::b_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('B'): // short month
-            formatters_.push_back(details::make_unique<details::B_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('c'): // datetime
-            formatters_.push_back(details::make_unique<details::c_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('C'): // year 2 digits
-            formatters_.push_back(details::make_unique<details::C_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('Y'): // year 4 digits
-            formatters_.push_back(details::make_unique<details::Y_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('D'):
-        case('x'): // datetime MM/DD/YY
-            formatters_.push_back(details::make_unique<details::D_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('m'): // month 1-12
-            formatters_.push_back(details::make_unique<details::m_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('d'): // day of month 1-31
-            formatters_.push_back(details::make_unique<details::d_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('H'): // hours 24
-            formatters_.push_back(details::make_unique<details::H_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('I'): // hours 12
-            formatters_.push_back(details::make_unique<details::I_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('M'): // minutes
-            formatters_.push_back(details::make_unique<details::M_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('S'): // seconds
-            formatters_.push_back(details::make_unique<details::S_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('e'): // milliseconds
-            formatters_.push_back(details::make_unique<details::e_formatter<Padder>>(padding));
-            break;
-
-        case('f'): // microseconds
-            formatters_.push_back(details::make_unique<details::f_formatter<Padder>>(padding));
-            break;
-
-        case('F'): // nanoseconds
-            formatters_.push_back(details::make_unique<details::F_formatter<Padder>>(padding));
-            break;
-
-        case('E'): // seconds since epoch
-            formatters_.push_back(details::make_unique<details::E_formatter<Padder>>(padding));
-            break;
-
-        case('p'): // am/pm
-            formatters_.push_back(details::make_unique<details::p_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('r'): // 12 hour clock 02:55:02 pm
-            formatters_.push_back(details::make_unique<details::r_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('R'): // 24-hour HH:MM time
-            formatters_.push_back(details::make_unique<details::R_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('T'):
-        case('X'): // ISO 8601 time format (HH:MM:SS)
-            formatters_.push_back(details::make_unique<details::T_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('z'): // timezone
-            formatters_.push_back(details::make_unique<details::z_formatter<Padder>>(padding));
-            need_localtime_ = true;
-            break;
-
-        case('P'): // pid
-            formatters_.push_back(details::make_unique<details::pid_formatter<Padder>>(padding));
-            break;
-
-        case('^'): // color range start
-            formatters_.push_back(details::make_unique<details::color_start_formatter>(padding));
-            break;
-
-        case('$'): // color range end
-            formatters_.push_back(details::make_unique<details::color_stop_formatter>(padding));
-            break;
-
-        case('@'): // source location (filename:filenumber)
-            formatters_.push_back(details::make_unique<details::source_location_formatter<Padder>>(padding));
-            break;
-
-        case('s'): // short source filename - without directory name
-            formatters_.push_back(details::make_unique<details::short_filename_formatter<Padder>>(padding));
-            break;
-
-        case('g'): // full source filename
-            formatters_.push_back(details::make_unique<details::source_filename_formatter<Padder>>(padding));
-            break;
-
-        case('#'): // source line number
-            formatters_.push_back(details::make_unique<details::source_linenum_formatter<Padder>>(padding));
-            break;
-
-        case('!'): // source funcname
-            formatters_.push_back(details::make_unique<details::source_funcname_formatter<Padder>>(padding));
-            break;
-
-        case('%'): // % char
-            formatters_.push_back(details::make_unique<details::ch_formatter>('%'));
-            break;
-
-        case('u'): // elapsed time since last log message in nanos
-            formatters_.push_back(
-                details::make_unique<details::elapsed_formatter<Padder, std::chrono::nanoseconds>>(padding));
-            break;
-
-        case('i'): // elapsed time since last log message in micros
-            formatters_.push_back(
-                details::make_unique<details::elapsed_formatter<Padder, std::chrono::microseconds>>(padding));
-            break;
-
-        case('o'): // elapsed time since last log message in millis
-            formatters_.push_back(
-                details::make_unique<details::elapsed_formatter<Padder, std::chrono::milliseconds>>(padding));
-            break;
-
-        case('O'): // elapsed time since last log message in seconds
-            formatters_.push_back(
-                details::make_unique<details::elapsed_formatter<Padder, std::chrono::seconds>>(padding));
-            break;
-
-        default: // Unknown flag appears as is
-            auto unknown_flag = details::make_unique<details::aggregate_formatter>();
-
-            if(!padding.truncate_)
-            {
-                unknown_flag->add_ch('%');
-                unknown_flag->add_ch(flag);
-                formatters_.push_back((std::move(unknown_flag)));
-            }
-            // fix issue #1617 (prev char was '!' and should have been treated as funcname flag instead of truncating
-            // flag) spdlog::set_pattern("[%10!] %v") => "[      main] some message" spdlog::set_pattern("[%3!!] %v") =>
-            // "[mai] some message"
-            else
-            {
-                padding.truncate_ = false;
-                formatters_.push_back(details::make_unique<details::source_funcname_formatter<Padder>>(padding));
-                unknown_flag->add_ch(flag);
-                formatters_.push_back((std::move(unknown_flag)));
-            }
-
-            break;
-    }
-}
-
-// Extract given pad spec (e.g. %8X, %=8X, %-8!X, %8!X, %=8!X, %-8!X, %+8!X)
-// Advance the given it pass the end of the padding spec found (if any)
-// Return padding.
-SPDLOG_INLINE details::padding_info
-              pattern_formatter::handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end)
-{
-    using details::padding_info;
-    using details::scoped_padder;
-    const size_t max_width = 64;
-    if(it == end)
-    {
-        return padding_info{};
-    }
-
-    padding_info::pad_side side;
-    switch(*it)
-    {
-        case '-':
-            side = padding_info::pad_side::right;
-            ++it;
-            break;
-        case '=':
-            side = padding_info::pad_side::center;
-            ++it;
-            break;
-        default:
-            side = details::padding_info::pad_side::left;
-            break;
-    }
-
-    if(it == end || !std::isdigit(static_cast<unsigned char>(*it)))
-    {
-        return padding_info{}; // no padding if no digit found here
-    }
-
-    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;
-    }
-
-    // search for the optional truncate marker '!'
-    bool truncate;
-    if(it != end && *it == '!')
-    {
-        truncate = true;
-        ++it;
-    }
-    else
-    {
-        truncate = false;
-    }
-    return details::padding_info{std::min<size_t>(width, max_width), side, truncate};
-}
-
-SPDLOG_INLINE void pattern_formatter::compile_pattern_(const std::string &pattern)
-{
-    auto                                          end = pattern.end();
-    std::unique_ptr<details::aggregate_formatter> user_chars;
-    formatters_.clear();
-    for(auto it = pattern.begin(); it != end; ++it)
-    {
-        if(*it == '%')
-        {
-            if(user_chars) // append user chars found so far
-            {
-                formatters_.push_back(std::move(user_chars));
-            }
-
-            auto padding = handle_padspec_(++it, end);
-
-            if(it != end)
-            {
-                if(padding.enabled())
-                {
-                    handle_flag_<details::scoped_padder>(*it, padding);
-                }
-                else
-                {
-                    handle_flag_<details::null_scoped_padder>(*it, padding);
-                }
-            }
-            else
-            {
-                break;
-            }
-        }
-        else // chars not following the % sign should be displayed as is
-        {
-            if(!user_chars)
-            {
-                user_chars = details::make_unique<details::aggregate_formatter>();
-            }
-            user_chars->add_ch(*it);
-        }
-    }
-    if(user_chars) // append raw chars found so far
-    {
-        formatters_.push_back(std::move(user_chars));
-    }
-}
-} // namespace spdlog
diff --git a/include/spdlog/pattern_formatter.h b/include/spdlog/pattern_formatter.h
deleted file mode 100644
index 4240fe31c..000000000
--- a/include/spdlog/pattern_formatter.h
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <chrono>
-#include <ctime>
-#include <memory>
-#include <spdlog/common.h>
-#include <spdlog/details/log_msg.h>
-#include <spdlog/details/os.h>
-#include <spdlog/formatter.h>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-namespace spdlog
-{
-namespace details
-{
-
-    // padding information.
-    struct padding_info
-    {
-        enum class pad_side
-        {
-            left,
-            right,
-            center
-        };
-
-        padding_info() = default;
-        padding_info(size_t width, padding_info::pad_side side, bool truncate) :
-            width_(width), side_(side), truncate_(truncate), enabled_(true)
-        {
-        }
-
-        bool     enabled() const { return enabled_; }
-        size_t   width_    = 0;
-        pad_side side_     = pad_side::left;
-        bool     truncate_ = false;
-        bool     enabled_  = false;
-    };
-
-    class SPDLOG_API 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, memory_buf_t &dest) = 0;
-
-    protected:
-        padding_info padinfo_;
-    };
-
-} // namespace details
-
-class SPDLOG_API custom_flag_formatter : public details::flag_formatter
-{
-public:
-    virtual std::unique_ptr<custom_flag_formatter> clone() const = 0;
-
-    void set_padding_info(const details::padding_info &padding) { flag_formatter::padinfo_ = padding; }
-};
-
-class SPDLOG_API pattern_formatter final : public formatter
-{
-public:
-    using custom_flags = std::unordered_map<char, std::unique_ptr<custom_flag_formatter>>;
-
-    explicit pattern_formatter(
-        std::string       pattern,
-        pattern_time_type time_type         = pattern_time_type::local,
-        std::string       eol               = spdlog::details::os::default_eol,
-        custom_flags      custom_user_flags = custom_flags());
-
-    // use default 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_formatter(const pattern_formatter &other)            = delete;
-    pattern_formatter &operator=(const pattern_formatter &other) = delete;
-
-    std::unique_ptr<formatter> clone() const override;
-    void                       format(const details::log_msg &msg, memory_buf_t &dest) override;
-
-    template <typename T, typename... Args>
-    pattern_formatter &add_flag(char flag, Args &&...args)
-    {
-        custom_handlers_[flag] = details::make_unique<T>(std::forward<Args>(args)...);
-        return *this;
-    }
-    void set_pattern(std::string pattern);
-    void need_localtime(bool need = true);
-
-private:
-    std::string                                           pattern_;
-    std::string                                           eol_;
-    pattern_time_type                                     pattern_time_type_;
-    bool                                                  need_localtime_;
-    std::tm                                               cached_tm_;
-    std::chrono::seconds                                  last_log_secs_;
-    std::vector<std::unique_ptr<details::flag_formatter>> formatters_;
-    custom_flags                                          custom_handlers_;
-
-    std::tm get_time_(const details::log_msg &msg);
-    template <typename Padder>
-    void handle_flag_(char flag, details::padding_info padding);
-
-    // Extract given pad spec (e.g. %8X)
-    // Advance the given it pass the end of the padding spec found (if any)
-    // Return padding.
-    static details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end);
-
-    void compile_pattern_(const std::string &pattern);
-};
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "pattern_formatter-inl.h"
-#endif
diff --git a/include/spdlog/sinks/android_sink.h b/include/spdlog/sinks/android_sink.h
deleted file mode 100644
index 016ade4b7..000000000
--- a/include/spdlog/sinks/android_sink.h
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifdef __ANDROID__
-
-#include <android/log.h>
-#include <chrono>
-#include <mutex>
-#include <spdlog/details/fmt_helper.h>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/details/os.h>
-#include <spdlog/details/synchronous_factory.h>
-#include <spdlog/sinks/base_sink.h>
-#include <string>
-#include <thread>
-#include <type_traits>
-
-#if !defined(SPDLOG_ANDROID_RETRIES)
-#define SPDLOG_ANDROID_RETRIES 2
-#endif
-
-namespace spdlog
-{
-namespace sinks
-{
-
-    /*
-     * Android sink
-     * (logging using __android_log_write or __android_log_buf_write depending on the specified BufferID)
-     */
-    template <typename Mutex, int BufferID = log_id::LOG_ID_MAIN>
-    class android_sink final : public base_sink<Mutex>
-    {
-    public:
-        explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false) :
-            tag_(std::move(tag)), use_raw_msg_(use_raw_msg)
-        {
-        }
-
-    protected:
-        void sink_it_(const details::log_msg &msg) override
-        {
-            const android_LogPriority priority = convert_to_android_(msg.level);
-            memory_buf_t              formatted;
-            if(use_raw_msg_)
-            {
-                details::fmt_helper::append_string_view(msg.payload, formatted);
-            }
-            else
-            {
-                base_sink<Mutex>::formatter_->format(msg, formatted);
-            }
-            formatted.push_back('\0');
-            const char *msg_output = formatted.data();
-
-            // See system/core/liblog/logger_write.c for explanation of return value
-            int ret         = android_log(priority, tag_.c_str(), msg_output);
-            int retry_count = 0;
-            while((ret == -11 /*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES))
-            {
-                details::os::sleep_for_millis(5);
-                ret = android_log(priority, tag_.c_str(), msg_output);
-                retry_count++;
-            }
-
-            if(ret < 0)
-            {
-                throw_spdlog_ex("logging to Android failed", ret);
-            }
-        }
-
-        void flush_() override {}
-
-    private:
-        // There might be liblog versions used, that do not support __android_log_buf_write. So we only compile and link
-        // against
-        // __android_log_buf_write, if user explicitely provides a non-default log buffer. Otherwise, when using the
-        // default log buffer, always log via __android_log_write.
-        template <int ID = BufferID>
-        typename std::enable_if<ID == static_cast<int>(log_id::LOG_ID_MAIN), int>::type
-        android_log(int prio, const char *tag, const char *text)
-        {
-            return __android_log_write(prio, tag, text);
-        }
-
-        template <int ID = BufferID>
-        typename std::enable_if<ID != static_cast<int>(log_id::LOG_ID_MAIN), int>::type
-        android_log(int prio, const char *tag, const char *text)
-        {
-            return __android_log_buf_write(ID, prio, tag, text);
-        }
-
-        static android_LogPriority convert_to_android_(spdlog::level::level_enum level)
-        {
-            switch(level)
-            {
-                case spdlog::level::trace:
-                    return ANDROID_LOG_VERBOSE;
-                case spdlog::level::debug:
-                    return ANDROID_LOG_DEBUG;
-                case spdlog::level::info:
-                    return ANDROID_LOG_INFO;
-                case spdlog::level::warn:
-                    return ANDROID_LOG_WARN;
-                case spdlog::level::err:
-                    return ANDROID_LOG_ERROR;
-                case spdlog::level::critical:
-                    return ANDROID_LOG_FATAL;
-                default:
-                    return ANDROID_LOG_DEFAULT;
-            }
-        }
-
-        std::string tag_;
-        bool        use_raw_msg_;
-    };
-
-    using android_sink_mt = android_sink<std::mutex>;
-    using android_sink_st = android_sink<details::null_mutex>;
-
-    template <int BufferId = log_id::LOG_ID_MAIN>
-    using android_sink_buf_mt = android_sink<std::mutex, BufferId>;
-    template <int BufferId = log_id::LOG_ID_MAIN>
-    using android_sink_buf_st = android_sink<details::null_mutex, BufferId>;
-
-} // namespace sinks
-
-// Create and register android syslog logger
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> android_logger_mt(const std::string &logger_name, const std::string &tag = "spdlog")
-{
-    return Factory::template create<sinks::android_sink_mt>(logger_name, tag);
-}
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> android_logger_st(const std::string &logger_name, const std::string &tag = "spdlog")
-{
-    return Factory::template create<sinks::android_sink_st>(logger_name, tag);
-}
-
-} // namespace spdlog
-
-#endif // __ANDROID__
\ No newline at end of file
diff --git a/include/spdlog/sinks/ansicolor_sink-inl.h b/include/spdlog/sinks/ansicolor_sink-inl.h
deleted file mode 100644
index d554f6bcc..000000000
--- a/include/spdlog/sinks/ansicolor_sink-inl.h
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/sinks/ansicolor_sink.h>
-#endif
-
-#include <spdlog/details/os.h>
-#include <spdlog/pattern_formatter.h>
-
-namespace spdlog
-{
-namespace sinks
-{
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE ansicolor_sink<ConsoleMutex>::ansicolor_sink(FILE *target_file, color_mode mode) :
-        target_file_(target_file),
-        mutex_(ConsoleMutex::mutex()),
-        formatter_(details::make_unique<spdlog::pattern_formatter>())
-
-    {
-        set_color_mode(mode);
-        colors_[level::trace]    = to_string_(white);
-        colors_[level::debug]    = to_string_(cyan);
-        colors_[level::info]     = to_string_(green);
-        colors_[level::warn]     = to_string_(yellow_bold);
-        colors_[level::err]      = to_string_(red_bold);
-        colors_[level::critical] = to_string_(bold_on_red);
-        colors_[level::off]      = to_string_(reset);
-    }
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color(level::level_enum color_level, string_view_t color)
-    {
-        std::lock_guard<mutex_t> lock(mutex_);
-        colors_[static_cast<size_t>(color_level)] = to_string_(color);
-    }
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
-    {
-        // Wrap the originally formatted message in color codes.
-        // If color is not supported in the terminal, log as is instead.
-        std::lock_guard<mutex_t> lock(mutex_);
-        msg.color_range_start = 0;
-        msg.color_range_end   = 0;
-        memory_buf_t formatted;
-        formatter_->format(msg, formatted);
-        if(should_do_colors_ && msg.color_range_end > msg.color_range_start)
-        {
-            // before color range
-            print_range_(formatted, 0, msg.color_range_start);
-            // in color range
-            print_ccode_(colors_[static_cast<size_t>(msg.level)]);
-            print_range_(formatted, msg.color_range_start, msg.color_range_end);
-            print_ccode_(reset);
-            // after color range
-            print_range_(formatted, msg.color_range_end, formatted.size());
-        }
-        else // no color
-        {
-            print_range_(formatted, 0, formatted.size());
-        }
-        fflush(target_file_);
-    }
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::flush()
-    {
-        std::lock_guard<mutex_t> lock(mutex_);
-        fflush(target_file_);
-    }
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_pattern(const std::string &pattern)
-    {
-        std::lock_guard<mutex_t> lock(mutex_);
-        formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
-    }
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
-    {
-        std::lock_guard<mutex_t> lock(mutex_);
-        formatter_ = std::move(sink_formatter);
-    }
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE bool ansicolor_sink<ConsoleMutex>::should_color()
-    {
-        return should_do_colors_;
-    }
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
-    {
-        switch(mode)
-        {
-            case color_mode::always:
-                should_do_colors_ = true;
-                return;
-            case color_mode::automatic:
-                should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal();
-                return;
-            case color_mode::never:
-                should_do_colors_ = false;
-                return;
-            default:
-                should_do_colors_ = false;
-        }
-    }
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_ccode_(const string_view_t &color_code)
-    {
-        fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_);
-    }
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE void
-    ansicolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
-    {
-        fwrite(formatted.data() + start, sizeof(char), end - start, target_file_);
-    }
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE std::string ansicolor_sink<ConsoleMutex>::to_string_(const string_view_t &sv)
-    {
-        return std::string(sv.data(), sv.size());
-    }
-
-    // ansicolor_stdout_sink
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE ansicolor_stdout_sink<ConsoleMutex>::ansicolor_stdout_sink(color_mode mode) :
-        ansicolor_sink<ConsoleMutex>(stdout, mode)
-    {
-    }
-
-    // ansicolor_stderr_sink
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE ansicolor_stderr_sink<ConsoleMutex>::ansicolor_stderr_sink(color_mode mode) :
-        ansicolor_sink<ConsoleMutex>(stderr, mode)
-    {
-    }
-
-} // namespace sinks
-} // namespace spdlog
diff --git a/include/spdlog/sinks/ansicolor_sink.h b/include/spdlog/sinks/ansicolor_sink.h
deleted file mode 100644
index bd1973a11..000000000
--- a/include/spdlog/sinks/ansicolor_sink.h
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <array>
-#include <memory>
-#include <mutex>
-#include <spdlog/details/console_globals.h>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/sinks/sink.h>
-#include <string>
-
-namespace spdlog
-{
-namespace sinks
-{
-
-    /**
-     * This sink prefixes the output with an ANSI escape sequence color code
-     * depending on the severity
-     * of the message.
-     * If no color terminal detected, omit the escape codes.
-     */
-
-    template <typename ConsoleMutex>
-    class ansicolor_sink : public sink
-    {
-    public:
-        using mutex_t = typename ConsoleMutex::mutex_t;
-        ansicolor_sink(FILE *target_file, color_mode mode);
-        ~ansicolor_sink() override = default;
-
-        ansicolor_sink(const ansicolor_sink &other) = delete;
-        ansicolor_sink(ansicolor_sink &&other)      = delete;
-
-        ansicolor_sink &operator=(const ansicolor_sink &other) = delete;
-        ansicolor_sink &operator=(ansicolor_sink &&other)      = delete;
-
-        void set_color(level::level_enum color_level, string_view_t color);
-        void set_color_mode(color_mode mode);
-        bool should_color();
-
-        void log(const details::log_msg &msg) override;
-        void flush() override;
-        void set_pattern(const std::string &pattern) final;
-        void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
-
-        // Formatting codes
-        const string_view_t reset      = "\033[m";
-        const string_view_t bold       = "\033[1m";
-        const string_view_t dark       = "\033[2m";
-        const string_view_t underline  = "\033[4m";
-        const string_view_t blink      = "\033[5m";
-        const string_view_t reverse    = "\033[7m";
-        const string_view_t concealed  = "\033[8m";
-        const string_view_t clear_line = "\033[K";
-
-        // Foreground colors
-        const string_view_t black   = "\033[30m";
-        const string_view_t red     = "\033[31m";
-        const string_view_t green   = "\033[32m";
-        const string_view_t yellow  = "\033[33m";
-        const string_view_t blue    = "\033[34m";
-        const string_view_t magenta = "\033[35m";
-        const string_view_t cyan    = "\033[36m";
-        const string_view_t white   = "\033[37m";
-
-        /// Background colors
-        const string_view_t on_black   = "\033[40m";
-        const string_view_t on_red     = "\033[41m";
-        const string_view_t on_green   = "\033[42m";
-        const string_view_t on_yellow  = "\033[43m";
-        const string_view_t on_blue    = "\033[44m";
-        const string_view_t on_magenta = "\033[45m";
-        const string_view_t on_cyan    = "\033[46m";
-        const string_view_t on_white   = "\033[47m";
-
-        /// Bold colors
-        const string_view_t yellow_bold = "\033[33m\033[1m";
-        const string_view_t red_bold    = "\033[31m\033[1m";
-        const string_view_t bold_on_red = "\033[1m\033[41m";
-
-    private:
-        FILE                                    *target_file_;
-        mutex_t                                 &mutex_;
-        bool                                     should_do_colors_;
-        std::unique_ptr<spdlog::formatter>       formatter_;
-        std::array<std::string, level::n_levels> colors_;
-        void                                     print_ccode_(const string_view_t &color_code);
-        void                                     print_range_(const memory_buf_t &formatted, size_t start, size_t end);
-        static std::string                       to_string_(const string_view_t &sv);
-    };
-
-    template <typename ConsoleMutex>
-    class ansicolor_stdout_sink : public ansicolor_sink<ConsoleMutex>
-    {
-    public:
-        explicit ansicolor_stdout_sink(color_mode mode = color_mode::automatic);
-    };
-
-    template <typename ConsoleMutex>
-    class ansicolor_stderr_sink : public ansicolor_sink<ConsoleMutex>
-    {
-    public:
-        explicit ansicolor_stderr_sink(color_mode mode = color_mode::automatic);
-    };
-
-    using ansicolor_stdout_sink_mt = ansicolor_stdout_sink<details::console_mutex>;
-    using ansicolor_stdout_sink_st = ansicolor_stdout_sink<details::console_nullmutex>;
-
-    using ansicolor_stderr_sink_mt = ansicolor_stderr_sink<details::console_mutex>;
-    using ansicolor_stderr_sink_st = ansicolor_stderr_sink<details::console_nullmutex>;
-
-} // namespace sinks
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "ansicolor_sink-inl.h"
-#endif
diff --git a/include/spdlog/sinks/base_sink-inl.h b/include/spdlog/sinks/base_sink-inl.h
deleted file mode 100644
index 2b477e56f..000000000
--- a/include/spdlog/sinks/base_sink-inl.h
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/sinks/base_sink.h>
-#endif
-
-#include <memory>
-#include <spdlog/common.h>
-#include <spdlog/pattern_formatter.h>
-
-template <typename Mutex>
-SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::base_sink() :
-    formatter_{details::make_unique<spdlog::pattern_formatter>()}
-{
-}
-
-template <typename Mutex>
-SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::base_sink(std::unique_ptr<spdlog::formatter> formatter) :
-    formatter_{std::move(formatter)}
-{
-}
-
-template <typename Mutex>
-void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::log(const details::log_msg &msg)
-{
-    std::lock_guard<Mutex> lock(mutex_);
-    sink_it_(msg);
-}
-
-template <typename Mutex>
-void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::flush()
-{
-    std::lock_guard<Mutex> lock(mutex_);
-    flush_();
-}
-
-template <typename Mutex>
-void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_pattern(const std::string &pattern)
-{
-    std::lock_guard<Mutex> lock(mutex_);
-    set_pattern_(pattern);
-}
-
-template <typename Mutex>
-void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
-{
-    std::lock_guard<Mutex> lock(mutex_);
-    set_formatter_(std::move(sink_formatter));
-}
-
-template <typename Mutex>
-void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_pattern_(const std::string &pattern)
-{
-    set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
-}
-
-template <typename Mutex>
-void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter)
-{
-    formatter_ = std::move(sink_formatter);
-}
diff --git a/include/spdlog/sinks/base_sink.h b/include/spdlog/sinks/base_sink.h
deleted file mode 100644
index 5eea770a3..000000000
--- a/include/spdlog/sinks/base_sink.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-//
-// base sink templated over a mutex (either dummy or real)
-// concrete implementation should override the sink_it_() and flush_()  methods.
-// locking is taken care of in this class - no locking needed by the
-// implementers..
-//
-
-#include <spdlog/common.h>
-#include <spdlog/details/log_msg.h>
-#include <spdlog/sinks/sink.h>
-
-namespace spdlog
-{
-namespace sinks
-{
-    template <typename Mutex>
-    class SPDLOG_API base_sink : public sink
-    {
-    public:
-        base_sink();
-        explicit base_sink(std::unique_ptr<spdlog::formatter> formatter);
-        ~base_sink() override = default;
-
-        base_sink(const base_sink &) = delete;
-        base_sink(base_sink &&)      = delete;
-
-        base_sink &operator=(const base_sink &) = delete;
-        base_sink &operator=(base_sink &&)      = delete;
-
-        void log(const details::log_msg &msg) final;
-        void flush() final;
-        void set_pattern(const std::string &pattern) final;
-        void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) final;
-
-    protected:
-        // sink formatter
-        std::unique_ptr<spdlog::formatter> formatter_;
-        Mutex                              mutex_;
-
-        virtual void sink_it_(const details::log_msg &msg) = 0;
-        virtual void flush_()                              = 0;
-        virtual void set_pattern_(const std::string &pattern);
-        virtual void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter);
-    };
-} // namespace sinks
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "base_sink-inl.h"
-#endif
diff --git a/include/spdlog/sinks/basic_file_sink-inl.h b/include/spdlog/sinks/basic_file_sink-inl.h
deleted file mode 100644
index 03309eaea..000000000
--- a/include/spdlog/sinks/basic_file_sink-inl.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/sinks/basic_file_sink.h>
-#endif
-
-#include <spdlog/common.h>
-#include <spdlog/details/os.h>
-
-namespace spdlog
-{
-namespace sinks
-{
-
-    template <typename Mutex>
-    SPDLOG_INLINE basic_file_sink<Mutex>::basic_file_sink(
-        const filename_t          &filename,
-        bool                       truncate,
-        const file_event_handlers &event_handlers) :
-        file_helper_{event_handlers}
-    {
-        file_helper_.open(filename, truncate);
-    }
-
-    template <typename Mutex>
-    SPDLOG_INLINE const filename_t &basic_file_sink<Mutex>::filename() const
-    {
-        return file_helper_.filename();
-    }
-
-    template <typename Mutex>
-    SPDLOG_INLINE void basic_file_sink<Mutex>::sink_it_(const details::log_msg &msg)
-    {
-        memory_buf_t formatted;
-        base_sink<Mutex>::formatter_->format(msg, formatted);
-        file_helper_.write(formatted);
-    }
-
-    template <typename Mutex>
-    SPDLOG_INLINE void basic_file_sink<Mutex>::flush_()
-    {
-        file_helper_.flush();
-    }
-
-} // namespace sinks
-} // namespace spdlog
diff --git a/include/spdlog/sinks/basic_file_sink.h b/include/spdlog/sinks/basic_file_sink.h
deleted file mode 100644
index 84ba75e44..000000000
--- a/include/spdlog/sinks/basic_file_sink.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <mutex>
-#include <spdlog/details/file_helper.h>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/details/synchronous_factory.h>
-#include <spdlog/sinks/base_sink.h>
-#include <string>
-
-namespace spdlog
-{
-namespace sinks
-{
-    /*
-     * Trivial file sink with single file as target
-     */
-    template <typename Mutex>
-    class basic_file_sink final : public base_sink<Mutex>
-    {
-    public:
-        explicit basic_file_sink(
-            const filename_t          &filename,
-            bool                       truncate       = false,
-            const file_event_handlers &event_handlers = {});
-        const filename_t &filename() const;
-
-    protected:
-        void sink_it_(const details::log_msg &msg) override;
-        void flush_() override;
-
-    private:
-        details::file_helper file_helper_;
-    };
-
-    using basic_file_sink_mt = basic_file_sink<std::mutex>;
-    using basic_file_sink_st = basic_file_sink<details::null_mutex>;
-
-} // namespace sinks
-
-//
-// factory functions
-//
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> basic_logger_mt(
-    const std::string         &logger_name,
-    const filename_t          &filename,
-    bool                       truncate       = false,
-    const file_event_handlers &event_handlers = {})
-{
-    return Factory::template create<sinks::basic_file_sink_mt>(logger_name, filename, truncate, event_handlers);
-}
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> basic_logger_st(
-    const std::string         &logger_name,
-    const filename_t          &filename,
-    bool                       truncate       = false,
-    const file_event_handlers &event_handlers = {})
-{
-    return Factory::template create<sinks::basic_file_sink_st>(logger_name, filename, truncate, event_handlers);
-}
-
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "basic_file_sink-inl.h"
-#endif
diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h
deleted file mode 100644
index b2f201a30..000000000
--- a/include/spdlog/sinks/daily_file_sink.h
+++ /dev/null
@@ -1,331 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <chrono>
-#include <cstdio>
-#include <ctime>
-#include <mutex>
-#include <spdlog/common.h>
-#include <spdlog/details/circular_q.h>
-#include <spdlog/details/file_helper.h>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/details/os.h>
-#include <spdlog/details/synchronous_factory.h>
-#include <spdlog/fmt/chrono.h>
-#include <spdlog/fmt/fmt.h>
-#include <spdlog/sinks/base_sink.h>
-#include <string>
-
-namespace spdlog
-{
-namespace sinks
-{
-
-    /*
-     * Generator of daily log file names in format basename.YYYY-MM-DD.ext
-     */
-    struct daily_filename_calculator
-    {
-        // Create filename for the form basename.YYYY-MM-DD
-        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_extension(filename);
-            return fmt_lib::format(
-                SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}")),
-                basename,
-                now_tm.tm_year + 1900,
-                now_tm.tm_mon + 1,
-                now_tm.tm_mday,
-                ext);
-        }
-    };
-
-    /*
-     * Generator of daily log file names with strftime format.
-     * Usages:
-     *    auto sink =  std::make_shared<spdlog::sinks::daily_file_format_sink_mt>("myapp-%Y-%m-%d:%H:%M:%S.log", hour,
-     * minute);" auto logger = spdlog::daily_logger_format_mt("loggername, "myapp-%Y-%m-%d:%X.log", hour,  minute)"
-     *
-     */
-    struct daily_filename_format_calculator
-    {
-        static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
-        {
-#ifdef SPDLOG_USE_STD_FORMAT
-            // adapted from fmtlib: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/chrono.h#L522-L546
-
-            filename_t tm_format;
-            tm_format.append(filename);
-            // By appending an extra space we can distinguish an empty result that
-            // indicates insufficient buffer size from a guaranteed non-empty result
-            // https://github.com/fmtlib/fmt/issues/2238
-            tm_format.push_back(' ');
-
-            const size_t MIN_SIZE = 10;
-            filename_t   buf;
-            buf.resize(MIN_SIZE);
-            for(;;)
-            {
-                size_t count = strftime(buf.data(), buf.size(), tm_format.c_str(), &now_tm);
-                if(count != 0)
-                {
-                    // Remove the extra space.
-                    buf.resize(count - 1);
-                    break;
-                }
-                buf.resize(buf.size() * 2);
-            }
-
-            return buf;
-#else
-            // generate fmt datetime format string, e.g. {:%Y-%m-%d}.
-            filename_t fmt_filename = fmt::format(SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{{:{}}}")), filename);
-
-            // MSVC doesn't allow fmt::runtime(..) with wchar, with fmtlib versions < 9.1.x
-#if defined(_MSC_VER) && defined(SPDLOG_WCHAR_FILENAMES) && FMT_VERSION < 90101
-            return fmt::format(fmt_filename, now_tm);
-#else
-            return fmt::format(SPDLOG_FMT_RUNTIME(fmt_filename), now_tm);
-#endif
-
-#endif
-        }
-
-    private:
-#if defined            __GNUC__
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"
-#endif
-
-        static size_t strftime(char *str, size_t count, const char *format, const std::tm *time)
-        {
-            return std::strftime(str, count, format, time);
-        }
-
-        static size_t strftime(wchar_t *str, size_t count, const wchar_t *format, const std::tm *time)
-        {
-            return std::wcsftime(str, count, format, time);
-        }
-
-#if defined(__GNUC__)
-#pragma GCC diagnostic pop
-#endif
-    };
-
-    /*
-     * Rotating file sink based on date.
-     * If truncate != false , the created file will be truncated.
-     * If max_files > 0, retain only the last max_files and delete previous.
-     */
-    template <typename Mutex, typename FileNameCalc = daily_filename_calculator>
-    class daily_file_sink final : public base_sink<Mutex>
-    {
-    public:
-        // create daily file sink which rotates on given time
-        daily_file_sink(
-            filename_t                 base_filename,
-            int                        rotation_hour,
-            int                        rotation_minute,
-            bool                       truncate       = false,
-            uint16_t                   max_files      = 0,
-            const file_event_handlers &event_handlers = {}) :
-            base_filename_(std::move(base_filename)),
-            rotation_h_(rotation_hour),
-            rotation_m_(rotation_minute),
-            file_helper_{event_handlers},
-            truncate_(truncate),
-            max_files_(max_files),
-            filenames_q_()
-        {
-            if(rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
-            {
-                throw_spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
-            }
-
-            auto now      = log_clock::now();
-            auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
-            file_helper_.open(filename, truncate_);
-            rotation_tp_ = next_rotation_tp_();
-
-            if(max_files_ > 0)
-            {
-                init_filenames_q_();
-            }
-        }
-
-        filename_t filename()
-        {
-            std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
-            return file_helper_.filename();
-        }
-
-    protected:
-        void sink_it_(const details::log_msg &msg) override
-        {
-            auto time          = msg.time;
-            bool should_rotate = time >= rotation_tp_;
-            if(should_rotate)
-            {
-                auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
-                file_helper_.open(filename, truncate_);
-                rotation_tp_ = next_rotation_tp_();
-            }
-            memory_buf_t formatted;
-            base_sink<Mutex>::formatter_->format(msg, formatted);
-            file_helper_.write(formatted);
-
-            // Do the cleaning only at the end because it might throw on failure.
-            if(should_rotate && max_files_ > 0)
-            {
-                delete_old_();
-            }
-        }
-
-        void flush_() override { file_helper_.flush(); }
-
-    private:
-        void init_filenames_q_()
-        {
-            using details::os::path_exists;
-
-            filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
-            std::vector<filename_t> filenames;
-            auto                    now = log_clock::now();
-            while(filenames.size() < max_files_)
-            {
-                auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
-                if(!path_exists(filename))
-                {
-                    break;
-                }
-                filenames.emplace_back(filename);
-                now -= std::chrono::hours(24);
-            }
-            for(auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
-            {
-                filenames_q_.push_back(std::move(*iter));
-            }
-        }
-
-        tm now_tm(log_clock::time_point tp)
-        {
-            time_t tnow = log_clock::to_time_t(tp);
-            return spdlog::details::os::localtime(tnow);
-        }
-
-        log_clock::time_point next_rotation_tp_()
-        {
-            auto now           = log_clock::now();
-            tm   date          = now_tm(now);
-            date.tm_hour       = rotation_h_;
-            date.tm_min        = rotation_m_;
-            date.tm_sec        = 0;
-            auto rotation_time = log_clock::from_time_t(std::mktime(&date));
-            if(rotation_time > now)
-            {
-                return rotation_time;
-            }
-            return {rotation_time + std::chrono::hours(24)};
-        }
-
-        // Delete the file N rotations ago.
-        // Throw spdlog_ex on failure to delete the old file.
-        void delete_old_()
-        {
-            using details::os::filename_to_str;
-            using details::os::remove_if_exists;
-
-            filename_t current_file = file_helper_.filename();
-            if(filenames_q_.full())
-            {
-                auto old_filename = std::move(filenames_q_.front());
-                filenames_q_.pop_front();
-                bool ok = remove_if_exists(old_filename) == 0;
-                if(!ok)
-                {
-                    filenames_q_.push_back(std::move(current_file));
-                    throw_spdlog_ex("Failed removing daily file " + filename_to_str(old_filename), errno);
-                }
-            }
-            filenames_q_.push_back(std::move(current_file));
-        }
-
-        filename_t                      base_filename_;
-        int                             rotation_h_;
-        int                             rotation_m_;
-        log_clock::time_point           rotation_tp_;
-        details::file_helper            file_helper_;
-        bool                            truncate_;
-        uint16_t                        max_files_;
-        details::circular_q<filename_t> filenames_q_;
-    };
-
-    using daily_file_sink_mt        = daily_file_sink<std::mutex>;
-    using daily_file_sink_st        = daily_file_sink<details::null_mutex>;
-    using daily_file_format_sink_mt = daily_file_sink<std::mutex, daily_filename_format_calculator>;
-    using daily_file_format_sink_st = daily_file_sink<details::null_mutex, daily_filename_format_calculator>;
-
-} // namespace sinks
-
-//
-// factory functions
-//
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> daily_logger_mt(
-    const std::string         &logger_name,
-    const filename_t          &filename,
-    int                        hour           = 0,
-    int                        minute         = 0,
-    bool                       truncate       = false,
-    uint16_t                   max_files      = 0,
-    const file_event_handlers &event_handlers = {})
-{
-    return Factory::template create<sinks::daily_file_sink_mt>(
-        logger_name, filename, hour, minute, truncate, max_files, event_handlers);
-}
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> daily_logger_format_mt(
-    const std::string         &logger_name,
-    const filename_t          &filename,
-    int                        hour           = 0,
-    int                        minute         = 0,
-    bool                       truncate       = false,
-    uint16_t                   max_files      = 0,
-    const file_event_handlers &event_handlers = {})
-{
-    return Factory::template create<sinks::daily_file_format_sink_mt>(
-        logger_name, filename, hour, minute, truncate, max_files, event_handlers);
-}
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> daily_logger_st(
-    const std::string         &logger_name,
-    const filename_t          &filename,
-    int                        hour           = 0,
-    int                        minute         = 0,
-    bool                       truncate       = false,
-    uint16_t                   max_files      = 0,
-    const file_event_handlers &event_handlers = {})
-{
-    return Factory::template create<sinks::daily_file_sink_st>(
-        logger_name, filename, hour, minute, truncate, max_files, event_handlers);
-}
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> daily_logger_format_st(
-    const std::string         &logger_name,
-    const filename_t          &filename,
-    int                        hour           = 0,
-    int                        minute         = 0,
-    bool                       truncate       = false,
-    uint16_t                   max_files      = 0,
-    const file_event_handlers &event_handlers = {})
-{
-    return Factory::template create<sinks::daily_file_format_sink_st>(
-        logger_name, filename, hour, minute, truncate, max_files, event_handlers);
-}
-} // namespace spdlog
diff --git a/include/spdlog/sinks/dist_sink.h b/include/spdlog/sinks/dist_sink.h
deleted file mode 100644
index 01ff83e7f..000000000
--- a/include/spdlog/sinks/dist_sink.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include "base_sink.h"
-
-#include <algorithm>
-#include <memory>
-#include <mutex>
-#include <spdlog/details/log_msg.h>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/pattern_formatter.h>
-#include <vector>
-
-// Distribution sink (mux). Stores a vector of sinks which get called when log
-// is called
-
-namespace spdlog
-{
-namespace sinks
-{
-
-    template <typename Mutex>
-    class dist_sink : public base_sink<Mutex>
-    {
-    public:
-        dist_sink() = default;
-        explicit dist_sink(std::vector<std::shared_ptr<sink>> sinks) : sinks_(sinks) {}
-
-        dist_sink(const dist_sink &)            = delete;
-        dist_sink &operator=(const dist_sink &) = delete;
-
-        void add_sink(std::shared_ptr<sink> sink)
-        {
-            std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
-            sinks_.push_back(sink);
-        }
-
-        void remove_sink(std::shared_ptr<sink> sink)
-        {
-            std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
-            sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
-        }
-
-        void set_sinks(std::vector<std::shared_ptr<sink>> sinks)
-        {
-            std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
-            sinks_ = std::move(sinks);
-        }
-
-        std::vector<std::shared_ptr<sink>> &sinks() { return sinks_; }
-
-    protected:
-        void sink_it_(const details::log_msg &msg) override
-        {
-            for(auto &sub_sink : sinks_)
-            {
-                if(sub_sink->should_log(msg.level))
-                {
-                    sub_sink->log(msg);
-                }
-            }
-        }
-
-        void flush_() override
-        {
-            for(auto &sub_sink : sinks_)
-            {
-                sub_sink->flush();
-            }
-        }
-
-        void set_pattern_(const std::string &pattern) override
-        {
-            set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
-        }
-
-        void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter) override
-        {
-            base_sink<Mutex>::formatter_ = std::move(sink_formatter);
-            for(auto &sub_sink : sinks_)
-            {
-                sub_sink->set_formatter(base_sink<Mutex>::formatter_->clone());
-            }
-        }
-        std::vector<std::shared_ptr<sink>> sinks_;
-    };
-
-    using dist_sink_mt = dist_sink<std::mutex>;
-    using dist_sink_st = dist_sink<details::null_mutex>;
-
-} // namespace sinks
-} // namespace spdlog
diff --git a/include/spdlog/sinks/dup_filter_sink.h b/include/spdlog/sinks/dup_filter_sink.h
deleted file mode 100644
index 858c8eb90..000000000
--- a/include/spdlog/sinks/dup_filter_sink.h
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include "dist_sink.h"
-
-#include <chrono>
-#include <cstdio>
-#include <mutex>
-#include <spdlog/details/log_msg.h>
-#include <spdlog/details/null_mutex.h>
-#include <string>
-
-// Duplicate message removal sink.
-// Skip the message if previous one is identical and less than "max_skip_duration" have passed
-//
-// Example:
-//
-//     #include <spdlog/sinks/dup_filter_sink.h>
-//
-//     int main() {
-//         auto dup_filter = std::make_shared<dup_filter_sink_st>(std::chrono::seconds(5));
-//         dup_filter->add_sink(std::make_shared<stdout_color_sink_mt>());
-//         spdlog::logger l("logger", dup_filter);
-//         l.info("Hello");
-//         l.info("Hello");
-//         l.info("Hello");
-//         l.info("Different Hello");
-//     }
-//
-// Will produce:
-//       [2019-06-25 17:50:56.511] [logger] [info] Hello
-//       [2019-06-25 17:50:56.512] [logger] [info] Skipped 3 duplicate messages..
-//       [2019-06-25 17:50:56.512] [logger] [info] Different Hello
-
-namespace spdlog
-{
-namespace sinks
-{
-    template <typename Mutex>
-    class dup_filter_sink : public dist_sink<Mutex>
-    {
-    public:
-        template <class Rep, class Period>
-        explicit dup_filter_sink(std::chrono::duration<Rep, Period> max_skip_duration) :
-            max_skip_duration_{max_skip_duration}
-        {
-        }
-
-    protected:
-        std::chrono::microseconds max_skip_duration_;
-        log_clock::time_point     last_msg_time_;
-        std::string               last_msg_payload_;
-        size_t                    skip_counter_ = 0;
-
-        void sink_it_(const details::log_msg &msg) override
-        {
-            bool filtered = filter_(msg);
-            if(!filtered)
-            {
-                skip_counter_ += 1;
-                return;
-            }
-
-            // log the "skipped.." message
-            if(skip_counter_ > 0)
-            {
-                char buf[64];
-                auto msg_size = ::snprintf(
-                    buf, sizeof(buf), "Skipped %u duplicate messages..", static_cast<unsigned>(skip_counter_));
-                if(msg_size > 0 && static_cast<size_t>(msg_size) < sizeof(buf))
-                {
-                    details::log_msg skipped_msg{
-                        msg.source, msg.logger_name, level::info, string_view_t{buf, static_cast<size_t>(msg_size)}};
-                    dist_sink<Mutex>::sink_it_(skipped_msg);
-                }
-            }
-
-            // log current message
-            dist_sink<Mutex>::sink_it_(msg);
-            last_msg_time_ = msg.time;
-            skip_counter_  = 0;
-            last_msg_payload_.assign(msg.payload.data(), msg.payload.data() + msg.payload.size());
-        }
-
-        // return whether the log msg should be displayed (true) or skipped (false)
-        bool filter_(const details::log_msg &msg)
-        {
-            auto filter_duration = msg.time - last_msg_time_;
-            return (filter_duration > max_skip_duration_) || (msg.payload != last_msg_payload_);
-        }
-    };
-
-    using dup_filter_sink_mt = dup_filter_sink<std::mutex>;
-    using dup_filter_sink_st = dup_filter_sink<details::null_mutex>;
-
-} // namespace sinks
-} // namespace spdlog
diff --git a/include/spdlog/sinks/hourly_file_sink.h b/include/spdlog/sinks/hourly_file_sink.h
deleted file mode 100644
index b8afa3483..000000000
--- a/include/spdlog/sinks/hourly_file_sink.h
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <chrono>
-#include <cstdio>
-#include <ctime>
-#include <mutex>
-#include <spdlog/common.h>
-#include <spdlog/details/circular_q.h>
-#include <spdlog/details/file_helper.h>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/details/os.h>
-#include <spdlog/details/synchronous_factory.h>
-#include <spdlog/fmt/fmt.h>
-#include <spdlog/sinks/base_sink.h>
-#include <string>
-
-namespace spdlog
-{
-namespace sinks
-{
-
-    /*
-     * Generator of Hourly log file names in format basename.YYYY-MM-DD-HH.ext
-     */
-    struct hourly_filename_calculator
-    {
-        // Create filename for the form basename.YYYY-MM-DD-H
-        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_extension(filename);
-            return fmt_lib::format(
-                SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}{}"),
-                basename,
-                now_tm.tm_year + 1900,
-                now_tm.tm_mon + 1,
-                now_tm.tm_mday,
-                now_tm.tm_hour,
-                ext);
-        }
-    };
-
-    /*
-     * Rotating file sink based on time.
-     * If truncate != false , the created file will be truncated.
-     * If max_files > 0, retain only the last max_files and delete previous.
-     */
-    template <typename Mutex, typename FileNameCalc = hourly_filename_calculator>
-    class hourly_file_sink final : public base_sink<Mutex>
-    {
-    public:
-        // create hourly file sink which rotates on given time
-        hourly_file_sink(
-            filename_t                 base_filename,
-            bool                       truncate       = false,
-            uint16_t                   max_files      = 0,
-            const file_event_handlers &event_handlers = {}) :
-            base_filename_(std::move(base_filename)),
-            file_helper_{event_handlers},
-            truncate_(truncate),
-            max_files_(max_files),
-            filenames_q_()
-        {
-            auto now      = log_clock::now();
-            auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
-            file_helper_.open(filename, truncate_);
-            remove_init_file_ = file_helper_.size() == 0;
-            rotation_tp_      = next_rotation_tp_();
-
-            if(max_files_ > 0)
-            {
-                init_filenames_q_();
-            }
-        }
-
-        filename_t filename()
-        {
-            std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
-            return file_helper_.filename();
-        }
-
-    protected:
-        void sink_it_(const details::log_msg &msg) override
-        {
-            auto time          = msg.time;
-            bool should_rotate = time >= rotation_tp_;
-            if(should_rotate)
-            {
-                if(remove_init_file_)
-                {
-                    file_helper_.close();
-                    details::os::remove(file_helper_.filename());
-                }
-                auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
-                file_helper_.open(filename, truncate_);
-                rotation_tp_ = next_rotation_tp_();
-            }
-            remove_init_file_ = false;
-            memory_buf_t formatted;
-            base_sink<Mutex>::formatter_->format(msg, formatted);
-            file_helper_.write(formatted);
-
-            // Do the cleaning only at the end because it might throw on failure.
-            if(should_rotate && max_files_ > 0)
-            {
-                delete_old_();
-            }
-        }
-
-        void flush_() override { file_helper_.flush(); }
-
-    private:
-        void init_filenames_q_()
-        {
-            using details::os::path_exists;
-
-            filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
-            std::vector<filename_t> filenames;
-            auto                    now = log_clock::now();
-            while(filenames.size() < max_files_)
-            {
-                auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
-                if(!path_exists(filename))
-                {
-                    break;
-                }
-                filenames.emplace_back(filename);
-                now -= std::chrono::hours(1);
-            }
-            for(auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
-            {
-                filenames_q_.push_back(std::move(*iter));
-            }
-        }
-
-        tm now_tm(log_clock::time_point tp)
-        {
-            time_t tnow = log_clock::to_time_t(tp);
-            return spdlog::details::os::localtime(tnow);
-        }
-
-        log_clock::time_point next_rotation_tp_()
-        {
-            auto now           = log_clock::now();
-            tm   date          = now_tm(now);
-            date.tm_min        = 0;
-            date.tm_sec        = 0;
-            auto rotation_time = log_clock::from_time_t(std::mktime(&date));
-            if(rotation_time > now)
-            {
-                return rotation_time;
-            }
-            return {rotation_time + std::chrono::hours(1)};
-        }
-
-        // Delete the file N rotations ago.
-        // Throw spdlog_ex on failure to delete the old file.
-        void delete_old_()
-        {
-            using details::os::filename_to_str;
-            using details::os::remove_if_exists;
-
-            filename_t current_file = file_helper_.filename();
-            if(filenames_q_.full())
-            {
-                auto old_filename = std::move(filenames_q_.front());
-                filenames_q_.pop_front();
-                bool ok = remove_if_exists(old_filename) == 0;
-                if(!ok)
-                {
-                    filenames_q_.push_back(std::move(current_file));
-                    SPDLOG_THROW(spdlog_ex("Failed removing hourly file " + filename_to_str(old_filename), errno));
-                }
-            }
-            filenames_q_.push_back(std::move(current_file));
-        }
-
-        filename_t                      base_filename_;
-        log_clock::time_point           rotation_tp_;
-        details::file_helper            file_helper_;
-        bool                            truncate_;
-        uint16_t                        max_files_;
-        details::circular_q<filename_t> filenames_q_;
-        bool                            remove_init_file_;
-    };
-
-    using hourly_file_sink_mt = hourly_file_sink<std::mutex>;
-    using hourly_file_sink_st = hourly_file_sink<details::null_mutex>;
-
-} // namespace sinks
-
-//
-// factory functions
-//
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> hourly_logger_mt(
-    const std::string         &logger_name,
-    const filename_t          &filename,
-    bool                       truncate       = false,
-    uint16_t                   max_files      = 0,
-    const file_event_handlers &event_handlers = {})
-{
-    return Factory::template create<sinks::hourly_file_sink_mt>(
-        logger_name, filename, truncate, max_files, event_handlers);
-}
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> hourly_logger_st(
-    const std::string         &logger_name,
-    const filename_t          &filename,
-    bool                       truncate       = false,
-    uint16_t                   max_files      = 0,
-    const file_event_handlers &event_handlers = {})
-{
-    return Factory::template create<sinks::hourly_file_sink_st>(
-        logger_name, filename, truncate, max_files, event_handlers);
-}
-} // namespace spdlog
diff --git a/include/spdlog/sinks/mongo_sink.h b/include/spdlog/sinks/mongo_sink.h
deleted file mode 100644
index 2c48a53b0..000000000
--- a/include/spdlog/sinks/mongo_sink.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-//
-// Custom sink for mongodb
-// Building and using requires mongocxx library.
-// For building mongocxx library check the url below
-// http://mongocxx.org/mongocxx-v3/installation/
-//
-
-#include "spdlog/common.h"
-#include "spdlog/details/log_msg.h"
-#include "spdlog/sinks/base_sink.h"
-
-#include <bsoncxx/builder/stream/document.hpp>
-#include <bsoncxx/types.hpp>
-#include <bsoncxx/view_or_value.hpp>
-#include <mongocxx/client.hpp>
-#include <mongocxx/instance.hpp>
-#include <mongocxx/uri.hpp>
-#include <spdlog/details/synchronous_factory.h>
-
-namespace spdlog
-{
-namespace sinks
-{
-    template <typename Mutex>
-    class mongo_sink : public base_sink<Mutex>
-    {
-    public:
-        mongo_sink(
-            const std::string &db_name,
-            const std::string &collection_name,
-            const std::string &uri = "mongodb://localhost:27017")
-        try : mongo_sink(std::make_shared<mongocxx::instance>(), db_name, collection_name, uri)
-        {
-        }
-        catch(const std::exception &e)
-        {
-            throw_spdlog_ex(fmt_lib::format("Error opening database: {}", e.what()));
-        }
-
-        mongo_sink(
-            std::shared_ptr<mongocxx::instance> instance,
-            const std::string                  &db_name,
-            const std::string                  &collection_name,
-            const std::string                  &uri = "mongodb://localhost:27017") :
-            instance_(std::move(instance)), db_name_(db_name), coll_name_(collection_name)
-        {
-            try
-            {
-                client_ = spdlog::details::make_unique<mongocxx::client>(mongocxx::uri{uri});
-            }
-            catch(const std::exception &e)
-            {
-                throw_spdlog_ex(fmt_lib::format("Error opening database: {}", e.what()));
-            }
-        }
-
-        ~mongo_sink() { flush_(); }
-
-    protected:
-        void sink_it_(const details::log_msg &msg) override
-        {
-            using bsoncxx::builder::stream::document;
-            using bsoncxx::builder::stream::finalize;
-
-            if(client_ != nullptr)
-            {
-                auto doc = document{} << "timestamp" << bsoncxx::types::b_date(msg.time) << "level"
-                                      << level::to_string_view(msg.level).data() << "level_num" << msg.level
-                                      << "message" << std::string(msg.payload.begin(), msg.payload.end())
-                                      << "logger_name" << std::string(msg.logger_name.begin(), msg.logger_name.end())
-                                      << "thread_id" << static_cast<int>(msg.thread_id) << finalize;
-                client_->database(db_name_).collection(coll_name_).insert_one(doc.view());
-            }
-        }
-
-        void flush_() override {}
-
-    private:
-        std::shared_ptr<mongocxx::instance> instance_;
-        std::string                         db_name_;
-        std::string                         coll_name_;
-        std::unique_ptr<mongocxx::client>   client_ = nullptr;
-    };
-
-#include "spdlog/details/null_mutex.h"
-
-#include <mutex>
-    using mongo_sink_mt = mongo_sink<std::mutex>;
-    using mongo_sink_st = mongo_sink<spdlog::details::null_mutex>;
-
-} // namespace sinks
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> mongo_logger_mt(
-    const std::string &logger_name,
-    const std::string &db_name,
-    const std::string &collection_name,
-    const std::string &uri = "mongodb://localhost:27017")
-{
-    return Factory::template create<sinks::mongo_sink_mt>(logger_name, db_name, collection_name, uri);
-}
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> mongo_logger_st(
-    const std::string &logger_name,
-    const std::string &db_name,
-    const std::string &collection_name,
-    const std::string &uri = "mongodb://localhost:27017")
-{
-    return Factory::template create<sinks::mongo_sink_st>(logger_name, db_name, collection_name, uri);
-}
-
-} // namespace spdlog
diff --git a/include/spdlog/sinks/msvc_sink.h b/include/spdlog/sinks/msvc_sink.h
deleted file mode 100644
index f89afb37f..000000000
--- a/include/spdlog/sinks/msvc_sink.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright(c) 2016 Alexander Dalshov & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-
-#if defined(_WIN32)
-
-#include <mutex>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/sinks/base_sink.h>
-#include <string>
-
-// Avoid including windows.h (https://stackoverflow.com/a/30741042)
-extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString);
-extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
-
-namespace spdlog
-{
-namespace sinks
-{
-    /*
-     * MSVC sink (logging using OutputDebugStringA)
-     */
-    template <typename Mutex>
-    class msvc_sink : public base_sink<Mutex>
-    {
-    public:
-        msvc_sink() = default;
-        msvc_sink(bool check_ebugger_present) : check_debbugger_present_{check_ebugger_present} {};
-
-    protected:
-        void sink_it_(const details::log_msg &msg) override
-        {
-            if(check_debbugger_present_ && !IsDebuggerPresent())
-            {
-                return;
-            }
-            memory_buf_t formatted;
-            base_sink<Mutex>::formatter_->format(msg, formatted);
-            formatted.push_back('\0'); // add a null terminator for OutputDebugStringA
-            OutputDebugStringA(formatted.data());
-        }
-
-        void flush_() override {}
-
-        bool check_debbugger_present_ = true;
-    };
-
-    using msvc_sink_mt = msvc_sink<std::mutex>;
-    using msvc_sink_st = msvc_sink<details::null_mutex>;
-
-    using windebug_sink_mt = msvc_sink_mt;
-    using windebug_sink_st = msvc_sink_st;
-
-} // namespace sinks
-} // namespace spdlog
-
-#endif
diff --git a/include/spdlog/sinks/null_sink.h b/include/spdlog/sinks/null_sink.h
deleted file mode 100644
index 3f47bad84..000000000
--- a/include/spdlog/sinks/null_sink.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <mutex>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/details/synchronous_factory.h>
-#include <spdlog/sinks/base_sink.h>
-
-namespace spdlog
-{
-namespace sinks
-{
-
-    template <typename Mutex>
-    class null_sink : public base_sink<Mutex>
-    {
-    protected:
-        void sink_it_(const details::log_msg &) override {}
-        void flush_() override {}
-    };
-
-    using null_sink_mt = null_sink<details::null_mutex>;
-    using null_sink_st = null_sink<details::null_mutex>;
-
-} // namespace sinks
-
-template <typename Factory = spdlog::synchronous_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 = spdlog::synchronous_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/include/spdlog/sinks/ostream_sink.h b/include/spdlog/sinks/ostream_sink.h
deleted file mode 100644
index 499b6d276..000000000
--- a/include/spdlog/sinks/ostream_sink.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <mutex>
-#include <ostream>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/sinks/base_sink.h>
-
-namespace spdlog
-{
-namespace sinks
-{
-    template <typename Mutex>
-    class ostream_sink final : public base_sink<Mutex>
-    {
-    public:
-        explicit ostream_sink(std::ostream &os, bool force_flush = false) : ostream_(os), force_flush_(force_flush) {}
-        ostream_sink(const ostream_sink &)            = delete;
-        ostream_sink &operator=(const ostream_sink &) = delete;
-
-    protected:
-        void sink_it_(const details::log_msg &msg) override
-        {
-            memory_buf_t formatted;
-            base_sink<Mutex>::formatter_->format(msg, formatted);
-            ostream_.write(formatted.data(), static_cast<std::streamsize>(formatted.size()));
-            if(force_flush_)
-            {
-                ostream_.flush();
-            }
-        }
-
-        void flush_() override { ostream_.flush(); }
-
-        std::ostream &ostream_;
-        bool          force_flush_;
-    };
-
-    using ostream_sink_mt = ostream_sink<std::mutex>;
-    using ostream_sink_st = ostream_sink<details::null_mutex>;
-
-} // namespace sinks
-} // namespace spdlog
diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h
deleted file mode 100644
index 87d70c0d2..000000000
--- a/include/spdlog/sinks/qt_sinks.h
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman, mguludag and spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-//
-// Custom sink for QPlainTextEdit or QTextEdit and its childs(QTextBrowser...
-// etc) Building and using requires Qt library.
-//
-
-#include "spdlog/common.h"
-#include "spdlog/details/log_msg.h"
-#include "spdlog/details/synchronous_factory.h"
-#include "spdlog/sinks/base_sink.h"
-
-#include <QPlainTextEdit>
-#include <QTextEdit>
-
-//
-// qt_sink class
-//
-namespace spdlog
-{
-namespace sinks
-{
-    template <typename Mutex>
-    class qt_sink : public base_sink<Mutex>
-    {
-    public:
-        qt_sink(QObject *qt_object, const std::string &meta_method)
-        {
-            qt_object_   = qt_object;
-            meta_method_ = meta_method;
-        }
-
-        ~qt_sink() { flush_(); }
-
-    protected:
-        void sink_it_(const details::log_msg &msg) override
-        {
-            memory_buf_t formatted;
-            base_sink<Mutex>::formatter_->format(msg, formatted);
-            string_view_t str = string_view_t(formatted.data(), formatted.size());
-            QMetaObject::invokeMethod(
-                qt_object_,
-                meta_method_.c_str(),
-                Qt::AutoConnection,
-                Q_ARG(QString, QString::fromUtf8(str.data(), static_cast<int>(str.size())).trimmed()));
-        }
-
-        void flush_() override {}
-
-    private:
-        QObject    *qt_object_ = nullptr;
-        std::string meta_method_;
-    };
-
-#include "spdlog/details/null_mutex.h"
-
-#include <mutex>
-    using qt_sink_mt = qt_sink<std::mutex>;
-    using qt_sink_st = qt_sink<spdlog::details::null_mutex>;
-} // namespace sinks
-
-//
-// Factory functions
-//
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger>
-qt_logger_mt(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append")
-{
-    return Factory::template create<sinks::qt_sink_mt>(logger_name, qt_object, meta_method);
-}
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger>
-qt_logger_st(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append")
-{
-    return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
-}
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> qt_logger_mt(
-    const std::string &logger_name,
-    QPlainTextEdit    *qt_object,
-    const std::string &meta_method = "appendPlainText")
-{
-    return Factory::template create<sinks::qt_sink_mt>(logger_name, qt_object, meta_method);
-}
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> qt_logger_st(
-    const std::string &logger_name,
-    QPlainTextEdit    *qt_object,
-    const std::string &meta_method = "appendPlainText")
-{
-    return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
-}
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger>
-qt_logger_mt(const std::string &logger_name, QObject *qt_object, const std::string &meta_method)
-{
-    return Factory::template create<sinks::qt_sink_mt>(logger_name, qt_object, meta_method);
-}
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger>
-qt_logger_st(const std::string &logger_name, QObject *qt_object, const std::string &meta_method)
-{
-    return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
-}
-} // namespace spdlog
diff --git a/include/spdlog/sinks/ringbuffer_sink.h b/include/spdlog/sinks/ringbuffer_sink.h
deleted file mode 100644
index a32db8cec..000000000
--- a/include/spdlog/sinks/ringbuffer_sink.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include "spdlog/details/circular_q.h"
-#include "spdlog/details/log_msg_buffer.h"
-#include "spdlog/details/null_mutex.h"
-#include "spdlog/sinks/base_sink.h"
-
-#include <mutex>
-#include <string>
-#include <vector>
-
-namespace spdlog
-{
-namespace sinks
-{
-    /*
-     * Ring buffer sink
-     */
-    template <typename Mutex>
-    class ringbuffer_sink final : public base_sink<Mutex>
-    {
-    public:
-        explicit ringbuffer_sink(size_t n_items) : q_{n_items} {}
-
-        std::vector<details::log_msg_buffer> last_raw(size_t lim = 0)
-        {
-            std::lock_guard<Mutex>               lock(base_sink<Mutex>::mutex_);
-            auto                                 items_available = q_.size();
-            auto                                 n_items = lim > 0 ? (std::min)(lim, items_available) : items_available;
-            std::vector<details::log_msg_buffer> ret;
-            ret.reserve(n_items);
-            for(size_t i = (items_available - n_items); i < items_available; i++)
-            {
-                ret.push_back(q_.at(i));
-            }
-            return ret;
-        }
-
-        std::vector<std::string> last_formatted(size_t lim = 0)
-        {
-            std::lock_guard<Mutex>   lock(base_sink<Mutex>::mutex_);
-            auto                     items_available = q_.size();
-            auto                     n_items         = lim > 0 ? (std::min)(lim, items_available) : items_available;
-            std::vector<std::string> ret;
-            ret.reserve(n_items);
-            for(size_t i = (items_available - n_items); i < items_available; i++)
-            {
-                memory_buf_t formatted;
-                base_sink<Mutex>::formatter_->format(q_.at(i), formatted);
-                ret.push_back(std::move(SPDLOG_BUF_TO_STRING(formatted)));
-            }
-            return ret;
-        }
-
-    protected:
-        void sink_it_(const details::log_msg &msg) override { q_.push_back(details::log_msg_buffer{msg}); }
-        void flush_() override {}
-
-    private:
-        details::circular_q<details::log_msg_buffer> q_;
-    };
-
-    using ringbuffer_sink_mt = ringbuffer_sink<std::mutex>;
-    using ringbuffer_sink_st = ringbuffer_sink<details::null_mutex>;
-
-} // namespace sinks
-
-} // namespace spdlog
diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h
deleted file mode 100644
index cb341d4ed..000000000
--- a/include/spdlog/sinks/rotating_file_sink-inl.h
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/sinks/rotating_file_sink.h>
-#endif
-
-#include <cerrno>
-#include <chrono>
-#include <ctime>
-#include <mutex>
-#include <spdlog/common.h>
-#include <spdlog/details/file_helper.h>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/fmt/fmt.h>
-#include <string>
-#include <tuple>
-
-namespace spdlog
-{
-namespace sinks
-{
-
-    template <typename Mutex>
-    SPDLOG_INLINE rotating_file_sink<Mutex>::rotating_file_sink(
-        filename_t                 base_filename,
-        std::size_t                max_size,
-        std::size_t                max_files,
-        bool                       rotate_on_open,
-        const file_event_handlers &event_handlers) :
-        base_filename_(std::move(base_filename)),
-        max_size_(max_size),
-        max_files_(max_files),
-        file_helper_{event_handlers}
-    {
-        if(max_size == 0)
-        {
-            throw_spdlog_ex("rotating sink constructor: max_size arg cannot be zero");
-        }
-
-        if(max_files > 200000)
-        {
-            throw_spdlog_ex("rotating sink constructor: max_files arg cannot exceed 200000");
-        }
-        file_helper_.open(calc_filename(base_filename_, 0));
-        current_size_ = file_helper_.size(); // expensive. called only once
-        if(rotate_on_open && current_size_ > 0)
-        {
-            rotate_();
-            current_size_ = 0;
-        }
-    }
-
-    // calc filename according to index and file extension if exists.
-    // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
-    template <typename Mutex>
-    SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::calc_filename(const filename_t &filename, std::size_t index)
-    {
-        if(index == 0u)
-        {
-            return filename;
-        }
-
-        filename_t basename, ext;
-        std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
-        return fmt_lib::format(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
-    }
-
-    template <typename Mutex>
-    SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::filename()
-    {
-        std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
-        return file_helper_.filename();
-    }
-
-    template <typename Mutex>
-    SPDLOG_INLINE void rotating_file_sink<Mutex>::sink_it_(const details::log_msg &msg)
-    {
-        memory_buf_t formatted;
-        base_sink<Mutex>::formatter_->format(msg, formatted);
-        auto new_size = current_size_ + formatted.size();
-
-        // rotate if the new estimated file size exceeds max size.
-        // rotate only if the real size > 0 to better deal with full disk (see issue #2261).
-        // we only check the real size when new_size > max_size_ because it is relatively expensive.
-        if(new_size > max_size_)
-        {
-            file_helper_.flush();
-            if(file_helper_.size() > 0)
-            {
-                rotate_();
-                new_size = formatted.size();
-            }
-        }
-        file_helper_.write(formatted);
-        current_size_ = new_size;
-    }
-
-    template <typename Mutex>
-    SPDLOG_INLINE void rotating_file_sink<Mutex>::flush_()
-    {
-        file_helper_.flush();
-    }
-
-    // Rotate files:
-    // log.txt -> log.1.txt
-    // log.1.txt -> log.2.txt
-    // log.2.txt -> log.3.txt
-    // log.3.txt -> delete
-    template <typename Mutex>
-    SPDLOG_INLINE void rotating_file_sink<Mutex>::rotate_()
-    {
-        using details::os::filename_to_str;
-        using details::os::path_exists;
-
-        file_helper_.close();
-        for(auto i = max_files_; i > 0; --i)
-        {
-            filename_t src = calc_filename(base_filename_, i - 1);
-            if(!path_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.
-    template <typename Mutex>
-    SPDLOG_INLINE bool
-    rotating_file_sink<Mutex>::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;
-    }
-
-} // namespace sinks
-} // namespace spdlog
diff --git a/include/spdlog/sinks/rotating_file_sink.h b/include/spdlog/sinks/rotating_file_sink.h
deleted file mode 100644
index 82e0f526f..000000000
--- a/include/spdlog/sinks/rotating_file_sink.h
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <chrono>
-#include <mutex>
-#include <spdlog/details/file_helper.h>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/details/synchronous_factory.h>
-#include <spdlog/sinks/base_sink.h>
-#include <string>
-
-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,
-            bool                       rotate_on_open = false,
-            const file_event_handlers &event_handlers = {});
-        static filename_t calc_filename(const filename_t &filename, std::size_t index);
-        filename_t        filename();
-
-    protected:
-        void sink_it_(const details::log_msg &msg) override;
-        void flush_() override;
-
-    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_();
-
-        // 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);
-
-        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 = spdlog::synchronous_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,
-    bool                       rotate_on_open = false,
-    const file_event_handlers &event_handlers = {})
-{
-    return Factory::template create<sinks::rotating_file_sink_mt>(
-        logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers);
-}
-
-template <typename Factory = spdlog::synchronous_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,
-    bool                       rotate_on_open = false,
-    const file_event_handlers &event_handlers = {})
-{
-    return Factory::template create<sinks::rotating_file_sink_st>(
-        logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers);
-}
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "rotating_file_sink-inl.h"
-#endif
diff --git a/include/spdlog/sinks/sink-inl.h b/include/spdlog/sinks/sink-inl.h
deleted file mode 100644
index a8dd6a6c3..000000000
--- a/include/spdlog/sinks/sink-inl.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/sinks/sink.h>
-#endif
-
-#include <spdlog/common.h>
-
-SPDLOG_INLINE bool spdlog::sinks::sink::should_log(spdlog::level::level_enum msg_level) const
-{
-    return msg_level >= level_.load(std::memory_order_relaxed);
-}
-
-SPDLOG_INLINE void spdlog::sinks::sink::set_level(level::level_enum log_level)
-{
-    level_.store(log_level, std::memory_order_relaxed);
-}
-
-SPDLOG_INLINE spdlog::level::level_enum spdlog::sinks::sink::level() const
-{
-    return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed));
-}
diff --git a/include/spdlog/sinks/sink.h b/include/spdlog/sinks/sink.h
deleted file mode 100644
index 40850cbb1..000000000
--- a/include/spdlog/sinks/sink.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <spdlog/details/log_msg.h>
-#include <spdlog/formatter.h>
-
-namespace spdlog
-{
-
-namespace sinks
-{
-    class SPDLOG_API sink
-    {
-    public:
-        virtual ~sink()                                                               = default;
-        virtual void log(const details::log_msg &msg)                                 = 0;
-        virtual void flush()                                                          = 0;
-        virtual void set_pattern(const std::string &pattern)                          = 0;
-        virtual void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) = 0;
-
-        void              set_level(level::level_enum log_level);
-        level::level_enum level() const;
-        bool              should_log(level::level_enum msg_level) const;
-
-    protected:
-        // sink log level - default is all
-        level_t level_{level::trace};
-    };
-
-} // namespace sinks
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "sink-inl.h"
-#endif
diff --git a/include/spdlog/sinks/stdout_color_sinks-inl.h b/include/spdlog/sinks/stdout_color_sinks-inl.h
deleted file mode 100644
index 9deda50f1..000000000
--- a/include/spdlog/sinks/stdout_color_sinks-inl.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/sinks/stdout_color_sinks.h>
-#endif
-
-#include <spdlog/common.h>
-#include <spdlog/logger.h>
-
-namespace spdlog
-{
-
-template <typename Factory>
-SPDLOG_INLINE std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name, color_mode mode)
-{
-    return Factory::template create<sinks::stdout_color_sink_mt>(logger_name, mode);
-}
-
-template <typename Factory>
-SPDLOG_INLINE std::shared_ptr<logger> stdout_color_st(const std::string &logger_name, color_mode mode)
-{
-    return Factory::template create<sinks::stdout_color_sink_st>(logger_name, mode);
-}
-
-template <typename Factory>
-SPDLOG_INLINE std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name, color_mode mode)
-{
-    return Factory::template create<sinks::stderr_color_sink_mt>(logger_name, mode);
-}
-
-template <typename Factory>
-SPDLOG_INLINE std::shared_ptr<logger> stderr_color_st(const std::string &logger_name, color_mode mode)
-{
-    return Factory::template create<sinks::stderr_color_sink_st>(logger_name, mode);
-}
-} // namespace spdlog
diff --git a/include/spdlog/sinks/stdout_color_sinks.h b/include/spdlog/sinks/stdout_color_sinks.h
deleted file mode 100644
index 227dea8ea..000000000
--- a/include/spdlog/sinks/stdout_color_sinks.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifdef _WIN32
-#include <spdlog/sinks/wincolor_sink.h>
-#else
-#include <spdlog/sinks/ansicolor_sink.h>
-#endif
-
-#include <spdlog/details/synchronous_factory.h>
-
-namespace spdlog
-{
-namespace sinks
-{
-#ifdef _WIN32
-    using stdout_color_sink_mt = wincolor_stdout_sink_mt;
-    using stdout_color_sink_st = wincolor_stdout_sink_st;
-    using stderr_color_sink_mt = wincolor_stderr_sink_mt;
-    using stderr_color_sink_st = wincolor_stderr_sink_st;
-#else
-    using stdout_color_sink_mt = ansicolor_stdout_sink_mt;
-    using stdout_color_sink_st = ansicolor_stdout_sink_st;
-    using stderr_color_sink_mt = ansicolor_stderr_sink_mt;
-    using stderr_color_sink_st = ansicolor_stderr_sink_st;
-#endif
-} // namespace sinks
-
-template <typename Factory = spdlog::synchronous_factory>
-std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic);
-
-template <typename Factory = spdlog::synchronous_factory>
-std::shared_ptr<logger> stdout_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic);
-
-template <typename Factory = spdlog::synchronous_factory>
-std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic);
-
-template <typename Factory = spdlog::synchronous_factory>
-std::shared_ptr<logger> stderr_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic);
-
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "stdout_color_sinks-inl.h"
-#endif
diff --git a/include/spdlog/sinks/stdout_sinks-inl.h b/include/spdlog/sinks/stdout_sinks-inl.h
deleted file mode 100644
index 06546bf7e..000000000
--- a/include/spdlog/sinks/stdout_sinks-inl.h
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/sinks/stdout_sinks.h>
-#endif
-
-#include <memory>
-#include <spdlog/details/console_globals.h>
-#include <spdlog/pattern_formatter.h>
-
-#ifdef _WIN32
-// under windows using fwrite to non-binary stream results in \r\r\n (see issue #1675)
-// so instead we use ::FileWrite
-#include <spdlog/details/windows_include.h>
-
-#ifndef _USING_V110_SDK71_ // fileapi.h doesn't exist in winxp
-#include <fileapi.h>       // WriteFile (..)
-#endif
-
-#include <io.h>    // _get_osfhandle(..)
-#include <stdio.h> // _fileno(..)
-#endif             // WIN32
-
-namespace spdlog
-{
-
-namespace sinks
-{
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE stdout_sink_base<ConsoleMutex>::stdout_sink_base(FILE *file) :
-        mutex_(ConsoleMutex::mutex()), file_(file), formatter_(details::make_unique<spdlog::pattern_formatter>())
-    {
-#ifdef _WIN32
-        // get windows handle from the FILE* object
-
-        handle_ = reinterpret_cast<HANDLE>(::_get_osfhandle(::_fileno(file_)));
-
-        // don't throw to support cases where no console is attached,
-        // and let the log method to do nothing if (handle_ == INVALID_HANDLE_VALUE).
-        // throw only if non stdout/stderr target is requested (probably regular file and not console).
-        if(handle_ == INVALID_HANDLE_VALUE && file != stdout && file != stderr)
-        {
-            throw_spdlog_ex("spdlog::stdout_sink_base: _get_osfhandle() failed", errno);
-        }
-#endif // WIN32
-    }
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &msg)
-    {
-#ifdef _WIN32
-        if(handle_ == INVALID_HANDLE_VALUE)
-        {
-            return;
-        }
-        std::lock_guard<mutex_t> lock(mutex_);
-        memory_buf_t             formatted;
-        formatter_->format(msg, formatted);
-        ::fflush(file_); // flush in case there is something in this file_ already
-        auto  size          = static_cast<DWORD>(formatted.size());
-        DWORD bytes_written = 0;
-        bool  ok            = ::WriteFile(handle_, formatted.data(), size, &bytes_written, nullptr) != 0;
-        if(!ok)
-        {
-            throw_spdlog_ex(
-                "stdout_sink_base: WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError()));
-        }
-#else
-        std::lock_guard<mutex_t> lock(mutex_);
-        memory_buf_t             formatted;
-        formatter_->format(msg, formatted);
-        ::fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
-        ::fflush(file_); // flush every line to terminal
-#endif // WIN32
-    }
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::flush()
-    {
-        std::lock_guard<mutex_t> lock(mutex_);
-        fflush(file_);
-    }
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::set_pattern(const std::string &pattern)
-    {
-        std::lock_guard<mutex_t> lock(mutex_);
-        formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
-    }
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
-    {
-        std::lock_guard<mutex_t> lock(mutex_);
-        formatter_ = std::move(sink_formatter);
-    }
-
-    // stdout sink
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE stdout_sink<ConsoleMutex>::stdout_sink() : stdout_sink_base<ConsoleMutex>(stdout)
-    {
-    }
-
-    // stderr sink
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE stderr_sink<ConsoleMutex>::stderr_sink() : stdout_sink_base<ConsoleMutex>(stderr)
-    {
-    }
-
-} // namespace sinks
-
-// factory methods
-template <typename Factory>
-SPDLOG_INLINE std::shared_ptr<logger> stdout_logger_mt(const std::string &logger_name)
-{
-    return Factory::template create<sinks::stdout_sink_mt>(logger_name);
-}
-
-template <typename Factory>
-SPDLOG_INLINE std::shared_ptr<logger> stdout_logger_st(const std::string &logger_name)
-{
-    return Factory::template create<sinks::stdout_sink_st>(logger_name);
-}
-
-template <typename Factory>
-SPDLOG_INLINE std::shared_ptr<logger> stderr_logger_mt(const std::string &logger_name)
-{
-    return Factory::template create<sinks::stderr_sink_mt>(logger_name);
-}
-
-template <typename Factory>
-SPDLOG_INLINE std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name)
-{
-    return Factory::template create<sinks::stderr_sink_st>(logger_name);
-}
-} // namespace spdlog
diff --git a/include/spdlog/sinks/stdout_sinks.h b/include/spdlog/sinks/stdout_sinks.h
deleted file mode 100644
index d12be28c3..000000000
--- a/include/spdlog/sinks/stdout_sinks.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <cstdio>
-#include <spdlog/details/console_globals.h>
-#include <spdlog/details/synchronous_factory.h>
-#include <spdlog/sinks/sink.h>
-
-#ifdef _WIN32
-#include <spdlog/details/windows_include.h>
-#endif
-
-namespace spdlog
-{
-
-namespace sinks
-{
-
-    template <typename ConsoleMutex>
-    class stdout_sink_base : public sink
-    {
-    public:
-        using mutex_t = typename ConsoleMutex::mutex_t;
-        explicit stdout_sink_base(FILE *file);
-        ~stdout_sink_base() override = default;
-
-        stdout_sink_base(const stdout_sink_base &other) = delete;
-        stdout_sink_base(stdout_sink_base &&other)      = delete;
-
-        stdout_sink_base &operator=(const stdout_sink_base &other) = delete;
-        stdout_sink_base &operator=(stdout_sink_base &&other)      = delete;
-
-        void log(const details::log_msg &msg) override;
-        void flush() override;
-        void set_pattern(const std::string &pattern) override;
-
-        void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
-
-    protected:
-        mutex_t                           &mutex_;
-        FILE                              *file_;
-        std::unique_ptr<spdlog::formatter> formatter_;
-#ifdef _WIN32
-        HANDLE handle_;
-#endif // WIN32
-    };
-
-    template <typename ConsoleMutex>
-    class stdout_sink : public stdout_sink_base<ConsoleMutex>
-    {
-    public:
-        stdout_sink();
-    };
-
-    template <typename ConsoleMutex>
-    class stderr_sink : public stdout_sink_base<ConsoleMutex>
-    {
-    public:
-        stderr_sink();
-    };
-
-    using stdout_sink_mt = stdout_sink<details::console_mutex>;
-    using stdout_sink_st = stdout_sink<details::console_nullmutex>;
-
-    using stderr_sink_mt = stderr_sink<details::console_mutex>;
-    using stderr_sink_st = stderr_sink<details::console_nullmutex>;
-
-} // namespace sinks
-
-// factory methods
-template <typename Factory = spdlog::synchronous_factory>
-std::shared_ptr<logger> stdout_logger_mt(const std::string &logger_name);
-
-template <typename Factory = spdlog::synchronous_factory>
-std::shared_ptr<logger> stdout_logger_st(const std::string &logger_name);
-
-template <typename Factory = spdlog::synchronous_factory>
-std::shared_ptr<logger> stderr_logger_mt(const std::string &logger_name);
-
-template <typename Factory = spdlog::synchronous_factory>
-std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name);
-
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "stdout_sinks-inl.h"
-#endif
diff --git a/include/spdlog/sinks/syslog_sink.h b/include/spdlog/sinks/syslog_sink.h
deleted file mode 100644
index 578a22573..000000000
--- a/include/spdlog/sinks/syslog_sink.h
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <array>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/details/synchronous_factory.h>
-#include <spdlog/sinks/base_sink.h>
-#include <string>
-#include <syslog.h>
-
-namespace spdlog
-{
-namespace sinks
-{
-    /**
-     * Sink that write to syslog using the `syscall()` library call.
-     */
-    template <typename Mutex>
-    class syslog_sink : public base_sink<Mutex>
-    {
-    public:
-        syslog_sink(std::string ident, int syslog_option, int syslog_facility, bool enable_formatting) :
-            enable_formatting_{enable_formatting},
-            syslog_levels_{
-                {/* spdlog::level::trace      */ LOG_DEBUG,
-                 /* spdlog::level::debug      */ LOG_DEBUG,
-                 /* spdlog::level::info       */ LOG_INFO,
-                 /* spdlog::level::warn       */ LOG_WARNING,
-                 /* spdlog::level::err        */ LOG_ERR,
-                 /* spdlog::level::critical   */ LOG_CRIT,
-                 /* spdlog::level::off        */ LOG_INFO}},
-            ident_{std::move(ident)}
-        {
-            // set ident to be program name if empty
-            ::openlog(ident_.empty() ? nullptr : ident_.c_str(), syslog_option, syslog_facility);
-        }
-
-        ~syslog_sink() override { ::closelog(); }
-
-        syslog_sink(const syslog_sink &)            = delete;
-        syslog_sink &operator=(const syslog_sink &) = delete;
-
-    protected:
-        void sink_it_(const details::log_msg &msg) override
-        {
-            string_view_t payload;
-            memory_buf_t  formatted;
-            if(enable_formatting_)
-            {
-                base_sink<Mutex>::formatter_->format(msg, formatted);
-                payload = string_view_t(formatted.data(), formatted.size());
-            }
-            else
-            {
-                payload = msg.payload;
-            }
-
-            size_t length = payload.size();
-            // limit to max int
-            if(length > static_cast<size_t>(std::numeric_limits<int>::max()))
-            {
-                length = static_cast<size_t>(std::numeric_limits<int>::max());
-            }
-
-            ::syslog(syslog_prio_from_level(msg), "%.*s", static_cast<int>(length), payload.data());
-        }
-
-        void flush_() override {}
-        bool enable_formatting_ = false;
-
-    private:
-        using levels_array = std::array<int, 7>;
-        levels_array syslog_levels_;
-        // must store the ident because the man says openlog might use the pointer as
-        // is and not a string copy
-        const std::string ident_;
-
-        //
-        // Simply maps spdlog's log level to syslog priority level.
-        //
-        int syslog_prio_from_level(const details::log_msg &msg) const
-        {
-            return syslog_levels_.at(static_cast<levels_array::size_type>(msg.level));
-        }
-    };
-
-    using syslog_sink_mt = syslog_sink<std::mutex>;
-    using syslog_sink_st = syslog_sink<details::null_mutex>;
-} // namespace sinks
-
-// Create and register a syslog logger
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> syslog_logger_mt(
-    const std::string &logger_name,
-    const std::string &syslog_ident      = "",
-    int                syslog_option     = 0,
-    int                syslog_facility   = LOG_USER,
-    bool               enable_formatting = false)
-{
-    return Factory::template create<sinks::syslog_sink_mt>(
-        logger_name, syslog_ident, syslog_option, syslog_facility, enable_formatting);
-}
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> syslog_logger_st(
-    const std::string &logger_name,
-    const std::string &syslog_ident      = "",
-    int                syslog_option     = 0,
-    int                syslog_facility   = LOG_USER,
-    bool               enable_formatting = false)
-{
-    return Factory::template create<sinks::syslog_sink_st>(
-        logger_name, syslog_ident, syslog_option, syslog_facility, enable_formatting);
-}
-} // namespace spdlog
diff --git a/include/spdlog/sinks/systemd_sink.h b/include/spdlog/sinks/systemd_sink.h
deleted file mode 100644
index afed5f986..000000000
--- a/include/spdlog/sinks/systemd_sink.h
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright(c) 2019 ZVYAGIN.Alexander@gmail.com
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <array>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/details/synchronous_factory.h>
-#include <spdlog/sinks/base_sink.h>
-#ifndef SD_JOURNAL_SUPPRESS_LOCATION
-#define SD_JOURNAL_SUPPRESS_LOCATION
-#endif
-#include <systemd/sd-journal.h>
-
-namespace spdlog
-{
-namespace sinks
-{
-
-    /**
-     * Sink that write to systemd journal using the `sd_journal_send()` library call.
-     */
-    template <typename Mutex>
-    class systemd_sink : public base_sink<Mutex>
-    {
-    public:
-        systemd_sink(std::string ident = "", bool enable_formatting = false) :
-            ident_{std::move(ident)},
-            enable_formatting_{enable_formatting},
-            syslog_levels_{
-                {/* spdlog::level::trace      */ LOG_DEBUG,
-                 /* spdlog::level::debug      */ LOG_DEBUG,
-                 /* spdlog::level::info       */ LOG_INFO,
-                 /* spdlog::level::warn       */ LOG_WARNING,
-                 /* spdlog::level::err        */ LOG_ERR,
-                 /* spdlog::level::critical   */ LOG_CRIT,
-                 /* spdlog::level::off        */ LOG_INFO}}
-        {
-        }
-
-        ~systemd_sink() override {}
-
-        systemd_sink(const systemd_sink &)            = delete;
-        systemd_sink &operator=(const systemd_sink &) = delete;
-
-    protected:
-        const std::string ident_;
-        bool              enable_formatting_ = false;
-        using levels_array                   = std::array<int, 7>;
-        levels_array syslog_levels_;
-
-        void sink_it_(const details::log_msg &msg) override
-        {
-            int           err;
-            string_view_t payload;
-            memory_buf_t  formatted;
-            if(enable_formatting_)
-            {
-                base_sink<Mutex>::formatter_->format(msg, formatted);
-                payload = string_view_t(formatted.data(), formatted.size());
-            }
-            else
-            {
-                payload = msg.payload;
-            }
-
-            size_t length = payload.size();
-            // limit to max int
-            if(length > static_cast<size_t>(std::numeric_limits<int>::max()))
-            {
-                length = static_cast<size_t>(std::numeric_limits<int>::max());
-            }
-
-            const string_view_t syslog_identifier = ident_.empty() ? msg.logger_name : ident_;
-
-            // Do not send source location if not available
-            if(msg.source.empty())
-            {
-                // Note: function call inside '()' to avoid macro expansion
-                err = (sd_journal_send)("MESSAGE=%.*s", static_cast<int>(length), payload.data(), "PRIORITY=%d", syslog_level(msg.level),
-                "SYSLOG_IDENTIFIER=%.*s", static_cast<int>(syslog_identifier.size()), syslog_identifier.data(), nullptr);
-            }
-            else
-            {
-                err = (sd_journal_send)("MESSAGE=%.*s", static_cast<int>(length), payload.data(), "PRIORITY=%d", syslog_level(msg.level),
-                "SYSLOG_IDENTIFIER=%.*s", static_cast<int>(syslog_identifier.size()), syslog_identifier.data(), "CODE_FILE=%s",
-                msg.source.filename, "CODE_LINE=%d", msg.source.line, "CODE_FUNC=%s", msg.source.funcname, nullptr);
-            }
-
-            if(err)
-            {
-                throw_spdlog_ex("Failed writing to systemd", errno);
-            }
-        }
-
-        int syslog_level(level::level_enum l) { return syslog_levels_.at(static_cast<levels_array::size_type>(l)); }
-
-        void flush_() override {}
-    };
-
-    using systemd_sink_mt = systemd_sink<std::mutex>;
-    using systemd_sink_st = systemd_sink<details::null_mutex>;
-} // namespace sinks
-
-// Create and register a syslog logger
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger>
-systemd_logger_mt(const std::string &logger_name, const std::string &ident = "", bool enable_formatting = false)
-{
-    return Factory::template create<sinks::systemd_sink_mt>(logger_name, ident, enable_formatting);
-}
-
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger>
-systemd_logger_st(const std::string &logger_name, const std::string &ident = "", bool enable_formatting = false)
-{
-    return Factory::template create<sinks::systemd_sink_st>(logger_name, ident, enable_formatting);
-}
-} // namespace spdlog
diff --git a/include/spdlog/sinks/tcp_sink.h b/include/spdlog/sinks/tcp_sink.h
deleted file mode 100644
index 3c4cd6959..000000000
--- a/include/spdlog/sinks/tcp_sink.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <spdlog/common.h>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/sinks/base_sink.h>
-#ifdef _WIN32
-#include <spdlog/details/tcp_client-windows.h>
-#else
-#include <spdlog/details/tcp_client.h>
-#endif
-
-#include <chrono>
-#include <functional>
-#include <mutex>
-#include <string>
-
-#pragma once
-
-// Simple tcp client sink
-// Connects to remote address and send the formatted log.
-// Will attempt to reconnect if connection drops.
-// If more complicated behaviour is needed (i.e get responses), you can inherit it and override the sink_it_ method.
-
-namespace spdlog
-{
-namespace sinks
-{
-
-    struct tcp_sink_config
-    {
-        std::string server_host;
-        int         server_port;
-        bool        lazy_connect = false; // if true connect on first log call instead of on construction
-
-        tcp_sink_config(std::string host, int port) : server_host{std::move(host)}, server_port{port} {}
-    };
-
-    template <typename Mutex>
-    class tcp_sink : public spdlog::sinks::base_sink<Mutex>
-    {
-    public:
-        // connect to tcp host/port or throw if failed
-        // host can be hostname or ip address
-
-        explicit tcp_sink(tcp_sink_config sink_config) : config_{std::move(sink_config)}
-        {
-            if(!config_.lazy_connect)
-            {
-                this->client_.connect(config_.server_host, config_.server_port);
-            }
-        }
-
-        ~tcp_sink() override = default;
-
-    protected:
-        void sink_it_(const spdlog::details::log_msg &msg) override
-        {
-            spdlog::memory_buf_t formatted;
-            spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
-            if(!client_.is_connected())
-            {
-                client_.connect(config_.server_host, config_.server_port);
-            }
-            client_.send(formatted.data(), formatted.size());
-        }
-
-        void                flush_() override {}
-        tcp_sink_config     config_;
-        details::tcp_client client_;
-    };
-
-    using tcp_sink_mt = tcp_sink<std::mutex>;
-    using tcp_sink_st = tcp_sink<spdlog::details::null_mutex>;
-
-} // namespace sinks
-} // namespace spdlog
diff --git a/include/spdlog/sinks/udp_sink.h b/include/spdlog/sinks/udp_sink.h
deleted file mode 100644
index ed5b2ab64..000000000
--- a/include/spdlog/sinks/udp_sink.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <spdlog/common.h>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/sinks/base_sink.h>
-#ifdef _WIN32
-#include <spdlog/details/udp_client-windows.h>
-#else
-#include <spdlog/details/udp_client.h>
-#endif
-
-#include <chrono>
-#include <functional>
-#include <mutex>
-#include <string>
-
-// Simple udp client sink
-// Sends formatted log via udp
-
-namespace spdlog
-{
-namespace sinks
-{
-
-    struct udp_sink_config
-    {
-        std::string server_host;
-        uint16_t    server_port;
-
-        udp_sink_config(std::string host, uint16_t port) : server_host{std::move(host)}, server_port{port} {}
-    };
-
-    template <typename Mutex>
-    class udp_sink : public spdlog::sinks::base_sink<Mutex>
-    {
-    public:
-        // host can be hostname or ip address
-        explicit udp_sink(udp_sink_config sink_config) : client_{sink_config.server_host, sink_config.server_port} {}
-
-        ~udp_sink() override = default;
-
-    protected:
-        void sink_it_(const spdlog::details::log_msg &msg) override
-        {
-            spdlog::memory_buf_t formatted;
-            spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
-            client_.send(formatted.data(), formatted.size());
-        }
-
-        void                flush_() override {}
-        details::udp_client client_;
-    };
-
-    using udp_sink_mt = udp_sink<std::mutex>;
-    using udp_sink_st = udp_sink<spdlog::details::null_mutex>;
-
-} // namespace sinks
-
-//
-// factory functions
-//
-template <typename Factory = spdlog::synchronous_factory>
-inline std::shared_ptr<logger> udp_logger_mt(const std::string &logger_name, sinks::udp_sink_config skin_config)
-{
-    return Factory::template create<sinks::udp_sink_mt>(logger_name, skin_config);
-}
-
-} // namespace spdlog
diff --git a/include/spdlog/sinks/win_eventlog_sink.h b/include/spdlog/sinks/win_eventlog_sink.h
deleted file mode 100644
index cee2df781..000000000
--- a/include/spdlog/sinks/win_eventlog_sink.h
+++ /dev/null
@@ -1,306 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-// Writing to Windows Event Log requires the registry entries below to be present, with the following modifications:
-// 1. <log_name>    should be replaced with your log name (e.g. your application name)
-// 2. <source_name> should be replaced with the specific source name and the key should be duplicated for
-//                  each source used in the application
-//
-// Since typically modifications of this kind require elevation, it's better to do it as a part of setup procedure.
-// The snippet below uses mscoree.dll as the message file as it exists on most of the Windows systems anyway and
-// happens to contain the needed resource.
-//
-// You can also specify a custom message file if needed.
-// Please refer to Event Log functions descriptions in MSDN for more details on custom message files.
-
-/*---------------------------------------------------------------------------------------
-
-Windows Registry Editor Version 5.00
-
-[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\<log_name>]
-
-[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\<log_name>\<source_name>]
-"TypesSupported"=dword:00000007
-"EventMessageFile"=hex(2):25,00,73,00,79,00,73,00,74,00,65,00,6d,00,72,00,6f,\
-  00,6f,00,74,00,25,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,\
-  5c,00,6d,00,73,00,63,00,6f,00,72,00,65,00,65,00,2e,00,64,00,6c,00,6c,00,00,\
-  00
-
------------------------------------------------------------------------------------------*/
-
-#pragma once
-
-#include <mutex>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/details/windows_include.h>
-#include <spdlog/sinks/base_sink.h>
-#include <string>
-#include <vector>
-#include <winbase.h>
-
-namespace spdlog
-{
-namespace sinks
-{
-
-    namespace win_eventlog
-    {
-
-        namespace internal
-        {
-
-            struct local_alloc_t
-            {
-                HLOCAL hlocal_;
-
-                SPDLOG_CONSTEXPR local_alloc_t() SPDLOG_NOEXCEPT : hlocal_(nullptr) {}
-
-                local_alloc_t(local_alloc_t const &)            = delete;
-                local_alloc_t &operator=(local_alloc_t const &) = delete;
-
-                ~local_alloc_t() SPDLOG_NOEXCEPT
-                {
-                    if(hlocal_)
-                    {
-                        LocalFree(hlocal_);
-                    }
-                }
-            };
-
-            /** Windows error */
-            struct win32_error : public spdlog_ex
-            {
-                /** Formats an error report line: "user-message: error-code (system message)" */
-                static std::string format(std::string const &user_message, DWORD error_code = GetLastError())
-                {
-                    std::string system_message;
-
-                    local_alloc_t format_message_result{};
-                    auto          format_message_succeeded = ::FormatMessageA(
-                        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
-                        nullptr,
-                        error_code,
-                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                        (LPSTR) &format_message_result.hlocal_,
-                        0,
-                        nullptr);
-
-                    if(format_message_succeeded && format_message_result.hlocal_)
-                    {
-                        system_message = fmt_lib::format(" ({})", (LPSTR) format_message_result.hlocal_);
-                    }
-
-                    return fmt_lib::format("{}: {}{}", user_message, error_code, system_message);
-                }
-
-                explicit win32_error(std::string const &func_name, DWORD error = GetLastError()) :
-                    spdlog_ex(format(func_name, error))
-                {
-                }
-            };
-
-            /** Wrapper for security identifiers (SID) on Windows */
-            struct sid_t
-            {
-                std::vector<char> buffer_;
-
-            public:
-                sid_t() {}
-
-                /** creates a wrapped SID copy */
-                static sid_t duplicate_sid(PSID psid)
-                {
-                    if(!::IsValidSid(psid))
-                    {
-                        throw_spdlog_ex("sid_t::sid_t(): invalid SID received");
-                    }
-
-                    auto const sid_length{::GetLengthSid(psid)};
-
-                    sid_t result;
-                    result.buffer_.resize(sid_length);
-                    if(!::CopySid(sid_length, (PSID) result.as_sid(), psid))
-                    {
-                        SPDLOG_THROW(win32_error("CopySid"));
-                    }
-
-                    return result;
-                }
-
-                /** Retrieves pointer to the internal buffer contents as SID* */
-                SID *as_sid() const { return buffer_.empty() ? nullptr : (SID *) buffer_.data(); }
-
-                /** Get SID for the current user */
-                static sid_t get_current_user_sid()
-                {
-                    /* create and init RAII holder for process token */
-                    struct process_token_t
-                    {
-                        HANDLE token_handle_ = INVALID_HANDLE_VALUE;
-                        explicit process_token_t(HANDLE process)
-                        {
-                            if(!::OpenProcessToken(process, TOKEN_QUERY, &token_handle_))
-                            {
-                                SPDLOG_THROW(win32_error("OpenProcessToken"));
-                            }
-                        }
-
-                        ~process_token_t() { ::CloseHandle(token_handle_); }
-
-                    } current_process_token(
-                        ::GetCurrentProcess()); // GetCurrentProcess returns pseudohandle, no leak here!
-
-                    // Get the required size, this is expected to fail with ERROR_INSUFFICIENT_BUFFER and return the
-                    // token size
-                    DWORD tusize = 0;
-                    if(::GetTokenInformation(current_process_token.token_handle_, TokenUser, NULL, 0, &tusize))
-                    {
-                        SPDLOG_THROW(win32_error("GetTokenInformation should fail"));
-                    }
-
-                    // get user token
-                    std::vector<unsigned char> buffer(static_cast<size_t>(tusize));
-                    if(!::GetTokenInformation(
-                           current_process_token.token_handle_, TokenUser, (LPVOID) buffer.data(), tusize, &tusize))
-                    {
-                        SPDLOG_THROW(win32_error("GetTokenInformation"));
-                    }
-
-                    // create a wrapper of the SID data as stored in the user token
-                    return sid_t::duplicate_sid(((TOKEN_USER *) buffer.data())->User.Sid);
-                }
-            };
-
-            struct eventlog
-            {
-                static WORD get_event_type(details::log_msg const &msg)
-                {
-                    switch(msg.level)
-                    {
-                        case level::trace:
-                        case level::debug:
-                            return EVENTLOG_SUCCESS;
-
-                        case level::info:
-                            return EVENTLOG_INFORMATION_TYPE;
-
-                        case level::warn:
-                            return EVENTLOG_WARNING_TYPE;
-
-                        case level::err:
-                        case level::critical:
-                        case level::off:
-                            return EVENTLOG_ERROR_TYPE;
-
-                        default:
-                            return EVENTLOG_INFORMATION_TYPE;
-                    }
-                }
-
-                static WORD get_event_category(details::log_msg const &msg) { return (WORD) msg.level; }
-            };
-
-        } // namespace internal
-
-        /*
-         * Windows Event Log sink
-         */
-        template <typename Mutex>
-        class win_eventlog_sink : public base_sink<Mutex>
-        {
-        private:
-            HANDLE          hEventLog_{NULL};
-            internal::sid_t current_user_sid_;
-            std::string     source_;
-            WORD            event_id_;
-
-            HANDLE event_log_handle()
-            {
-                if(!hEventLog_)
-                {
-                    hEventLog_ = ::RegisterEventSourceA(nullptr, source_.c_str());
-                    if(!hEventLog_ || hEventLog_ == (HANDLE) ERROR_ACCESS_DENIED)
-                    {
-                        SPDLOG_THROW(internal::win32_error("RegisterEventSource"));
-                    }
-                }
-
-                return hEventLog_;
-            }
-
-        protected:
-            void sink_it_(const details::log_msg &msg) override
-            {
-                using namespace internal;
-
-                bool         succeeded;
-                memory_buf_t formatted;
-                base_sink<Mutex>::formatter_->format(msg, formatted);
-                formatted.push_back('\0');
-
-#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
-                wmemory_buf_t buf;
-                details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), buf);
-
-                LPCWSTR lp_wstr = buf.data();
-                succeeded       = static_cast<bool>(::ReportEventW(
-                    event_log_handle(),
-                    eventlog::get_event_type(msg),
-                    eventlog::get_event_category(msg),
-                    event_id_,
-                    current_user_sid_.as_sid(),
-                    1,
-                    0,
-                    &lp_wstr,
-                    nullptr));
-#else
-                LPCSTR lp_str = formatted.data();
-                succeeded     = static_cast<bool>(::ReportEventA(
-                    event_log_handle(),
-                    eventlog::get_event_type(msg),
-                    eventlog::get_event_category(msg),
-                    event_id_,
-                    current_user_sid_.as_sid(),
-                    1,
-                    0,
-                    &lp_str,
-                    nullptr));
-#endif
-
-                if(!succeeded)
-                {
-                    SPDLOG_THROW(win32_error("ReportEvent"));
-                }
-            }
-
-            void flush_() override {}
-
-        public:
-            win_eventlog_sink(std::string const &source, WORD event_id = 1000 /* according to mscoree.dll */) :
-                source_(source), event_id_(event_id)
-            {
-                try
-                {
-                    current_user_sid_ = internal::sid_t::get_current_user_sid();
-                }
-                catch(...)
-                {
-                    // get_current_user_sid() is unlikely to fail and if it does, we can still proceed without
-                    // current_user_sid but in the event log the record will have no user name
-                }
-            }
-
-            ~win_eventlog_sink()
-            {
-                if(hEventLog_)
-                    DeregisterEventSource(hEventLog_);
-            }
-        };
-
-    } // namespace win_eventlog
-
-    using win_eventlog_sink_mt = win_eventlog::win_eventlog_sink<std::mutex>;
-    using win_eventlog_sink_st = win_eventlog::win_eventlog_sink<details::null_mutex>;
-
-} // namespace sinks
-} // namespace spdlog
diff --git a/include/spdlog/sinks/wincolor_sink-inl.h b/include/spdlog/sinks/wincolor_sink-inl.h
deleted file mode 100644
index 18b033368..000000000
--- a/include/spdlog/sinks/wincolor_sink-inl.h
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/sinks/wincolor_sink.h>
-#endif
-
-#include <spdlog/common.h>
-#include <spdlog/details/windows_include.h>
-#include <spdlog/pattern_formatter.h>
-#include <wincon.h>
-
-namespace spdlog
-{
-namespace sinks
-{
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE wincolor_sink<ConsoleMutex>::wincolor_sink(void *out_handle, color_mode mode) :
-        out_handle_(out_handle),
-        mutex_(ConsoleMutex::mutex()),
-        formatter_(details::make_unique<spdlog::pattern_formatter>())
-    {
-        set_color_mode_impl(mode);
-        // set level colors
-        colors_[level::trace]    = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;      // white
-        colors_[level::debug]    = FOREGROUND_GREEN | FOREGROUND_BLUE;                       // cyan
-        colors_[level::info]     = FOREGROUND_GREEN;                                         // green
-        colors_[level::warn]     = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; // intense yellow
-        colors_[level::err]      = FOREGROUND_RED | FOREGROUND_INTENSITY;                    // intense red
-        colors_[level::critical] = BACKGROUND_RED | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
-                                   FOREGROUND_INTENSITY; // intense white on red background
-        colors_[level::off] = 0;
-    }
-
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE wincolor_sink<ConsoleMutex>::~wincolor_sink()
-    {
-        this->flush();
-    }
-
-    // change the color for the given level
-    template <typename ConsoleMutex>
-    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color(level::level_enum level, std::uint16_t color)
-    {
-        std::lock_guard<mutex_t> lock(mutex_);
-        colors_[static_cast<size_t>(level)] = color;
-    }
-
-    template <typename ConsoleMutex>
-    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
-    {
-        if(out_handle_ == nullptr || out_handle_ == INVALID_HANDLE_VALUE)
-        {
-            return;
-        }
-
-        std::lock_guard<mutex_t> lock(mutex_);
-        msg.color_range_start = 0;
-        msg.color_range_end   = 0;
-        memory_buf_t formatted;
-        formatter_->format(msg, formatted);
-        if(should_do_colors_ && msg.color_range_end > msg.color_range_start)
-        {
-            // before color range
-            print_range_(formatted, 0, msg.color_range_start);
-            // in color range
-            auto orig_attribs = static_cast<WORD>(set_foreground_color_(colors_[static_cast<size_t>(msg.level)]));
-            print_range_(formatted, msg.color_range_start, msg.color_range_end);
-            // reset to orig colors
-            ::SetConsoleTextAttribute(static_cast<HANDLE>(out_handle_), orig_attribs);
-            print_range_(formatted, msg.color_range_end, formatted.size());
-        }
-        else // print without colors if color range is invalid (or color is disabled)
-        {
-            write_to_file_(formatted);
-        }
-    }
-
-    template <typename ConsoleMutex>
-    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::flush()
-    {
-        // windows console always flushed?
-    }
-
-    template <typename ConsoleMutex>
-    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_pattern(const std::string &pattern)
-    {
-        std::lock_guard<mutex_t> lock(mutex_);
-        formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
-    }
-
-    template <typename ConsoleMutex>
-    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
-    {
-        std::lock_guard<mutex_t> lock(mutex_);
-        formatter_ = std::move(sink_formatter);
-    }
-
-    template <typename ConsoleMutex>
-    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
-    {
-        std::lock_guard<mutex_t> lock(mutex_);
-        set_color_mode_impl(mode);
-    }
-
-    template <typename ConsoleMutex>
-    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode_impl(color_mode mode)
-    {
-        if(mode == color_mode::automatic)
-        {
-            // should do colors only if out_handle_  points to actual console.
-            DWORD console_mode;
-            bool  in_console  = ::GetConsoleMode(static_cast<HANDLE>(out_handle_), &console_mode) != 0;
-            should_do_colors_ = in_console;
-        }
-        else
-        {
-            should_do_colors_ = mode == color_mode::always ? true : false;
-        }
-    }
-
-    // set foreground color and return the orig console attributes (for resetting later)
-    template <typename ConsoleMutex>
-    std::uint16_t SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_foreground_color_(std::uint16_t attribs)
-    {
-        CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
-        if(!::GetConsoleScreenBufferInfo(static_cast<HANDLE>(out_handle_), &orig_buffer_info))
-        {
-            // just return white if failed getting console info
-            return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
-        }
-
-        // change only the foreground bits (lowest 4 bits)
-        auto new_attribs = static_cast<WORD>(attribs) | (orig_buffer_info.wAttributes & 0xfff0);
-        auto ignored     = ::SetConsoleTextAttribute(static_cast<HANDLE>(out_handle_), static_cast<WORD>(new_attribs));
-        (void) (ignored);
-        return static_cast<std::uint16_t>(orig_buffer_info.wAttributes); // return orig attribs
-    }
-
-    // print a range of formatted message to console
-    template <typename ConsoleMutex>
-    void SPDLOG_INLINE
-    wincolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
-    {
-        if(end > start)
-        {
-            auto size = static_cast<DWORD>(end - start);
-            auto ignored =
-                ::WriteConsoleA(static_cast<HANDLE>(out_handle_), formatted.data() + start, size, nullptr, nullptr);
-            (void) (ignored);
-        }
-    }
-
-    template <typename ConsoleMutex>
-    void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::write_to_file_(const memory_buf_t &formatted)
-    {
-        auto  size          = static_cast<DWORD>(formatted.size());
-        DWORD bytes_written = 0;
-        auto  ignored = ::WriteFile(static_cast<HANDLE>(out_handle_), formatted.data(), size, &bytes_written, nullptr);
-        (void) (ignored);
-    }
-
-    // wincolor_stdout_sink
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE wincolor_stdout_sink<ConsoleMutex>::wincolor_stdout_sink(color_mode mode) :
-        wincolor_sink<ConsoleMutex>(::GetStdHandle(STD_OUTPUT_HANDLE), mode)
-    {
-    }
-
-    // wincolor_stderr_sink
-    template <typename ConsoleMutex>
-    SPDLOG_INLINE wincolor_stderr_sink<ConsoleMutex>::wincolor_stderr_sink(color_mode mode) :
-        wincolor_sink<ConsoleMutex>(::GetStdHandle(STD_ERROR_HANDLE), mode)
-    {
-    }
-} // namespace sinks
-} // namespace spdlog
diff --git a/include/spdlog/sinks/wincolor_sink.h b/include/spdlog/sinks/wincolor_sink.h
deleted file mode 100644
index 43e6b575f..000000000
--- a/include/spdlog/sinks/wincolor_sink.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <array>
-#include <cstdint>
-#include <memory>
-#include <mutex>
-#include <spdlog/common.h>
-#include <spdlog/details/console_globals.h>
-#include <spdlog/details/null_mutex.h>
-#include <spdlog/sinks/sink.h>
-#include <string>
-
-namespace spdlog
-{
-namespace sinks
-{
-    /*
-     * Windows color console sink. Uses WriteConsoleA to write to the console with
-     * colors
-     */
-    template <typename ConsoleMutex>
-    class wincolor_sink : public sink
-    {
-    public:
-        wincolor_sink(void *out_handle, color_mode mode);
-        ~wincolor_sink() override;
-
-        wincolor_sink(const wincolor_sink &other)            = delete;
-        wincolor_sink &operator=(const wincolor_sink &other) = delete;
-
-        // change the color for the given level
-        void set_color(level::level_enum level, std::uint16_t color);
-        void log(const details::log_msg &msg) final override;
-        void flush() final override;
-        void set_pattern(const std::string &pattern) override final;
-        void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override final;
-        void set_color_mode(color_mode mode);
-
-    protected:
-        using mutex_t = typename ConsoleMutex::mutex_t;
-        void                                      *out_handle_;
-        mutex_t                                   &mutex_;
-        bool                                       should_do_colors_;
-        std::unique_ptr<spdlog::formatter>         formatter_;
-        std::array<std::uint16_t, level::n_levels> colors_;
-
-        // set foreground color and return the orig console attributes (for resetting later)
-        std::uint16_t set_foreground_color_(std::uint16_t attribs);
-
-        // print a range of formatted message to console
-        void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
-
-        // in case we are redirected to file (not in console mode)
-        void write_to_file_(const memory_buf_t &formatted);
-
-        void set_color_mode_impl(color_mode mode);
-    };
-
-    template <typename ConsoleMutex>
-    class wincolor_stdout_sink : public wincolor_sink<ConsoleMutex>
-    {
-    public:
-        explicit wincolor_stdout_sink(color_mode mode = color_mode::automatic);
-    };
-
-    template <typename ConsoleMutex>
-    class wincolor_stderr_sink : public wincolor_sink<ConsoleMutex>
-    {
-    public:
-        explicit wincolor_stderr_sink(color_mode mode = color_mode::automatic);
-    };
-
-    using wincolor_stdout_sink_mt = wincolor_stdout_sink<details::console_mutex>;
-    using wincolor_stdout_sink_st = wincolor_stdout_sink<details::console_nullmutex>;
-
-    using wincolor_stderr_sink_mt = wincolor_stderr_sink<details::console_mutex>;
-    using wincolor_stderr_sink_st = wincolor_stderr_sink<details::console_nullmutex>;
-} // namespace sinks
-} // namespace spdlog
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "wincolor_sink-inl.h"
-#endif
diff --git a/include/spdlog/spdlog-inl.h b/include/spdlog/spdlog-inl.h
deleted file mode 100644
index f4adac1e6..000000000
--- a/include/spdlog/spdlog-inl.h
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#ifndef SPDLOG_HEADER_ONLY
-#include <spdlog/spdlog.h>
-#endif
-
-#include <spdlog/common.h>
-#include <spdlog/pattern_formatter.h>
-
-namespace spdlog
-{
-
-SPDLOG_INLINE void initialize_logger(std::shared_ptr<logger> logger)
-{
-    details::registry::instance().initialize_logger(std::move(logger));
-}
-
-SPDLOG_INLINE std::shared_ptr<logger> get(const std::string &name)
-{
-    return details::registry::instance().get(name);
-}
-
-SPDLOG_INLINE void set_formatter(std::unique_ptr<spdlog::formatter> formatter)
-{
-    details::registry::instance().set_formatter(std::move(formatter));
-}
-
-SPDLOG_INLINE void set_pattern(std::string pattern, pattern_time_type time_type)
-{
-    set_formatter(std::unique_ptr<spdlog::formatter>(new pattern_formatter(std::move(pattern), time_type)));
-}
-
-SPDLOG_INLINE void enable_backtrace(size_t n_messages)
-{
-    details::registry::instance().enable_backtrace(n_messages);
-}
-
-SPDLOG_INLINE void disable_backtrace()
-{
-    details::registry::instance().disable_backtrace();
-}
-
-SPDLOG_INLINE void dump_backtrace()
-{
-    default_logger_raw()->dump_backtrace();
-}
-
-SPDLOG_INLINE level::level_enum get_level()
-{
-    return default_logger_raw()->level();
-}
-
-SPDLOG_INLINE bool should_log(level::level_enum log_level)
-{
-    return default_logger_raw()->should_log(log_level);
-}
-
-SPDLOG_INLINE void set_level(level::level_enum log_level)
-{
-    details::registry::instance().set_level(log_level);
-}
-
-SPDLOG_INLINE void flush_on(level::level_enum log_level)
-{
-    details::registry::instance().flush_on(log_level);
-}
-
-SPDLOG_INLINE void set_error_handler(void (*handler)(const std::string &msg))
-{
-    details::registry::instance().set_error_handler(handler);
-}
-
-SPDLOG_INLINE void register_logger(std::shared_ptr<logger> logger)
-{
-    details::registry::instance().register_logger(std::move(logger));
-}
-
-SPDLOG_INLINE void apply_all(const std::function<void(std::shared_ptr<logger>)> &fun)
-{
-    details::registry::instance().apply_all(fun);
-}
-
-SPDLOG_INLINE void drop(const std::string &name)
-{
-    details::registry::instance().drop(name);
-}
-
-SPDLOG_INLINE void drop_all()
-{
-    details::registry::instance().drop_all();
-}
-
-SPDLOG_INLINE void shutdown()
-{
-    details::registry::instance().shutdown();
-}
-
-SPDLOG_INLINE void set_automatic_registration(bool automatic_registration)
-{
-    details::registry::instance().set_automatic_registration(automatic_registration);
-}
-
-SPDLOG_INLINE std::shared_ptr<spdlog::logger> default_logger()
-{
-    return details::registry::instance().default_logger();
-}
-
-SPDLOG_INLINE spdlog::logger *default_logger_raw()
-{
-    return details::registry::instance().get_default_raw();
-}
-
-SPDLOG_INLINE void set_default_logger(std::shared_ptr<spdlog::logger> default_logger)
-{
-    details::registry::instance().set_default_logger(std::move(default_logger));
-}
-
-} // namespace spdlog
diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h
deleted file mode 100644
index 4dafb5ce3..000000000
--- a/include/spdlog/spdlog.h
+++ /dev/null
@@ -1,354 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-// spdlog main header file.
-// see example.cpp for usage example
-
-#ifndef SPDLOG_H
-#define SPDLOG_H
-
-#pragma once
-
-#include <chrono>
-#include <functional>
-#include <memory>
-#include <spdlog/common.h>
-#include <spdlog/details/registry.h>
-#include <spdlog/details/synchronous_factory.h>
-#include <spdlog/logger.h>
-#include <spdlog/version.h>
-#include <string>
-
-namespace spdlog
-{
-
-using default_factory = synchronous_factory;
-
-// Create and register a logger with a templated sink type
-// The logger's level, formatter and flush level will be set according the
-// global settings.
-//
-// Example:
-//   spdlog::create<daily_file_sink_st>("logger_name", "dailylog_filename", 11, 59);
-template <typename Sink, typename... SinkArgs>
-inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&...sink_args)
-{
-    return default_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
-}
-
-// Initialize and register a logger,
-// formatter and flush level will be set according the global settings.
-//
-// Useful for initializing manually created loggers with the global settings.
-//
-// Example:
-//   auto mylogger = std::make_shared<spdlog::logger>("mylogger", ...);
-//   spdlog::initialize_logger(mylogger);
-SPDLOG_API void initialize_logger(std::shared_ptr<logger> logger);
-
-// Return an existing logger or nullptr if a logger with such name doesn't
-// exist.
-// example: spdlog::get("my_logger")->info("hello {}", "world");
-SPDLOG_API std::shared_ptr<logger> get(const std::string &name);
-
-// Set global formatter. Each sink in each logger will get a clone of this object
-SPDLOG_API void set_formatter(std::unique_ptr<spdlog::formatter> formatter);
-
-// Set global format string.
-// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
-SPDLOG_API void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
-
-// enable global backtrace support
-SPDLOG_API void enable_backtrace(size_t n_messages);
-
-// disable global backtrace support
-SPDLOG_API void disable_backtrace();
-
-// call dump backtrace on default logger
-SPDLOG_API void dump_backtrace();
-
-// Get global logging level
-SPDLOG_API level::level_enum get_level();
-
-// Set global logging level
-SPDLOG_API void set_level(level::level_enum log_level);
-
-// Determine whether the default logger should log messages with a certain level
-SPDLOG_API bool should_log(level::level_enum lvl);
-
-// Set global flush level
-SPDLOG_API void flush_on(level::level_enum log_level);
-
-// Start/Restart a periodic flusher thread
-// Warning: Use only if all your loggers are thread safe!
-template <typename Rep, typename Period>
-inline void flush_every(std::chrono::duration<Rep, Period> interval)
-{
-    details::registry::instance().flush_every(interval);
-}
-
-// Set global error handler
-SPDLOG_API void set_error_handler(void (*handler)(const std::string &msg));
-
-// Register the given logger with the given name
-SPDLOG_API void register_logger(std::shared_ptr<logger> logger);
-
-// Apply a user defined function on all registered loggers
-// Example:
-// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});
-SPDLOG_API void apply_all(const std::function<void(std::shared_ptr<logger>)> &fun);
-
-// Drop the reference to the given logger
-SPDLOG_API void drop(const std::string &name);
-
-// Drop all references from the registry
-SPDLOG_API void drop_all();
-
-// stop any running threads started by spdlog and clean registry loggers
-SPDLOG_API void shutdown();
-
-// Automatic registration of loggers when using spdlog::create() or spdlog::create_async
-SPDLOG_API void set_automatic_registration(bool automatic_registration);
-
-// API for using default logger (stdout_color_mt),
-// e.g: spdlog::info("Message {}", 1);
-//
-// 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);
-//
-// 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.
-
-SPDLOG_API std::shared_ptr<spdlog::logger> default_logger();
-
-SPDLOG_API spdlog::logger *default_logger_raw();
-
-SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
-
-template <typename... Args>
-inline void log(source_loc source, level::level_enum lvl, format_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->log(source, lvl, fmt, std::forward<Args>(args)...);
-}
-
-template <typename... Args>
-inline void log(level::level_enum lvl, format_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
-}
-
-template <typename... Args>
-inline void trace(format_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->trace(fmt, std::forward<Args>(args)...);
-}
-
-template <typename... Args>
-inline void debug(format_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->debug(fmt, std::forward<Args>(args)...);
-}
-
-template <typename... Args>
-inline void info(format_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->info(fmt, std::forward<Args>(args)...);
-}
-
-template <typename... Args>
-inline void warn(format_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->warn(fmt, std::forward<Args>(args)...);
-}
-
-template <typename... Args>
-inline void error(format_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->error(fmt, std::forward<Args>(args)...);
-}
-
-template <typename... Args>
-inline void critical(format_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->critical(fmt, std::forward<Args>(args)...);
-}
-
-template <typename T>
-inline void log(source_loc source, level::level_enum lvl, const T &msg)
-{
-    default_logger_raw()->log(source, lvl, msg);
-}
-
-template <typename T>
-inline void log(level::level_enum lvl, const T &msg)
-{
-    default_logger_raw()->log(lvl, msg);
-}
-
-#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
-template <typename... Args>
-inline void log(source_loc source, level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->log(source, lvl, fmt, std::forward<Args>(args)...);
-}
-
-template <typename... Args>
-inline void log(level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
-}
-
-template <typename... Args>
-inline void trace(wformat_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->trace(fmt, std::forward<Args>(args)...);
-}
-
-template <typename... Args>
-inline void debug(wformat_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->debug(fmt, std::forward<Args>(args)...);
-}
-
-template <typename... Args>
-inline void info(wformat_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->info(fmt, std::forward<Args>(args)...);
-}
-
-template <typename... Args>
-inline void warn(wformat_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->warn(fmt, std::forward<Args>(args)...);
-}
-
-template <typename... Args>
-inline void error(wformat_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->error(fmt, std::forward<Args>(args)...);
-}
-
-template <typename... Args>
-inline void critical(wformat_string_t<Args...> fmt, Args &&...args)
-{
-    default_logger_raw()->critical(fmt, std::forward<Args>(args)...);
-}
-#endif
-
-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);
-}
-
-} // 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
-//
-
-#ifndef SPDLOG_NO_SOURCE_LOC
-#define SPDLOG_LOGGER_CALL(logger, level, ...)                                                                         \
-    (logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, level, __VA_ARGS__)
-#else
-#define SPDLOG_LOGGER_CALL(logger, level, ...) (logger)->log(spdlog::source_loc{}, level, __VA_ARGS__)
-#endif
-
-#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_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_LOGGER_DEBUG(logger, ...) (void) 0
-#define SPDLOG_DEBUG(...)                (void) 0
-#endif
-
-#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_LOGGER_INFO(logger, ...) (void) 0
-#define SPDLOG_INFO(...)                (void) 0
-#endif
-
-#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
-
-#ifdef SPDLOG_HEADER_ONLY
-#include "spdlog-inl.h"
-#endif
-
-#endif // SPDLOG_H
diff --git a/include/spdlog/stopwatch.h b/include/spdlog/stopwatch.h
deleted file mode 100644
index 0ceffd79f..000000000
--- a/include/spdlog/stopwatch.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#include <chrono>
-#include <spdlog/fmt/fmt.h>
-
-// Stopwatch support for spdlog  (using std::chrono::steady_clock).
-// Displays elapsed seconds since construction as double.
-//
-// Usage:
-//
-// spdlog::stopwatch sw;
-// ...
-// spdlog::debug("Elapsed: {} seconds", sw);    =>  "Elapsed 0.005116733 seconds"
-// spdlog::info("Elapsed: {:.6} seconds", sw);  =>  "Elapsed 0.005163 seconds"
-//
-//
-// If other units are needed (e.g. millis instead of double), include "fmt/chrono.h" and use
-// "duration_cast<..>(sw.elapsed())":
-//
-// #include <spdlog/fmt/chrono.h>
-//..
-// using std::chrono::duration_cast;
-// using std::chrono::milliseconds;
-// spdlog::info("Elapsed {}", duration_cast<milliseconds>(sw.elapsed())); => "Elapsed 5ms"
-
-namespace spdlog
-{
-class stopwatch
-{
-    using clock = std::chrono::steady_clock;
-    std::chrono::time_point<clock> start_tp_;
-
-public:
-    stopwatch() : start_tp_{clock::now()} {}
-
-    std::chrono::duration<double> elapsed() const { return std::chrono::duration<double>(clock::now() - start_tp_); }
-
-    void reset() { start_tp_ = clock::now(); }
-};
-} // namespace spdlog
-
-// Support for fmt formatting  (e.g. "{:012.9}" or just "{}")
-namespace
-#ifdef SPDLOG_USE_STD_FORMAT
-    std
-#else
-    fmt
-#endif
-{
-template <>
-struct formatter<spdlog::stopwatch> : formatter<double>
-{
-    template <typename FormatContext>
-    auto format(const spdlog::stopwatch &sw, FormatContext &ctx) -> decltype(ctx.out())
-    {
-        return formatter<double>::format(sw.elapsed().count(), ctx);
-    }
-};
-} // namespace std
diff --git a/include/spdlog/tweakme.h b/include/spdlog/tweakme.h
deleted file mode 100644
index 5bcb5ff42..000000000
--- a/include/spdlog/tweakme.h
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// Edit this file to squeeze more performance, and to customize supported
-// features
-//
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-// Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used.
-// This clock is less accurate - can be off by dozens of millis - depending on
-// the kernel HZ.
-// Uncomment to use it instead of the regular clock.
-//
-// #define SPDLOG_CLOCK_COARSE
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-// Uncomment if source location logging is not needed.
-// This will prevent spdlog from using __FILE__, __LINE__ and SPDLOG_FUNCTION
-//
-// #define SPDLOG_NO_SOURCE_LOC
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-// Uncomment if thread id logging is not needed (i.e. no %t in the log pattern).
-// This will prevent spdlog from querying the thread id on each log call.
-//
-// WARNING: If the log pattern contains thread id (i.e, %t) while this flag is
-// on, zero will be logged as thread id.
-//
-// #define SPDLOG_NO_THREAD_ID
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-// 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_NO_TLS
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-// Uncomment to avoid spdlog's usage of atomic log levels
-// Use only if your code never modifies a logger's log levels concurrently by
-// different threads.
-//
-// #define SPDLOG_NO_ATOMIC_LEVELS
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-// Uncomment to enable usage of wchar_t for file names on Windows.
-//
-// #define SPDLOG_WCHAR_FILENAMES
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-// Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows)
-//
-// #define SPDLOG_EOL ";-)\n"
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-// Uncomment to override default folder separators ("/" or "\\/" under
-// Linux/Windows). Each character in the string is treated as a different
-// separator.
-//
-// #define SPDLOG_FOLDER_SEPS "\\"
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-// Uncomment to use your own copy of the fmt library instead of spdlog's copy.
-// In this case spdlog will try to include <fmt/format.h> so set your -I flag
-// accordingly.
-//
-// #define SPDLOG_FMT_EXTERNAL
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-// Uncomment to use C++20 std::format instead of fmt.
-//
-// #define SPDLOG_USE_STD_FORMAT
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-// Uncomment to enable wchar_t support (convert to utf8)
-//
-// #define SPDLOG_WCHAR_TO_UTF8_SUPPORT
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-// Uncomment to prevent child processes from inheriting log file descriptors
-//
-// #define SPDLOG_PREVENT_CHILD_FD
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-// Uncomment to customize level names (e.g. "MY TRACE")
-//
-// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", "MY ERROR", "MY CRITICAL", "OFF" }
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-// Uncomment to customize short level names (e.g. "MT")
-// These can be longer than one character.
-//
-// #define SPDLOG_SHORT_LEVEL_NAMES { "T", "D", "I", "W", "E", "C", "O" }
-///////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////////////
-// 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.
-//
-// #ifdef __PRETTY_FUNCTION__
-// # define SPDLOG_FUNCTION __PRETTY_FUNCTION__
-// #else
-// # define SPDLOG_FUNCTION __FUNCTION__
-// #endif
-///////////////////////////////////////////////////////////////////////////////
diff --git a/include/spdlog/version.h b/include/spdlog/version.h
deleted file mode 100644
index 5717beadd..000000000
--- a/include/spdlog/version.h
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
-// Distributed under the MIT License (http://opensource.org/licenses/MIT)
-
-#pragma once
-
-#define SPDLOG_VER_MAJOR 1
-#define SPDLOG_VER_MINOR 11
-#define SPDLOG_VER_PATCH 0
-
-#define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH)
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index 0ba50c19b..75d36276c 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -17,7 +17,7 @@
  */
 
 #include "logwindow.h"
-#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
+#include "logger.h"
 
 #include "ui_logwindow.h"
 
@@ -37,10 +37,12 @@ LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
     mUi->logText->setReadOnly(true);
 
     std::vector<spdlog::sink_ptr> sinks;
-    sinks.push_back(std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>());
+    sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());
     sinks.push_back(std::make_shared<spdlog::sinks::qt_sink_mt>(mUi->logText, "appendPlainText"));
 
     mLogger = std::make_shared<spdlog::logger>("Logging", std::begin(sinks), std::end(sinks));
-    mLogger->set_pattern("%! in %s line %#: %v");
+    mLogger->set_pattern("[%s:%#:%!][%l] %v");
+    mLogger->set_level(spdlog::level::trace);
     spdlog::register_logger(mLogger);
-}
+    spdlog::set_default_logger(mLogger);
+}
\ No newline at end of file
diff --git a/src/petrack.cpp b/src/petrack.cpp
index 4acc1dfed..6efb6b724 100644
--- a/src/petrack.cpp
+++ b/src/petrack.cpp
@@ -168,8 +168,7 @@ Petrack::Petrack() :
 
     mLogWindow = new LogWindow(this, nullptr);
     mLogWindow->setWindowFlags(Qt::Window);
-    mLogWindow->setWindowTitle("Logging");
-    SPDLOG_LOGGER_INFO(spdlog::get("Logging"), "Test");
+    mLogWindow->setWindowTitle("Log");
 
 
     mPlayerWidget = new Player(mAnimation, this);
diff --git a/ui/logwindow.ui b/ui/logwindow.ui
index d82423554..43ee51d85 100644
--- a/ui/logwindow.ui
+++ b/ui/logwindow.ui
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>400</width>
-    <height>300</height>
+    <width>800</width>
+    <height>600</height>
    </rect>
   </property>
   <property name="windowTitle">
-- 
GitLab


From 768c7b7b101a4e94046a82a3f9e9f3a7ba71795f Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Tue, 24 Jan 2023 11:44:24 +0100
Subject: [PATCH 09/22] formatting

---
 include/helper.h  |  3 +--
 src/helper.cpp    | 10 ----------
 src/logwindow.cpp |  2 +-
 3 files changed, 2 insertions(+), 13 deletions(-)

diff --git a/include/helper.h b/include/helper.h
index 90742702e..d01d8cbe3 100644
--- a/include/helper.h
+++ b/include/helper.h
@@ -275,8 +275,7 @@ inline clock_t getElapsedTime()
     lastTime = clock();
     return diffTime;
 }
-bool        newerThanVersion(const QString &q1, const QString &q2);
-std::string splitFileName(std::string fileName);
+bool newerThanVersion(const QString &q1, const QString &q2);
 
 /**
  * Computes the median of the values in a given vector.
diff --git a/src/helper.cpp b/src/helper.cpp
index 8d9828b5d..0311e679e 100644
--- a/src/helper.cpp
+++ b/src/helper.cpp
@@ -256,13 +256,3 @@ bool newerThanVersion(const QString &q1, const QString &q2)
     }
     return false;
 }
-std::string splitFileName(std::string fileName)
-{
-    std::string delimiter = "/";
-    size_t      pos       = 0;
-    while((pos = fileName.find(delimiter)) != std::string::npos)
-    {
-        fileName.erase(0, pos + delimiter.length());
-    }
-    return fileName;
-}
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index 75d36276c..6d5cc6b0b 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -17,8 +17,8 @@
  */
 
 #include "logwindow.h"
-#include "logger.h"
 
+#include "logger.h"
 #include "ui_logwindow.h"
 
 LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
-- 
GitLab


From 98496318e348390957bb652020bc58588b6a11e6 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Tue, 24 Jan 2023 12:27:43 +0100
Subject: [PATCH 10/22] qt sink is added to default logger

---
 include/logwindow.h |  5 ++---
 src/logwindow.cpp   | 13 ++++---------
 2 files changed, 6 insertions(+), 12 deletions(-)

diff --git a/include/logwindow.h b/include/logwindow.h
index b3e2e73d3..27d24e9d8 100644
--- a/include/logwindow.h
+++ b/include/logwindow.h
@@ -39,9 +39,8 @@ public:
     LogWindow(QWidget *parent, Ui::LogWindow *mUi);
 
 private:
-    Ui::LogWindow                  *mUi;
-    Petrack                        *mMainWindow;
-    std::shared_ptr<spdlog::logger> mLogger;
+    Ui::LogWindow *mUi;
+    Petrack       *mMainWindow;
 };
 
 #endif // LOGWINDOW_H
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index 6d5cc6b0b..a32decb6d 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -19,6 +19,7 @@
 #include "logwindow.h"
 
 #include "logger.h"
+#include "spdlog/sinks/dist_sink.h"
 #include "ui_logwindow.h"
 
 LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
@@ -36,13 +37,7 @@ LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
     mUi->setupUi(this);
     mUi->logText->setReadOnly(true);
 
-    std::vector<spdlog::sink_ptr> sinks;
-    sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());
-    sinks.push_back(std::make_shared<spdlog::sinks::qt_sink_mt>(mUi->logText, "appendPlainText"));
-
-    mLogger = std::make_shared<spdlog::logger>("Logging", std::begin(sinks), std::end(sinks));
-    mLogger->set_pattern("[%s:%#:%!][%l] %v");
-    mLogger->set_level(spdlog::level::trace);
-    spdlog::register_logger(mLogger);
-    spdlog::set_default_logger(mLogger);
+    spdlog::default_logger()->sinks().push_back(
+        std::make_shared<spdlog::sinks::qt_sink_mt>(mUi->logText, "appendPlainText"));
+    spdlog::default_logger()->set_pattern("[%s:%#:%!][%l] %v");
 }
\ No newline at end of file
-- 
GitLab


From af8b4b5cf7894aa9eaf487cecc9fe2ab2031b91c Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Fri, 27 Jan 2023 12:47:50 +0100
Subject: [PATCH 11/22] implemented review comments

---
 include/logger.h  | 3 ++-
 src/logwindow.cpp | 4 ++--
 src/petrack.cpp   | 1 -
 ui/control.ui     | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/include/logger.h b/include/logger.h
index 6302b86ac..13bb99ecc 100644
--- a/include/logger.h
+++ b/include/logger.h
@@ -30,6 +30,7 @@ namespace logger
 {
 const std::string messageBoxLoggerName = "pMessageBox";
 const std::string messageBoxLogFormat  = "[{}:{}:{}][{}] {}";
+const std::string logFormat = "[%s:%#:%!][%l] %v";
 
 inline void setupLogger()
 {
@@ -37,7 +38,7 @@ inline void setupLogger()
     spdlog::set_level(spdlog::level::trace);
 
     // Note: when changing this, also adapt messageBoxLogFormat accordingly!
-    spdlog::set_pattern("[%s:%#:%!][%l] %v");
+    spdlog::set_pattern(logFormat);
 
     // setup logger, which is used when also a dialog is displayed to the user
     auto messageBoxLogger = spdlog::stdout_logger_mt("pMessageBox");
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index a32decb6d..dab2dcd99 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -39,5 +39,5 @@ LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
 
     spdlog::default_logger()->sinks().push_back(
         std::make_shared<spdlog::sinks::qt_sink_mt>(mUi->logText, "appendPlainText"));
-    spdlog::default_logger()->set_pattern("[%s:%#:%!][%l] %v");
-}
\ No newline at end of file
+    spdlog::default_logger()->set_pattern(logger::logFormat);
+}
diff --git a/src/petrack.cpp b/src/petrack.cpp
index 6efb6b724..92627202b 100644
--- a/src/petrack.cpp
+++ b/src/petrack.cpp
@@ -170,7 +170,6 @@ Petrack::Petrack() :
     mLogWindow->setWindowFlags(Qt::Window);
     mLogWindow->setWindowTitle("Log");
 
-
     mPlayerWidget = new Player(mAnimation, this);
 
     QVBoxLayout *vLayout = new QVBoxLayout;
diff --git a/ui/control.ui b/ui/control.ui
index cc5d995ef..4573cf361 100644
--- a/ui/control.ui
+++ b/ui/control.ui
@@ -74,7 +74,7 @@
       <enum>Qt::LeftToRight</enum>
      </property>
      <property name="currentIndex">
-      <number>1</number>
+      <number>0</number>
      </property>
      <property name="tabsClosable">
       <bool>false</bool>
-- 
GitLab


From 14f719c98cee4f377e551d8b67387b7f2cb27735 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Fri, 27 Jan 2023 12:52:12 +0100
Subject: [PATCH 12/22] formatting

---
 include/logger.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/logger.h b/include/logger.h
index 13bb99ecc..f344482d4 100644
--- a/include/logger.h
+++ b/include/logger.h
@@ -30,7 +30,7 @@ namespace logger
 {
 const std::string messageBoxLoggerName = "pMessageBox";
 const std::string messageBoxLogFormat  = "[{}:{}:{}][{}] {}";
-const std::string logFormat = "[%s:%#:%!][%l] %v";
+const std::string logFormat            = "[%s:%#:%!][%l] %v";
 
 inline void setupLogger()
 {
-- 
GitLab


From 8a0a3851adac73b67d5e77f0f57df2785598472d Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Fri, 3 Feb 2023 09:08:48 +0100
Subject: [PATCH 13/22] adding a destructor for LogWindow to pass unit tests

---
 include/logwindow.h |  8 ++++++--
 src/logwindow.cpp   | 10 ++++++++--
 2 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/include/logwindow.h b/include/logwindow.h
index 27d24e9d8..e8b945a62 100644
--- a/include/logwindow.h
+++ b/include/logwindow.h
@@ -21,6 +21,7 @@
 
 #include "spdlog//spdlog.h"
 #include "spdlog/logger.h"
+#include "spdlog/sinks/dist_sink.h"
 #include "spdlog/sinks/qt_sinks.h"
 
 #include <QWidget>
@@ -37,10 +38,13 @@ class LogWindow : public QWidget
 
 public:
     LogWindow(QWidget *parent, Ui::LogWindow *mUi);
+    ~LogWindow();
 
 private:
-    Ui::LogWindow *mUi;
-    Petrack       *mMainWindow;
+    Ui::LogWindow                                        *mUi;
+    Petrack                                              *mMainWindow;
+    std::shared_ptr<spdlog::sinks::dist_sink<std::mutex>> distSink;
+    std::shared_ptr<spdlog::sinks::qt_sink<std::mutex>>   qtSink;
 };
 
 #endif // LOGWINDOW_H
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index dab2dcd99..0aeafcc46 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -37,7 +37,13 @@ LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
     mUi->setupUi(this);
     mUi->logText->setReadOnly(true);
 
-    spdlog::default_logger()->sinks().push_back(
-        std::make_shared<spdlog::sinks::qt_sink_mt>(mUi->logText, "appendPlainText"));
+    distSink = std::make_shared<spdlog::sinks::dist_sink_mt>();
+    qtSink   = std::make_shared<spdlog::sinks::qt_sink_mt>(mUi->logText, "appendPlainText");
+    distSink->add_sink(qtSink);
+    spdlog::default_logger()->sinks().push_back(distSink);
     spdlog::default_logger()->set_pattern(logger::logFormat);
 }
+LogWindow::~LogWindow()
+{
+    distSink->remove_sink(qtSink);
+}
-- 
GitLab


From e18f7d092909302c34173b6a6ec67b595e3e1433 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Fri, 3 Feb 2023 12:25:45 +0100
Subject: [PATCH 14/22] delet ui pointer, destructor virtual, constructor
 explicit, deleted move/copy functions

---
 include/logwindow.h | 8 ++++++--
 src/logwindow.cpp   | 1 +
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/include/logwindow.h b/include/logwindow.h
index e8b945a62..cf164492d 100644
--- a/include/logwindow.h
+++ b/include/logwindow.h
@@ -37,8 +37,12 @@ class LogWindow : public QWidget
     Q_OBJECT
 
 public:
-    LogWindow(QWidget *parent, Ui::LogWindow *mUi);
-    ~LogWindow();
+    explicit LogWindow(QWidget *parent, Ui::LogWindow *mUi);
+    virtual ~LogWindow();
+    LogWindow(const LogWindow &)       = delete;
+    LogWindow &operator=(LogWindow)    = delete;
+    LogWindow(const LogWindow &&)      = delete;
+    LogWindow &operator=(LogWindow &&) = delete;
 
 private:
     Ui::LogWindow                                        *mUi;
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index 0aeafcc46..c71ce8e7c 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -45,5 +45,6 @@ LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
 }
 LogWindow::~LogWindow()
 {
+    delete mUi;
     distSink->remove_sink(qtSink);
 }
-- 
GitLab


From 0b54c0fe3591258a7ffb131a936001b05414e6f6 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Thu, 23 Mar 2023 16:18:05 +0100
Subject: [PATCH 15/22] save log to file

---
 include/logwindow.h |  3 +++
 src/logwindow.cpp   | 13 +++++++++++++
 ui/logwindow.ui     | 10 ++++++++++
 3 files changed, 26 insertions(+)

diff --git a/include/logwindow.h b/include/logwindow.h
index cf164492d..03eb88930 100644
--- a/include/logwindow.h
+++ b/include/logwindow.h
@@ -37,6 +37,9 @@ class LogWindow : public QWidget
     Q_OBJECT
 
 public:
+
+    void saveLog();
+
     explicit LogWindow(QWidget *parent, Ui::LogWindow *mUi);
     virtual ~LogWindow();
     LogWindow(const LogWindow &)       = delete;
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index c71ce8e7c..cc6bfdd50 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -42,6 +42,19 @@ LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
     distSink->add_sink(qtSink);
     spdlog::default_logger()->sinks().push_back(distSink);
     spdlog::default_logger()->set_pattern(logger::logFormat);
+    connect(mUi->saveLogButton, &QPushButton::clicked, this, &LogWindow::saveLog);
+}
+void LogWindow::saveLog(){
+    QString fileName = QFileDialog::getSaveFileName(
+        this, tr("Select txt file"), "", tr("txt file (*.txt);;All files (*.*)"));
+
+    QFile logFile(fileName);
+    logFile.open(QIODevice::Append | QIODevice::Text);
+    QTextStream outstream(&logFile);
+    outstream << mUi->logText->toPlainText() << Qt::endl;
+    logFile.close();
+
+
 }
 LogWindow::~LogWindow()
 {
diff --git a/ui/logwindow.ui b/ui/logwindow.ui
index 43ee51d85..59f1c17f8 100644
--- a/ui/logwindow.ui
+++ b/ui/logwindow.ui
@@ -21,6 +21,16 @@
      </item>
     </layout>
    </item>
+   <item>
+    <widget class="QPushButton" name="saveLogButton">
+     <property name="toolTip">
+      <string>Save Log from Window to file</string>
+     </property>
+     <property name="text">
+      <string>Save Log</string>
+     </property>
+    </widget>
+   </item>
   </layout>
  </widget>
  <resources/>
-- 
GitLab


From fc94799d61a3a84f98704b985564147243d0dfb6 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Thu, 23 Mar 2023 16:18:52 +0100
Subject: [PATCH 16/22] formatting

---
 include/logwindow.h | 1 -
 src/logwindow.cpp   | 9 ++++-----
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/include/logwindow.h b/include/logwindow.h
index 03eb88930..31d593a62 100644
--- a/include/logwindow.h
+++ b/include/logwindow.h
@@ -37,7 +37,6 @@ class LogWindow : public QWidget
     Q_OBJECT
 
 public:
-
     void saveLog();
 
     explicit LogWindow(QWidget *parent, Ui::LogWindow *mUi);
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index cc6bfdd50..004dc9aea 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -44,17 +44,16 @@ LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
     spdlog::default_logger()->set_pattern(logger::logFormat);
     connect(mUi->saveLogButton, &QPushButton::clicked, this, &LogWindow::saveLog);
 }
-void LogWindow::saveLog(){
-    QString fileName = QFileDialog::getSaveFileName(
-        this, tr("Select txt file"), "", tr("txt file (*.txt);;All files (*.*)"));
+void LogWindow::saveLog()
+{
+    QString fileName =
+        QFileDialog::getSaveFileName(this, tr("Select txt file"), "", tr("txt file (*.txt);;All files (*.*)"));
 
     QFile logFile(fileName);
     logFile.open(QIODevice::Append | QIODevice::Text);
     QTextStream outstream(&logFile);
     outstream << mUi->logText->toPlainText() << Qt::endl;
     logFile.close();
-
-
 }
 LogWindow::~LogWindow()
 {
-- 
GitLab


From 40d132985ae4fa91a1cabd9357bb65ef43f578a2 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Fri, 24 Mar 2023 12:10:03 +0100
Subject: [PATCH 17/22] base for ringbuffer + rebase

---
 include/logger.h  | 4 ++++
 src/logwindow.cpp | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/include/logger.h b/include/logger.h
index 3090528d2..3456ba43e 100644
--- a/include/logger.h
+++ b/include/logger.h
@@ -24,6 +24,7 @@
 #include <spdlog/fmt/bundled/format.h>
 #include <spdlog/fmt/ostr.h>
 #include <spdlog/sinks/stdout_sinks.h>
+#include <spdlog/sinks/ringbuffer_sink.h>
 #include <spdlog/spdlog.h>
 
 namespace logger
@@ -34,6 +35,9 @@ const std::string logFormat            = "[%s:%#:%!][%l] %v";
 
 inline void setupLogger()
 {
+    auto ringbuffer_sink = std::make_shared<spdlog::sinks::ringbuffer_sink_mt>(128);
+    spdlog::default_logger()->sinks().push_back(ringbuffer_sink);
+
     // setup global logger, which should be used to display message on the command line only
     spdlog::set_level(spdlog::level::trace);
 
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index 004dc9aea..13e16ce26 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -20,6 +20,7 @@
 
 #include "logger.h"
 #include "spdlog/sinks/dist_sink.h"
+#include "spdlog/sinks/ringbuffer_sink.h"
 #include "ui_logwindow.h"
 
 LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
@@ -37,6 +38,9 @@ LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
     mUi->setupUi(this);
     mUi->logText->setReadOnly(true);
 
+
+    auto ringBufferSink = spdlog::default_logger()->sinks()[1];
+
     distSink = std::make_shared<spdlog::sinks::dist_sink_mt>();
     qtSink   = std::make_shared<spdlog::sinks::qt_sink_mt>(mUi->logText, "appendPlainText");
     distSink->add_sink(qtSink);
-- 
GitLab


From 2fdc575cb6c01dad24f0c019f870ce7a6b49e1f1 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Fri, 24 Mar 2023 12:10:53 +0100
Subject: [PATCH 18/22] formatting

---
 include/logger.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/logger.h b/include/logger.h
index 3456ba43e..2d752f8f8 100644
--- a/include/logger.h
+++ b/include/logger.h
@@ -23,8 +23,8 @@
 #include <QString>
 #include <spdlog/fmt/bundled/format.h>
 #include <spdlog/fmt/ostr.h>
-#include <spdlog/sinks/stdout_sinks.h>
 #include <spdlog/sinks/ringbuffer_sink.h>
+#include <spdlog/sinks/stdout_sinks.h>
 #include <spdlog/spdlog.h>
 
 namespace logger
-- 
GitLab


From 1c9e56c51488ef9e45ad180d02689f3178bd2565 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Mon, 27 Mar 2023 09:35:59 +0200
Subject: [PATCH 19/22] changed copyright year and used new connect syntax for
 qaction

---
 include/logwindow.h | 2 +-
 src/logwindow.cpp   | 2 +-
 src/petrack.cpp     | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/include/logwindow.h b/include/logwindow.h
index 31d593a62..c08e0038f 100644
--- a/include/logwindow.h
+++ b/include/logwindow.h
@@ -1,6 +1,6 @@
 /*
  * PeTrack - Software for tracking pedestrians movement in videos
- * Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
+ * Copyright (C) 2023 Forschungszentrum Jülich GmbH, IAS-7
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index 13e16ce26..f75b1443d 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -1,6 +1,6 @@
 /*
  * PeTrack - Software for tracking pedestrians movement in videos
- * Copyright (C) 2022 Forschungszentrum Jülich GmbH, IAS-7
+ * Copyright (C) 2023 Forschungszentrum Jülich GmbH, IAS-7
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
diff --git a/src/petrack.cpp b/src/petrack.cpp
index 23ed6d27a..de8db8c96 100644
--- a/src/petrack.cpp
+++ b/src/petrack.cpp
@@ -1859,7 +1859,7 @@ void Petrack::createActions()
     connect(mHideControlsAct, SIGNAL(changed()), this, SLOT(showHideControlWidget()));
 
     mShowLogWindowAct = new QAction(tr("&Show log window"), this);
-    connect(mShowLogWindowAct, SIGNAL(triggered()), this, SLOT(showLogWindow()));
+    connect(mShowLogWindowAct, &QAction::triggered, this, &Petrack::showLogWindow);
 
     mCropZoomViewAct = new QAction(tr("&Transform while saving"), this); // Crop and zoom while saving
     mCropZoomViewAct->setCheckable(true);
-- 
GitLab


From 55839cce5039ae96b8aee79eb14a5bc0fabb325a Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Tue, 18 Apr 2023 13:50:25 +0200
Subject: [PATCH 20/22] messages from main.cpp are displayed in logwindow
 correctly

---
 include/logger.h  |  7 ++++---
 src/logwindow.cpp | 10 ++++++++--
 2 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/include/logger.h b/include/logger.h
index 2d752f8f8..9beecb051 100644
--- a/include/logger.h
+++ b/include/logger.h
@@ -35,9 +35,6 @@ const std::string logFormat            = "[%s:%#:%!][%l] %v";
 
 inline void setupLogger()
 {
-    auto ringbuffer_sink = std::make_shared<spdlog::sinks::ringbuffer_sink_mt>(128);
-    spdlog::default_logger()->sinks().push_back(ringbuffer_sink);
-
     // setup global logger, which should be used to display message on the command line only
     spdlog::set_level(spdlog::level::trace);
 
@@ -47,6 +44,10 @@ inline void setupLogger()
     // setup logger, which is used when also a dialog is displayed to the user
     auto messageBoxLogger = spdlog::stdout_logger_mt("pMessageBox");
     messageBoxLogger->set_pattern("%v");
+
+    // add ringbuffer_sink to default logger to store messages for logwindow
+    auto ringbufferSink = std::make_shared<spdlog::sinks::ringbuffer_sink_mt>(10);
+    spdlog::default_logger()->sinks().push_back(ringbufferSink);
 }
 } // namespace logger
 
diff --git a/src/logwindow.cpp b/src/logwindow.cpp
index f75b1443d..314880f74 100644
--- a/src/logwindow.cpp
+++ b/src/logwindow.cpp
@@ -38,8 +38,14 @@ LogWindow::LogWindow(QWidget *parent, Ui::LogWindow *ui) : QWidget(parent)
     mUi->setupUi(this);
     mUi->logText->setReadOnly(true);
 
-
-    auto ringBufferSink = spdlog::default_logger()->sinks()[1];
+    // extract messages from the ringbuffer_sink
+    auto ringbufferSink = static_cast<spdlog::sinks::ringbuffer_sink_mt *>(spdlog::default_logger()->sinks()[1].get());
+    std::vector<std::string> logMessages = ringbufferSink->last_formatted(10);
+    for(std::string s : logMessages)
+    {
+        s.erase(std::remove(s.begin(), s.end(), '\n'), s.cend());
+        mUi->logText->appendPlainText(QString::fromStdString(s));
+    }
 
     distSink = std::make_shared<spdlog::sinks::dist_sink_mt>();
     qtSink   = std::make_shared<spdlog::sinks::qt_sink_mt>(mUi->logText, "appendPlainText");
-- 
GitLab


From 544c978fad4ec7ac0010aff85dc647d159646b4f Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Wed, 19 Apr 2023 14:59:57 +0200
Subject: [PATCH 21/22] review comments + rebase

---
 include/logger.h | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/include/logger.h b/include/logger.h
index 9beecb051..668bdfeb1 100644
--- a/include/logger.h
+++ b/include/logger.h
@@ -29,25 +29,26 @@
 
 namespace logger
 {
+// Note: when changing this, also adapt messageBoxLogFormat accordingly!
+const std::string logFormat            = "[%s:%#:%!][%l] %v";
 const std::string messageBoxLoggerName = "pMessageBox";
 const std::string messageBoxLogFormat  = "[{}:{}:{}][{}] {}";
-const std::string logFormat            = "[%s:%#:%!][%l] %v";
 
 inline void setupLogger()
 {
+    // add ringbuffer_sink to default logger to store messages for logwindow
+    auto ringbufferSink = std::make_shared<spdlog::sinks::ringbuffer_sink_mt>(10);
+    spdlog::default_logger()->sinks().push_back(ringbufferSink);
+
     // setup global logger, which should be used to display message on the command line only
     spdlog::set_level(spdlog::level::trace);
 
-    // Note: when changing this, also adapt messageBoxLogFormat accordingly!
     spdlog::set_pattern(logFormat);
 
     // setup logger, which is used when also a dialog is displayed to the user
     auto messageBoxLogger = spdlog::stdout_logger_mt("pMessageBox");
     messageBoxLogger->set_pattern("%v");
 
-    // add ringbuffer_sink to default logger to store messages for logwindow
-    auto ringbufferSink = std::make_shared<spdlog::sinks::ringbuffer_sink_mt>(10);
-    spdlog::default_logger()->sinks().push_back(ringbufferSink);
 }
 } // namespace logger
 
-- 
GitLab


From d51a1ea54f9ef72e1409f609a0d3b53b2e33ec63 Mon Sep 17 00:00:00 2001
From: ld3812s <luke.dressen@alumni.fh-aachen.de>
Date: Wed, 19 Apr 2023 15:02:56 +0200
Subject: [PATCH 22/22] formatting

---
 include/logger.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/include/logger.h b/include/logger.h
index 668bdfeb1..372e95e02 100644
--- a/include/logger.h
+++ b/include/logger.h
@@ -48,7 +48,6 @@ inline void setupLogger()
     // setup logger, which is used when also a dialog is displayed to the user
     auto messageBoxLogger = spdlog::stdout_logger_mt("pMessageBox");
     messageBoxLogger->set_pattern("%v");
-
 }
 } // namespace logger
 
-- 
GitLab