diff options
78 files changed, 12498 insertions, 385 deletions
@@ -36,4 +36,3 @@ Makefile.in  # project specific  build/* -.pc diff --git a/.travis.yml b/.travis.yml index be68f7f..b3dd807 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ before_install:    - if [ $TRAVIS_OS_NAME == osx ]; then brew update && brew install libconfig log4cpp; fi  install: -  - curl -L https://github.com/nukedzn/psocksxx/releases/download/v1.0.0/psocksxx-1.0.0.tar.gz | tar -zx -C /tmp +  - curl -L https://github.com/nukedzn/psocksxx/releases/download/v1.1.1/psocksxx-1.1.1.tar.gz | tar -zx -C /tmp    - cd /tmp/psocksxx-* && ./configure --prefix=/tmp/root && make && make install    - if [ $TRAVIS_OS_NAME == linux ]; then curl -L http://www.hyperrealm.com/packages/libconfig-1.4.10.tar.gz | tar -zx -C /tmp; fi    - if [ $TRAVIS_OS_NAME == linux ]; then cd /tmp/libconfig-* && ./configure --prefix=/tmp/root && make && make install; fi @@ -51,8 +51,7 @@ You can download the source distributions from https://github.com/uditha-atukora  ## Dependencies  * [psocksxx >= 0.0.6](https://nukedzn.github.io/psocksxx/) -* [libconfig++ >= 1.4](http://www.hyperrealm.com/libconfig/) -* log4cpp >= 1.0 +* [libconfig++ >= 1.4](https://github.com/hyperrealm/libconfig)  * python 2.7 (for modpy module) @@ -1,8 +1,11 @@ +2.0.0 - 15th June 2018 +	*   Use spdlog instead of log4cpp as the logger (issue #6) +  1.0.2 - 19th November 2017 -	*    Fix broken v1.0.1 release +	*   Fix broken v1.0.1 release  1.0.1 - 19th November 2017 -	*    Make builds reproducible (https://bugs.debian.org/882112) +	*   Make builds reproducible (https://bugs.debian.org/882112)  1.0.0 - 25th November 2015  	*   [New] Configurable communication (socket) timeouts diff --git a/conf/bitz-server.conf.in b/conf/bitz-server.conf.in index 416a555..5032a70 100644 --- a/conf/bitz-server.conf.in +++ b/conf/bitz-server.conf.in @@ -6,7 +6,6 @@ port = 1344;  pid_file     = "@localstatedir@/run/bitz/bitz-server.pid";  log_file     = "@localstatedir@/log/bitz/bitz-server.log"; -log_category = "bitz-server";  # maximum number of workers  max_workers         = 1; diff --git a/configure.ac b/configure.ac index d972716..199e35a 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@  # Process this file with autoconf to produce a configure script.  AC_PREREQ([2.68]) -AC_INIT([bitz-server], [1.0.2], [https://github.com/uditha-atukorala/bitz-server/issues]) +AC_INIT([bitz-server], [2.0.0], [https://github.com/uditha-atukorala/bitz-server/issues])  AC_CONFIG_AUX_DIR([aux-build])  AC_CONFIG_MACRO_DIR([aux-build/m4])  AC_CONFIG_HEADERS([include/config.h]) @@ -49,6 +49,7 @@ AC_PROG_LIBTOOL  # Language  AC_LANG(C++) +AX_CXX_COMPILE_STDCXX_11()  # Options  AC_ARG_WITH([config], @@ -75,9 +76,6 @@ PKG_PROG_PKG_CONFIG  PKG_CHECK_MODULES([libconfig], [libconfig++ >= 1.4],,  	AC_MSG_ERROR([libconfig++ 1.4 or newer not found.])  ) -PKG_CHECK_MODULES([log4cpp], [log4cpp >= 1.0],, -	AC_MSG_ERROR([log4cpp 1.0 or newer not found.]) -)  PKG_CHECK_MODULES([psocksxx], [psocksxx >= 0.0.6],,  	AC_MSG_ERROR([psocksxx 0.0.6 or newer not found.])  ) diff --git a/debian/bitz-server-doc.docs b/debian/bitz-server-doc.docs deleted file mode 100644 index 42061c0..0000000 --- a/debian/bitz-server-doc.docs +++ /dev/null @@ -1 +0,0 @@ -README.md
\ No newline at end of file diff --git a/debian/changelog b/debian/changelog index 2a2779a..240effb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,25 @@ +bitz-server (2.0.0-1) unstable; urgency=medium + +  * New upstream release: +    - Switch from log4cpp to spdlog. +  * Migrate to debhelper 11: +    - Change debian/compat to 11. +    - Bump minimum debhelper version in debian/control to >= 11. +  * Declare compliance with Debian Policy 4.1.5 (No changes needed). +  * debian/control: +    - Switch Vcs-* to new location. +    - Change Build-Depends from liblog4cpp5-dev to libspdlog-dev. +  * debian/rules: +    - Switch from deprecated dh_systemd_enable to dh_installsystemd. +  * debian/copyright: +    - Use secure copyright format URI. +    - Add new sections for lib/spdlog. +    - Bump years to 2018. +  * New debian/patches/0100-spelling.patch. +  * Remove debian/bitz-server-doc.docs to prevent twin install of README.md. + + -- Jörg Frings-Fürst <debian@jff.email>  Tue, 10 Jul 2018 20:15:54 +0200 +  bitz-server (1.0.2-1) unstable; urgency=medium    * New upstream release: diff --git a/debian/compat b/debian/compat index f599e28..b4de394 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -10 +11 diff --git a/debian/control b/debian/control index 843180f..7a631d0 100644 --- a/debian/control +++ b/debian/control @@ -4,19 +4,19 @@ Priority: optional  Maintainer: Jörg Frings-Fürst <debian@jff.email>  Uploaders: Uditha Atukorala <ua@nuked.zone>  Build-Depends: - debhelper (>= 10), + debhelper (>= 11),   dh-python,   libconfig++-dev, - liblog4cpp5-dev, + libspdlog-dev,   libpsocksxx-dev,   python-dev,   libpython-dev  Build-Depends-Indep:   doxygen -Standards-Version: 4.1.2.0 +Standards-Version: 4.1.5  Homepage: https://github.com/uditha-atukorala/bitz-server -Vcs-Git: https://anonscm.debian.org/cgit/collab-maint/bitz-server.git -Vcs-Browser: https://anonscm.debian.org/cgit/collab-maint/bitz-server.git +Vcs-Git: git://jff.email/opt/git/bitz-server.git +Vcs-Browser: https://jff.email/cgit/bitz-server.git/  Package: bitz-server  Architecture: any diff --git a/debian/copyright b/debian/copyright index 443ff37..b78aa13 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,4 +1,4 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/  Upstream-Name: bitz-server  Source: https://github.com/uditha-atukorala/bitz-server @@ -6,8 +6,29 @@ Files: *  Copyright: 2012-2017 Uditha Atukorala <ua@nuked.zone>  License: GPL-3+ +Files: lib/spdlog/* +Copyright: 2015-2016 Gabi Melman +License: MIT + +Files: lib/spdlog/fmt/bundled/* +Copyright: 2012-2016 Victor Zverovich +License: BSD-2 + +Files: lib/spdlog/sinks/dist_sink.h +Copyright: 2015      David Schury, Gabi Melman +License: MIT + +Files: lib/spdlog/sinks/ansicolor_sink.h +       lib/spdlog/sinks/wincolor_sink.h +Copyright: 2016-2017 spdlog authors +License: MIT + +Files: lib/spdlog/sinks/windebug_sink.h +Copyright: 2017      Alexander Dalshov +License: MIT +  Files: debian/* -Copyright: 2014-2017 Jörg Frings-Fürst <debian@jff.email> +Copyright: 2014-2018 Jörg Frings-Fürst <debian@jff.email>  License: GPL-3+  License: GPL-3+ @@ -26,3 +47,53 @@ License: GPL-3+   .   On Debian systems, the complete text of the GNU General   Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". + +License: BSD-2 + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + . +     * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + . +     * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +License: MIT + All rights reserved.  No part of this source code may be reproduced, + stored in a retrieval system, or transmitted, in any form or by any + means, electronic, mechanical, photocopying, recording or otherwise, + except as stated in the end-user licence agreement, without the prior + permission of the copyright owners. + . + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, provided + that the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation, and that the name of OSF, UI or X/Open not be used in + advertising or publicity pertaining to distribution of the software + without specific, written prior permission.  OSF, UI and X/Open make + no representations about the suitability of this software for any purpose. + It is provided "as is" without express or implied warranty. + . + OSF, UI and X/Open DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + EVENT SHALL OSF, UI or X/Open BE LIABLE FOR ANY SPECIAL, INDIRECT OR + CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. diff --git a/debian/patches/0100-spelling.patch b/debian/patches/0100-spelling.patch new file mode 100644 index 0000000..2e3e112 --- /dev/null +++ b/debian/patches/0100-spelling.patch @@ -0,0 +1,18 @@ +Description: Correct spelling +Author: Jörg Frings-Fürst <debian@jff.email> +Forwarded: no +Last-Update: 2018-07-09 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +Index: trunk/lib/spdlog/common.h +=================================================================== +--- trunk.orig/lib/spdlog/common.h ++++ trunk/lib/spdlog/common.h +@@ -183,6 +183,6 @@ using filename_t = std::string; +     }                                                                                                                                      \ +     catch (...)                                                                                                                            \ +     {                                                                                                                                      \ +-        _err_handler("Unknown exeption in logger");                                                                                        \ ++        _err_handler("Unknown exception in logger");                                                                                        \ +     } + } // namespace spdlog diff --git a/debian/patches/series b/debian/patches/series index 537cd0a..0405e13 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,2 +1,3 @@  0001-disable_search_doc.patch  0600-man_page_spelling.patch +0100-spelling.patch diff --git a/debian/rules b/debian/rules index c0918f1..8e8a98a 100755 --- a/debian/rules +++ b/debian/rules @@ -38,5 +38,5 @@ override_dh_makeshlibs:  	dh_makeshlibs  	dpkg-gensymbols -v$(VERSION) -plibicap1 -override_dh_systemd_enable: -	dh_systemd_enable -pbitz-server --no-enable bitz-server.service +override_dh_installsystemd: +	dh_installsystemd -pbitz-server --no-enable --name=bitz-server bitz-server.service diff --git a/lib/Makefile.am b/lib/Makefile.am index af3b92e..a02b25d 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,3 +1,7 @@  ## [bitz-server] lib/  SUBDIRS = icap +# include spdlog headers in distributions +EXTRA_DIST = \ +	spdlog + diff --git a/lib/spdlog/async_logger.h b/lib/spdlog/async_logger.h new file mode 100644 index 0000000..75641d2 --- /dev/null +++ b/lib/spdlog/async_logger.h @@ -0,0 +1,72 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +// Very fast asynchronous logger (millions of logs per second on an average desktop) +// Uses pre allocated lockfree queue for maximum throughput even under large number of threads. +// 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) +//    3. will throw spdlog_ex upon log exceptions +// Upon destruction, logs all remaining messages in the queue before destructing.. + +#include "common.h" +#include "logger.h" + +#include <chrono> +#include <functional> +#include <memory> +#include <string> + +namespace spdlog { + +namespace details { +class async_log_helper; +} + +class async_logger SPDLOG_FINAL : public logger +{ +public: +    template<class It> +    async_logger(const std::string &logger_name, const It &begin, const It &end, size_t queue_size, +        const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, +        const std::function<void()> &worker_warmup_cb = nullptr, +        const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), +        const std::function<void()> &worker_teardown_cb = nullptr); + +    async_logger(const std::string &logger_name, sinks_init_list sinks, size_t queue_size, +        const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, +        const std::function<void()> &worker_warmup_cb = nullptr, +        const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), +        const std::function<void()> &worker_teardown_cb = nullptr); + +    async_logger(const std::string &logger_name, sink_ptr single_sink, size_t queue_size, +        const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, +        const std::function<void()> &worker_warmup_cb = nullptr, +        const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), +        const std::function<void()> &worker_teardown_cb = nullptr); + +    // Wait for the queue to be empty, and flush synchronously +    // Warning: this can potentially last forever as we wait it to complete +    void flush() override; + +    // Error handler +    void set_error_handler(log_err_handler) override; +    log_err_handler error_handler() override; + +protected: +    void _sink_it(details::log_msg &msg) override; +    void _set_formatter(spdlog::formatter_ptr msg_formatter) override; +    void _set_pattern(const std::string &pattern, pattern_time_type pattern_time) override; + +private: +    std::unique_ptr<details::async_log_helper> _async_log_helper; +}; +} // namespace spdlog + +#include "details/async_logger_impl.h" diff --git a/lib/spdlog/common.h b/lib/spdlog/common.h new file mode 100644 index 0000000..e02cb0b --- /dev/null +++ b/lib/spdlog/common.h @@ -0,0 +1,188 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#define SPDLOG_VERSION "0.17.0" + +#include "tweakme.h" + +#include <atomic> +#include <chrono> +#include <exception> +#include <functional> +#include <initializer_list> +#include <memory> +#include <string> +#include <unordered_map> + +#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) +#include <codecvt> +#include <locale> +#endif + +#include "details/null_mutex.h" + +// visual studio upto 2013 does not support noexcept nor constexpr +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define SPDLOG_NOEXCEPT throw() +#define SPDLOG_CONSTEXPR +#else +#define SPDLOG_NOEXCEPT noexcept +#define SPDLOG_CONSTEXPR constexpr +#endif + +// final keyword support. On by default. See tweakme.h +#if defined(SPDLOG_NO_FINAL) +#define SPDLOG_FINAL +#else +#define SPDLOG_FINAL final +#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 + +#include "fmt/fmt.h" + +namespace spdlog { + +class formatter; + +namespace sinks { +class 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 formatter_ptr = std::shared_ptr<spdlog::formatter>; +#if defined(SPDLOG_NO_ATOMIC_LEVELS) +using level_t = details::null_atomic_int; +#else +using level_t = std::atomic<int>; +#endif + +using log_err_handler = std::function<void(const std::string &err_msg)>; + +// Log level enum +namespace level { +enum level_enum +{ +    trace = 0, +    debug = 1, +    info = 2, +    warn = 3, +    err = 4, +    critical = 5, +    off = 6 +}; + +#if !defined(SPDLOG_LEVEL_NAMES) +#define SPDLOG_LEVEL_NAMES                                                                                                                 \ +    {                                                                                                                                      \ +        "trace", "debug", "info", "warning", "error", "critical", "off"                                                                    \ +    } +#endif +static const char *level_names[] SPDLOG_LEVEL_NAMES; + +static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"}; + +inline const char *to_str(spdlog::level::level_enum l) +{ +    return level_names[l]; +} + +inline const char *to_short_str(spdlog::level::level_enum l) +{ +    return short_level_names[l]; +} +inline spdlog::level::level_enum from_str(const std::string &name) +{ +    static std::unordered_map<std::string, level_enum> name_to_level = // map string->level +        {{level_names[0], level::trace},                               // trace +            {level_names[1], level::debug},                            // debug +            {level_names[2], level::info},                             // info +            {level_names[3], level::warn},                             // warn +            {level_names[4], level::err},                              // err +            {level_names[5], level::critical},                         // critical +            {level_names[6], level::off}};                             // off + +    auto lvl_it = name_to_level.find(name); +    return lvl_it != name_to_level.end() ? lvl_it->second : level::off; +} + +using level_hasher = std::hash<int>; +} // namespace level + +// +// Async overflow policy - block by default. +// +enum class async_overflow_policy +{ +    block_retry,    // Block / yield / sleep until message can be enqueued +    discard_log_msg // Discard the message it enqueue fails +}; + +// +// 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_ex : public std::exception +{ +public: +    explicit spdlog_ex(std::string msg) +        : _msg(std::move(msg)) +    { +    } + +    spdlog_ex(const std::string &msg, int last_errno) +    { +        fmt::MemoryWriter writer; +        fmt::format_system_error(writer, last_errno, msg); +        _msg = writer.str(); +    } + +    const char *what() const SPDLOG_NOEXCEPT override +    { +        return _msg.c_str(); +    } + +private: +    std::string _msg; +}; + +// +// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) +// +#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) +using filename_t = std::wstring; +#else +using filename_t = std::string; +#endif + +#define SPDLOG_CATCH_AND_HANDLE                                                                                                            \ +    catch (const std::exception &ex)                                                                                                       \ +    {                                                                                                                                      \ +        _err_handler(ex.what());                                                                                                           \ +    }                                                                                                                                      \ +    catch (...)                                                                                                                            \ +    {                                                                                                                                      \ +        _err_handler("Unknown exeption in logger");                                                                                        \ +    } +} // namespace spdlog diff --git a/lib/spdlog/contrib/README.md b/lib/spdlog/contrib/README.md new file mode 100644 index 0000000..e3abc34 --- /dev/null +++ b/lib/spdlog/contrib/README.md @@ -0,0 +1 @@ +Please put here your contribs. Popular contribs will be moved to main tree after stablization diff --git a/lib/spdlog/contrib/sinks/.gitignore b/lib/spdlog/contrib/sinks/.gitignore new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/spdlog/contrib/sinks/.gitignore @@ -0,0 +1 @@ + diff --git a/lib/spdlog/contrib/sinks/step_file_sink.h b/lib/spdlog/contrib/sinks/step_file_sink.h new file mode 100644 index 0000000..85e24e9 --- /dev/null +++ b/lib/spdlog/contrib/sinks/step_file_sink.h @@ -0,0 +1,216 @@ +#pragma once + +#include "../../details/file_helper.h" +#include "../../details/null_mutex.h" +#include "../../fmt/fmt.h" +#include "../../sinks/base_sink.h" + +#include <algorithm> +#include <cerrno> +#include <chrono> +#include <cstdio> +#include <ctime> +#include <mutex> +#include <string> + +// Example for spdlog.h +// +// Create a file logger which creates new files with a specified time step and fixed file size: +// +// std::shared_ptr<logger> step_logger_mt(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, const +// filename_t &tmp_ext = ".tmp", unsigned max_file_size = std::numeric_limits<unsigned>::max(), bool delete_empty_files = true, const +// filename_t &file_header = ""); std::shared_ptr<logger> step_logger_st(const std::string &logger_name, const filename_t &filename, +// unsigned seconds = 60, const filename_t &tmp_ext = ".tmp", unsigned max_file_size = std::numeric_limits<unsigned>::max()); + +// Example for spdlog_impl.h +// Create a file logger that creates new files with a specified increment +// inline std::shared_ptr<spdlog::logger> spdlog::step_logger_mt( +//     const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, const filename_t &tmp_ext, unsigned max_file_size, +//     bool delete_empty_files, const filename_t &file_header) +// { +//     return create<spdlog::sinks::step_file_sink_mt>(logger_name, filename_fmt, seconds, tmp_ext, max_file_size, delete_empty_files, +//     file_header); +// } + +// inline std::shared_ptr<spdlog::logger> spdlog::step_logger_st( +//     const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, const filename_t &tmp_ext, unsigned max_file_size, +//     bool delete_empty_files, const filename_t &file_header) +// { +//     return create<spdlog::sinks::step_file_sink_st>(logger_name, filename_fmt, seconds, tmp_ext, max_file_size, delete_empty_files, +//     file_header); +// } + +namespace spdlog { +namespace sinks { + +/* + * Default generator of step log file names. + */ +struct default_step_file_name_calculator +{ +    // Create filename for the form filename_YYYY-MM-DD_hh-mm-ss.ext +    static std::tuple<filename_t, filename_t> calc_filename(const filename_t &filename, const filename_t &tmp_ext) +    { +        std::tm tm = spdlog::details::os::localtime(); +        filename_t basename, ext; +        std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); +        std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; +        w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, +            tm.tm_hour, tm.tm_min, tm.tm_sec, tmp_ext); +        return std::make_tuple(w.str(), ext); +    } +}; + +/* + * Rotating file sink based on size and a specified time step + */ +template<class Mutex, class FileNameCalc = default_step_file_name_calculator> +class step_file_sink SPDLOG_FINAL : public base_sink<Mutex> +{ +public: +    step_file_sink(filename_t base_filename, unsigned step_seconds, filename_t tmp_ext, unsigned max_size, bool delete_empty_files, +        filename_t file_header) +        : _base_filename(std::move(base_filename)) +        , _tmp_ext(std::move(tmp_ext)) +        , _step_seconds(step_seconds) +        , _max_size(max_size) +        , _delete_empty_files(delete_empty_files) +    { +        if (step_seconds == 0) +        { +            throw spdlog_ex("step_file_sink: Invalid time step in ctor"); +        } + +        if (!file_header.empty()) +        { +            pattern_formatter formatter_for_file_header("%v"); +            _file_header.raw << file_header; +            formatter_for_file_header.format(_file_header); +        } + +        if (max_size <= _file_header.formatted.size()) +        { +            throw spdlog_ex("step_file_sink: Invalid max log size in ctor"); +        } + +        _tp = _next_tp(); +        std::tie(_current_filename, _ext) = FileNameCalc::calc_filename(_base_filename, _tmp_ext); + +        if (_tmp_ext == _ext) +        { +            throw spdlog_ex("step_file_sink: The temporary extension matches the specified in ctor"); +        } + +        _file_helper.open(_current_filename); +        _current_size = _file_helper.size(); // expensive. called only once + +        if (!_current_size) +        { +            _current_size += _file_header.formatted.size(); +            if (_current_size) +                _file_helper.write(_file_header); +        } +    } + +    ~step_file_sink() +    { +        try +        { +            close_current_file(); +        } +        catch (...) +        { +        } +    } + +protected: +    void _sink_it(const details::log_msg &msg) override +    { +        auto msg_size = msg.formatted.size(); + +        if (std::chrono::system_clock::now() >= _tp || _current_size + msg_size > _max_size) +        { +            filename_t new_filename; +            std::tie(new_filename, std::ignore) = FileNameCalc::calc_filename(_base_filename, _tmp_ext); + +            bool change_occured = !details::file_helper::file_exists(new_filename); +            if (change_occured) +            { +                close_current_file(); + +                _current_filename = std::move(new_filename); + +                _file_helper.open(_current_filename); +            } + +            _tp = _next_tp(); + +            if (change_occured) +            { +                _current_size = _file_header.formatted.size(); +                if (_current_size) +                    _file_helper.write(_file_header); +            } +        } + +        _current_size += msg_size; +        _file_helper.write(msg); +    } + +    void _flush() override +    { +        _file_helper.flush(); +    } + +private: +    std::chrono::system_clock::time_point _next_tp() +    { +        return std::chrono::system_clock::now() + _step_seconds; +    } + +    void close_current_file() +    { +        using details::os::filename_to_str; + +        // Delete empty files, if required +        if (_delete_empty_files && _current_size <= _file_header.formatted.size()) +        { +            if (details::os::remove(_current_filename) != 0) +            { +                throw spdlog_ex("step_file_sink: not remove " + filename_to_str(_current_filename), errno); +            } + +            return; +        } + +        filename_t target; +        std::tie(target, std::ignore) = details::file_helper::split_by_extenstion(_current_filename); +        target += _ext; + +        if (details::file_helper::file_exists(_current_filename) && details::os::rename(_current_filename, target) != 0) +        { +            throw spdlog_ex( +                "step_file_sink: failed renaming " + filename_to_str(_current_filename) + " to " + filename_to_str(target), errno); +        } +    } + +    const filename_t _base_filename; +    const filename_t _tmp_ext; +    const std::chrono::seconds _step_seconds; +    const unsigned _max_size; +    const bool _delete_empty_files; + +    std::chrono::system_clock::time_point _tp; +    filename_t _current_filename; +    filename_t _ext; +    unsigned _current_size; + +    details::file_helper _file_helper; +    details::log_msg _file_header; +}; + +using step_file_sink_mt = step_file_sink<std::mutex>; +using step_file_sink_st = step_file_sink<details::null_mutex>; + +} // namespace sinks +} // namespace spdlog diff --git a/lib/spdlog/details/async_log_helper.h b/lib/spdlog/details/async_log_helper.h new file mode 100644 index 0000000..49789b9 --- /dev/null +++ b/lib/spdlog/details/async_log_helper.h @@ -0,0 +1,333 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +// async log helper : +// Process logs asynchronously using a back thread. +// +// If the internal queue of log messages reaches its max size, +// then the client call will block until there is more room. +// + +#pragma once + +#include "../common.h" +#include "../details/log_msg.h" +#include "../details/mpmc_blocking_q.h" +#include "../details/os.h" +#include "../formatter.h" +#include "../sinks/sink.h" + +#include <chrono> +#include <condition_variable> +#include <exception> +#include <functional> +#include <memory> +#include <string> +#include <thread> +#include <utility> +#include <vector> + +namespace spdlog { +namespace details { + +class async_log_helper +{ +    // Async msg to move to/from the queue +    // Movable only. should never be copied +    enum class async_msg_type +    { +        log, +        flush, +        terminate +    }; + +    struct async_msg +    { +        level::level_enum level; +        log_clock::time_point time; +        size_t thread_id; +        std::string txt; +        async_msg_type msg_type; +        size_t msg_id; + +        async_msg() = default; +        ~async_msg() = default; + +        explicit async_msg(async_msg_type m_type) +            : level(level::info) +            , thread_id(0) +            , msg_type(m_type) +            , msg_id(0) +        { +        } + +        async_msg(async_msg &&other) = default; +        async_msg &operator=(async_msg &&other) = default; + +        // never copy or assign. should only be moved.. +        async_msg(const async_msg &) = delete; +        async_msg &operator=(const async_msg &other) = delete; + +        // construct from log_msg +        explicit async_msg(const details::log_msg &m) +            : level(m.level) +            , time(m.time) +            , thread_id(m.thread_id) +            , txt(m.raw.data(), m.raw.size()) +            , msg_type(async_msg_type::log) +            , msg_id(m.msg_id) +        { +        } + +        // copy into log_msg +        void fill_log_msg(log_msg &msg, std::string *logger_name) +        { +            msg.logger_name = logger_name; +            msg.level = level; +            msg.time = time; +            msg.thread_id = thread_id; +            msg.raw.clear(); +            msg.raw << txt; +            msg.msg_id = msg_id; +        } +    }; + +public: +    using item_type = async_msg; +    using q_type = details::mpmc_bounded_queue<item_type>; + +    using clock = std::chrono::steady_clock; + +    async_log_helper(std::string logger_name, formatter_ptr formatter, std::vector<sink_ptr> sinks, size_t queue_size, +        const log_err_handler err_handler, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, +        std::function<void()> worker_warmup_cb = nullptr, +        const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), +        std::function<void()> worker_teardown_cb = nullptr); + +    void log(const details::log_msg &msg); + +    // stop logging and join the back thread +    ~async_log_helper(); + +    async_log_helper(const async_log_helper &) = delete; +    async_log_helper &operator=(const async_log_helper &) = delete; + +    void set_formatter(formatter_ptr msg_formatter); + +    void flush(); + +    void set_error_handler(spdlog::log_err_handler err_handler); + +private: +    std::string _logger_name; +    formatter_ptr _formatter; +    std::vector<std::shared_ptr<sinks::sink>> _sinks; + +    // queue of messages to log +    q_type _q; + +    log_err_handler _err_handler; + +    std::chrono::time_point<log_clock> _last_flush; + +    // overflow policy +    const async_overflow_policy _overflow_policy; + +    // worker thread warmup callback - one can set thread priority, affinity, etc +    const std::function<void()> _worker_warmup_cb; + +    // auto periodic sink flush parameter +    const std::chrono::milliseconds _flush_interval_ms; + +    // worker thread teardown callback +    const std::function<void()> _worker_teardown_cb; + +    std::mutex null_mutex_; +    // null_mutex null_mutex_; +    std::condition_variable_any not_empty_cv_; +    std::condition_variable_any not_full_cv_; + +    // worker thread +    std::thread _worker_thread; + +    void enqueue_msg(async_msg &&new_msg, async_overflow_policy policy); + +    // worker thread main loop +    void worker_loop(); + +    // dequeue next message from the queue and process it. +    // return false if termination of the queue is required +    bool process_next_msg(); + +    void handle_flush_interval(); + +    void flush_sinks(); +}; +} // namespace details +} // namespace spdlog + +/////////////////////////////////////////////////////////////////////////////// +// async_sink class implementation +/////////////////////////////////////////////////////////////////////////////// +inline spdlog::details::async_log_helper::async_log_helper(std::string logger_name, formatter_ptr formatter, std::vector<sink_ptr> sinks, +    size_t queue_size, log_err_handler err_handler, const async_overflow_policy overflow_policy, std::function<void()> worker_warmup_cb, +    const std::chrono::milliseconds &flush_interval_ms, std::function<void()> worker_teardown_cb) +    : _logger_name(std::move(logger_name)) +    , _formatter(std::move(formatter)) +    , _sinks(std::move(sinks)) +    , _q(queue_size) +    , _err_handler(std::move(err_handler)) +    , _last_flush(os::now()) +    , _overflow_policy(overflow_policy) +    , _worker_warmup_cb(std::move(worker_warmup_cb)) +    , _flush_interval_ms(flush_interval_ms) +    , _worker_teardown_cb(std::move(worker_teardown_cb)) +{ +    _worker_thread = std::thread(&async_log_helper::worker_loop, this); +} + +// send to the worker thread terminate message, and join it. +inline spdlog::details::async_log_helper::~async_log_helper() +{ +    try +    { +        enqueue_msg(async_msg(async_msg_type::terminate), async_overflow_policy::block_retry); +        _worker_thread.join(); +    } +    catch (...) // don't crash in destructor +    { +    } +} + +// try to push and block until succeeded (if the policy is not to discard when the queue is full) +inline void spdlog::details::async_log_helper::log(const details::log_msg &msg) +{ +    enqueue_msg(async_msg(msg), _overflow_policy); +} + +inline void spdlog::details::async_log_helper::enqueue_msg(details::async_log_helper::async_msg &&new_msg, async_overflow_policy policy) +{ + +    // block until succeeded pushing to the queue +    if (policy == async_overflow_policy::block_retry) +    { +        _q.enqueue(std::move(new_msg)); +    } +    else +    { +        _q.enqueue_nowait(std::move(new_msg)); +    } +} + +// optionally wait for the queue be empty and request flush from the sinks +inline void spdlog::details::async_log_helper::flush() +{ +    enqueue_msg(async_msg(async_msg_type::flush), _overflow_policy); +} + +inline void spdlog::details::async_log_helper::worker_loop() +{ +    if (_worker_warmup_cb) +    { +        _worker_warmup_cb(); +    } +    auto active = true; +    while (active) +    { +        try +        { +            active = process_next_msg(); +        } +        SPDLOG_CATCH_AND_HANDLE +    } +    if (_worker_teardown_cb) +    { +        _worker_teardown_cb(); +    } +} + +// process next message in the queue +// return true if this thread should still be active (while no terminate msg was received) +inline bool spdlog::details::async_log_helper::process_next_msg() +{ +    async_msg incoming_async_msg; +    bool dequeued = _q.dequeue_for(incoming_async_msg, std::chrono::seconds(2)); +    if (!dequeued) +    { +        handle_flush_interval(); +        return true; +    } + +    switch (incoming_async_msg.msg_type) +    { +    case async_msg_type::flush: +        flush_sinks(); +        return true; + +    case async_msg_type::terminate: +        flush_sinks(); +        return false; + +    default: +        log_msg incoming_log_msg; +        incoming_async_msg.fill_log_msg(incoming_log_msg, &_logger_name); +        _formatter->format(incoming_log_msg); +        for (auto &s : _sinks) +        { +            if (s->should_log(incoming_log_msg.level)) +            { +                try +                { +                    s->log(incoming_log_msg); +                } +                SPDLOG_CATCH_AND_HANDLE +            } +        } +        handle_flush_interval(); +        return true; +    } +    assert(false); +    return true; // should not be reached +} + +inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter) +{ +    _formatter = std::move(msg_formatter); +} + +inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler) +{ +    _err_handler = std::move(err_handler); +} + +// flush all sinks if _flush_interval_ms has expired. +inline void spdlog::details::async_log_helper::handle_flush_interval() +{ +    if (_flush_interval_ms == std::chrono::milliseconds::zero()) +    { +        return; +    } +    auto delta = details::os::now() - _last_flush; +    ; +    if (delta >= _flush_interval_ms) +    { +        flush_sinks(); +    } +} + +// flush all sinks if _flush_interval_ms has expired. only called if queue is empty +inline void spdlog::details::async_log_helper::flush_sinks() +{ + +    for (auto &s : _sinks) +    { +        try +        { +            s->flush(); +        } +        SPDLOG_CATCH_AND_HANDLE +    } +    _last_flush = os::now(); +} diff --git a/lib/spdlog/details/async_logger_impl.h b/lib/spdlog/details/async_logger_impl.h new file mode 100644 index 0000000..33ab4b9 --- /dev/null +++ b/lib/spdlog/details/async_logger_impl.h @@ -0,0 +1,95 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +// Async Logger implementation +// Use an async_sink (queue per logger) to perform the logging in a worker thread + +#include "../async_logger.h" +#include "../details/async_log_helper.h" + +#include <chrono> +#include <functional> +#include <memory> +#include <string> + +template<class It> +inline spdlog::async_logger::async_logger(const std::string &logger_name, const It &begin, const It &end, size_t queue_size, +    const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb, +    const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb) +    : logger(logger_name, begin, end) +    , _async_log_helper(new details::async_log_helper(logger_name, _formatter, _sinks, queue_size, _err_handler, overflow_policy, +          worker_warmup_cb, flush_interval_ms, worker_teardown_cb)) +{ +} + +inline spdlog::async_logger::async_logger(const std::string &logger_name, sinks_init_list sinks_list, size_t queue_size, +    const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb, +    const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb) +    : async_logger(logger_name, sinks_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, +          worker_teardown_cb) +{ +} + +inline spdlog::async_logger::async_logger(const std::string &logger_name, sink_ptr single_sink, size_t queue_size, +    const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb, +    const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb) +    : async_logger( +          logger_name, {std::move(single_sink)}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) +{ +} + +inline void spdlog::async_logger::flush() +{ +    _async_log_helper->flush(); +} + +// Error handler +inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler) +{ +    _err_handler = err_handler; +    _async_log_helper->set_error_handler(err_handler); +} +inline spdlog::log_err_handler spdlog::async_logger::error_handler() +{ +    return _err_handler; +} + +inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter) +{ +    _formatter = msg_formatter; +    _async_log_helper->set_formatter(_formatter); +} + +inline void spdlog::async_logger::_set_pattern(const std::string &pattern, pattern_time_type pattern_time) +{ +    _formatter = std::make_shared<pattern_formatter>(pattern, pattern_time); +    _async_log_helper->set_formatter(_formatter); +} + +inline void spdlog::async_logger::_sink_it(details::log_msg &msg) +{ +    try +    { +#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) +        _incr_msg_counter(msg); +#endif +        _async_log_helper->log(msg); +        if (_should_flush_on(msg)) +        { +            _async_log_helper->flush(); // do async flush +        } +    } +    catch (const std::exception &ex) +    { +        _err_handler(ex.what()); +    } +    catch (...) +    { +        _err_handler("Unknown exception in logger " + _name); +        throw; +    } +} diff --git a/lib/spdlog/details/file_helper.h b/lib/spdlog/details/file_helper.h new file mode 100644 index 0000000..d30a79b --- /dev/null +++ b/lib/spdlog/details/file_helper.h @@ -0,0 +1,151 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +// Helper class for file sink +// When failing to open a file, retry several times(5) with small delay between the tries(10 ms) +// Throw spdlog_ex exception on errors + +#include "../details/log_msg.h" +#include "../details/os.h" + +#include <cerrno> +#include <chrono> +#include <cstdio> +#include <string> +#include <thread> +#include <tuple> + +namespace spdlog { +namespace details { + +class file_helper +{ + +public: +    const int open_tries = 5; +    const int open_interval = 10; + +    explicit file_helper() = default; + +    file_helper(const file_helper &) = delete; +    file_helper &operator=(const file_helper &) = delete; + +    ~file_helper() +    { +        close(); +    } + +    void open(const filename_t &fname, bool truncate = false) +    { +        close(); +        auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab"); +        _filename = fname; +        for (int tries = 0; tries < open_tries; ++tries) +        { +            if (!os::fopen_s(&_fd, fname, mode)) +            { +                return; +            } + +            details::os::sleep_for_millis(open_interval); +        } + +        throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno); +    } + +    void reopen(bool truncate) +    { +        if (_filename.empty()) +        { +            throw spdlog_ex("Failed re opening file - was not opened before"); +        } +        open(_filename, truncate); +    } + +    void flush() +    { +        std::fflush(_fd); +    } + +    void close() +    { +        if (_fd != nullptr) +        { +            std::fclose(_fd); +            _fd = nullptr; +        } +    } + +    void write(const log_msg &msg) +    { +        size_t msg_size = msg.formatted.size(); +        auto data = msg.formatted.data(); +        if (std::fwrite(data, 1, msg_size, _fd) != msg_size) +        { +            throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno); +        } +    } + +    size_t size() const +    { +        if (_fd == nullptr) +        { +            throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)); +        } +        return os::filesize(_fd); +    } + +    const filename_t &filename() const +    { +        return _filename; +    } + +    static bool file_exists(const filename_t &fname) +    { +        return os::file_exists(fname); +    } + +    // +    // 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_extenstion(const spdlog::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, spdlog::filename_t()); +        } + +        // treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile" +        auto folder_index = fname.rfind(details::os::folder_sep); +        if (folder_index != fname.npos && folder_index >= ext_index - 1) +        { +            return std::make_tuple(fname, spdlog::filename_t()); +        } + +        // finally - return a valid base and extension tuple +        return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index)); +    } + +private: +    FILE *_fd{nullptr}; +    filename_t _filename; +}; +} // namespace details +} // namespace spdlog diff --git a/lib/spdlog/details/log_msg.h b/lib/spdlog/details/log_msg.h new file mode 100644 index 0000000..1d079aa --- /dev/null +++ b/lib/spdlog/details/log_msg.h @@ -0,0 +1,48 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "../common.h" +#include "../details/os.h" + +#include <string> +#include <utility> + +namespace spdlog { +namespace details { +struct log_msg +{ +    log_msg() = default; +    log_msg(const std::string *loggers_name, level::level_enum lvl) +        : logger_name(loggers_name) +        , level(lvl) +    { +#ifndef SPDLOG_NO_DATETIME +        time = os::now(); +#endif + +#ifndef SPDLOG_NO_THREAD_ID +        thread_id = os::thread_id(); +#endif +    } + +    log_msg(const log_msg &other) = delete; +    log_msg &operator=(log_msg &&other) = delete; +    log_msg(log_msg &&other) = delete; + +    const std::string *logger_name{nullptr}; +    level::level_enum level; +    log_clock::time_point time; +    size_t thread_id; +    fmt::MemoryWriter raw; +    fmt::MemoryWriter formatted; +    size_t msg_id{0}; +    // wrap this range with color codes +    size_t color_range_start{0}; +    size_t color_range_end{0}; +}; +} // namespace details +} // namespace spdlog diff --git a/lib/spdlog/details/logger_impl.h b/lib/spdlog/details/logger_impl.h new file mode 100644 index 0000000..084ecdd --- /dev/null +++ b/lib/spdlog/details/logger_impl.h @@ -0,0 +1,348 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "../logger.h" + +#include <memory> +#include <string> + +// create logger with given name, sinks and the default pattern formatter +// all other ctors will call this one +template<class It> +inline spdlog::logger::logger(std::string logger_name, const It &begin, const It &end) +    : _name(std::move(logger_name)) +    , _sinks(begin, end) +    , _formatter(std::make_shared<pattern_formatter>("%+")) +    , _level(level::info) +    , _flush_level(level::off) +    , _last_err_time(0) +    , _msg_counter(1) // message counter will start from 1. 0-message id will be reserved for controll messages +{ +    _err_handler = [this](const std::string &msg) { this->_default_err_handler(msg); }; +} + +// ctor with sinks as init list +inline spdlog::logger::logger(const std::string &logger_name, sinks_init_list sinks_list) +    : logger(logger_name, sinks_list.begin(), sinks_list.end()) +{ +} + +// ctor with single sink +inline spdlog::logger::logger(const std::string &logger_name, spdlog::sink_ptr single_sink) +    : logger(logger_name, {std::move(single_sink)}) +{ +} + +inline spdlog::logger::~logger() = default; + +inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter) +{ +    _set_formatter(std::move(msg_formatter)); +} + +inline void spdlog::logger::set_pattern(const std::string &pattern, pattern_time_type pattern_time) +{ +    _set_pattern(pattern, pattern_time); +} + +template<typename... Args> +inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args) +{ +    if (!should_log(lvl)) +    { +        return; +    } + +    try +    { +        details::log_msg log_msg(&_name, lvl); + +#if defined(SPDLOG_FMT_PRINTF) +        fmt::printf(log_msg.raw, fmt, args...); +#else +        log_msg.raw.write(fmt, args...); +#endif +        _sink_it(log_msg); +    } +    SPDLOG_CATCH_AND_HANDLE +} + +template<typename... Args> +inline void spdlog::logger::log(level::level_enum lvl, const char *msg) +{ +    if (!should_log(lvl)) +    { +        return; +    } +    try +    { +        details::log_msg log_msg(&_name, lvl); +        log_msg.raw << msg; +        _sink_it(log_msg); +    } +    SPDLOG_CATCH_AND_HANDLE +} + +template<typename T> +inline void spdlog::logger::log(level::level_enum lvl, const T &msg) +{ +    if (!should_log(lvl)) +    { +        return; +    } +    try +    { +        details::log_msg log_msg(&_name, lvl); +        log_msg.raw << msg; +        _sink_it(log_msg); +    } +    SPDLOG_CATCH_AND_HANDLE +} + +template<typename Arg1, typename... Args> +inline void spdlog::logger::trace(const char *fmt, const Arg1 &arg1, const Args &... args) +{ +    log(level::trace, fmt, arg1, args...); +} + +template<typename Arg1, typename... Args> +inline void spdlog::logger::debug(const char *fmt, const Arg1 &arg1, const Args &... args) +{ +    log(level::debug, fmt, arg1, args...); +} + +template<typename Arg1, typename... Args> +inline void spdlog::logger::info(const char *fmt, const Arg1 &arg1, const Args &... args) +{ +    log(level::info, fmt, arg1, args...); +} + +template<typename Arg1, typename... Args> +inline void spdlog::logger::warn(const char *fmt, const Arg1 &arg1, const Args &... args) +{ +    log(level::warn, fmt, arg1, args...); +} + +template<typename Arg1, typename... Args> +inline void spdlog::logger::error(const char *fmt, const Arg1 &arg1, const Args &... args) +{ +    log(level::err, fmt, arg1, args...); +} + +template<typename Arg1, typename... Args> +inline void spdlog::logger::critical(const char *fmt, const Arg1 &arg1, const Args &... args) +{ +    log(level::critical, fmt, arg1, args...); +} + +template<typename T> +inline void spdlog::logger::trace(const T &msg) +{ +    log(level::trace, msg); +} + +template<typename T> +inline void spdlog::logger::debug(const T &msg) +{ +    log(level::debug, msg); +} + +template<typename T> +inline void spdlog::logger::info(const T &msg) +{ +    log(level::info, msg); +} + +template<typename T> +inline void spdlog::logger::warn(const T &msg) +{ +    log(level::warn, msg); +} + +template<typename T> +inline void spdlog::logger::error(const T &msg) +{ +    log(level::err, msg); +} + +template<typename T> +inline void spdlog::logger::critical(const T &msg) +{ +    log(level::critical, msg); +} + +#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT +#include <codecvt> +#include <locale> + +template<typename... Args> +inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *msg) +{ +    std::wstring_convert<std::codecvt_utf8<wchar_t>> conv; + +    log(lvl, conv.to_bytes(msg)); +} + +template<typename... Args> +inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args) +{ +    fmt::WMemoryWriter wWriter; + +    wWriter.write(fmt, args...); +    log(lvl, wWriter.c_str()); +} + +template<typename... Args> +inline void spdlog::logger::trace(const wchar_t *fmt, const Args &... args) +{ +    log(level::trace, fmt, args...); +} + +template<typename... Args> +inline void spdlog::logger::debug(const wchar_t *fmt, const Args &... args) +{ +    log(level::debug, fmt, args...); +} + +template<typename... Args> +inline void spdlog::logger::info(const wchar_t *fmt, const Args &... args) +{ +    log(level::info, fmt, args...); +} + +template<typename... Args> +inline void spdlog::logger::warn(const wchar_t *fmt, const Args &... args) +{ +    log(level::warn, fmt, args...); +} + +template<typename... Args> +inline void spdlog::logger::error(const wchar_t *fmt, const Args &... args) +{ +    log(level::err, fmt, args...); +} + +template<typename... Args> +inline void spdlog::logger::critical(const wchar_t *fmt, const Args &... args) +{ +    log(level::critical, fmt, args...); +} + +#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT + +// +// name and level +// +inline const std::string &spdlog::logger::name() const +{ +    return _name; +} + +inline void spdlog::logger::set_level(spdlog::level::level_enum log_level) +{ +    _level.store(log_level); +} + +inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) +{ +    _err_handler = std::move(err_handler); +} + +inline spdlog::log_err_handler spdlog::logger::error_handler() +{ +    return _err_handler; +} + +inline void spdlog::logger::flush_on(level::level_enum log_level) +{ +    _flush_level.store(log_level); +} + +inline spdlog::level::level_enum spdlog::logger::level() const +{ +    return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed)); +} + +inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const +{ +    return msg_level >= _level.load(std::memory_order_relaxed); +} + +// +// protected virtual called at end of each user log call (if enabled) by the line_logger +// +inline void spdlog::logger::_sink_it(details::log_msg &msg) +{ +#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) +    _incr_msg_counter(msg); +#endif +    _formatter->format(msg); +    for (auto &sink : _sinks) +    { +        if (sink->should_log(msg.level)) +        { +            sink->log(msg); +        } +    } + +    if (_should_flush_on(msg)) +    { +        flush(); +    } +} + +inline void spdlog::logger::_set_pattern(const std::string &pattern, pattern_time_type pattern_time) +{ +    _formatter = std::make_shared<pattern_formatter>(pattern, pattern_time); +} + +inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter) +{ +    _formatter = std::move(msg_formatter); +} + +inline void spdlog::logger::flush() +{ +    try +    { +        for (auto &sink : _sinks) +        { +            sink->flush(); +        } +    } +    SPDLOG_CATCH_AND_HANDLE +} + +inline void spdlog::logger::_default_err_handler(const std::string &msg) +{ +    auto now = time(nullptr); +    if (now - _last_err_time < 60) +    { +        return; +    } +    _last_err_time = now; +    auto tm_time = details::os::localtime(now); +    char date_buf[100]; +    std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); +    fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg); +} + +inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg) +{ +    const auto flush_level = _flush_level.load(std::memory_order_relaxed); +    return (msg.level >= flush_level) && (msg.level != level::off); +} + +inline void spdlog::logger::_incr_msg_counter(details::log_msg &msg) +{ +    msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed); +} + +inline const std::vector<spdlog::sink_ptr> &spdlog::logger::sinks() const +{ +    return _sinks; +} diff --git a/lib/spdlog/details/mpmc_blocking_q.h b/lib/spdlog/details/mpmc_blocking_q.h new file mode 100644 index 0000000..d4a8a41 --- /dev/null +++ b/lib/spdlog/details/mpmc_blocking_q.h @@ -0,0 +1,84 @@ +#pragma once + +// +// Copyright(c) 2018 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +// async log helper : +// multi producer-multi consumer blocking queue +// enqueue(..) - will block until room found to put the new message +// enqueue_nowait(..) - will return immediatly with false if no room left in the queue +// dequeue_for(..) - will block until the queue is not empty or timeout passed + +#include <condition_variable> +#include <mutex> +#include <queue> + +namespace spdlog { +namespace details { + +template<typename T> +class mpmc_bounded_queue +{ +public: +    using item_type = T; +    explicit mpmc_bounded_queue(size_t max_items) +        : max_items_(max_items) +    { +    } + +    // 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_.size() < this->max_items_; }); +            q_.push(std::move(item)); +        } +        push_cv_.notify_one(); +    } + +    // try to enqueue and return immdeialty false if no room left +    bool enqueue_nowait(T &&item) +    { +        { +            std::unique_lock<std::mutex> lock(queue_mutex_); +            if (q_.size() == this->max_items_) +            { +                return false; +            } +            q_.push(std::forward<T>(item)); +        } +        push_cv_.notify_one(); +        return true; +    } + +    // try to dequeue item. if no item found. wait upto 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_.size() > 0; })) +            { +                return false; +            } + +            popped_item = std::move(q_.front()); +            q_.pop(); +        } +        pop_cv_.notify_one(); +        return true; +    } + +private: +    size_t max_items_; +    std::mutex queue_mutex_; +    std::condition_variable push_cv_; +    std::condition_variable pop_cv_; + +    std::queue<T> q_; +}; +} // namespace details +} // namespace spdlog diff --git a/lib/spdlog/details/null_mutex.h b/lib/spdlog/details/null_mutex.h new file mode 100644 index 0000000..3f495bd --- /dev/null +++ b/lib/spdlog/details/null_mutex.h @@ -0,0 +1,45 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include <atomic> +// null, no cost dummy "mutex" and dummy "atomic" int + +namespace spdlog { +namespace details { +struct null_mutex +{ +    void lock() {} +    void unlock() {} +    bool try_lock() +    { +        return true; +    } +}; + +struct null_atomic_int +{ +    int value; +    null_atomic_int() = default; + +    explicit null_atomic_int(int val) +        : value(val) +    { +    } + +    int load(std::memory_order) const +    { +        return value; +    } + +    void store(int val) +    { +        value = val; +    } +}; + +} // namespace details +} // namespace spdlog diff --git a/lib/spdlog/details/os.h b/lib/spdlog/details/os.h new file mode 100644 index 0000000..28eb53c --- /dev/null +++ b/lib/spdlog/details/os.h @@ -0,0 +1,431 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// +#pragma once + +#include "../common.h" + +#include <algorithm> +#include <chrono> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <ctime> +#include <functional> +#include <string> +#include <sys/stat.h> +#include <sys/types.h> +#include <thread> + +#ifdef _WIN32 + +#ifndef NOMINMAX +#define NOMINMAX // prevent windows redefining min/max +#endif + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <io.h>      // _get_osfhandle and _isatty support +#include <process.h> //  _get_pid support +#include <windows.h> + +#ifdef __MINGW32__ +#include <share.h> +#endif + +#else // unix + +#include <fcntl.h> +#include <unistd.h> + +#ifdef __linux__ +#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id + +#elif __FreeBSD__ +#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id +#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 { + +inline spdlog::log_clock::time_point now() +{ + +#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 +} +inline std::tm localtime(const std::time_t &time_tt) +{ + +#ifdef _WIN32 +    std::tm tm; +    localtime_s(&tm, &time_tt); +#else +    std::tm tm; +    localtime_r(&time_tt, &tm); +#endif +    return tm; +} + +inline std::tm localtime() +{ +    std::time_t now_t = time(nullptr); +    return localtime(now_t); +} + +inline std::tm gmtime(const std::time_t &time_tt) +{ + +#ifdef _WIN32 +    std::tm tm; +    gmtime_s(&tm, &time_tt); +#else +    std::tm tm; +    gmtime_r(&time_tt, &tm); +#endif +    return tm; +} + +inline std::tm gmtime() +{ +    std::time_t now_t = time(nullptr); +    return gmtime(now_t); +} +inline bool operator==(const std::tm &tm1, const std::tm &tm2) +{ +    return (tm1.tm_sec == tm2.tm_sec && tm1.tm_min == tm2.tm_min && tm1.tm_hour == tm2.tm_hour && tm1.tm_mday == tm2.tm_mday && +            tm1.tm_mon == tm2.tm_mon && tm1.tm_year == tm2.tm_year && tm1.tm_isdst == tm2.tm_isdst); +} + +inline bool operator!=(const std::tm &tm1, const std::tm &tm2) +{ +    return !(tm1 == tm2); +} + +// 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 +#ifdef _WIN32 +SPDLOG_CONSTEXPR static const char folder_sep = '\\'; +#else +SPDLOG_CONSTEXPR static const char folder_sep = '/'; +#endif + +inline void prevent_child_fd(FILE *f) +{ + +#ifdef _WIN32 +#if !defined(__cplusplus_winrt) +    auto file_handle = (HANDLE)_get_osfhandle(_fileno(f)); +    if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) +        throw spdlog_ex("SetHandleInformation failed", errno); +#endif +#else +    auto fd = fileno(f); +    if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) +    { +        throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno); +    } +#endif +} + +// fopen_s on non windows for writing +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_DENYWR); +#else +    *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); +#endif +#else // unix +    *fp = fopen((filename.c_str()), mode.c_str()); +#endif + +#ifdef SPDLOG_PREVENT_CHILD_FD +    if (*fp != nullptr) +    { +        prevent_child_fd(*fp); +    } +#endif +    return *fp == nullptr; +} + +inline int remove(const filename_t &filename) +{ +#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) +    return _wremove(filename.c_str()); +#else +    return std::remove(filename.c_str()); +#endif +} + +inline int rename(const filename_t &filename1, const filename_t &filename2) +{ +#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 if file exists +inline bool file_exists(const filename_t &filename) +{ +#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 && !(attribs & FILE_ATTRIBUTE_DIRECTORY)); +#else // common linux/unix all have the stat system call +    struct stat buffer; +    return (stat(filename.c_str(), &buffer) == 0); +#endif +} + +// Return file size according to open FILE* object +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 _WIN64 // 64 bits +    struct _stat64 st; +    if (_fstat64(fd, &st) == 0) +    { +        return st.st_size; +    } + +#else // windows 32 bits +    long ret = _filelength(fd); +    if (ret >= 0) +    { +        return static_cast<size_t>(ret); +    } +#endif + +#else // unix +    int fd = fileno(f); +    // 64 bits(but not in osx or cygwin, where fstat64 is deprecated) +#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__) +    struct stat64 st; +    if (fstat64(fd, &st) == 0) +    { +        return static_cast<size_t>(st.st_size); +    } +#else // unix 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 utc offset in minutes or throw spdlog_ex on failure +inline int utc_minutes_offset(const std::tm &tm = details::os::localtime()) +{ + +#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::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) +    // '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 */ +                + (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) +inline size_t _thread_id() +{ +#ifdef _WIN32 +    return static_cast<size_t>(::GetCurrentThreadId()); +#elif __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 __FreeBSD__ +    long tid; +    thr_self(&tid); +    return static_cast<size_t>(tid); +#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) +inline size_t thread_id() +{ +#if defined(SPDLOG_DISABLE_TID_CACHING) || (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt) ||                       \ +    (defined(__clang__) && !__has_feature(cxx_thread_local)) +    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 +inline void sleep_for_millis(int milliseconds) +{ +#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) +#define SPDLOG_FILENAME_T(s) L##s +inline std::string filename_to_str(const filename_t &filename) +{ +    std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c; +    return c.to_bytes(filename); +} +#else +#define SPDLOG_FILENAME_T(s) s +inline std::string filename_to_str(const filename_t &filename) +{ +    return filename; +} +#endif + +inline int pid() +{ + +#ifdef _WIN32 +    return static_cast<int>(::GetCurrentProcessId()); +#else +    return static_cast<int>(::getpid()); +#endif +} + +// Determine if the terminal supports colors +// Source: https://github.com/agauniyal/rang/ +inline bool is_color_terminal() +{ +#ifdef _WIN32 +    return true; +#else +    static constexpr const char *Terms[] = { +        "ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}; + +    const char *env_p = std::getenv("TERM"); +    if (env_p == nullptr) +    { +        return false; +    } + +    static const bool result = +        std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; }); +    return result; +#endif +} + +// Detrmine if the terminal attached +// Source: https://github.com/agauniyal/rang/ +inline bool in_terminal(FILE *file) +{ + +#ifdef _WIN32 +    return _isatty(_fileno(file)) != 0; +#else +    return isatty(fileno(file)) != 0; +#endif +} +} // namespace os +} // namespace details +} // namespace spdlog diff --git a/lib/spdlog/details/pattern_formatter_impl.h b/lib/spdlog/details/pattern_formatter_impl.h new file mode 100644 index 0000000..e3d7087 --- /dev/null +++ b/lib/spdlog/details/pattern_formatter_impl.h @@ -0,0 +1,707 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "../details/log_msg.h" +#include "../details/os.h" +#include "../fmt/fmt.h" +#include "../formatter.h" + +#include <array> +#include <chrono> +#include <ctime> +#include <memory> +#include <mutex> +#include <string> +#include <thread> +#include <utility> +#include <vector> + +namespace spdlog { +namespace details { +class flag_formatter +{ +public: +    virtual ~flag_formatter() = default; +    virtual void format(details::log_msg &msg, const std::tm &tm_time) = 0; +}; + +/////////////////////////////////////////////////////////////////////// +// name & level pattern appenders +/////////////////////////////////////////////////////////////////////// +class name_formatter : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &) override +    { +        msg.formatted << *msg.logger_name; +    } +}; + +// log level appender +class level_formatter : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &) override +    { +        msg.formatted << level::to_str(msg.level); +    } +}; + +// short log level appender +class short_level_formatter : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &) override +    { +        msg.formatted << level::to_short_str(msg.level); +    } +}; + +/////////////////////////////////////////////////////////////////////// +// 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 const std::string days[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +class a_formatter : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        msg.formatted << days[tm_time.tm_wday]; +    } +}; + +// Full weekday name +static const std::string full_days[]{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; +class A_formatter : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        msg.formatted << full_days[tm_time.tm_wday]; +    } +}; + +// Abbreviated month +static const std::string months[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}; +class b_formatter : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        msg.formatted << months[tm_time.tm_mon]; +    } +}; + +// Full month name +static const std::string full_months[]{ +    "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; +class B_formatter : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        msg.formatted << full_months[tm_time.tm_mon]; +    } +}; + +// write 2 ints separated by sep with padding of 2 +static fmt::MemoryWriter &pad_n_join(fmt::MemoryWriter &w, int v1, int v2, char sep) +{ +    w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0'); +    return w; +} + +// write 3 ints separated by sep with padding of 2 +static fmt::MemoryWriter &pad_n_join(fmt::MemoryWriter &w, int v1, int v2, int v3, char sep) +{ +    w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0'); +    return w; +} + +// Date and time representation (Thu Aug 23 15:35:46 2014) +class c_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        msg.formatted << days[tm_time.tm_wday] << ' ' << months[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' '; +        pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900; +    } +}; + +// year - 2 digit +class C_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        msg.formatted << fmt::pad(tm_time.tm_year % 100, 2, '0'); +    } +}; + +// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01 +class D_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/'); +    } +}; + +// year - 4 digit +class Y_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        msg.formatted << tm_time.tm_year + 1900; +    } +}; + +// month 1-12 +class m_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0'); +    } +}; + +// day of month 1-31 +class d_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0'); +    } +}; + +// hours in 24 format 0-23 +class H_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0'); +    } +}; + +// hours in 12 format 1-12 +class I_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        msg.formatted << fmt::pad(to12h(tm_time), 2, '0'); +    } +}; + +// minutes 0-59 +class M_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        msg.formatted << fmt::pad(tm_time.tm_min, 2, '0'); +    } +}; + +// seconds 0-59 +class S_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0'); +    } +}; + +// milliseconds +class e_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &) override +    { +        auto duration = msg.time.time_since_epoch(); +        auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000; +        msg.formatted << fmt::pad(static_cast<int>(millis), 3, '0'); +    } +}; + +// microseconds +class f_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &) override +    { +        auto duration = msg.time.time_since_epoch(); +        auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count() % 1000000; +        msg.formatted << fmt::pad(static_cast<int>(micros), 6, '0'); +    } +}; + +// nanoseconds +class F_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &) override +    { +        auto duration = msg.time.time_since_epoch(); +        auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000; +        msg.formatted << fmt::pad(static_cast<int>(ns), 9, '0'); +    } +}; + +class E_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &) override +    { +        auto duration = msg.time.time_since_epoch(); +        auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count(); +        msg.formatted << seconds; +    } +}; + +// AM/PM +class p_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        msg.formatted << ampm(tm_time); +    } +}; + +// 12 hour clock 02:55:02 pm +class r_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        pad_n_join(msg.formatted, to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << ampm(tm_time); +    } +}; + +// 24-hour HH:MM time, equivalent to %H:%M +class R_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, ':'); +    } +}; + +// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S +class T_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +        pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':'); +    } +}; + +// ISO 8601 offset from UTC in timezone (+-HH:MM) +class z_formatter SPDLOG_FINAL : public flag_formatter +{ +public: +    const std::chrono::seconds cache_refresh = std::chrono::seconds(5); + +    z_formatter() = default; +    z_formatter(const z_formatter &) = delete; +    z_formatter &operator=(const z_formatter &) = delete; + +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +#ifdef _WIN32 +        int total_minutes = get_cached_offset(msg, tm_time); +#else +        // No need to chache under gcc, +        // it is very fast (already stored in tm.tm_gmtoff) +        int total_minutes = os::utc_minutes_offset(tm_time); +#endif +        bool is_negative = total_minutes < 0; +        char sign; +        if (is_negative) +        { +            total_minutes = -total_minutes; +            sign = '-'; +        } +        else +        { +            sign = '+'; +        } + +        int h = total_minutes / 60; +        int m = total_minutes % 60; +        msg.formatted << sign; +        pad_n_join(msg.formatted, h, m, ':'); +    } + +private: +    log_clock::time_point _last_update{std::chrono::seconds(0)}; +    int _offset_minutes{0}; +    std::mutex _mutex; + +    int get_cached_offset(const log_msg &msg, const std::tm &tm_time) +    { +        std::lock_guard<std::mutex> l(_mutex); +        if (msg.time - _last_update >= cache_refresh) +        { +            _offset_minutes = os::utc_minutes_offset(tm_time); +            _last_update = msg.time; +        } +        return _offset_minutes; +    } +}; + +// Thread id +class t_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &) override +    { +        msg.formatted << msg.thread_id; +    } +}; + +// Current pid +class pid_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &) override +    { +        msg.formatted << details::os::pid(); +    } +}; + +// message counter formatter +class i_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &) override +    { +        msg.formatted << fmt::pad(msg.msg_id, 6, '0'); +    } +}; + +class v_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &) override +    { +        msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size()); +    } +}; + +class ch_formatter SPDLOG_FINAL : public flag_formatter +{ +public: +    explicit ch_formatter(char ch) +        : _ch(ch) +    { +    } +    void format(details::log_msg &msg, const std::tm &) override +    { +        msg.formatted << _ch; +    } + +private: +    char _ch; +}; + +// aggregate user chars to display as is +class aggregate_formatter SPDLOG_FINAL : public flag_formatter +{ +public: +    aggregate_formatter() = default; + +    void add_ch(char ch) +    { +        _str += ch; +    } +    void format(details::log_msg &msg, const std::tm &) override +    { +        msg.formatted << _str; +    } + +private: +    std::string _str; +}; + +// mark the color range. expect it to be in the form of "%^colored text%$" +class color_start_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &) override +    { +        msg.color_range_start = msg.formatted.size(); +    } +}; +class color_stop_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &) override +    { +        msg.color_range_end = msg.formatted.size(); +    } +}; + +// Full info formatter +// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v +class full_formatter SPDLOG_FINAL : public flag_formatter +{ +    void format(details::log_msg &msg, const std::tm &tm_time) override +    { +#ifndef SPDLOG_NO_DATETIME +        auto duration = msg.time.time_since_epoch(); +        auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000; + +        /* Slower version(while still very fast - about 3.2 million lines/sec under 10 threads), +        msg.formatted.write("[{:d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}.{:03d}] [{}] [{}] {} ", +        tm_time.tm_year + 1900, +        tm_time.tm_mon + 1, +        tm_time.tm_mday, +        tm_time.tm_hour, +        tm_time.tm_min, +        tm_time.tm_sec, +        static_cast<int>(millis), +        msg.logger_name, +        level::to_str(msg.level), +        msg.raw.str());*/ + +        // Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads) +        msg.formatted << '[' << static_cast<unsigned int>(tm_time.tm_year + 1900) << '-' +                      << fmt::pad(static_cast<unsigned int>(tm_time.tm_mon + 1), 2, '0') << '-' +                      << fmt::pad(static_cast<unsigned int>(tm_time.tm_mday), 2, '0') << ' ' +                      << fmt::pad(static_cast<unsigned int>(tm_time.tm_hour), 2, '0') << ':' +                      << fmt::pad(static_cast<unsigned int>(tm_time.tm_min), 2, '0') << ':' +                      << fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.' +                      << fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] "; + +        // no datetime needed +#else +        (void)tm_time; +#endif + +#ifndef SPDLOG_NO_NAME +        msg.formatted << '[' << *msg.logger_name << "] "; +#endif + +        msg.formatted << '['; +        // wrap the level name with color +        msg.color_range_start = msg.formatted.size(); +        msg.formatted << level::to_str(msg.level); +        msg.color_range_end = msg.formatted.size(); +        msg.formatted << "] " << fmt::StringRef(msg.raw.data(), msg.raw.size()); +    } +}; + +} // namespace details +} // namespace spdlog +/////////////////////////////////////////////////////////////////////////////// +// pattern_formatter inline impl +/////////////////////////////////////////////////////////////////////////////// +inline spdlog::pattern_formatter::pattern_formatter(const std::string &pattern, pattern_time_type pattern_time, std::string eol) +    : _eol(std::move(eol)) +    , _pattern_time(pattern_time) +{ +    compile_pattern(pattern); +} + +inline void spdlog::pattern_formatter::compile_pattern(const std::string &pattern) +{ +    auto end = pattern.end(); +    std::unique_ptr<details::aggregate_formatter> user_chars; +    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)); +            } +            // if( +            if (++it != end) +            { +                handle_flag(*it); +            } +            else +            { +                break; +            } +        } +        else // chars not following the % sign should be displayed as is +        { +            if (!user_chars) +            { +                user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter()); +            } +            user_chars->add_ch(*it); +        } +    } +    if (user_chars) // append raw chars found so far +    { +        _formatters.push_back(std::move(user_chars)); +    } +} +inline void spdlog::pattern_formatter::handle_flag(char flag) +{ +    switch (flag) +    { +    // logger name +    case 'n': +        _formatters.emplace_back(new details::name_formatter()); +        break; + +    case 'l': +        _formatters.emplace_back(new details::level_formatter()); +        break; + +    case 'L': +        _formatters.emplace_back(new details::short_level_formatter()); +        break; + +    case ('t'): +        _formatters.emplace_back(new details::t_formatter()); +        break; + +    case ('v'): +        _formatters.emplace_back(new details::v_formatter()); +        break; + +    case ('a'): +        _formatters.emplace_back(new details::a_formatter()); +        break; + +    case ('A'): +        _formatters.emplace_back(new details::A_formatter()); +        break; + +    case ('b'): +    case ('h'): +        _formatters.emplace_back(new details::b_formatter()); +        break; + +    case ('B'): +        _formatters.emplace_back(new details::B_formatter()); +        break; +    case ('c'): +        _formatters.emplace_back(new details::c_formatter()); +        break; + +    case ('C'): +        _formatters.emplace_back(new details::C_formatter()); +        break; + +    case ('Y'): +        _formatters.emplace_back(new details::Y_formatter()); +        break; + +    case ('D'): +    case ('x'): + +        _formatters.emplace_back(new details::D_formatter()); +        break; + +    case ('m'): +        _formatters.emplace_back(new details::m_formatter()); +        break; + +    case ('d'): +        _formatters.emplace_back(new details::d_formatter()); +        break; + +    case ('H'): +        _formatters.emplace_back(new details::H_formatter()); +        break; + +    case ('I'): +        _formatters.emplace_back(new details::I_formatter()); +        break; + +    case ('M'): +        _formatters.emplace_back(new details::M_formatter()); +        break; + +    case ('S'): +        _formatters.emplace_back(new details::S_formatter()); +        break; + +    case ('e'): +        _formatters.emplace_back(new details::e_formatter()); +        break; + +    case ('f'): +        _formatters.emplace_back(new details::f_formatter()); +        break; +    case ('F'): +        _formatters.emplace_back(new details::F_formatter()); +        break; + +    case ('E'): +        _formatters.emplace_back(new details::E_formatter()); +        break; + +    case ('p'): +        _formatters.emplace_back(new details::p_formatter()); +        break; + +    case ('r'): +        _formatters.emplace_back(new details::r_formatter()); +        break; + +    case ('R'): +        _formatters.emplace_back(new details::R_formatter()); +        break; + +    case ('T'): +    case ('X'): +        _formatters.emplace_back(new details::T_formatter()); +        break; + +    case ('z'): +        _formatters.emplace_back(new details::z_formatter()); +        break; + +    case ('+'): +        _formatters.emplace_back(new details::full_formatter()); +        break; + +    case ('P'): +        _formatters.emplace_back(new details::pid_formatter()); +        break; + +    case ('i'): +        _formatters.emplace_back(new details::i_formatter()); +        break; + +    case ('^'): +        _formatters.emplace_back(new details::color_start_formatter()); +        break; + +    case ('$'): +        _formatters.emplace_back(new details::color_stop_formatter()); +        break; + +    default: // Unknown flag appears as is +        _formatters.emplace_back(new details::ch_formatter('%')); +        _formatters.emplace_back(new details::ch_formatter(flag)); +        break; +    } +} + +inline std::tm spdlog::pattern_formatter::get_time(details::log_msg &msg) +{ +    if (_pattern_time == 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)); +} + +inline void spdlog::pattern_formatter::format(details::log_msg &msg) +{ + +#ifndef SPDLOG_NO_DATETIME +    auto tm_time = get_time(msg); +#else +    std::tm tm_time; +#endif +    for (auto &f : _formatters) +    { +        f->format(msg, tm_time); +    } +    // write eol +    msg.formatted << _eol; +} diff --git a/lib/spdlog/details/registry.h b/lib/spdlog/details/registry.h new file mode 100644 index 0000000..614220d --- /dev/null +++ b/lib/spdlog/details/registry.h @@ -0,0 +1,263 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +// Loggers registy of unique name->logger pointer +// An attempt to create a logger with an already existing name will be ignored +// If user requests a non existing logger, nullptr will be returned +// This class is thread safe + +#include "../async_logger.h" +#include "../common.h" +#include "../details/null_mutex.h" +#include "../logger.h" + +#include <chrono> +#include <functional> +#include <memory> +#include <mutex> +#include <string> +#include <unordered_map> + +namespace spdlog { +namespace details { +template<class Mutex> +class registry_t +{ +public: +    registry_t<Mutex>(const registry_t<Mutex> &) = delete; +    registry_t<Mutex> &operator=(const registry_t<Mutex> &) = delete; + +    void register_logger(std::shared_ptr<logger> logger) +    { +        std::lock_guard<Mutex> lock(_mutex); +        auto logger_name = logger->name(); +        throw_if_exists(logger_name); +        _loggers[logger_name] = logger; +    } + +    std::shared_ptr<logger> get(const std::string &logger_name) +    { +        std::lock_guard<Mutex> lock(_mutex); +        auto found = _loggers.find(logger_name); +        return found == _loggers.end() ? nullptr : found->second; +    } + +    template<class It> +    std::shared_ptr<logger> create(const std::string &logger_name, const It &sinks_begin, const It &sinks_end) +    { +        std::lock_guard<Mutex> lock(_mutex); +        throw_if_exists(logger_name); +        std::shared_ptr<logger> new_logger; +        if (_async_mode) +        { +            new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, +                _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb); +        } +        else +        { +            new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end); +        } + +        if (_formatter) +        { +            new_logger->set_formatter(_formatter); +        } + +        if (_err_handler) +        { +            new_logger->set_error_handler(_err_handler); +        } + +        new_logger->set_level(_level); +        new_logger->flush_on(_flush_level); + +        // Add to registry +        _loggers[logger_name] = new_logger; +        return new_logger; +    } + +    template<class It> +    std::shared_ptr<async_logger> create_async(const std::string &logger_name, size_t queue_size, +        const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb, +        const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb, const It &sinks_begin, +        const It &sinks_end) +    { +        std::lock_guard<Mutex> lock(_mutex); +        throw_if_exists(logger_name); +        auto new_logger = std::make_shared<async_logger>( +            logger_name, sinks_begin, sinks_end, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb); + +        if (_formatter) +        { +            new_logger->set_formatter(_formatter); +        } + +        if (_err_handler) +        { +            new_logger->set_error_handler(_err_handler); +        } + +        new_logger->set_level(_level); +        new_logger->flush_on(_flush_level); + +        // Add to registry +        _loggers[logger_name] = new_logger; +        return new_logger; +    } + +    void apply_all(std::function<void(std::shared_ptr<logger>)> fun) +    { +        std::lock_guard<Mutex> lock(_mutex); +        for (auto &l : _loggers) +        { +            fun(l.second); +        } +    } + +    void drop(const std::string &logger_name) +    { +        std::lock_guard<Mutex> lock(_mutex); +        _loggers.erase(logger_name); +    } + +    void drop_all() +    { +        std::lock_guard<Mutex> lock(_mutex); +        _loggers.clear(); +    } + +    std::shared_ptr<logger> create(const std::string &logger_name, sinks_init_list sinks) +    { +        return create(logger_name, sinks.begin(), sinks.end()); +    } + +    std::shared_ptr<logger> create(const std::string &logger_name, sink_ptr sink) +    { +        return create(logger_name, {sink}); +    } + +    std::shared_ptr<async_logger> create_async(const std::string &logger_name, size_t queue_size, +        const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb, +        const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb, sinks_init_list sinks) +    { +        return create_async( +            logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks.begin(), sinks.end()); +    } + +    std::shared_ptr<async_logger> create_async(const std::string &logger_name, size_t queue_size, +        const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb, +        const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb, sink_ptr sink) +    { +        return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, {sink}); +    } + +    void formatter(formatter_ptr f) +    { +        std::lock_guard<Mutex> lock(_mutex); +        _formatter = f; +        for (auto &l : _loggers) +        { +            l.second->set_formatter(_formatter); +        } +    } + +    void set_pattern(const std::string &pattern) +    { +        std::lock_guard<Mutex> lock(_mutex); +        _formatter = std::make_shared<pattern_formatter>(pattern); +        for (auto &l : _loggers) +        { +            l.second->set_formatter(_formatter); +        } +    } + +    void set_level(level::level_enum log_level) +    { +        std::lock_guard<Mutex> lock(_mutex); +        for (auto &l : _loggers) +        { +            l.second->set_level(log_level); +        } +        _level = log_level; +    } + +    void flush_on(level::level_enum log_level) +    { +        std::lock_guard<Mutex> lock(_mutex); +        for (auto &l : _loggers) +        { +            l.second->flush_on(log_level); +        } +        _flush_level = log_level; +    } + +    void set_error_handler(log_err_handler handler) +    { +        for (auto &l : _loggers) +        { +            l.second->set_error_handler(handler); +        } +        _err_handler = handler; +    } + +    void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb, +        const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb) +    { +        std::lock_guard<Mutex> lock(_mutex); +        _async_mode = true; +        _async_q_size = q_size; +        _overflow_policy = overflow_policy; +        _worker_warmup_cb = worker_warmup_cb; +        _flush_interval_ms = flush_interval_ms; +        _worker_teardown_cb = worker_teardown_cb; +    } + +    void set_sync_mode() +    { +        std::lock_guard<Mutex> lock(_mutex); +        _async_mode = false; +    } + +    static registry_t<Mutex> &instance() +    { +        static registry_t<Mutex> s_instance; +        return s_instance; +    } + +private: +    registry_t<Mutex>() = default; + +    void 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"); +        } +    } + +    Mutex _mutex; +    std::unordered_map<std::string, std::shared_ptr<logger>> _loggers; +    formatter_ptr _formatter; +    level::level_enum _level = level::info; +    level::level_enum _flush_level = level::off; +    log_err_handler _err_handler; +    bool _async_mode = false; +    size_t _async_q_size = 0; +    async_overflow_policy _overflow_policy = async_overflow_policy::block_retry; +    std::function<void()> _worker_warmup_cb; +    std::chrono::milliseconds _flush_interval_ms{std::chrono::milliseconds::zero()}; +    std::function<void()> _worker_teardown_cb; +}; + +#ifdef SPDLOG_NO_REGISTRY_MUTEX +using registry = registry_t<spdlog::details::null_mutex>; +#else +using registry = registry_t<std::mutex>; +#endif + +} // namespace details +} // namespace spdlog diff --git a/lib/spdlog/details/spdlog_impl.h b/lib/spdlog/details/spdlog_impl.h new file mode 100644 index 0000000..4c36383 --- /dev/null +++ b/lib/spdlog/details/spdlog_impl.h @@ -0,0 +1,278 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +// +// Global registry functions +// +#include "../details/registry.h" +#include "../sinks/file_sinks.h" +#include "../sinks/stdout_sinks.h" +#include "../spdlog.h" +#ifdef SPDLOG_ENABLE_SYSLOG +#include "../sinks/syslog_sink.h" +#endif + +#if defined _WIN32 && !defined(__cplusplus_winrt) +#include "../sinks/wincolor_sink.h" +#else +#include "../sinks/ansicolor_sink.h" +#endif + +#ifdef __ANDROID__ +#include "../sinks/android_sink.h" +#endif + +#include <chrono> +#include <functional> +#include <memory> +#include <string> + +inline void spdlog::register_logger(std::shared_ptr<logger> logger) +{ +    return details::registry::instance().register_logger(std::move(logger)); +} + +inline std::shared_ptr<spdlog::logger> spdlog::get(const std::string &name) +{ +    return details::registry::instance().get(name); +} + +inline void spdlog::drop(const std::string &name) +{ +    details::registry::instance().drop(name); +} + +// Create multi/single threaded simple file logger +inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate) +{ +    return create<spdlog::sinks::simple_file_sink_mt>(logger_name, filename, truncate); +} + +inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate) +{ +    return create<spdlog::sinks::simple_file_sink_st>(logger_name, filename, truncate); +} + +// Create multi/single threaded rotating file logger +inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt( +    const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files) +{ +    return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files); +} + +inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st( +    const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files) +{ +    return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files); +} + +// Create file logger which creates new file at midnight): +inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt( +    const std::string &logger_name, const filename_t &filename, int hour, int minute) +{ +    return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, hour, minute); +} + +inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st( +    const std::string &logger_name, const filename_t &filename, int hour, int minute) +{ +    return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute); +} + +// +// stdout/stderr loggers +// +inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string &logger_name) +{ +    return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance()); +} + +inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string &logger_name) +{ +    return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance()); +} + +inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string &logger_name) +{ +    return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance()); +} + +inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string &logger_name) +{ +    return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance()); +} + +// +// stdout/stderr color loggers +// +#if defined _WIN32 && !defined(__cplusplus_winrt) + +inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string &logger_name) +{ +    auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>(); +    return spdlog::details::registry::instance().create(logger_name, sink); +} + +inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string &logger_name) +{ +    auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_st>(); +    return spdlog::details::registry::instance().create(logger_name, sink); +} + +inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string &logger_name) +{ +    auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_mt>(); +    return spdlog::details::registry::instance().create(logger_name, sink); +} + +inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string &logger_name) +{ +    auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_st>(); +    return spdlog::details::registry::instance().create(logger_name, sink); +} + +#else // ansi terminal colors + +inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string &logger_name) +{ +    auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>(); +    return spdlog::details::registry::instance().create(logger_name, sink); +} + +inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string &logger_name) +{ +    auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>(); +    return spdlog::details::registry::instance().create(logger_name, sink); +} + +inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string &logger_name) +{ +    auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>(); +    return spdlog::details::registry::instance().create(logger_name, sink); +} + +inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string &logger_name) +{ +    auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_st>(); +    return spdlog::details::registry::instance().create(logger_name, sink); +} +#endif + +#ifdef SPDLOG_ENABLE_SYSLOG +// Create syslog logger +inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger( +    const std::string &logger_name, const std::string &syslog_ident, int syslog_option, int syslog_facility) +{ +    return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option, syslog_facility); +} +#endif + +#ifdef __ANDROID__ +inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string &logger_name, const std::string &tag) +{ +    return create<spdlog::sinks::android_sink>(logger_name, tag); +} +#endif + +// Create and register a logger a single sink +inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, const spdlog::sink_ptr &sink) +{ +    return details::registry::instance().create(logger_name, sink); +} + +// Create logger with multiple sinks +inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, spdlog::sinks_init_list sinks) +{ +    return details::registry::instance().create(logger_name, sinks); +} + +template<typename Sink, typename... Args> +inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, Args... args) +{ +    sink_ptr sink = std::make_shared<Sink>(args...); +    return details::registry::instance().create(logger_name, {sink}); +} + +template<class It> +inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, const It &sinks_begin, const It &sinks_end) +{ +    return details::registry::instance().create(logger_name, sinks_begin, sinks_end); +} + +// Create and register an async logger with a single sink +inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string &logger_name, const sink_ptr &sink, size_t queue_size, +    const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb, +    const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb) +{ +    return details::registry::instance().create_async( +        logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sink); +} + +// Create and register an async logger with multiple sinks +inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string &logger_name, sinks_init_list sinks, size_t queue_size, +    const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb, +    const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb) +{ +    return details::registry::instance().create_async( +        logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks); +} + +template<class It> +inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string &logger_name, const It &sinks_begin, const It &sinks_end, +    size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb, +    const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb) +{ +    return details::registry::instance().create_async( +        logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks_begin, sinks_end); +} + +inline void spdlog::set_formatter(spdlog::formatter_ptr f) +{ +    details::registry::instance().formatter(std::move(f)); +} + +inline void spdlog::set_pattern(const std::string &format_string) +{ +    return details::registry::instance().set_pattern(format_string); +} + +inline void spdlog::set_level(level::level_enum log_level) +{ +    return details::registry::instance().set_level(log_level); +} + +inline void spdlog::flush_on(level::level_enum log_level) +{ +    return details::registry::instance().flush_on(log_level); +} + +inline void spdlog::set_error_handler(log_err_handler handler) +{ +    return details::registry::instance().set_error_handler(std::move(handler)); +} + +inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, +    const std::function<void()> &worker_warmup_cb, const std::chrono::milliseconds &flush_interval_ms, +    const std::function<void()> &worker_teardown_cb) +{ +    details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb); +} + +inline void spdlog::set_sync_mode() +{ +    details::registry::instance().set_sync_mode(); +} + +inline void spdlog::apply_all(std::function<void(std::shared_ptr<logger>)> fun) +{ +    details::registry::instance().apply_all(std::move(fun)); +} + +inline void spdlog::drop_all() +{ +    details::registry::instance().drop_all(); +} diff --git a/lib/spdlog/fmt/bundled/LICENSE.rst b/lib/spdlog/fmt/bundled/LICENSE.rst new file mode 100644 index 0000000..eb6be65 --- /dev/null +++ b/lib/spdlog/fmt/bundled/LICENSE.rst @@ -0,0 +1,23 @@ +Copyright (c) 2012 - 2016, Victor Zverovich + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +   list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +   this list of conditions and the following disclaimer in the documentation +   and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/lib/spdlog/fmt/bundled/format.cc b/lib/spdlog/fmt/bundled/format.cc new file mode 100644 index 0000000..2d236bc --- /dev/null +++ b/lib/spdlog/fmt/bundled/format.cc @@ -0,0 +1,495 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - 2016, Victor Zverovich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this +    list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, +    this list of conditions and the following disclaimer in the documentation +    and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "format.h" + +#include <string.h> + +#include <cctype> +#include <cerrno> +#include <climits> +#include <cmath> +#include <cstdarg> +#include <cstddef>  // for std::ptrdiff_t + +#if defined(_WIN32) && defined(__MINGW32__) +# include <cstring> +#endif + +#if FMT_USE_WINDOWS_H +# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN) +#  define WIN32_LEAN_AND_MEAN +# endif +# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) +#  include <windows.h> +# else +#  define NOMINMAX +#  include <windows.h> +#  undef NOMINMAX +# 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 + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4127)  // conditional expression is constant +# pragma warning(disable: 4702)  // unreachable code +// Disable deprecation warning for strerror. The latter is not called but +// MSVC fails to detect it. +# pragma warning(disable: 4996) +#endif + +// Dummy implementations of strerror_r and strerror_s called if corresponding +// system functions are not available. +FMT_MAYBE_UNUSED +static inline fmt::internal::Null<> strerror_r(int, char *, ...) { +  return fmt::internal::Null<>(); +} +FMT_MAYBE_UNUSED +static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) { +  return fmt::internal::Null<>(); +} + +namespace fmt { + +FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {} +FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {} +FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {} + +namespace { + +#ifndef _MSC_VER +# define FMT_SNPRINTF snprintf +#else  // _MSC_VER +inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { +  va_list args; +  va_start(args, format); +  int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); +  va_end(args); +  return result; +} +# define FMT_SNPRINTF fmt_snprintf +#endif  // _MSC_VER + +#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) +# define FMT_SWPRINTF snwprintf +#else +# define FMT_SWPRINTF swprintf +#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) + +const char RESET_COLOR[] = "\x1b[0m"; + +typedef void (*FormatFunc)(Writer &, int, StringRef); + +// Portable thread-safe version of strerror. +// Sets buffer to point to a string describing the error code. +// This can be either a pointer to a string stored in buffer, +// or a pointer to some static immutable string. +// Returns one of the following values: +//   0      - success +//   ERANGE - buffer is not large enough to store the error message +//   other  - failure +// Buffer should be at least of size 1. +int safe_strerror( +    int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { +  FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer"); + +  class StrError { +   private: +    int error_code_; +    char *&buffer_; +    std::size_t buffer_size_; + +    // A noop assignment operator to avoid bogus warnings. +    void operator=(const StrError &) {} + +    // Handle the result of XSI-compliant version of strerror_r. +    int handle(int result) { +      // glibc versions before 2.13 return result in errno. +      return result == -1 ? errno : result; +    } + +    // Handle the result of GNU-specific version of strerror_r. +    int handle(char *message) { +      // If the buffer is full then the message is probably truncated. +      if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) +        return ERANGE; +      buffer_ = message; +      return 0; +    } + +    // Handle the case when strerror_r is not available. +    int handle(internal::Null<>) { +      return fallback(strerror_s(buffer_, buffer_size_, error_code_)); +    } + +    // Fallback to strerror_s when strerror_r is not available. +    int fallback(int result) { +      // If the buffer is full then the message is probably truncated. +      return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? +            ERANGE : result; +    } + +#ifdef __c2__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + +    // Fallback to strerror if strerror_r and strerror_s are not available. +    int fallback(internal::Null<>) { +      errno = 0; +      buffer_ = strerror(error_code_); +      return errno; +    } + +#ifdef __c2__ +# pragma clang diagnostic pop +#endif + +   public: +    StrError(int err_code, char *&buf, std::size_t buf_size) +      : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} + +    int run() { +      return handle(strerror_r(error_code_, buffer_, buffer_size_)); +    } +  }; +  return StrError(error_code, buffer, buffer_size).run(); +} + +void format_error_code(Writer &out, int error_code, +                       StringRef message) FMT_NOEXCEPT { +  // Report error code making sure that the output fits into +  // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential +  // bad_alloc. +  out.clear(); +  static const char SEP[] = ": "; +  static const char ERROR_STR[] = "error "; +  // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. +  std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; +  typedef internal::IntTraits<int>::MainType MainType; +  MainType abs_value = static_cast<MainType>(error_code); +  if (internal::is_negative(error_code)) { +    abs_value = 0 - abs_value; +    ++error_code_size; +  } +  error_code_size += internal::count_digits(abs_value); +  if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size) +    out << message << SEP; +  out << ERROR_STR << error_code; +  assert(out.size() <= internal::INLINE_BUFFER_SIZE); +} + +void report_error(FormatFunc func, int error_code, +                  StringRef message) FMT_NOEXCEPT { +  MemoryWriter full_message; +  func(full_message, error_code, message); +  // Use Writer::data instead of Writer::c_str to avoid potential memory +  // allocation. +  std::fwrite(full_message.data(), full_message.size(), 1, stderr); +  std::fputc('\n', stderr); +} +}  // namespace + +FMT_FUNC void SystemError::init( +    int err_code, CStringRef format_str, ArgList args) { +  error_code_ = err_code; +  MemoryWriter w; +  format_system_error(w, err_code, format(format_str, args)); +  std::runtime_error &base = *this; +  base = std::runtime_error(w.str()); +} + +template <typename T> +int internal::CharTraits<char>::format_float( +    char *buffer, std::size_t size, const char *format, +    unsigned width, int precision, T value) { +  if (width == 0) { +    return precision < 0 ? +        FMT_SNPRINTF(buffer, size, format, value) : +        FMT_SNPRINTF(buffer, size, format, precision, value); +  } +  return precision < 0 ? +      FMT_SNPRINTF(buffer, size, format, width, value) : +      FMT_SNPRINTF(buffer, size, format, width, precision, value); +} + +template <typename T> +int internal::CharTraits<wchar_t>::format_float( +    wchar_t *buffer, std::size_t size, const wchar_t *format, +    unsigned width, int precision, T value) { +  if (width == 0) { +    return precision < 0 ? +        FMT_SWPRINTF(buffer, size, format, value) : +        FMT_SWPRINTF(buffer, size, format, precision, value); +  } +  return precision < 0 ? +      FMT_SWPRINTF(buffer, size, format, width, value) : +      FMT_SWPRINTF(buffer, size, format, width, precision, value); +} + +template <typename T> +const char internal::BasicData<T>::DIGITS[] = +    "0001020304050607080910111213141516171819" +    "2021222324252627282930313233343536373839" +    "4041424344454647484950515253545556575859" +    "6061626364656667686970717273747576777879" +    "8081828384858687888990919293949596979899"; + +#define FMT_POWERS_OF_10(factor) \ +  factor * 10, \ +  factor * 100, \ +  factor * 1000, \ +  factor * 10000, \ +  factor * 100000, \ +  factor * 1000000, \ +  factor * 10000000, \ +  factor * 100000000, \ +  factor * 1000000000 + +template <typename T> +const uint32_t internal::BasicData<T>::POWERS_OF_10_32[] = { +  0, FMT_POWERS_OF_10(1) +}; + +template <typename T> +const uint64_t internal::BasicData<T>::POWERS_OF_10_64[] = { +  0, +  FMT_POWERS_OF_10(1), +  FMT_POWERS_OF_10(ULongLong(1000000000)), +  // Multiply several constants instead of using a single long long constant +  // to avoid warnings about C++98 not supporting long long. +  ULongLong(1000000000) * ULongLong(1000000000) * 10 +}; + +FMT_FUNC void internal::report_unknown_type(char code, const char *type) { +  (void)type; +  if (std::isprint(static_cast<unsigned char>(code))) { +    FMT_THROW(FormatError( +        format("unknown format code '{}' for {}", code, type))); +  } +  FMT_THROW(FormatError( +      format("unknown format code '\\x{:02x}' for {}", +        static_cast<unsigned>(code), type))); +} + +#if FMT_USE_WINDOWS_H + +FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) { +  static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; +  if (s.size() > INT_MAX) +    FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG)); +  int s_size = static_cast<int>(s.size()); +  int length = MultiByteToWideChar( +      CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0); +  if (length == 0) +    FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); +  buffer_.resize(length + 1); +  length = MultiByteToWideChar( +    CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length); +  if (length == 0) +    FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); +  buffer_[length] = 0; +} + +FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) { +  if (int error_code = convert(s)) { +    FMT_THROW(WindowsError(error_code, +        "cannot convert string from UTF-16 to UTF-8")); +  } +} + +FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) { +  if (s.size() > INT_MAX) +    return ERROR_INVALID_PARAMETER; +  int s_size = static_cast<int>(s.size()); +  int length = WideCharToMultiByte( +    CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL); +  if (length == 0) +    return GetLastError(); +  buffer_.resize(length + 1); +  length = WideCharToMultiByte( +    CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL); +  if (length == 0) +    return GetLastError(); +  buffer_[length] = 0; +  return 0; +} + +FMT_FUNC void WindowsError::init( +    int err_code, CStringRef format_str, ArgList args) { +  error_code_ = err_code; +  MemoryWriter w; +  internal::format_windows_error(w, err_code, format(format_str, args)); +  std::runtime_error &base = *this; +  base = std::runtime_error(w.str()); +} + +FMT_FUNC void internal::format_windows_error( +    Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { +  FMT_TRY { +    MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer; +    buffer.resize(INLINE_BUFFER_SIZE); +    for (;;) { +      wchar_t *system_message = &buffer[0]; +      int result = FormatMessageW( +        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, +        FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), +        system_message, static_cast<uint32_t>(buffer.size()), FMT_NULL); +      if (result != 0) { +        UTF16ToUTF8 utf8_message; +        if (utf8_message.convert(system_message) == ERROR_SUCCESS) { +          out << message << ": " << utf8_message; +          return; +        } +        break; +      } +      if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) +        break;  // Can't get error message, report error code instead. +      buffer.resize(buffer.size() * 2); +    } +  } FMT_CATCH(...) {} +  fmt::format_error_code(out, error_code, message);  // 'fmt::' is for bcc32. +} + +#endif  // FMT_USE_WINDOWS_H + +FMT_FUNC void format_system_error( +    Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { +  FMT_TRY { +    internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer; +    buffer.resize(internal::INLINE_BUFFER_SIZE); +    for (;;) { +      char *system_message = &buffer[0]; +      int result = safe_strerror(error_code, system_message, buffer.size()); +      if (result == 0) { +        out << message << ": " << system_message; +        return; +      } +      if (result != ERANGE) +        break;  // Can't get error message, report error code instead. +      buffer.resize(buffer.size() * 2); +    } +  } FMT_CATCH(...) {} +  fmt::format_error_code(out, error_code, message);  // 'fmt::' is for bcc32. +} + +template <typename Char> +void internal::FixedBuffer<Char>::grow(std::size_t) { +  FMT_THROW(std::runtime_error("buffer overflow")); +} + +FMT_FUNC internal::Arg internal::FormatterBase::do_get_arg( +    unsigned arg_index, const char *&error) { +  internal::Arg arg = args_[arg_index]; +  switch (arg.type) { +  case internal::Arg::NONE: +    error = "argument index out of range"; +    break; +  case internal::Arg::NAMED_ARG: +    arg = *static_cast<const internal::Arg*>(arg.pointer); +    break; +  default: +    /*nothing*/; +  } +  return arg; +} + +FMT_FUNC void report_system_error( +    int error_code, fmt::StringRef message) FMT_NOEXCEPT { +  // 'fmt::' is for bcc32. +  report_error(format_system_error, error_code, message); +} + +#if FMT_USE_WINDOWS_H +FMT_FUNC void report_windows_error( +    int error_code, fmt::StringRef message) FMT_NOEXCEPT { +  // 'fmt::' is for bcc32. +  report_error(internal::format_windows_error, error_code, message); +} +#endif + +FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) { +  MemoryWriter w; +  w.write(format_str, args); +  std::fwrite(w.data(), 1, w.size(), f); +} + +FMT_FUNC void print(CStringRef format_str, ArgList args) { +  print(stdout, format_str, args); +} + +FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) { +  char escape[] = "\x1b[30m"; +  escape[3] = static_cast<char>('0' + c); +  std::fputs(escape, stdout); +  print(format, args); +  std::fputs(RESET_COLOR, stdout); +} + +#ifndef FMT_HEADER_ONLY + +template struct internal::BasicData<void>; + +// Explicit instantiations for char. + +template void internal::FixedBuffer<char>::grow(std::size_t); + +template FMT_API int internal::CharTraits<char>::format_float( +    char *buffer, std::size_t size, const char *format, +    unsigned width, int precision, double value); + +template FMT_API int internal::CharTraits<char>::format_float( +    char *buffer, std::size_t size, const char *format, +    unsigned width, int precision, long double value); + +// Explicit instantiations for wchar_t. + +template void internal::FixedBuffer<wchar_t>::grow(std::size_t); + +template FMT_API int internal::CharTraits<wchar_t>::format_float( +    wchar_t *buffer, std::size_t size, const wchar_t *format, +    unsigned width, int precision, double value); + +template FMT_API int internal::CharTraits<wchar_t>::format_float( +    wchar_t *buffer, std::size_t size, const wchar_t *format, +    unsigned width, int precision, long double value); + +#endif  // FMT_HEADER_ONLY + +}  // namespace fmt + +#ifdef _MSC_VER +# pragma warning(pop) +#endif diff --git a/lib/spdlog/fmt/bundled/format.h b/lib/spdlog/fmt/bundled/format.h new file mode 100644 index 0000000..b4295cd --- /dev/null +++ b/lib/spdlog/fmt/bundled/format.h @@ -0,0 +1,4920 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - 2016, Victor Zverovich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this +    list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, +    this list of conditions and the following disclaimer in the documentation +    and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FMT_FORMAT_H_ +#define FMT_FORMAT_H_ + +#define FMT_INCLUDE +#include <cassert> +#include <clocale> +#include <cmath> +#include <cstdio> +#include <cstring> +#include <limits> +#include <memory> +#include <stdexcept> +#include <string> +#include <utility> // for std::pair +#include <vector> +#undef FMT_INCLUDE + +// The fmt library version in the form major * 10000 + minor * 100 + patch. +#define FMT_VERSION 40100 + +#if defined(__has_include) +#define FMT_HAS_INCLUDE(x) __has_include(x) +#else +#define FMT_HAS_INCLUDE(x) 0 +#endif + +#if (FMT_HAS_INCLUDE(<string_view>) && __cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) +#include <string_view> +#define FMT_HAS_STRING_VIEW 1 +#else +#define FMT_HAS_STRING_VIEW 0 +#endif + +#if defined _SECURE_SCL && _SECURE_SCL +#define FMT_SECURE_SCL _SECURE_SCL +#else +#define FMT_SECURE_SCL 0 +#endif + +#if FMT_SECURE_SCL +#include <iterator> +#endif + +#ifdef _MSC_VER +#define FMT_MSC_VER _MSC_VER +#else +#define FMT_MSC_VER 0 +#endif + +#if FMT_MSC_VER && FMT_MSC_VER <= 1500 +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +typedef __int64 intmax_t; +#else +#include <stdint.h> +#endif + +#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) +#ifdef FMT_EXPORT +#define FMT_API __declspec(dllexport) +#elif defined(FMT_SHARED) +#define FMT_API __declspec(dllimport) +#endif +#endif +#ifndef FMT_API +#define FMT_API +#endif + +#ifdef __GNUC__ +#define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#define FMT_GCC_EXTENSION __extension__ +#if FMT_GCC_VERSION >= 406 +#pragma GCC diagnostic push +// Disable the warning about "long long" which is sometimes reported even +// when using __extension__. +#pragma GCC diagnostic ignored "-Wlong-long" +// Disable the warning about declaration shadowing because it affects too +// many valid cases. +#pragma GCC diagnostic ignored "-Wshadow" +// Disable the warning about implicit conversions that may change the sign of +// an integer; silencing it otherwise would require many explicit casts. +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif +#if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__ +#define FMT_HAS_GXX_CXX11 1 +#endif +#else +#define FMT_GCC_VERSION 0 +#define FMT_GCC_EXTENSION +#define FMT_HAS_GXX_CXX11 0 +#endif + +#if defined(__INTEL_COMPILER) +#define FMT_ICC_VERSION __INTEL_COMPILER +#elif defined(__ICL) +#define FMT_ICC_VERSION __ICL +#endif + +#if defined(__clang__) && !defined(FMT_ICC_VERSION) +#define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#pragma clang diagnostic ignored "-Wpadded" +#endif + +#ifdef __GNUC_LIBSTD__ +#define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) +#endif + +#ifdef __has_feature +#define FMT_HAS_FEATURE(x) __has_feature(x) +#else +#define FMT_HAS_FEATURE(x) 0 +#endif + +#ifdef __has_builtin +#define FMT_HAS_BUILTIN(x) __has_builtin(x) +#else +#define FMT_HAS_BUILTIN(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 + +#if FMT_HAS_CPP_ATTRIBUTE(maybe_unused) +#define FMT_HAS_CXX17_ATTRIBUTE_MAYBE_UNUSED +// VC++ 1910 support /std: option and that will set _MSVC_LANG macro +// Clang with Microsoft CodeGen doesn't define _MSVC_LANG macro +#elif defined(_MSVC_LANG) && _MSVC_LANG > 201402 && _MSC_VER >= 1910 +#define FMT_HAS_CXX17_ATTRIBUTE_MAYBE_UNUSED +#endif + +#ifdef FMT_HAS_CXX17_ATTRIBUTE_MAYBE_UNUSED +#define FMT_MAYBE_UNUSED [[maybe_unused]] +// g++/clang++ also support [[gnu::unused]]. However, we don't use it. +#elif defined(__GNUC__) +#define FMT_MAYBE_UNUSED __attribute__((unused)) +#else +#define FMT_MAYBE_UNUSED +#endif + +// Use the compiler's attribute noreturn +#if defined(__MINGW32__) || defined(__MINGW64__) +#define FMT_NORETURN __attribute__((noreturn)) +#elif FMT_HAS_CPP_ATTRIBUTE(noreturn) && __cplusplus >= 201103L +#define FMT_NORETURN [[noreturn]] +#else +#define FMT_NORETURN +#endif + +#ifndef FMT_USE_VARIADIC_TEMPLATES +// Variadic templates are available in GCC since version 4.4 +// (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++ +// since version 2013. +#define FMT_USE_VARIADIC_TEMPLATES                                                                                                         \ +    (FMT_HAS_FEATURE(cxx_variadic_templates) || (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800) +#endif + +#ifndef FMT_USE_RVALUE_REFERENCES +// Don't use rvalue references when compiling with clang and an old libstdc++ +// as the latter doesn't provide std::move. +#if defined(FMT_GNUC_LIBSTD_VERSION) && FMT_GNUC_LIBSTD_VERSION <= 402 +#define FMT_USE_RVALUE_REFERENCES 0 +#else +#define FMT_USE_RVALUE_REFERENCES                                                                                                          \ +    (FMT_HAS_FEATURE(cxx_rvalue_references) || (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1600) +#endif +#endif + +#if __cplusplus >= 201103L || FMT_MSC_VER >= 1700 +#define FMT_USE_ALLOCATOR_TRAITS 1 +#else +#define FMT_USE_ALLOCATOR_TRAITS 0 +#endif + +// Check if exceptions are disabled. +#if defined(__GNUC__) && !defined(__EXCEPTIONS) +#define FMT_EXCEPTIONS 0 +#endif +#if FMT_MSC_VER && !_HAS_EXCEPTIONS +#define FMT_EXCEPTIONS 0 +#endif +#ifndef FMT_EXCEPTIONS +#define FMT_EXCEPTIONS 1 +#endif + +#ifndef FMT_THROW +#if FMT_EXCEPTIONS +#define FMT_THROW(x) throw x +#else +#define FMT_THROW(x) assert(false) +#endif +#endif + +// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). +#ifndef FMT_USE_NOEXCEPT +#define FMT_USE_NOEXCEPT 0 +#endif + +#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 +#define FMT_DETECTED_NOEXCEPT noexcept +#else +#define FMT_DETECTED_NOEXCEPT throw() +#endif + +#ifndef FMT_NOEXCEPT +#if FMT_EXCEPTIONS +#define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT +#else +#define FMT_NOEXCEPT +#endif +#endif + +// This is needed because GCC still uses throw() in its headers when exceptions +// are disabled. +#if FMT_GCC_VERSION +#define FMT_DTOR_NOEXCEPT FMT_DETECTED_NOEXCEPT +#else +#define FMT_DTOR_NOEXCEPT FMT_NOEXCEPT +#endif + +#ifndef FMT_OVERRIDE +#if (defined(FMT_USE_OVERRIDE) && FMT_USE_OVERRIDE) || FMT_HAS_FEATURE(cxx_override) || (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) ||   \ +    FMT_MSC_VER >= 1900 +#define FMT_OVERRIDE override +#else +#define FMT_OVERRIDE +#endif +#endif + +#ifndef FMT_NULL +#if FMT_HAS_FEATURE(cxx_nullptr) || (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1600 +#define FMT_NULL nullptr +#else +#define FMT_NULL NULL +#endif +#endif + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#ifndef FMT_USE_DELETED_FUNCTIONS +#define FMT_USE_DELETED_FUNCTIONS 0 +#endif + +#if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) ||                \ +    FMT_MSC_VER >= 1800 +#define FMT_DELETED_OR_UNDEFINED = delete +#define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName)                                                                                             \ +    TypeName(const TypeName &) = delete;                                                                                                   \ +    TypeName &operator=(const TypeName &) = delete +#else +#define FMT_DELETED_OR_UNDEFINED +#define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName)                                                                                             \ +    TypeName(const TypeName &);                                                                                                            \ +    TypeName &operator=(const TypeName &) +#endif + +#ifndef FMT_USE_DEFAULTED_FUNCTIONS +#define FMT_USE_DEFAULTED_FUNCTIONS 0 +#endif + +#ifndef FMT_DEFAULTED_COPY_CTOR +#if FMT_USE_DEFAULTED_FUNCTIONS || FMT_HAS_FEATURE(cxx_defaulted_functions) || (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) ||            \ +    FMT_MSC_VER >= 1800 +#define FMT_DEFAULTED_COPY_CTOR(TypeName) TypeName(const TypeName &) = default; +#else +#define FMT_DEFAULTED_COPY_CTOR(TypeName) +#endif +#endif + +#ifndef FMT_USE_USER_DEFINED_LITERALS +// All compilers which support UDLs also support variadic templates. This +// makes the fmt::literals implementation easier. However, an explicit check +// for variadic templates is added here just in case. +// For Intel's compiler both it and the system gcc/msc must support UDLs. +#if FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES &&                                                                             \ +    (FMT_HAS_FEATURE(cxx_user_literals) || (FMT_GCC_VERSION >= 407 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900) &&                        \ +    (!defined(FMT_ICC_VERSION) || FMT_ICC_VERSION >= 1500) +#define FMT_USE_USER_DEFINED_LITERALS 1 +#else +#define FMT_USE_USER_DEFINED_LITERALS 0 +#endif +#endif + +#ifndef FMT_USE_EXTERN_TEMPLATES +#define FMT_USE_EXTERN_TEMPLATES (FMT_CLANG_VERSION >= 209 || (FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11)) +#endif + +#ifdef FMT_HEADER_ONLY +// If header only do not use extern templates. +#undef FMT_USE_EXTERN_TEMPLATES +#define FMT_USE_EXTERN_TEMPLATES 0 +#endif + +#ifndef FMT_ASSERT +#define FMT_ASSERT(condition, message) assert((condition) && message) +#endif + +// __builtin_clz is broken in clang with Microsoft CodeGen: +// https://github.com/fmtlib/fmt/issues/519 +#ifndef _MSC_VER +#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) +#define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +#endif + +#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) +#define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +#endif +#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_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(_MANAGED) +#include <intrin.h> // _BitScanReverse, _BitScanReverse64 + +namespace fmt { +namespace internal { +// avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning +#ifndef __clang__ +#pragma intrinsic(_BitScanReverse) +#endif +inline uint32_t clz(uint32_t x) +{ +    unsigned long r = 0; +    _BitScanReverse(&r, x); + +    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. +#pragma warning(suppress : 6102) +    return 31 - r; +} +#define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) + +// avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning +#if defined(_WIN64) && !defined(__clang__) +#pragma intrinsic(_BitScanReverse64) +#endif + +inline uint32_t clzll(uint64_t x) +{ +    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 + +    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. +#pragma warning(suppress : 6102) +    return 63 - r; +} +#define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) +} // namespace internal +} // namespace fmt +#endif + +namespace fmt { +namespace internal { +struct DummyInt +{ +    int data[2]; +    operator int() const +    { +        return 0; +    } +}; +typedef std::numeric_limits<fmt::internal::DummyInt> FPUtil; + +// Dummy implementations of system functions such as signbit and ecvt called +// if the latter are not available. +inline DummyInt signbit(...) +{ +    return DummyInt(); +} +inline DummyInt _ecvt_s(...) +{ +    return DummyInt(); +} +inline DummyInt isinf(...) +{ +    return DummyInt(); +} +inline DummyInt _finite(...) +{ +    return DummyInt(); +} +inline DummyInt isnan(...) +{ +    return DummyInt(); +} +inline DummyInt _isnan(...) +{ +    return DummyInt(); +} + +// A helper function to suppress bogus "conditional expression is constant" +// warnings. +template<typename T> +inline T const_check(T value) +{ +    return value; +} +} // namespace internal +} // namespace fmt + +namespace std { +// Standard permits specialization of std::numeric_limits. This specialization +// is used to resolve ambiguity between isinf and std::isinf in glibc: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48891 +// and the same for isnan and signbit. +template<> +class numeric_limits<fmt::internal::DummyInt> : public std::numeric_limits<int> +{ +public: +    // Portable version of isinf. +    template<typename T> +    static bool isinfinity(T x) +    { +        using namespace fmt::internal; +        // The resolution "priority" is: +        // isinf macro > std::isinf > ::isinf > fmt::internal::isinf +        if (const_check(sizeof(isinf(x)) == sizeof(bool) || sizeof(isinf(x)) == sizeof(int))) +        { +            return isinf(x) != 0; +        } +        return !_finite(static_cast<double>(x)); +    } + +    // Portable version of isnan. +    template<typename T> +    static bool isnotanumber(T x) +    { +        using namespace fmt::internal; +        if (const_check(sizeof(isnan(x)) == sizeof(bool) || sizeof(isnan(x)) == sizeof(int))) +        { +            return isnan(x) != 0; +        } +        return _isnan(static_cast<double>(x)) != 0; +    } + +    // Portable version of signbit. +    static bool isnegative(double x) +    { +        using namespace fmt::internal; +        if (const_check(sizeof(signbit(x)) == sizeof(bool) || sizeof(signbit(x)) == sizeof(int))) +        { +            return signbit(x) != 0; +        } +        if (x < 0) +            return true; +        if (!isnotanumber(x)) +            return false; +        int dec = 0, sign = 0; +        char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail. +        _ecvt_s(buffer, sizeof(buffer), x, 0, &dec, &sign); +        return sign != 0; +    } +}; +} // namespace std + +namespace fmt { + +// Fix the warning about long long on older versions of GCC +// that don't support the diagnostic pragma. +FMT_GCC_EXTENSION typedef long long LongLong; +FMT_GCC_EXTENSION typedef unsigned long long ULongLong; + +#if FMT_USE_RVALUE_REFERENCES +using std::move; +#endif + +template<typename Char> +class BasicWriter; + +typedef BasicWriter<char> Writer; +typedef BasicWriter<wchar_t> WWriter; + +template<typename Char> +class ArgFormatter; + +struct FormatSpec; + +template<typename Impl, typename Char, typename Spec = fmt::FormatSpec> +class BasicPrintfArgFormatter; + +template<typename CharType, typename ArgFormatter = fmt::ArgFormatter<CharType>> +class BasicFormatter; + +/** +  \rst +  A string reference. It can be constructed from a C string or +  ``std::basic_string``. + +  You can use one of the following typedefs for common character types: + +  +------------+-------------------------+ +  | Type       | Definition              | +  +============+=========================+ +  | StringRef  | BasicStringRef<char>    | +  +------------+-------------------------+ +  | WStringRef | BasicStringRef<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(StringRef format_str, const Args & ... args); + +    format("{}", 42); +    format(std::string("{}"), 42); +  \endrst + */ +template<typename Char> +class BasicStringRef +{ +private: +    const Char *data_; +    std::size_t size_; + +public: +    /** Constructs a string reference object from a C string and a size. */ +    BasicStringRef(const Char *s, std::size_t size) +        : data_(s) +        , size_(size) +    { +    } + +    /** +      \rst +      Constructs a string reference object from a C string computing +      the size with ``std::char_traits<Char>::length``. +      \endrst +     */ +    BasicStringRef(const Char *s) +        : data_(s) +        , size_(std::char_traits<Char>::length(s)) +    { +    } + +    /** +      \rst +      Constructs a string reference from a ``std::basic_string`` object. +      \endrst +     */ +    template<typename Allocator> +    BasicStringRef(const std::basic_string<Char, std::char_traits<Char>, Allocator> &s) +        : data_(s.c_str()) +        , size_(s.size()) +    { +    } + +#if FMT_HAS_STRING_VIEW +    /** +      \rst +      Constructs a string reference from a ``std::basic_string_view`` object. +      \endrst +     */ +    BasicStringRef(const std::basic_string_view<Char, std::char_traits<Char>> &s) +        : data_(s.data()) +        , size_(s.size()) +    { +    } + +    /** +     \rst +     Converts a string reference to an ``std::string_view`` object. +     \endrst +    */ +    explicit operator std::basic_string_view<Char>() const FMT_NOEXCEPT +    { +        return std::basic_string_view<Char>(data_, size_); +    } +#endif + +    /** +      \rst +      Converts a string reference to an ``std::string`` object. +      \endrst +     */ +    std::basic_string<Char> to_string() const +    { +        return std::basic_string<Char>(data_, size_); +    } + +    /** Returns a pointer to the string data. */ +    const Char *data() const +    { +        return data_; +    } + +    /** Returns the string size. */ +    std::size_t size() const +    { +        return size_; +    } + +    // Lexicographically compare this string reference to other. +    int compare(BasicStringRef other) const +    { +        std::size_t size = size_ < other.size_ ? size_ : other.size_; +        int result = std::char_traits<Char>::compare(data_, other.data_, size); +        if (result == 0) +            result = size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1); +        return result; +    } + +    friend bool operator==(BasicStringRef lhs, BasicStringRef rhs) +    { +        return lhs.compare(rhs) == 0; +    } +    friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) +    { +        return lhs.compare(rhs) != 0; +    } +    friend bool operator<(BasicStringRef lhs, BasicStringRef rhs) +    { +        return lhs.compare(rhs) < 0; +    } +    friend bool operator<=(BasicStringRef lhs, BasicStringRef rhs) +    { +        return lhs.compare(rhs) <= 0; +    } +    friend bool operator>(BasicStringRef lhs, BasicStringRef rhs) +    { +        return lhs.compare(rhs) > 0; +    } +    friend bool operator>=(BasicStringRef lhs, BasicStringRef rhs) +    { +        return lhs.compare(rhs) >= 0; +    } +}; + +typedef BasicStringRef<char> StringRef; +typedef BasicStringRef<wchar_t> WStringRef; + +/** +  \rst +  A reference to a null terminated string. It can be constructed from a C +  string or ``std::basic_string``. + +  You can use one of the following typedefs for common character types: + +  +-------------+--------------------------+ +  | Type        | Definition               | +  +=============+==========================+ +  | CStringRef  | BasicCStringRef<char>    | +  +-------------+--------------------------+ +  | WCStringRef | BasicCStringRef<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(CStringRef format_str, const Args & ... args); + +    format("{}", 42); +    format(std::string("{}"), 42); +  \endrst + */ +template<typename Char> +class BasicCStringRef +{ +private: +    const Char *data_; + +public: +    /** Constructs a string reference object from a C string. */ +    BasicCStringRef(const Char *s) +        : data_(s) +    { +    } + +    /** +      \rst +      Constructs a string reference from a ``std::basic_string`` object. +      \endrst +     */ +    template<typename Allocator> +    BasicCStringRef(const std::basic_string<Char, std::char_traits<Char>, Allocator> &s) +        : data_(s.c_str()) +    { +    } + +    /** Returns the pointer to a C string. */ +    const Char *c_str() const +    { +        return data_; +    } +}; + +typedef BasicCStringRef<char> CStringRef; +typedef BasicCStringRef<wchar_t> WCStringRef; + +/** A formatting error such as invalid format string. */ +class FormatError : public std::runtime_error +{ +public: +    explicit FormatError(CStringRef message) +        : std::runtime_error(message.c_str()) +    { +    } +    FormatError(const FormatError &ferr) +        : std::runtime_error(ferr) +    { +    } +    FMT_API ~FormatError() FMT_DTOR_NOEXCEPT FMT_OVERRIDE; +}; + +namespace internal { + +// MakeUnsigned<T>::Type gives an unsigned type corresponding to integer type T. +template<typename T> +struct MakeUnsigned +{ +    typedef T Type; +}; + +#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U)                                                                                                 \ +    template<>                                                                                                                             \ +    struct MakeUnsigned<T>                                                                                                                 \ +    {                                                                                                                                      \ +        typedef U Type;                                                                                                                    \ +    } + +FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); +FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); +FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); +FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); +FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); +FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); + +// Casts nonnegative integer to unsigned. +template<typename Int> +inline typename MakeUnsigned<Int>::Type to_unsigned(Int value) +{ +    FMT_ASSERT(value >= 0, "negative value"); +    return static_cast<typename MakeUnsigned<Int>::Type>(value); +} + +// The number of characters to store in the MemoryBuffer object itself +// to avoid dynamic memory allocation. +enum +{ +    INLINE_BUFFER_SIZE = 500 +}; + +#if FMT_SECURE_SCL +// Use checked iterator to avoid warnings on MSVC. +template<typename T> +inline stdext::checked_array_iterator<T *> make_ptr(T *ptr, std::size_t size) +{ +    return stdext::checked_array_iterator<T *>(ptr, size); +} +#else +template<typename T> +inline T *make_ptr(T *ptr, std::size_t) +{ +    return ptr; +} +#endif +} // namespace internal + +/** +  \rst +  A buffer supporting a subset of ``std::vector``'s operations. +  \endrst + */ +template<typename T> +class Buffer +{ +private: +    FMT_DISALLOW_COPY_AND_ASSIGN(Buffer); + +protected: +    T *ptr_; +    std::size_t size_; +    std::size_t capacity_; + +    Buffer(T *ptr = FMT_NULL, std::size_t capacity = 0) +        : ptr_(ptr) +        , size_(0) +        , capacity_(capacity) +    { +    } + +    /** +      \rst +      Increases the buffer capacity to hold at least *size* elements updating +      ``ptr_`` and ``capacity_``. +      \endrst +     */ +    virtual void grow(std::size_t size) = 0; + +public: +    virtual ~Buffer() {} + +    /** Returns the size of this buffer. */ +    std::size_t size() const +    { +        return size_; +    } + +    /** Returns the capacity of this buffer. */ +    std::size_t capacity() const +    { +        return capacity_; +    } + +    /** +      Resizes the buffer. If T is a POD type new elements may not be initialized. +     */ +    void resize(std::size_t new_size) +    { +        if (new_size > capacity_) +            grow(new_size); +        size_ = new_size; +    } + +    /** +      \rst +      Reserves space to store at least *capacity* elements. +      \endrst +     */ +    void reserve(std::size_t capacity) +    { +        if (capacity > capacity_) +            grow(capacity); +    } + +    void clear() FMT_NOEXCEPT +    { +        size_ = 0; +    } + +    void push_back(const T &value) +    { +        if (size_ == capacity_) +            grow(size_ + 1); +        ptr_[size_++] = value; +    } + +    /** Appends data to the end of the buffer. */ +    template<typename U> +    void append(const U *begin, const U *end); + +    T &operator[](std::size_t index) +    { +        return ptr_[index]; +    } +    const T &operator[](std::size_t index) const +    { +        return ptr_[index]; +    } +}; + +template<typename T> +template<typename U> +void Buffer<T>::append(const U *begin, const U *end) +{ +    FMT_ASSERT(end >= begin, "negative value"); +    std::size_t new_size = size_ + static_cast<std::size_t>(end - begin); +    if (new_size > capacity_) +        grow(new_size); +    std::uninitialized_copy(begin, end, internal::make_ptr(ptr_, capacity_) + size_); +    size_ = new_size; +} + +namespace internal { + +// A memory buffer for trivially copyable/constructible types with the first +// SIZE elements stored in the object itself. +template<typename T, std::size_t SIZE, typename Allocator = std::allocator<T>> +class MemoryBuffer : private Allocator, public Buffer<T> +{ +private: +    T data_[SIZE]; + +    // Deallocate memory allocated by the buffer. +    void deallocate() +    { +        if (this->ptr_ != data_) +            Allocator::deallocate(this->ptr_, this->capacity_); +    } + +protected: +    void grow(std::size_t size) FMT_OVERRIDE; + +public: +    explicit MemoryBuffer(const Allocator &alloc = Allocator()) +        : Allocator(alloc) +        , Buffer<T>(data_, SIZE) +    { +    } +    ~MemoryBuffer() FMT_OVERRIDE +    { +        deallocate(); +    } + +#if FMT_USE_RVALUE_REFERENCES +private: +    // Move data from other to this buffer. +    void move(MemoryBuffer &other) +    { +        Allocator &this_alloc = *this, &other_alloc = other; +        this_alloc = std::move(other_alloc); +        this->size_ = other.size_; +        this->capacity_ = other.capacity_; +        if (other.ptr_ == other.data_) +        { +            this->ptr_ = data_; +            std::uninitialized_copy(other.data_, other.data_ + this->size_, make_ptr(data_, this->capacity_)); +        } +        else +        { +            this->ptr_ = other.ptr_; +            // Set pointer to the inline array so that delete is not called +            // when deallocating. +            other.ptr_ = other.data_; +        } +    } + +public: +    MemoryBuffer(MemoryBuffer &&other) +    { +        move(other); +    } + +    MemoryBuffer &operator=(MemoryBuffer &&other) +    { +        assert(this != &other); +        deallocate(); +        move(other); +        return *this; +    } +#endif + +    // Returns a copy of the allocator associated with this buffer. +    Allocator get_allocator() const +    { +        return *this; +    } +}; + +template<typename T, std::size_t SIZE, typename Allocator> +void MemoryBuffer<T, SIZE, Allocator>::grow(std::size_t size) +{ +    std::size_t new_capacity = this->capacity_ + this->capacity_ / 2; +    if (size > new_capacity) +        new_capacity = size; +#if FMT_USE_ALLOCATOR_TRAITS +    T *new_ptr = std::allocator_traits<Allocator>::allocate(*this, new_capacity, FMT_NULL); +#else +    T *new_ptr = this->allocate(new_capacity, FMT_NULL); +#endif +    // The following code doesn't throw, so the raw pointer above doesn't leak. +    std::uninitialized_copy(this->ptr_, this->ptr_ + this->size_, make_ptr(new_ptr, new_capacity)); +    std::size_t old_capacity = this->capacity_; +    T *old_ptr = this->ptr_; +    this->capacity_ = new_capacity; +    this->ptr_ = new_ptr; +    // deallocate may throw (at least in principle), but it doesn't matter since +    // the buffer already uses the new storage and will deallocate it in case +    // of exception. +    if (old_ptr != data_) +        Allocator::deallocate(old_ptr, old_capacity); +} + +// A fixed-size buffer. +template<typename Char> +class FixedBuffer : public fmt::Buffer<Char> +{ +public: +    FixedBuffer(Char *array, std::size_t size) +        : fmt::Buffer<Char>(array, size) +    { +    } + +protected: +    FMT_API void grow(std::size_t size) FMT_OVERRIDE; +}; + +template<typename Char> +class BasicCharTraits +{ +public: +#if FMT_SECURE_SCL +    typedef stdext::checked_array_iterator<Char *> CharPtr; +#else +    typedef Char *CharPtr; +#endif +    static Char cast(int value) +    { +        return static_cast<Char>(value); +    } +}; + +template<typename Char> +class CharTraits; + +template<> +class CharTraits<char> : public BasicCharTraits<char> +{ +private: +    // Conversion from wchar_t to char is not allowed. +    static char convert(wchar_t); + +public: +    static char convert(char value) +    { +        return value; +    } + +    // Formats a floating-point number. +    template<typename T> +    FMT_API static int format_float(char *buffer, std::size_t size, const char *format, unsigned width, int precision, T value); +}; + +#if FMT_USE_EXTERN_TEMPLATES +extern template int CharTraits<char>::format_float<double>( +    char *buffer, std::size_t size, const char *format, unsigned width, int precision, double value); +extern template int CharTraits<char>::format_float<long double>( +    char *buffer, std::size_t size, const char *format, unsigned width, int precision, long double value); +#endif + +template<> +class CharTraits<wchar_t> : public BasicCharTraits<wchar_t> +{ +public: +    static wchar_t convert(char value) +    { +        return value; +    } +    static wchar_t convert(wchar_t value) +    { +        return value; +    } + +    template<typename T> +    FMT_API static int format_float(wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, T value); +}; + +#if FMT_USE_EXTERN_TEMPLATES +extern template int CharTraits<wchar_t>::format_float<double>( +    wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, double value); +extern template int CharTraits<wchar_t>::format_float<long double>( +    wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, long double value); +#endif + +// Checks if a number is negative - used to avoid warnings. +template<bool IsSigned> +struct SignChecker +{ +    template<typename T> +    static bool is_negative(T value) +    { +        return value < 0; +    } +}; + +template<> +struct SignChecker<false> +{ +    template<typename T> +    static bool is_negative(T) +    { +        return false; +    } +}; + +// 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> +inline bool is_negative(T value) +{ +    return SignChecker<std::numeric_limits<T>::is_signed>::is_negative(value); +} + +// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise. +template<bool FitsIn32Bits> +struct TypeSelector +{ +    typedef uint32_t Type; +}; + +template<> +struct TypeSelector<false> +{ +    typedef uint64_t Type; +}; + +template<typename T> +struct IntTraits +{ +    // Smallest of uint32_t and uint64_t that is large enough to represent +    // all values of T. +    typedef typename TypeSelector<std::numeric_limits<T>::digits <= 32>::Type MainType; +}; + +FMT_API FMT_NORETURN void report_unknown_type(char code, const char *type); + +// Static data is placed in this class template to allow header-only +// configuration. +template<typename T = void> +struct FMT_API BasicData +{ +    static const uint32_t POWERS_OF_10_32[]; +    static const uint64_t POWERS_OF_10_64[]; +    static const char DIGITS[]; +}; + +#if FMT_USE_EXTERN_TEMPLATES +extern template struct BasicData<void>; +#endif + +typedef BasicData<> Data; + +#ifdef FMT_BUILTIN_CLZLL +// Returns the number of decimal digits in n. Leading zeros are not counted +// except for n == 0 in which case count_digits returns 1. +inline unsigned count_digits(uint64_t n) +{ +    // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 +    // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. +    int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; +    return to_unsigned(t) - (n < Data::POWERS_OF_10_64[t]) + 1; +} +#else +// Fallback version of count_digits used when __builtin_clz is not available. +inline unsigned count_digits(uint64_t n) +{ +    unsigned count = 1; +    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; +    } +} +#endif + +#ifdef FMT_BUILTIN_CLZ +// Optional version of count_digits for better performance on 32-bit platforms. +inline unsigned count_digits(uint32_t n) +{ +    int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; +    return to_unsigned(t) - (n < Data::POWERS_OF_10_32[t]) + 1; +} +#endif + +// A functor that doesn't add a thousands separator. +struct NoThousandsSep +{ +    template<typename Char> +    void operator()(Char *) +    { +    } +}; + +// A functor that adds a thousands separator. +class ThousandsSep +{ +private: +    fmt::StringRef sep_; + +    // Index of a decimal digit with the least significant digit having index 0. +    unsigned digit_index_; + +public: +    explicit ThousandsSep(fmt::StringRef sep) +        : sep_(sep) +        , digit_index_(0) +    { +    } + +    template<typename Char> +    void operator()(Char *&buffer) +    { +        if (++digit_index_ % 3 != 0) +            return; +        buffer -= sep_.size(); +        std::uninitialized_copy(sep_.data(), sep_.data() + sep_.size(), internal::make_ptr(buffer, sep_.size())); +    } +}; + +// Formats a decimal unsigned integer value writing into buffer. +// thousands_sep is a functor that is called after writing each char to +// add a thousands separator if necessary. +template<typename UInt, typename Char, typename ThousandsSep> +inline void format_decimal(Char *buffer, UInt value, unsigned num_digits, ThousandsSep thousands_sep) +{ +    buffer += num_digits; +    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. +        unsigned index = static_cast<unsigned>((value % 100) * 2); +        value /= 100; +        *--buffer = Data::DIGITS[index + 1]; +        thousands_sep(buffer); +        *--buffer = Data::DIGITS[index]; +        thousands_sep(buffer); +    } +    if (value < 10) +    { +        *--buffer = static_cast<char>('0' + value); +        return; +    } +    unsigned index = static_cast<unsigned>(value * 2); +    *--buffer = Data::DIGITS[index + 1]; +    thousands_sep(buffer); +    *--buffer = Data::DIGITS[index]; +} + +template<typename UInt, typename Char> +inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) +{ +    format_decimal(buffer, value, num_digits, NoThousandsSep()); +    return; +} + +#ifndef _WIN32 +#define FMT_USE_WINDOWS_H 0 +#elif !defined(FMT_USE_WINDOWS_H) +#define FMT_USE_WINDOWS_H 1 +#endif + +// Define FMT_USE_WINDOWS_H to 0 to disable use of windows.h. +// All the functionality that relies on it will be disabled too. +#if FMT_USE_WINDOWS_H +// A converter from UTF-8 to UTF-16. +// It is only provided for Windows since other systems support UTF-8 natively. +class UTF8ToUTF16 +{ +private: +    MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer_; + +public: +    FMT_API explicit UTF8ToUTF16(StringRef s); +    operator WStringRef() const +    { +        return WStringRef(&buffer_[0], size()); +    } +    size_t size() const +    { +        return buffer_.size() - 1; +    } +    const wchar_t *c_str() const +    { +        return &buffer_[0]; +    } +    std::wstring str() const +    { +        return std::wstring(&buffer_[0], size()); +    } +}; + +// A converter from UTF-16 to UTF-8. +// It is only provided for Windows since other systems support UTF-8 natively. +class UTF16ToUTF8 +{ +private: +    MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer_; + +public: +    UTF16ToUTF8() {} +    FMT_API explicit UTF16ToUTF8(WStringRef s); +    operator StringRef() const +    { +        return StringRef(&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(WStringRef s); +}; + +FMT_API void format_windows_error(fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT; +#endif + +// A formatting argument value. +struct Value +{ +    template<typename Char> +    struct StringValue +    { +        const Char *value; +        std::size_t size; +    }; + +    typedef void (*FormatFunc)(void *formatter, const void *arg, void *format_str_ptr); + +    struct CustomValue +    { +        const void *value; +        FormatFunc format; +    }; + +    union +    { +        int int_value; +        unsigned uint_value; +        LongLong long_long_value; +        ULongLong ulong_long_value; +        double double_value; +        long double long_double_value; +        const void *pointer; +        StringValue<char> string; +        StringValue<signed char> sstring; +        StringValue<unsigned char> ustring; +        StringValue<wchar_t> wstring; +        CustomValue custom; +    }; + +    enum Type +    { +        NONE, +        NAMED_ARG, +        // Integer types should go first, +        INT, +        UINT, +        LONG_LONG, +        ULONG_LONG, +        BOOL, +        CHAR, +        LAST_INTEGER_TYPE = CHAR, +        // followed by floating-point types. +        DOUBLE, +        LONG_DOUBLE, +        LAST_NUMERIC_TYPE = LONG_DOUBLE, +        CSTRING, +        STRING, +        WSTRING, +        POINTER, +        CUSTOM +    }; +}; + +// A formatting argument. It is a trivially copyable/constructible type to +// allow storage in internal::MemoryBuffer. +struct Arg : Value +{ +    Type type; +}; + +template<typename Char> +struct NamedArg; +template<typename Char, typename T> +struct NamedArgWithType; + +template<typename T = void> +struct Null +{ +}; + +// A helper class template to enable or disable overloads taking wide +// characters and strings in MakeValue. +template<typename T, typename Char> +struct WCharHelper +{ +    typedef Null<T> Supported; +    typedef T Unsupported; +}; + +template<typename T> +struct WCharHelper<T, wchar_t> +{ +    typedef T Supported; +    typedef Null<T> Unsupported; +}; + +typedef char Yes[1]; +typedef char No[2]; + +template<typename T> +T &get(); + +// These are non-members to workaround an overload resolution bug in bcc32. +Yes &convert(fmt::ULongLong); +No &convert(...); + +template<typename T, bool ENABLE_CONVERSION> +struct ConvertToIntImpl +{ +    enum +    { +        value = ENABLE_CONVERSION +    }; +}; + +template<typename T, bool ENABLE_CONVERSION> +struct ConvertToIntImpl2 +{ +    enum +    { +        value = false +    }; +}; + +template<typename T> +struct ConvertToIntImpl2<T, true> +{ +    enum +    { +        // Don't convert numeric types. +        value = ConvertToIntImpl<T, !std::numeric_limits<T>::is_specialized>::value +    }; +}; + +template<typename T> +struct ConvertToInt +{ +    enum +    { +        enable_conversion = sizeof(fmt::internal::convert(get<T>())) == sizeof(Yes) +    }; +    enum +    { +        value = ConvertToIntImpl2<T, enable_conversion>::value +    }; +}; + +#define FMT_DISABLE_CONVERSION_TO_INT(Type)                                                                                                \ +    template<>                                                                                                                             \ +    struct ConvertToInt<Type>                                                                                                              \ +    {                                                                                                                                      \ +        enum                                                                                                                               \ +        {                                                                                                                                  \ +            value = 0                                                                                                                      \ +        };                                                                                                                                 \ +    } + +// Silence warnings about convering float to int. +FMT_DISABLE_CONVERSION_TO_INT(float); +FMT_DISABLE_CONVERSION_TO_INT(double); +FMT_DISABLE_CONVERSION_TO_INT(long double); + +template<bool B, class T = void> +struct EnableIf +{ +}; + +template<class T> +struct EnableIf<true, T> +{ +    typedef T type; +}; + +template<bool B, class T, class F> +struct Conditional +{ +    typedef T type; +}; + +template<class T, class F> +struct Conditional<false, T, F> +{ +    typedef F type; +}; + +// For bcc32 which doesn't understand ! in template arguments. +template<bool> +struct Not +{ +    enum +    { +        value = 0 +    }; +}; + +template<> +struct Not<false> +{ +    enum +    { +        value = 1 +    }; +}; + +template<typename T> +struct FalseType +{ +    enum +    { +        value = 0 +    }; +}; + +template<typename T, T> +struct LConvCheck +{ +    LConvCheck(int) {} +}; + +// Returns the thousands separator for the current locale. +// We check if ``lconv`` contains ``thousands_sep`` because on Android +// ``lconv`` is stubbed as an empty struct. +template<typename LConv> +inline StringRef thousands_sep(LConv *lc, LConvCheck<char * LConv::*, &LConv::thousands_sep> = 0) +{ +    return lc->thousands_sep; +} + +inline fmt::StringRef thousands_sep(...) +{ +    return ""; +} + +#define FMT_CONCAT(a, b) a##b + +#if FMT_GCC_VERSION >= 303 +#define FMT_UNUSED __attribute__((unused)) +#else +#define FMT_UNUSED +#endif + +#ifndef FMT_USE_STATIC_ASSERT +#define FMT_USE_STATIC_ASSERT 0 +#endif + +#if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600 +#define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message) +#else +#define FMT_CONCAT_(a, b) FMT_CONCAT(a, b) +#define FMT_STATIC_ASSERT(cond, message) typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED +#endif + +template<typename Formatter> +void format_arg(Formatter &, ...) +{ +    FMT_STATIC_ASSERT(FalseType<Formatter>::value, "Cannot format argument. To enable the use of ostream " +                                                   "operator<< include fmt/ostream.h. Otherwise provide " +                                                   "an overload of format_arg."); +} + +// Makes an Arg object from any type. +template<typename Formatter> +class MakeValue : public Arg +{ +public: +    typedef typename Formatter::Char Char; + +private: +    // The following two methods are private to disallow formatting of +    // arbitrary pointers. 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. +    // Do not implement! +    template<typename T> +    MakeValue(const T *value); +    template<typename T> +    MakeValue(T *value); + +    // The following methods are private to disallow formatting of wide +    // characters and strings into narrow strings as in +    //   fmt::format("{}", L"test"); +    // To fix this, use a wide format string: fmt::format(L"{}", L"test"). +#if !FMT_MSC_VER || defined(_NATIVE_WCHAR_T_DEFINED) +    MakeValue(typename WCharHelper<wchar_t, Char>::Unsupported); +#endif +    MakeValue(typename WCharHelper<wchar_t *, Char>::Unsupported); +    MakeValue(typename WCharHelper<const wchar_t *, Char>::Unsupported); +    MakeValue(typename WCharHelper<const std::wstring &, Char>::Unsupported); +#if FMT_HAS_STRING_VIEW +    MakeValue(typename WCharHelper<const std::wstring_view &, Char>::Unsupported); +#endif +    MakeValue(typename WCharHelper<WStringRef, Char>::Unsupported); + +    void set_string(StringRef str) +    { +        string.value = str.data(); +        string.size = str.size(); +    } + +    void set_string(WStringRef str) +    { +        wstring.value = str.data(); +        wstring.size = str.size(); +    } + +    // Formats an argument of a custom type, such as a user-defined class. +    template<typename T> +    static void format_custom_arg(void *formatter, const void *arg, void *format_str_ptr) +    { +        format_arg(*static_cast<Formatter *>(formatter), *static_cast<const Char **>(format_str_ptr), *static_cast<const T *>(arg)); +    } + +public: +    MakeValue() {} + +#define FMT_MAKE_VALUE_(Type, field, TYPE, rhs)                                                                                            \ +    MakeValue(Type value)                                                                                                                  \ +    {                                                                                                                                      \ +        field = rhs;                                                                                                                       \ +    }                                                                                                                                      \ +    static uint64_t type(Type)                                                                                                             \ +    {                                                                                                                                      \ +        return Arg::TYPE;                                                                                                                  \ +    } + +#define FMT_MAKE_VALUE(Type, field, TYPE) FMT_MAKE_VALUE_(Type, field, TYPE, value) + +    FMT_MAKE_VALUE(bool, int_value, BOOL) +    FMT_MAKE_VALUE(short, int_value, INT) +    FMT_MAKE_VALUE(unsigned short, uint_value, UINT) +    FMT_MAKE_VALUE(int, int_value, INT) +    FMT_MAKE_VALUE(unsigned, uint_value, UINT) + +    MakeValue(long value) +    { +        // 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. +        if (const_check(sizeof(long) == sizeof(int))) +            int_value = static_cast<int>(value); +        else +            long_long_value = value; +    } +    static uint64_t type(long) +    { +        return sizeof(long) == sizeof(int) ? Arg::INT : Arg::LONG_LONG; +    } + +    MakeValue(unsigned long value) +    { +        if (const_check(sizeof(unsigned long) == sizeof(unsigned))) +            uint_value = static_cast<unsigned>(value); +        else +            ulong_long_value = value; +    } +    static uint64_t type(unsigned long) +    { +        return sizeof(unsigned long) == sizeof(unsigned) ? Arg::UINT : Arg::ULONG_LONG; +    } + +    FMT_MAKE_VALUE(LongLong, long_long_value, LONG_LONG) +    FMT_MAKE_VALUE(ULongLong, ulong_long_value, ULONG_LONG) +    FMT_MAKE_VALUE(float, double_value, DOUBLE) +    FMT_MAKE_VALUE(double, double_value, DOUBLE) +    FMT_MAKE_VALUE(long double, long_double_value, LONG_DOUBLE) +    FMT_MAKE_VALUE(signed char, int_value, INT) +    FMT_MAKE_VALUE(unsigned char, uint_value, UINT) +    FMT_MAKE_VALUE(char, int_value, CHAR) + +#if __cplusplus >= 201103L +    template<typename T, typename = typename std::enable_if<std::is_enum<T>::value && ConvertToInt<T>::value>::type> +    MakeValue(T value) +    { +        int_value = value; +    } + +    template<typename T, typename = typename std::enable_if<std::is_enum<T>::value && ConvertToInt<T>::value>::type> +    static uint64_t type(T) +    { +        return Arg::INT; +    } +#endif + +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +    MakeValue(typename WCharHelper<wchar_t, Char>::Supported value) +    { +        int_value = value; +    } +    static uint64_t type(wchar_t) +    { +        return Arg::CHAR; +    } +#endif + +#define FMT_MAKE_STR_VALUE(Type, TYPE)                                                                                                     \ +    MakeValue(Type value)                                                                                                                  \ +    {                                                                                                                                      \ +        set_string(value);                                                                                                                 \ +    }                                                                                                                                      \ +    static uint64_t type(Type)                                                                                                             \ +    {                                                                                                                                      \ +        return Arg::TYPE;                                                                                                                  \ +    } + +    FMT_MAKE_VALUE(char *, string.value, CSTRING) +    FMT_MAKE_VALUE(const char *, string.value, CSTRING) +    FMT_MAKE_VALUE(signed char *, sstring.value, CSTRING) +    FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING) +    FMT_MAKE_VALUE(unsigned char *, ustring.value, CSTRING) +    FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) +    FMT_MAKE_STR_VALUE(const std::string &, STRING) +#if FMT_HAS_STRING_VIEW +    FMT_MAKE_STR_VALUE(const std::string_view &, STRING) +#endif +    FMT_MAKE_STR_VALUE(StringRef, STRING) +    FMT_MAKE_VALUE_(CStringRef, string.value, CSTRING, value.c_str()) + +#define FMT_MAKE_WSTR_VALUE(Type, TYPE)                                                                                                    \ +    MakeValue(typename WCharHelper<Type, Char>::Supported value)                                                                           \ +    {                                                                                                                                      \ +        set_string(value);                                                                                                                 \ +    }                                                                                                                                      \ +    static uint64_t type(Type)                                                                                                             \ +    {                                                                                                                                      \ +        return Arg::TYPE;                                                                                                                  \ +    } + +    FMT_MAKE_WSTR_VALUE(wchar_t *, WSTRING) +    FMT_MAKE_WSTR_VALUE(const wchar_t *, WSTRING) +    FMT_MAKE_WSTR_VALUE(const std::wstring &, WSTRING) +#if FMT_HAS_STRING_VIEW +    FMT_MAKE_WSTR_VALUE(const std::wstring_view &, WSTRING) +#endif +    FMT_MAKE_WSTR_VALUE(WStringRef, WSTRING) + +    FMT_MAKE_VALUE(void *, pointer, POINTER) +    FMT_MAKE_VALUE(const void *, pointer, POINTER) + +    template<typename T> +    MakeValue(const T &value, typename EnableIf<Not<ConvertToInt<T>::value>::value, int>::type = 0) +    { +        custom.value = &value; +        custom.format = &format_custom_arg<T>; +    } + +    template<typename T> +    static typename EnableIf<Not<ConvertToInt<T>::value>::value, uint64_t>::type type(const T &) +    { +        return Arg::CUSTOM; +    } + +    // Additional template param `Char_` is needed here because make_type always +    // uses char. +    template<typename Char_> +    MakeValue(const NamedArg<Char_> &value) +    { +        pointer = &value; +    } +    template<typename Char_, typename T> +    MakeValue(const NamedArgWithType<Char_, T> &value) +    { +        pointer = &value; +    } + +    template<typename Char_> +    static uint64_t type(const NamedArg<Char_> &) +    { +        return Arg::NAMED_ARG; +    } +    template<typename Char_, typename T> +    static uint64_t type(const NamedArgWithType<Char_, T> &) +    { +        return Arg::NAMED_ARG; +    } +}; + +template<typename Formatter> +class MakeArg : public Arg +{ +public: +    MakeArg() +    { +        type = Arg::NONE; +    } + +    template<typename T> +    MakeArg(const T &value) +        : Arg(MakeValue<Formatter>(value)) +    { +        type = static_cast<Arg::Type>(MakeValue<Formatter>::type(value)); +    } +}; + +template<typename Char> +struct NamedArg : Arg +{ +    BasicStringRef<Char> name; + +    template<typename T> +    NamedArg(BasicStringRef<Char> argname, const T &value) +        : Arg(MakeArg<BasicFormatter<Char>>(value)) +        , name(argname) +    { +    } +}; + +template<typename Char, typename T> +struct NamedArgWithType : NamedArg<Char> +{ +    NamedArgWithType(BasicStringRef<Char> argname, const T &value) +        : NamedArg<Char>(argname, value) +    { +    } +}; + +class RuntimeError : public std::runtime_error +{ +protected: +    RuntimeError() +        : std::runtime_error("") +    { +    } +    RuntimeError(const RuntimeError &rerr) +        : std::runtime_error(rerr) +    { +    } +    FMT_API ~RuntimeError() FMT_DTOR_NOEXCEPT FMT_OVERRIDE; +}; + +template<typename Char> +class ArgMap; +} // namespace internal + +/** An argument list. */ +class ArgList +{ +private: +    // To reduce compiled code size per formatting function call, types of first +    // MAX_PACKED_ARGS arguments are passed in the types_ field. +    uint64_t types_; +    union +    { +        // If the number of arguments is less than MAX_PACKED_ARGS, the argument +        // values are stored in values_, otherwise they are stored in args_. +        // This is done to reduce compiled code size as 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 internal::Value *values_; +        const internal::Arg *args_; +    }; + +    internal::Arg::Type type(unsigned index) const +    { +        return type(types_, index); +    } + +    template<typename Char> +    friend class internal::ArgMap; + +public: +    // Maximum number of arguments with packed types. +    enum +    { +        MAX_PACKED_ARGS = 16 +    }; + +    ArgList() +        : types_(0) +    { +    } + +    ArgList(ULongLong types, const internal::Value *values) +        : types_(types) +        , values_(values) +    { +    } +    ArgList(ULongLong types, const internal::Arg *args) +        : types_(types) +        , args_(args) +    { +    } + +    uint64_t types() const +    { +        return types_; +    } + +    /** Returns the argument at specified index. */ +    internal::Arg operator[](unsigned index) const +    { +        using internal::Arg; +        Arg arg; +        bool use_values = type(MAX_PACKED_ARGS - 1) == Arg::NONE; +        if (index < MAX_PACKED_ARGS) +        { +            Arg::Type arg_type = type(index); +            internal::Value &val = arg; +            if (arg_type != Arg::NONE) +                val = use_values ? values_[index] : args_[index]; +            arg.type = arg_type; +            return arg; +        } +        if (use_values) +        { +            // The index is greater than the number of arguments that can be stored +            // in values, so return a "none" argument. +            arg.type = Arg::NONE; +            return arg; +        } +        for (unsigned i = MAX_PACKED_ARGS; i <= index; ++i) +        { +            if (args_[i].type == Arg::NONE) +                return args_[i]; +        } +        return args_[index]; +    } + +    static internal::Arg::Type type(uint64_t types, unsigned index) +    { +        unsigned shift = index * 4; +        uint64_t mask = 0xf; +        return static_cast<internal::Arg::Type>((types & (mask << shift)) >> shift); +    } +}; + +#define FMT_DISPATCH(call) static_cast<Impl *>(this)->call + +/** +  \rst +  An argument visitor based on the `curiously recurring template pattern +  <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_. + +  To use `~fmt::ArgVisitor` define a subclass that implements some or all of the +  visit methods with the same signatures as the methods in `~fmt::ArgVisitor`, +  for example, `~fmt::ArgVisitor::visit_int()`. +  Pass the subclass as the *Impl* template parameter. Then calling +  `~fmt::ArgVisitor::visit` for some argument will dispatch to a visit method +  specific to the argument type. For example, if the argument type is +  ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass +  will be called. If the subclass doesn't contain a method with this signature, +  then a corresponding method of `~fmt::ArgVisitor` will be called. + +  **Example**:: + +    class MyArgVisitor : public fmt::ArgVisitor<MyArgVisitor, void> { +     public: +      void visit_int(int value) { fmt::print("{}", value); } +      void visit_double(double value) { fmt::print("{}", value ); } +    }; +  \endrst + */ +template<typename Impl, typename Result> +class ArgVisitor +{ +private: +    typedef internal::Arg Arg; + +public: +    void report_unhandled_arg() {} + +    Result visit_unhandled_arg() +    { +        FMT_DISPATCH(report_unhandled_arg()); +        return Result(); +    } + +    /** Visits an ``int`` argument. **/ +    Result visit_int(int value) +    { +        return FMT_DISPATCH(visit_any_int(value)); +    } + +    /** Visits a ``long long`` argument. **/ +    Result visit_long_long(LongLong value) +    { +        return FMT_DISPATCH(visit_any_int(value)); +    } + +    /** Visits an ``unsigned`` argument. **/ +    Result visit_uint(unsigned value) +    { +        return FMT_DISPATCH(visit_any_int(value)); +    } + +    /** Visits an ``unsigned long long`` argument. **/ +    Result visit_ulong_long(ULongLong value) +    { +        return FMT_DISPATCH(visit_any_int(value)); +    } + +    /** Visits a ``bool`` argument. **/ +    Result visit_bool(bool value) +    { +        return FMT_DISPATCH(visit_any_int(value)); +    } + +    /** Visits a ``char`` or ``wchar_t`` argument. **/ +    Result visit_char(int value) +    { +        return FMT_DISPATCH(visit_any_int(value)); +    } + +    /** Visits an argument of any integral type. **/ +    template<typename T> +    Result visit_any_int(T) +    { +        return FMT_DISPATCH(visit_unhandled_arg()); +    } + +    /** Visits a ``double`` argument. **/ +    Result visit_double(double value) +    { +        return FMT_DISPATCH(visit_any_double(value)); +    } + +    /** Visits a ``long double`` argument. **/ +    Result visit_long_double(long double value) +    { +        return FMT_DISPATCH(visit_any_double(value)); +    } + +    /** Visits a ``double`` or ``long double`` argument. **/ +    template<typename T> +    Result visit_any_double(T) +    { +        return FMT_DISPATCH(visit_unhandled_arg()); +    } + +    /** Visits a null-terminated C string (``const char *``) argument. **/ +    Result visit_cstring(const char *) +    { +        return FMT_DISPATCH(visit_unhandled_arg()); +    } + +    /** Visits a string argument. **/ +    Result visit_string(Arg::StringValue<char>) +    { +        return FMT_DISPATCH(visit_unhandled_arg()); +    } + +    /** Visits a wide string argument. **/ +    Result visit_wstring(Arg::StringValue<wchar_t>) +    { +        return FMT_DISPATCH(visit_unhandled_arg()); +    } + +    /** Visits a pointer argument. **/ +    Result visit_pointer(const void *) +    { +        return FMT_DISPATCH(visit_unhandled_arg()); +    } + +    /** Visits an argument of a custom (user-defined) type. **/ +    Result visit_custom(Arg::CustomValue) +    { +        return FMT_DISPATCH(visit_unhandled_arg()); +    } + +    /** +      \rst +      Visits an argument dispatching to the appropriate visit method based on +      the argument type. For example, if the argument type is ``double`` then +      the `~fmt::ArgVisitor::visit_double()` method of the *Impl* class will be +      called. +      \endrst +     */ +    Result visit(const Arg &arg) +    { +        switch (arg.type) +        { +        case Arg::NONE: +        case Arg::NAMED_ARG: +            FMT_ASSERT(false, "invalid argument type"); +            break; +        case Arg::INT: +            return FMT_DISPATCH(visit_int(arg.int_value)); +        case Arg::UINT: +            return FMT_DISPATCH(visit_uint(arg.uint_value)); +        case Arg::LONG_LONG: +            return FMT_DISPATCH(visit_long_long(arg.long_long_value)); +        case Arg::ULONG_LONG: +            return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value)); +        case Arg::BOOL: +            return FMT_DISPATCH(visit_bool(arg.int_value != 0)); +        case Arg::CHAR: +            return FMT_DISPATCH(visit_char(arg.int_value)); +        case Arg::DOUBLE: +            return FMT_DISPATCH(visit_double(arg.double_value)); +        case Arg::LONG_DOUBLE: +            return FMT_DISPATCH(visit_long_double(arg.long_double_value)); +        case Arg::CSTRING: +            return FMT_DISPATCH(visit_cstring(arg.string.value)); +        case Arg::STRING: +            return FMT_DISPATCH(visit_string(arg.string)); +        case Arg::WSTRING: +            return FMT_DISPATCH(visit_wstring(arg.wstring)); +        case Arg::POINTER: +            return FMT_DISPATCH(visit_pointer(arg.pointer)); +        case Arg::CUSTOM: +            return FMT_DISPATCH(visit_custom(arg.custom)); +        } +        return Result(); +    } +}; + +enum Alignment +{ +    ALIGN_DEFAULT, +    ALIGN_LEFT, +    ALIGN_RIGHT, +    ALIGN_CENTER, +    ALIGN_NUMERIC +}; + +// Flags. +enum +{ +    SIGN_FLAG = 1, +    PLUS_FLAG = 2, +    MINUS_FLAG = 4, +    HASH_FLAG = 8, +    CHAR_FLAG = 0x10 // Argument has char type - used in error reporting. +}; + +// An empty format specifier. +struct EmptySpec +{ +}; + +// A type specifier. +template<char TYPE> +struct TypeSpec : EmptySpec +{ +    Alignment align() const +    { +        return ALIGN_DEFAULT; +    } +    unsigned width() const +    { +        return 0; +    } +    int precision() const +    { +        return -1; +    } +    bool flag(unsigned) const +    { +        return false; +    } +    char type() const +    { +        return TYPE; +    } +    char type_prefix() const +    { +        return TYPE; +    } +    char fill() const +    { +        return ' '; +    } +}; + +// A width specifier. +struct WidthSpec +{ +    unsigned width_; +    // Fill is always wchar_t and cast to char if necessary to avoid having +    // two specialization of WidthSpec and its subclasses. +    wchar_t fill_; + +    WidthSpec(unsigned width, wchar_t fill) +        : width_(width) +        , fill_(fill) +    { +    } + +    unsigned width() const +    { +        return width_; +    } +    wchar_t fill() const +    { +        return fill_; +    } +}; + +// An alignment specifier. +struct AlignSpec : WidthSpec +{ +    Alignment align_; + +    AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT) +        : WidthSpec(width, fill) +        , align_(align) +    { +    } + +    Alignment align() const +    { +        return align_; +    } + +    int precision() const +    { +        return -1; +    } +}; + +// An alignment and type specifier. +template<char TYPE> +struct AlignTypeSpec : AlignSpec +{ +    AlignTypeSpec(unsigned width, wchar_t fill) +        : AlignSpec(width, fill) +    { +    } + +    bool flag(unsigned) const +    { +        return false; +    } +    char type() const +    { +        return TYPE; +    } +    char type_prefix() const +    { +        return TYPE; +    } +}; + +// A full format specifier. +struct FormatSpec : AlignSpec +{ +    unsigned flags_; +    int precision_; +    char type_; + +    FormatSpec(unsigned width = 0, char type = 0, wchar_t fill = ' ') +        : AlignSpec(width, fill) +        , flags_(0) +        , precision_(-1) +        , type_(type) +    { +    } + +    bool flag(unsigned f) const +    { +        return (flags_ & f) != 0; +    } +    int precision() const +    { +        return precision_; +    } +    char type() const +    { +        return type_; +    } +    char type_prefix() const +    { +        return type_; +    } +}; + +// An integer format specifier. +template<typename T, typename SpecT = TypeSpec<0>, typename Char = char> +class IntFormatSpec : public SpecT +{ +private: +    T value_; + +public: +    IntFormatSpec(T val, const SpecT &spec = SpecT()) +        : SpecT(spec) +        , value_(val) +    { +    } + +    T value() const +    { +        return value_; +    } +}; + +// A string format specifier. +template<typename Char> +class StrFormatSpec : public AlignSpec +{ +private: +    const Char *str_; + +public: +    template<typename FillChar> +    StrFormatSpec(const Char *str, unsigned width, FillChar fill) +        : AlignSpec(width, fill) +        , str_(str) +    { +        internal::CharTraits<Char>::convert(FillChar()); +    } + +    const Char *str() const +    { +        return str_; +    } +}; + +/** +  Returns an integer format specifier to format the value in base 2. + */ +IntFormatSpec<int, TypeSpec<'b'>> bin(int value); + +/** +  Returns an integer format specifier to format the value in base 8. + */ +IntFormatSpec<int, TypeSpec<'o'>> oct(int value); + +/** +  Returns an integer format specifier to format the value in base 16 using +  lower-case letters for the digits above 9. + */ +IntFormatSpec<int, TypeSpec<'x'>> hex(int value); + +/** +  Returns an integer formatter format specifier to format in base 16 using +  upper-case letters for the digits above 9. + */ +IntFormatSpec<int, TypeSpec<'X'>> hexu(int value); + +/** +  \rst +  Returns an integer format specifier to pad the formatted argument with the +  fill character to the specified width using the default (right) numeric +  alignment. + +  **Example**:: + +    MemoryWriter out; +    out << pad(hex(0xcafe), 8, '0'); +    // out.str() == "0000cafe" + +  \endrst + */ +template<char TYPE_CODE, typename Char> +IntFormatSpec<int, AlignTypeSpec<TYPE_CODE>, Char> pad(int value, unsigned width, Char fill = ' '); + +#define FMT_DEFINE_INT_FORMATTERS(TYPE)                                                                                                    \ +    inline IntFormatSpec<TYPE, TypeSpec<'b'>> bin(TYPE value)                                                                              \ +    {                                                                                                                                      \ +        return IntFormatSpec<TYPE, TypeSpec<'b'>>(value, TypeSpec<'b'>());                                                                 \ +    }                                                                                                                                      \ +                                                                                                                                           \ +    inline IntFormatSpec<TYPE, TypeSpec<'o'>> oct(TYPE value)                                                                              \ +    {                                                                                                                                      \ +        return IntFormatSpec<TYPE, TypeSpec<'o'>>(value, TypeSpec<'o'>());                                                                 \ +    }                                                                                                                                      \ +                                                                                                                                           \ +    inline IntFormatSpec<TYPE, TypeSpec<'x'>> hex(TYPE value)                                                                              \ +    {                                                                                                                                      \ +        return IntFormatSpec<TYPE, TypeSpec<'x'>>(value, TypeSpec<'x'>());                                                                 \ +    }                                                                                                                                      \ +                                                                                                                                           \ +    inline IntFormatSpec<TYPE, TypeSpec<'X'>> hexu(TYPE value)                                                                             \ +    {                                                                                                                                      \ +        return IntFormatSpec<TYPE, TypeSpec<'X'>>(value, TypeSpec<'X'>());                                                                 \ +    }                                                                                                                                      \ +                                                                                                                                           \ +    template<char TYPE_CODE>                                                                                                               \ +    inline IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE>> pad(IntFormatSpec<TYPE, TypeSpec<TYPE_CODE>> f, unsigned width)                   \ +    {                                                                                                                                      \ +        return IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE>>(f.value(), AlignTypeSpec<TYPE_CODE>(width, ' '));                             \ +    }                                                                                                                                      \ +                                                                                                                                           \ +    /* For compatibility with older compilers we provide two overloads for pad, */                                                         \ +    /* one that takes a fill character and one that doesn't. In the future this */                                                         \ +    /* can be replaced with one overload making the template argument Char      */                                                         \ +    /* default to char (C++11). */                                                                                                         \ +    template<char TYPE_CODE, typename Char>                                                                                                \ +    inline IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE>, Char> pad(                                                                        \ +        IntFormatSpec<TYPE, TypeSpec<TYPE_CODE>, Char> f, unsigned width, Char fill)                                                       \ +    {                                                                                                                                      \ +        return IntFormatSpec<TYPE, AlignTypeSpec<TYPE_CODE>, Char>(f.value(), AlignTypeSpec<TYPE_CODE>(width, fill));                      \ +    }                                                                                                                                      \ +                                                                                                                                           \ +    inline IntFormatSpec<TYPE, AlignTypeSpec<0>> pad(TYPE value, unsigned width)                                                           \ +    {                                                                                                                                      \ +        return IntFormatSpec<TYPE, AlignTypeSpec<0>>(value, AlignTypeSpec<0>(width, ' '));                                                 \ +    }                                                                                                                                      \ +                                                                                                                                           \ +    template<typename Char>                                                                                                                \ +    inline IntFormatSpec<TYPE, AlignTypeSpec<0>, Char> pad(TYPE value, unsigned width, Char fill)                                          \ +    {                                                                                                                                      \ +        return IntFormatSpec<TYPE, AlignTypeSpec<0>, Char>(value, AlignTypeSpec<0>(width, fill));                                          \ +    } + +FMT_DEFINE_INT_FORMATTERS(int) +FMT_DEFINE_INT_FORMATTERS(long) +FMT_DEFINE_INT_FORMATTERS(unsigned) +FMT_DEFINE_INT_FORMATTERS(unsigned long) +FMT_DEFINE_INT_FORMATTERS(LongLong) +FMT_DEFINE_INT_FORMATTERS(ULongLong) + +/** +  \rst +  Returns a string formatter that pads the formatted argument with the fill +  character to the specified width using the default (left) string alignment. + +  **Example**:: + +    std::string s = str(MemoryWriter() << pad("abc", 8)); +    // s == "abc     " + +  \endrst + */ +template<typename Char> +inline StrFormatSpec<Char> pad(const Char *str, unsigned width, Char fill = ' ') +{ +    return StrFormatSpec<Char>(str, width, fill); +} + +inline StrFormatSpec<wchar_t> pad(const wchar_t *str, unsigned width, char fill = ' ') +{ +    return StrFormatSpec<wchar_t>(str, width, fill); +} + +namespace internal { + +template<typename Char> +class ArgMap +{ +private: +    typedef std::vector<std::pair<fmt::BasicStringRef<Char>, internal::Arg>> MapType; +    typedef typename MapType::value_type Pair; + +    MapType map_; + +public: +    void init(const ArgList &args); + +    const internal::Arg *find(const fmt::BasicStringRef<Char> &name) const +    { +        // The list is unsorted, so just return the first matching name. +        for (typename MapType::const_iterator it = map_.begin(), end = map_.end(); it != end; ++it) +        { +            if (it->first == name) +                return &it->second; +        } +        return FMT_NULL; +    } +}; + +template<typename Char> +void ArgMap<Char>::init(const ArgList &args) +{ +    if (!map_.empty()) +        return; +    typedef internal::NamedArg<Char> NamedArg; +    const NamedArg *named_arg = FMT_NULL; +    bool use_values = args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE; +    if (use_values) +    { +        for (unsigned i = 0; /*nothing*/; ++i) +        { +            internal::Arg::Type arg_type = args.type(i); +            switch (arg_type) +            { +            case internal::Arg::NONE: +                return; +            case internal::Arg::NAMED_ARG: +                named_arg = static_cast<const NamedArg *>(args.values_[i].pointer); +                map_.push_back(Pair(named_arg->name, *named_arg)); +                break; +            default: +                /*nothing*/ +                ; +            } +        } +        return; +    } +    for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) +    { +        internal::Arg::Type arg_type = args.type(i); +        if (arg_type == internal::Arg::NAMED_ARG) +        { +            named_arg = static_cast<const NamedArg *>(args.args_[i].pointer); +            map_.push_back(Pair(named_arg->name, *named_arg)); +        } +    } +    for (unsigned i = ArgList::MAX_PACKED_ARGS; /*nothing*/; ++i) +    { +        switch (args.args_[i].type) +        { +        case internal::Arg::NONE: +            return; +        case internal::Arg::NAMED_ARG: +            named_arg = static_cast<const NamedArg *>(args.args_[i].pointer); +            map_.push_back(Pair(named_arg->name, *named_arg)); +            break; +        default: +            /*nothing*/ +            ; +        } +    } +} + +template<typename Impl, typename Char, typename Spec = fmt::FormatSpec> +class ArgFormatterBase : public ArgVisitor<Impl, void> +{ +private: +    BasicWriter<Char> &writer_; +    Spec &spec_; + +    FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatterBase); + +    void write_pointer(const void *p) +    { +        spec_.flags_ = HASH_FLAG; +        spec_.type_ = 'x'; +        writer_.write_int(reinterpret_cast<uintptr_t>(p), spec_); +    } + +    // workaround MSVC two-phase lookup issue +    typedef internal::Arg Arg; + +protected: +    BasicWriter<Char> &writer() +    { +        return writer_; +    } +    Spec &spec() +    { +        return spec_; +    } + +    void write(bool value) +    { +        const char *str_value = value ? "true" : "false"; +        Arg::StringValue<char> str = {str_value, std::strlen(str_value)}; +        writer_.write_str(str, spec_); +    } + +    void write(const char *value) +    { +        Arg::StringValue<char> str = {value, value ? std::strlen(value) : 0}; +        writer_.write_str(str, spec_); +    } + +public: +    typedef Spec SpecType; + +    ArgFormatterBase(BasicWriter<Char> &w, Spec &s) +        : writer_(w) +        , spec_(s) +    { +    } + +    template<typename T> +    void visit_any_int(T value) +    { +        writer_.write_int(value, spec_); +    } + +    template<typename T> +    void visit_any_double(T value) +    { +        writer_.write_double(value, spec_); +    } + +    void visit_bool(bool value) +    { +        if (spec_.type_) +        { +            visit_any_int(value); +            return; +        } +        write(value); +    } + +    void visit_char(int value) +    { +        if (spec_.type_ && spec_.type_ != 'c') +        { +            spec_.flags_ |= CHAR_FLAG; +            writer_.write_int(value, spec_); +            return; +        } +        if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0) +            FMT_THROW(FormatError("invalid format specifier for char")); +        typedef typename BasicWriter<Char>::CharPtr CharPtr; +        Char fill = internal::CharTraits<Char>::cast(spec_.fill()); +        CharPtr out = CharPtr(); +        const unsigned CHAR_SIZE = 1; +        if (spec_.width_ > CHAR_SIZE) +        { +            out = writer_.grow_buffer(spec_.width_); +            if (spec_.align_ == ALIGN_RIGHT) +            { +                std::uninitialized_fill_n(out, spec_.width_ - CHAR_SIZE, fill); +                out += spec_.width_ - CHAR_SIZE; +            } +            else if (spec_.align_ == ALIGN_CENTER) +            { +                out = writer_.fill_padding(out, spec_.width_, internal::const_check(CHAR_SIZE), fill); +            } +            else +            { +                std::uninitialized_fill_n(out + CHAR_SIZE, spec_.width_ - CHAR_SIZE, fill); +            } +        } +        else +        { +            out = writer_.grow_buffer(CHAR_SIZE); +        } +        *out = internal::CharTraits<Char>::cast(value); +    } + +    void visit_cstring(const char *value) +    { +        if (spec_.type_ == 'p') +            return write_pointer(value); +        write(value); +    } + +    // Qualification with "internal" here and below is a workaround for nvcc. +    void visit_string(internal::Arg::StringValue<char> value) +    { +        writer_.write_str(value, spec_); +    } + +    using ArgVisitor<Impl, void>::visit_wstring; + +    void visit_wstring(internal::Arg::StringValue<Char> value) +    { +        writer_.write_str(value, spec_); +    } + +    void visit_pointer(const void *value) +    { +        if (spec_.type_ && spec_.type_ != 'p') +            report_unknown_type(spec_.type_, "pointer"); +        write_pointer(value); +    } +}; + +class FormatterBase +{ +private: +    ArgList args_; +    int next_arg_index_; + +    // Returns the argument with specified index. +    FMT_API Arg do_get_arg(unsigned arg_index, const char *&error); + +protected: +    const ArgList &args() const +    { +        return args_; +    } + +    explicit FormatterBase(const ArgList &args) +    { +        args_ = args; +        next_arg_index_ = 0; +    } + +    // Returns the next argument. +    Arg next_arg(const char *&error) +    { +        if (next_arg_index_ >= 0) +            return do_get_arg(internal::to_unsigned(next_arg_index_++), error); +        error = "cannot switch from manual to automatic argument indexing"; +        return Arg(); +    } + +    // Checks if manual indexing is used and returns the argument with +    // specified index. +    Arg get_arg(unsigned arg_index, const char *&error) +    { +        return check_no_auto_index(error) ? do_get_arg(arg_index, error) : Arg(); +    } + +    bool check_no_auto_index(const char *&error) +    { +        if (next_arg_index_ > 0) +        { +            error = "cannot switch from automatic to manual argument indexing"; +            return false; +        } +        next_arg_index_ = -1; +        return true; +    } + +    template<typename Char> +    void write(BasicWriter<Char> &w, const Char *start, const Char *end) +    { +        if (start != end) +            w << BasicStringRef<Char>(start, internal::to_unsigned(end - start)); +    } +}; +} // namespace internal + +/** +  \rst +  An argument formatter based on the `curiously recurring template pattern +  <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_. + +  To use `~fmt::BasicArgFormatter` define a subclass that implements some or +  all of the visit methods with the same signatures as the methods in +  `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`. +  Pass the subclass as the *Impl* template parameter. When a formatting +  function processes an argument, it will dispatch to a visit method +  specific to the argument type. For example, if the argument type is +  ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass +  will be called. If the subclass doesn't contain a method with this signature, +  then a corresponding method of `~fmt::BasicArgFormatter` or its superclass +  will be called. +  \endrst + */ +template<typename Impl, typename Char, typename Spec = fmt::FormatSpec> +class BasicArgFormatter : public internal::ArgFormatterBase<Impl, Char, Spec> +{ +private: +    BasicFormatter<Char, Impl> &formatter_; +    const Char *format_; + +public: +    /** +      \rst +      Constructs an argument formatter object. +      *formatter* is a reference to the main formatter object, *spec* contains +      format specifier information for standard argument types, and *fmt* points +      to the part of the format string being parsed for custom argument types. +      \endrst +     */ +    BasicArgFormatter(BasicFormatter<Char, Impl> &formatter, Spec &spec, const Char *fmt) +        : internal::ArgFormatterBase<Impl, Char, Spec>(formatter.writer(), spec) +        , formatter_(formatter) +        , format_(fmt) +    { +    } + +    /** Formats an argument of a custom (user-defined) type. */ +    void visit_custom(internal::Arg::CustomValue c) +    { +        c.format(&formatter_, c.value, &format_); +    } +}; + +/** The default argument formatter. */ +template<typename Char> +class ArgFormatter : public BasicArgFormatter<ArgFormatter<Char>, Char, FormatSpec> +{ +public: +    /** Constructs an argument formatter object. */ +    ArgFormatter(BasicFormatter<Char> &formatter, FormatSpec &spec, const Char *fmt) +        : BasicArgFormatter<ArgFormatter<Char>, Char, FormatSpec>(formatter, spec, fmt) +    { +    } +}; + +/** This template formats data and writes the output to a writer. */ +template<typename CharType, typename ArgFormatter> +class BasicFormatter : private internal::FormatterBase +{ +public: +    /** The character type for the output. */ +    typedef CharType Char; + +private: +    BasicWriter<Char> &writer_; +    internal::ArgMap<Char> map_; + +    FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter); + +    using internal::FormatterBase::get_arg; + +    // Checks if manual indexing is used and returns the argument with +    // specified name. +    internal::Arg get_arg(BasicStringRef<Char> arg_name, const char *&error); + +    // Parses argument index and returns corresponding argument. +    internal::Arg parse_arg_index(const Char *&s); + +    // Parses argument name and returns corresponding argument. +    internal::Arg parse_arg_name(const Char *&s); + +public: +    /** +     \rst +     Constructs a ``BasicFormatter`` object. References to the arguments and +     the writer are stored in the formatter object so make sure they have +     appropriate lifetimes. +     \endrst +     */ +    BasicFormatter(const ArgList &args, BasicWriter<Char> &w) +        : internal::FormatterBase(args) +        , writer_(w) +    { +    } + +    /** Returns a reference to the writer associated with this formatter. */ +    BasicWriter<Char> &writer() +    { +        return writer_; +    } + +    /** Formats stored arguments and writes the output to the writer. */ +    void format(BasicCStringRef<Char> format_str); + +    // Formats a single argument and advances format_str, a format string pointer. +    const Char *format(const Char *&format_str, const internal::Arg &arg); +}; + +// Generates a comma-separated list with results of applying f to +// numbers 0..n-1. +#define FMT_GEN(n, f) FMT_GEN##n(f) +#define FMT_GEN1(f) f(0) +#define FMT_GEN2(f) FMT_GEN1(f), f(1) +#define FMT_GEN3(f) FMT_GEN2(f), f(2) +#define FMT_GEN4(f) FMT_GEN3(f), f(3) +#define FMT_GEN5(f) FMT_GEN4(f), f(4) +#define FMT_GEN6(f) FMT_GEN5(f), f(5) +#define FMT_GEN7(f) FMT_GEN6(f), f(6) +#define FMT_GEN8(f) FMT_GEN7(f), f(7) +#define FMT_GEN9(f) FMT_GEN8(f), f(8) +#define FMT_GEN10(f) FMT_GEN9(f), f(9) +#define FMT_GEN11(f) FMT_GEN10(f), f(10) +#define FMT_GEN12(f) FMT_GEN11(f), f(11) +#define FMT_GEN13(f) FMT_GEN12(f), f(12) +#define FMT_GEN14(f) FMT_GEN13(f), f(13) +#define FMT_GEN15(f) FMT_GEN14(f), f(14) + +namespace internal { +inline uint64_t make_type() +{ +    return 0; +} + +template<typename T> +inline uint64_t make_type(const T &arg) +{ +    return MakeValue<BasicFormatter<char>>::type(arg); +} + +template<std::size_t N, bool /*IsPacked*/ = (N < ArgList::MAX_PACKED_ARGS)> +struct ArgArray; + +template<std::size_t N> +struct ArgArray<N, true /*IsPacked*/> +{ +    // '+' is used to silence GCC -Wduplicated-branches warning. +    typedef Value Type[N > 0 ? N : +1]; + +    template<typename Formatter, typename T> +    static Value make(const T &value) +    { +#ifdef __clang__ +        Value result = MakeValue<Formatter>(value); +        // Workaround a bug in Apple LLVM version 4.2 (clang-425.0.28) of clang: +        // https://github.com/fmtlib/fmt/issues/276 +        (void)result.custom.format; +        return result; +#else +        return MakeValue<Formatter>(value); +#endif +    } +}; + +template<std::size_t N> +struct ArgArray<N, false /*IsPacked*/> +{ +    typedef Arg Type[N + 1]; // +1 for the list end Arg::NONE + +    template<typename Formatter, typename T> +    static Arg make(const T &value) +    { +        return MakeArg<Formatter>(value); +    } +}; + +#if FMT_USE_VARIADIC_TEMPLATES +template<typename Arg, typename... Args> +inline uint64_t make_type(const Arg &first, const Args &... tail) +{ +    return make_type(first) | (make_type(tail...) << 4); +} + +#else + +struct ArgType +{ +    uint64_t type; + +    ArgType() +        : type(0) +    { +    } + +    template<typename T> +    ArgType(const T &arg) +        : type(make_type(arg)) +    { +    } +}; + +#define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType() + +inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) +{ +    return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) | (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | +           (t7.type << 28) | (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) | (t12.type << 48) | (t13.type << 52) | +           (t14.type << 56); +} +#endif +} // namespace internal + +#define FMT_MAKE_TEMPLATE_ARG(n) typename T##n +#define FMT_MAKE_ARG_TYPE(n) T##n +#define FMT_MAKE_ARG(n) const T##n &v##n +#define FMT_ASSIGN_char(n) arr[n] = fmt::internal::MakeValue<fmt::BasicFormatter<char>>(v##n) +#define FMT_ASSIGN_wchar_t(n) arr[n] = fmt::internal::MakeValue<fmt::BasicFormatter<wchar_t>>(v##n) + +#if FMT_USE_VARIADIC_TEMPLATES +// Defines a variadic function returning void. +#define FMT_VARIADIC_VOID(func, arg_type)                                                                                                  \ +    template<typename... Args>                                                                                                             \ +    void func(arg_type arg0, const Args &... args)                                                                                         \ +    {                                                                                                                                      \ +        typedef fmt::internal::ArgArray<sizeof...(Args)> ArgArray;                                                                         \ +        typename ArgArray::Type array{ArgArray::template make<fmt::BasicFormatter<Char>>(args)...};                                        \ +        func(arg0, fmt::ArgList(fmt::internal::make_type(args...), array));                                                                \ +    } + +// Defines a variadic constructor. +#define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type)                                                                                \ +    template<typename... Args>                                                                                                             \ +    ctor(arg0_type arg0, arg1_type arg1, const Args &... args)                                                                             \ +    {                                                                                                                                      \ +        typedef fmt::internal::ArgArray<sizeof...(Args)> ArgArray;                                                                         \ +        typename ArgArray::Type array{ArgArray::template make<fmt::BasicFormatter<Char>>(args)...};                                        \ +        func(arg0, arg1, fmt::ArgList(fmt::internal::make_type(args...), array));                                                          \ +    } + +#else + +#define FMT_MAKE_REF(n) fmt::internal::MakeValue<fmt::BasicFormatter<Char>>(v##n) +#define FMT_MAKE_REF2(n) v##n + +// Defines a wrapper for a function taking one argument of type arg_type +// and n additional arguments of arbitrary types. +#define FMT_WRAP1(func, arg_type, n)                                                                                                       \ +    template<FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)>                                                                                            \ +    inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG))                                                                              \ +    {                                                                                                                                      \ +        const fmt::internal::ArgArray<n>::Type array = {FMT_GEN(n, FMT_MAKE_REF)};                                                         \ +        func(arg1, fmt::ArgList(fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array));                                              \ +    } + +// Emulates a variadic function returning void on a pre-C++11 compiler. +#define FMT_VARIADIC_VOID(func, arg_type)                                                                                                  \ +    inline void func(arg_type arg)                                                                                                         \ +    {                                                                                                                                      \ +        func(arg, fmt::ArgList());                                                                                                         \ +    }                                                                                                                                      \ +    FMT_WRAP1(func, arg_type, 1)                                                                                                           \ +    FMT_WRAP1(func, arg_type, 2)                                                                                                           \ +    FMT_WRAP1(func, arg_type, 3)                                                                                                           \ +    FMT_WRAP1(func, arg_type, 4)                                                                                                           \ +    FMT_WRAP1(func, arg_type, 5)                                                                                                           \ +    FMT_WRAP1(func, arg_type, 6)                                                                                                           \ +    FMT_WRAP1(func, arg_type, 7) FMT_WRAP1(func, arg_type, 8) FMT_WRAP1(func, arg_type, 9) FMT_WRAP1(func, arg_type, 10) + +#define FMT_CTOR(ctor, func, arg0_type, arg1_type, n)                                                                                      \ +    template<FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)>                                                                                            \ +    ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG))                                                                         \ +    {                                                                                                                                      \ +        const fmt::internal::ArgArray<n>::Type array = {FMT_GEN(n, FMT_MAKE_REF)};                                                         \ +        func(arg0, arg1, fmt::ArgList(fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array));                                        \ +    } + +// Emulates a variadic constructor on a pre-C++11 compiler. +#define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type)                                                                                \ +    FMT_CTOR(ctor, func, arg0_type, arg1_type, 1)                                                                                          \ +    FMT_CTOR(ctor, func, arg0_type, arg1_type, 2)                                                                                          \ +    FMT_CTOR(ctor, func, arg0_type, arg1_type, 3)                                                                                          \ +    FMT_CTOR(ctor, func, arg0_type, arg1_type, 4)                                                                                          \ +    FMT_CTOR(ctor, func, arg0_type, arg1_type, 5)                                                                                          \ +    FMT_CTOR(ctor, func, arg0_type, arg1_type, 6)                                                                                          \ +    FMT_CTOR(ctor, func, arg0_type, arg1_type, 7)                                                                                          \ +    FMT_CTOR(ctor, func, arg0_type, arg1_type, 8)                                                                                          \ +    FMT_CTOR(ctor, func, arg0_type, arg1_type, 9)                                                                                          \ +    FMT_CTOR(ctor, func, arg0_type, arg1_type, 10) +#endif + +// Generates a comma-separated list with results of applying f to pairs +// (argument, index). +#define FMT_FOR_EACH1(f, x0) f(x0, 0) +#define FMT_FOR_EACH2(f, x0, x1) FMT_FOR_EACH1(f, x0), f(x1, 1) +#define FMT_FOR_EACH3(f, x0, x1, x2) FMT_FOR_EACH2(f, x0, x1), f(x2, 2) +#define FMT_FOR_EACH4(f, x0, x1, x2, x3) FMT_FOR_EACH3(f, x0, x1, x2), f(x3, 3) +#define FMT_FOR_EACH5(f, x0, x1, x2, x3, x4) FMT_FOR_EACH4(f, x0, x1, x2, x3), f(x4, 4) +#define FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5) FMT_FOR_EACH5(f, x0, x1, x2, x3, x4), f(x5, 5) +#define FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6) FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5), f(x6, 6) +#define FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7) FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6), f(x7, 7) +#define FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8) FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7), f(x8, 8) +#define FMT_FOR_EACH10(f, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9) + +/** + An error returned by an operating system or a language runtime, + for example a file opening error. +*/ +class SystemError : public internal::RuntimeError +{ +private: +    FMT_API void init(int err_code, CStringRef format_str, ArgList args); + +protected: +    int error_code_; + +    typedef char Char; // For FMT_VARIADIC_CTOR. + +    SystemError() {} + +public: +    /** +     \rst +     Constructs a :class:`fmt::SystemError` object with a description +     formatted with `fmt::format_system_error`. *message* and additional +     arguments passed into the constructor are formatted similarly to +     `fmt::format`. + +     **Example**:: + +       // This throws a SystemError 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::SystemError(errno, "cannot open file '{}'", filename); +     \endrst +    */ +    SystemError(int error_code, CStringRef message) +    { +        init(error_code, message, ArgList()); +    } +    FMT_DEFAULTED_COPY_CTOR(SystemError) +    FMT_VARIADIC_CTOR(SystemError, init, int, CStringRef) + +    FMT_API ~SystemError() FMT_DTOR_NOEXCEPT FMT_OVERRIDE; + +    int error_code() const +    { +        return error_code_; +    } +}; + +/** +  \rst +  Formats an error returned by an operating system or a language runtime, +  for example a file opening error, and writes it to *out* in the following +  form: + +  .. 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``. +  If *error_code* is not a valid error code such as -1, the system message +  may look like "Unknown error -1" and is platform-dependent. +  \endrst + */ +FMT_API void format_system_error(fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT; + +/** +  \rst +  This template provides operations for formatting and writing data into +  a character stream. The output is stored in a buffer provided by a subclass +  such as :class:`fmt::BasicMemoryWriter`. + +  You can use one of the following typedefs for common character types: + +  +---------+----------------------+ +  | Type    | Definition           | +  +=========+======================+ +  | Writer  | BasicWriter<char>    | +  +---------+----------------------+ +  | WWriter | BasicWriter<wchar_t> | +  +---------+----------------------+ + +  \endrst + */ +template<typename Char> +class BasicWriter +{ +private: +    // Output buffer. +    Buffer<Char> &buffer_; + +    FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter); + +    typedef typename internal::CharTraits<Char>::CharPtr CharPtr; + +#if FMT_SECURE_SCL +    // Returns pointer value. +    static Char *get(CharPtr p) +    { +        return p.base(); +    } +#else +    static Char *get(Char *p) +    { +        return p; +    } +#endif + +    // Fills the padding around the content and returns the pointer to the +    // content area. +    static CharPtr fill_padding(CharPtr buffer, unsigned total_size, std::size_t content_size, wchar_t fill); + +    // Grows the buffer by n characters and returns a pointer to the newly +    // allocated area. +    CharPtr grow_buffer(std::size_t n) +    { +        std::size_t size = buffer_.size(); +        buffer_.resize(size + n); +        return internal::make_ptr(&buffer_[size], n); +    } + +    // Writes an unsigned decimal integer. +    template<typename UInt> +    Char *write_unsigned_decimal(UInt value, unsigned prefix_size = 0) +    { +        unsigned num_digits = internal::count_digits(value); +        Char *ptr = get(grow_buffer(prefix_size + num_digits)); +        internal::format_decimal(ptr + prefix_size, value, num_digits); +        return ptr; +    } + +    // Writes a decimal integer. +    template<typename Int> +    void write_decimal(Int value) +    { +        typedef typename internal::IntTraits<Int>::MainType MainType; +        MainType abs_value = static_cast<MainType>(value); +        if (internal::is_negative(value)) +        { +            abs_value = 0 - abs_value; +            *write_unsigned_decimal(abs_value, 1) = '-'; +        } +        else +        { +            write_unsigned_decimal(abs_value, 0); +        } +    } + +    // Prepare a buffer for integer formatting. +    CharPtr prepare_int_buffer(unsigned num_digits, const EmptySpec &, const char *prefix, unsigned prefix_size) +    { +        unsigned size = prefix_size + num_digits; +        CharPtr p = grow_buffer(size); +        std::uninitialized_copy(prefix, prefix + prefix_size, p); +        return p + size - 1; +    } + +    template<typename Spec> +    CharPtr prepare_int_buffer(unsigned num_digits, const Spec &spec, const char *prefix, unsigned prefix_size); + +    // Formats an integer. +    template<typename T, typename Spec> +    void write_int(T value, Spec spec); + +    // Formats a floating-point number (double or long double). +    template<typename T, typename Spec> +    void write_double(T value, const Spec &spec); + +    // Writes a formatted string. +    template<typename StrChar> +    CharPtr write_str(const StrChar *s, std::size_t size, const AlignSpec &spec); + +    template<typename StrChar, typename Spec> +    void write_str(const internal::Arg::StringValue<StrChar> &str, const Spec &spec); + +    // This following methods are private to disallow writing wide characters +    // and strings to a char stream. If you want to print a wide string as a +    // pointer as std::ostream does, cast it to const void*. +    // Do not implement! +    void operator<<(typename internal::WCharHelper<wchar_t, Char>::Unsupported); +    void operator<<(typename internal::WCharHelper<const wchar_t *, Char>::Unsupported); + +    // Appends floating-point length specifier to the format string. +    // The second argument is only used for overload resolution. +    void append_float_length(Char *&format_ptr, long double) +    { +        *format_ptr++ = 'L'; +    } + +    template<typename T> +    void append_float_length(Char *&, T) +    { +    } + +    template<typename Impl, typename Char_, typename Spec_> +    friend class internal::ArgFormatterBase; + +    template<typename Impl, typename Char_, typename Spec_> +    friend class BasicPrintfArgFormatter; + +protected: +    /** +      Constructs a ``BasicWriter`` object. +     */ +    explicit BasicWriter(Buffer<Char> &b) +        : buffer_(b) +    { +    } + +public: +    /** +      \rst +      Destroys a ``BasicWriter`` object. +      \endrst +     */ +    virtual ~BasicWriter() {} + +    /** +      Returns the total number of characters written. +     */ +    std::size_t size() const +    { +        return buffer_.size(); +    } + +    /** +      Returns a pointer to the output buffer content. No terminating null +      character is appended. +     */ +    const Char *data() const FMT_NOEXCEPT +    { +        return &buffer_[0]; +    } + +    /** +      Returns a pointer to the output buffer content with terminating null +      character appended. +     */ +    const Char *c_str() const +    { +        std::size_t size = buffer_.size(); +        buffer_.reserve(size + 1); +        buffer_[size] = '\0'; +        return &buffer_[0]; +    } + +    /** +      \rst +      Returns the content of the output buffer as an `std::string`. +      \endrst +     */ +    std::basic_string<Char> str() const +    { +        return std::basic_string<Char>(&buffer_[0], buffer_.size()); +    } + +    /** +      \rst +      Writes formatted data. + +      *args* is an argument list representing arbitrary arguments. + +      **Example**:: + +         MemoryWriter out; +         out.write("Current point:\n"); +         out.write("({:+f}, {:+f})", -3.14, 3.14); + +      This will write the following output to the ``out`` object: + +      .. code-block:: none + +         Current point: +         (-3.140000, +3.140000) + +      The output can be accessed using :func:`data()`, :func:`c_str` or +      :func:`str` methods. + +      See also :ref:`syntax`. +      \endrst +     */ +    void write(BasicCStringRef<Char> format, ArgList args) +    { +        BasicFormatter<Char>(args, *this).format(format); +    } +    FMT_VARIADIC_VOID(write, BasicCStringRef<Char>) + +    BasicWriter &operator<<(int value) +    { +        write_decimal(value); +        return *this; +    } +    BasicWriter &operator<<(unsigned value) +    { +        return *this << IntFormatSpec<unsigned>(value); +    } +    BasicWriter &operator<<(long value) +    { +        write_decimal(value); +        return *this; +    } +    BasicWriter &operator<<(unsigned long value) +    { +        return *this << IntFormatSpec<unsigned long>(value); +    } +    BasicWriter &operator<<(LongLong value) +    { +        write_decimal(value); +        return *this; +    } + +    /** +      \rst +      Formats *value* and writes it to the stream. +      \endrst +     */ +    BasicWriter &operator<<(ULongLong value) +    { +        return *this << IntFormatSpec<ULongLong>(value); +    } + +    BasicWriter &operator<<(double value) +    { +        write_double(value, FormatSpec()); +        return *this; +    } + +    /** +      \rst +      Formats *value* using the general format for floating-point numbers +      (``'g'``) and writes it to the stream. +      \endrst +     */ +    BasicWriter &operator<<(long double value) +    { +        write_double(value, FormatSpec()); +        return *this; +    } + +    /** +      Writes a character to the stream. +     */ +    BasicWriter &operator<<(char value) +    { +        buffer_.push_back(value); +        return *this; +    } + +    BasicWriter &operator<<(typename internal::WCharHelper<wchar_t, Char>::Supported value) +    { +        buffer_.push_back(value); +        return *this; +    } + +    /** +      \rst +      Writes *value* to the stream. +      \endrst +     */ +    BasicWriter &operator<<(fmt::BasicStringRef<Char> value) +    { +        const Char *str = value.data(); +        buffer_.append(str, str + value.size()); +        return *this; +    } + +    BasicWriter &operator<<(typename internal::WCharHelper<StringRef, Char>::Supported value) +    { +        const char *str = value.data(); +        buffer_.append(str, str + value.size()); +        return *this; +    } + +    template<typename T, typename Spec, typename FillChar> +    BasicWriter &operator<<(IntFormatSpec<T, Spec, FillChar> spec) +    { +        internal::CharTraits<Char>::convert(FillChar()); +        write_int(spec.value(), spec); +        return *this; +    } + +    template<typename StrChar> +    BasicWriter &operator<<(const StrFormatSpec<StrChar> &spec) +    { +        const StrChar *s = spec.str(); +        write_str(s, std::char_traits<Char>::length(s), spec); +        return *this; +    } + +    void clear() FMT_NOEXCEPT +    { +        buffer_.clear(); +    } + +    Buffer<Char> &buffer() FMT_NOEXCEPT +    { +        return buffer_; +    } +}; + +template<typename Char> +template<typename StrChar> +typename BasicWriter<Char>::CharPtr BasicWriter<Char>::write_str(const StrChar *s, std::size_t size, const AlignSpec &spec) +{ +    CharPtr out = CharPtr(); +    if (spec.width() > size) +    { +        out = grow_buffer(spec.width()); +        Char fill = internal::CharTraits<Char>::cast(spec.fill()); +        if (spec.align() == ALIGN_RIGHT) +        { +            std::uninitialized_fill_n(out, spec.width() - size, fill); +            out += spec.width() - size; +        } +        else if (spec.align() == ALIGN_CENTER) +        { +            out = fill_padding(out, spec.width(), size, fill); +        } +        else +        { +            std::uninitialized_fill_n(out + size, spec.width() - size, fill); +        } +    } +    else +    { +        out = grow_buffer(size); +    } +    std::uninitialized_copy(s, s + size, out); +    return out; +} + +template<typename Char> +template<typename StrChar, typename Spec> +void BasicWriter<Char>::write_str(const internal::Arg::StringValue<StrChar> &s, const Spec &spec) +{ +    // Check if StrChar is convertible to Char. +    internal::CharTraits<Char>::convert(StrChar()); +    if (spec.type_ && spec.type_ != 's') +        internal::report_unknown_type(spec.type_, "string"); +    const StrChar *str_value = s.value; +    std::size_t str_size = s.size; +    if (str_size == 0) +    { +        if (!str_value) +        { +            FMT_THROW(FormatError("string pointer is null")); +        } +    } +    std::size_t precision = static_cast<std::size_t>(spec.precision_); +    if (spec.precision_ >= 0 && precision < str_size) +        str_size = precision; +    write_str(str_value, str_size, spec); +} + +template<typename Char> +typename BasicWriter<Char>::CharPtr BasicWriter<Char>::fill_padding( +    CharPtr buffer, unsigned total_size, std::size_t content_size, wchar_t fill) +{ +    std::size_t padding = total_size - content_size; +    std::size_t left_padding = padding / 2; +    Char fill_char = internal::CharTraits<Char>::cast(fill); +    std::uninitialized_fill_n(buffer, left_padding, fill_char); +    buffer += left_padding; +    CharPtr content = buffer; +    std::uninitialized_fill_n(buffer + content_size, padding - left_padding, fill_char); +    return content; +} + +template<typename Char> +template<typename Spec> +typename BasicWriter<Char>::CharPtr BasicWriter<Char>::prepare_int_buffer( +    unsigned num_digits, const Spec &spec, const char *prefix, unsigned prefix_size) +{ +    unsigned width = spec.width(); +    Alignment align = spec.align(); +    Char fill = internal::CharTraits<Char>::cast(spec.fill()); +    if (spec.precision() > static_cast<int>(num_digits)) +    { +        // Octal prefix '0' is counted as a digit, so ignore it if precision +        // is specified. +        if (prefix_size > 0 && prefix[prefix_size - 1] == '0') +            --prefix_size; +        unsigned number_size = prefix_size + internal::to_unsigned(spec.precision()); +        AlignSpec subspec(number_size, '0', ALIGN_NUMERIC); +        if (number_size >= width) +            return prepare_int_buffer(num_digits, subspec, prefix, prefix_size); +        buffer_.reserve(width); +        unsigned fill_size = width - number_size; +        if (align != ALIGN_LEFT) +        { +            CharPtr p = grow_buffer(fill_size); +            std::uninitialized_fill(p, p + fill_size, fill); +        } +        CharPtr result = prepare_int_buffer(num_digits, subspec, prefix, prefix_size); +        if (align == ALIGN_LEFT) +        { +            CharPtr p = grow_buffer(fill_size); +            std::uninitialized_fill(p, p + fill_size, fill); +        } +        return result; +    } +    unsigned size = prefix_size + num_digits; +    if (width <= size) +    { +        CharPtr p = grow_buffer(size); +        std::uninitialized_copy(prefix, prefix + prefix_size, p); +        return p + size - 1; +    } +    CharPtr p = grow_buffer(width); +    CharPtr end = p + width; +    if (align == ALIGN_LEFT) +    { +        std::uninitialized_copy(prefix, prefix + prefix_size, p); +        p += size; +        std::uninitialized_fill(p, end, fill); +    } +    else if (align == ALIGN_CENTER) +    { +        p = fill_padding(p, width, size, fill); +        std::uninitialized_copy(prefix, prefix + prefix_size, p); +        p += size; +    } +    else +    { +        if (align == ALIGN_NUMERIC) +        { +            if (prefix_size != 0) +            { +                p = std::uninitialized_copy(prefix, prefix + prefix_size, p); +                size -= prefix_size; +            } +        } +        else +        { +            std::uninitialized_copy(prefix, prefix + prefix_size, end - size); +        } +        std::uninitialized_fill(p, end - size, fill); +        p = end; +    } +    return p - 1; +} + +template<typename Char> +template<typename T, typename Spec> +void BasicWriter<Char>::write_int(T value, Spec spec) +{ +    unsigned prefix_size = 0; +    typedef typename internal::IntTraits<T>::MainType UnsignedType; +    UnsignedType abs_value = static_cast<UnsignedType>(value); +    char prefix[4] = ""; +    if (internal::is_negative(value)) +    { +        prefix[0] = '-'; +        ++prefix_size; +        abs_value = 0 - abs_value; +    } +    else if (spec.flag(SIGN_FLAG)) +    { +        prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' '; +        ++prefix_size; +    } +    switch (spec.type()) +    { +    case 0: +    case 'd': +    { +        unsigned num_digits = internal::count_digits(abs_value); +        CharPtr p = prepare_int_buffer(num_digits, spec, prefix, prefix_size) + 1; +        internal::format_decimal(get(p), abs_value, 0); +        break; +    } +    case 'x': +    case 'X': +    { +        UnsignedType n = abs_value; +        if (spec.flag(HASH_FLAG)) +        { +            prefix[prefix_size++] = '0'; +            prefix[prefix_size++] = spec.type_prefix(); +        } +        unsigned num_digits = 0; +        do +        { +            ++num_digits; +        } while ((n >>= 4) != 0); +        Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); +        n = abs_value; +        const char *digits = spec.type() == 'x' ? "0123456789abcdef" : "0123456789ABCDEF"; +        do +        { +            *p-- = digits[n & 0xf]; +        } while ((n >>= 4) != 0); +        break; +    } +    case 'b': +    case 'B': +    { +        UnsignedType n = abs_value; +        if (spec.flag(HASH_FLAG)) +        { +            prefix[prefix_size++] = '0'; +            prefix[prefix_size++] = spec.type_prefix(); +        } +        unsigned num_digits = 0; +        do +        { +            ++num_digits; +        } while ((n >>= 1) != 0); +        Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); +        n = abs_value; +        do +        { +            *p-- = static_cast<Char>('0' + (n & 1)); +        } while ((n >>= 1) != 0); +        break; +    } +    case 'o': +    { +        UnsignedType n = abs_value; +        if (spec.flag(HASH_FLAG)) +            prefix[prefix_size++] = '0'; +        unsigned num_digits = 0; +        do +        { +            ++num_digits; +        } while ((n >>= 3) != 0); +        Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); +        n = abs_value; +        do +        { +            *p-- = static_cast<Char>('0' + (n & 7)); +        } while ((n >>= 3) != 0); +        break; +    } +    case 'n': +    { +        unsigned num_digits = internal::count_digits(abs_value); +        fmt::StringRef sep = ""; +#if !(defined(ANDROID) || defined(__ANDROID__)) +        sep = internal::thousands_sep(std::localeconv()); +#endif +        unsigned size = static_cast<unsigned>(num_digits + sep.size() * ((num_digits - 1) / 3)); +        CharPtr p = prepare_int_buffer(size, spec, prefix, prefix_size) + 1; +        internal::format_decimal(get(p), abs_value, 0, internal::ThousandsSep(sep)); +        break; +    } +    default: +        internal::report_unknown_type(spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer"); +        break; +    } +} + +template<typename Char> +template<typename T, typename Spec> +void BasicWriter<Char>::write_double(T value, const Spec &spec) +{ +    // Check type. +    char type = spec.type(); +    bool upper = false; +    switch (type) +    { +    case 0: +        type = 'g'; +        break; +    case 'e': +    case 'f': +    case 'g': +    case 'a': +        break; +    case 'F': +#if FMT_MSC_VER +        // MSVC's printf doesn't support 'F'. +        type = 'f'; +#endif +    // Fall through. +    case 'E': +    case 'G': +    case 'A': +        upper = true; +        break; +    default: +        internal::report_unknown_type(type, "double"); +        break; +    } + +    char sign = 0; +    // Use isnegative instead of value < 0 because the latter is always +    // false for NaN. +    if (internal::FPUtil::isnegative(static_cast<double>(value))) +    { +        sign = '-'; +        value = -value; +    } +    else if (spec.flag(SIGN_FLAG)) +    { +        sign = spec.flag(PLUS_FLAG) ? '+' : ' '; +    } + +    if (internal::FPUtil::isnotanumber(value)) +    { +        // Format NaN ourselves because sprintf's output is not consistent +        // across platforms. +        std::size_t nan_size = 4; +        const char *nan = upper ? " NAN" : " nan"; +        if (!sign) +        { +            --nan_size; +            ++nan; +        } +        CharPtr out = write_str(nan, nan_size, spec); +        if (sign) +            *out = sign; +        return; +    } + +    if (internal::FPUtil::isinfinity(value)) +    { +        // Format infinity ourselves because sprintf's output is not consistent +        // across platforms. +        std::size_t inf_size = 4; +        const char *inf = upper ? " INF" : " inf"; +        if (!sign) +        { +            --inf_size; +            ++inf; +        } +        CharPtr out = write_str(inf, inf_size, spec); +        if (sign) +            *out = sign; +        return; +    } + +    std::size_t offset = buffer_.size(); +    unsigned width = spec.width(); +    if (sign) +    { +        buffer_.reserve(buffer_.size() + (width > 1u ? width : 1u)); +        if (width > 0) +            --width; +        ++offset; +    } + +    // Build format string. +    enum +    { +        MAX_FORMAT_SIZE = 10 +    }; // longest format: %#-*.*Lg +    Char format[MAX_FORMAT_SIZE]; +    Char *format_ptr = format; +    *format_ptr++ = '%'; +    unsigned width_for_sprintf = width; +    if (spec.flag(HASH_FLAG)) +        *format_ptr++ = '#'; +    if (spec.align() == ALIGN_CENTER) +    { +        width_for_sprintf = 0; +    } +    else +    { +        if (spec.align() == ALIGN_LEFT) +            *format_ptr++ = '-'; +        if (width != 0) +            *format_ptr++ = '*'; +    } +    if (spec.precision() >= 0) +    { +        *format_ptr++ = '.'; +        *format_ptr++ = '*'; +    } + +    append_float_length(format_ptr, value); +    *format_ptr++ = type; +    *format_ptr = '\0'; + +    // Format using snprintf. +    Char fill = internal::CharTraits<Char>::cast(spec.fill()); +    unsigned n = 0; +    Char *start = FMT_NULL; +    for (;;) +    { +        std::size_t buffer_size = buffer_.capacity() - offset; +#if FMT_MSC_VER +        // MSVC's vsnprintf_s doesn't work with zero size, so reserve +        // space for at least one extra character to make the size non-zero. +        // Note that the buffer's capacity will increase by more than 1. +        if (buffer_size == 0) +        { +            buffer_.reserve(offset + 1); +            buffer_size = buffer_.capacity() - offset; +        } +#endif +        start = &buffer_[offset]; +        int result = internal::CharTraits<Char>::format_float(start, buffer_size, format, width_for_sprintf, spec.precision(), value); +        if (result >= 0) +        { +            n = internal::to_unsigned(result); +            if (offset + n < buffer_.capacity()) +                break; // The buffer is large enough - continue with formatting. +            buffer_.reserve(offset + n + 1); +        } +        else +        { +            // If result is negative we ask to increase the capacity by at least 1, +            // but as std::vector, the buffer grows exponentially. +            buffer_.reserve(buffer_.capacity() + 1); +        } +    } +    if (sign) +    { +        if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || *start != ' ') +        { +            *(start - 1) = sign; +            sign = 0; +        } +        else +        { +            *(start - 1) = fill; +        } +        ++n; +    } +    if (spec.align() == ALIGN_CENTER && spec.width() > n) +    { +        width = spec.width(); +        CharPtr p = grow_buffer(width); +        std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(Char)); +        fill_padding(p, spec.width(), n, fill); +        return; +    } +    if (spec.fill() != ' ' || sign) +    { +        while (*start == ' ') +            *start++ = fill; +        if (sign) +            *(start - 1) = sign; +    } +    grow_buffer(n); +} + +/** +  \rst +  This class template provides operations for formatting and writing data +  into a character stream. The output is stored in a memory buffer that grows +  dynamically. + +  You can use one of the following typedefs for common character types +  and the standard allocator: + +  +---------------+-----------------------------------------------------+ +  | Type          | Definition                                          | +  +===============+=====================================================+ +  | MemoryWriter  | BasicMemoryWriter<char, std::allocator<char>>       | +  +---------------+-----------------------------------------------------+ +  | WMemoryWriter | BasicMemoryWriter<wchar_t, std::allocator<wchar_t>> | +  +---------------+-----------------------------------------------------+ + +  **Example**:: + +     MemoryWriter out; +     out << "The answer is " << 42 << "\n"; +     out.write("({:+f}, {:+f})", -3.14, 3.14); + +  This will write the following output to the ``out`` object: + +  .. code-block:: none + +     The answer is 42 +     (-3.140000, +3.140000) + +  The output can be converted to an ``std::string`` with ``out.str()`` or +  accessed as a C string with ``out.c_str()``. +  \endrst + */ +template<typename Char, typename Allocator = std::allocator<Char>> +class BasicMemoryWriter : public BasicWriter<Char> +{ +private: +    internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE, Allocator> buffer_; + +public: +    explicit BasicMemoryWriter(const Allocator &alloc = Allocator()) +        : BasicWriter<Char>(buffer_) +        , buffer_(alloc) +    { +    } + +#if FMT_USE_RVALUE_REFERENCES +    /** +      \rst +      Constructs a :class:`fmt::BasicMemoryWriter` object moving the content +      of the other object to it. +      \endrst +     */ +    BasicMemoryWriter(BasicMemoryWriter &&other) +        : BasicWriter<Char>(buffer_) +        , buffer_(std::move(other.buffer_)) +    { +    } + +    /** +      \rst +      Moves the content of the other ``BasicMemoryWriter`` object to this one. +      \endrst +     */ +    BasicMemoryWriter &operator=(BasicMemoryWriter &&other) +    { +        buffer_ = std::move(other.buffer_); +        return *this; +    } +#endif +}; + +typedef BasicMemoryWriter<char> MemoryWriter; +typedef BasicMemoryWriter<wchar_t> WMemoryWriter; + +/** +  \rst +  This class template provides operations for formatting and writing data +  into a fixed-size array. For writing into a dynamically growing buffer +  use :class:`fmt::BasicMemoryWriter`. + +  Any write method will throw ``std::runtime_error`` if the output doesn't fit +  into the array. + +  You can use one of the following typedefs for common character types: + +  +--------------+---------------------------+ +  | Type         | Definition                | +  +==============+===========================+ +  | ArrayWriter  | BasicArrayWriter<char>    | +  +--------------+---------------------------+ +  | WArrayWriter | BasicArrayWriter<wchar_t> | +  +--------------+---------------------------+ +  \endrst + */ +template<typename Char> +class BasicArrayWriter : public BasicWriter<Char> +{ +private: +    internal::FixedBuffer<Char> buffer_; + +public: +    /** +     \rst +     Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the +     given size. +     \endrst +     */ +    BasicArrayWriter(Char *array, std::size_t size) +        : BasicWriter<Char>(buffer_) +        , buffer_(array, size) +    { +    } + +    /** +     \rst +     Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the +     size known at compile time. +     \endrst +     */ +    template<std::size_t SIZE> +    explicit BasicArrayWriter(Char (&array)[SIZE]) +        : BasicWriter<Char>(buffer_) +        , buffer_(array, SIZE) +    { +    } +}; + +typedef BasicArrayWriter<char> ArrayWriter; +typedef BasicArrayWriter<wchar_t> WArrayWriter; + +// 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, StringRef message) FMT_NOEXCEPT; + +#if FMT_USE_WINDOWS_H + +/** A Windows error. */ +class WindowsError : public SystemError +{ +private: +    FMT_API void init(int error_code, CStringRef format_str, ArgList args); + +public: +    /** +     \rst +     Constructs a :class:`fmt::WindowsError` 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 WindowsError 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::WindowsError(GetLastError(), +                                 "cannot open file '{}'", filename); +       } +     \endrst +    */ +    WindowsError(int error_code, CStringRef message) +    { +        init(error_code, message, ArgList()); +    } +    FMT_VARIADIC_CTOR(WindowsError, init, int, CStringRef) +}; + +// 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, StringRef message) FMT_NOEXCEPT; + +#endif + +enum Color +{ +    BLACK, +    RED, +    GREEN, +    YELLOW, +    BLUE, +    MAGENTA, +    CYAN, +    WHITE +}; + +/** +  Formats a string and prints it to stdout using ANSI escape sequences +  to specify color (experimental). +  Example: +    print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23); + */ +FMT_API void print_colored(Color c, CStringRef format, ArgList args); + +/** +  \rst +  Formats arguments and returns the result as a string. + +  **Example**:: + +    std::string message = format("The answer is {}", 42); +  \endrst +*/ +inline std::string format(CStringRef format_str, ArgList args) +{ +    MemoryWriter w; +    w.write(format_str, args); +    return w.str(); +} + +inline std::wstring format(WCStringRef format_str, ArgList args) +{ +    WMemoryWriter w; +    w.write(format_str, args); +    return w.str(); +} + +/** +  \rst +  Prints formatted data to the file *f*. + +  **Example**:: + +    print(stderr, "Don't {}!", "panic"); +  \endrst + */ +FMT_API void print(std::FILE *f, CStringRef format_str, ArgList args); + +/** +  \rst +  Prints formatted data to ``stdout``. + +  **Example**:: + +    print("Elapsed time: {0:.2f} seconds", 1.23); +  \endrst + */ +FMT_API void print(CStringRef format_str, ArgList args); + +/** +  Fast integer formatter. + */ +class FormatInt +{ +private: +    // Buffer should be large enough to hold all digits (digits10 + 1), +    // a sign and a null character. +    enum +    { +        BUFFER_SIZE = std::numeric_limits<ULongLong>::digits10 + 3 +    }; +    mutable char buffer_[BUFFER_SIZE]; +    char *str_; + +    // Formats value in reverse and returns the number of digits. +    char *format_decimal(ULongLong value) +    { +        char *buffer_end = buffer_ + BUFFER_SIZE - 1; +        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. +            unsigned index = static_cast<unsigned>((value % 100) * 2); +            value /= 100; +            *--buffer_end = internal::Data::DIGITS[index + 1]; +            *--buffer_end = internal::Data::DIGITS[index]; +        } +        if (value < 10) +        { +            *--buffer_end = static_cast<char>('0' + value); +            return buffer_end; +        } +        unsigned index = static_cast<unsigned>(value * 2); +        *--buffer_end = internal::Data::DIGITS[index + 1]; +        *--buffer_end = internal::Data::DIGITS[index]; +        return buffer_end; +    } + +    void FormatSigned(LongLong value) +    { +        ULongLong abs_value = static_cast<ULongLong>(value); +        bool negative = value < 0; +        if (negative) +            abs_value = 0 - abs_value; +        str_ = format_decimal(abs_value); +        if (negative) +            *--str_ = '-'; +    } + +public: +    explicit FormatInt(int value) +    { +        FormatSigned(value); +    } +    explicit FormatInt(long value) +    { +        FormatSigned(value); +    } +    explicit FormatInt(LongLong value) +    { +        FormatSigned(value); +    } +    explicit FormatInt(unsigned value) +        : str_(format_decimal(value)) +    { +    } +    explicit FormatInt(unsigned long value) +        : str_(format_decimal(value)) +    { +    } +    explicit FormatInt(ULongLong value) +        : str_(format_decimal(value)) +    { +    } + +    /** Returns the number of characters written to the output buffer. */ +    std::size_t size() const +    { +        return internal::to_unsigned(buffer_ - str_ + BUFFER_SIZE - 1); +    } + +    /** +      Returns a pointer to the output buffer content. No terminating null +      character is appended. +     */ +    const char *data() const +    { +        return str_; +    } + +    /** +      Returns a pointer to the output buffer content with terminating null +      character appended. +     */ +    const char *c_str() const +    { +        buffer_[BUFFER_SIZE - 1] = '\0'; +        return str_; +    } + +    /** +      \rst +      Returns the content of the output buffer as an ``std::string``. +      \endrst +     */ +    std::string str() const +    { +        return std::string(str_, size()); +    } +}; + +// Formats a decimal integer value writing into buffer and returns +// a pointer to the end of the formatted string. This function doesn't +// write a terminating null character. +template<typename T> +inline void format_decimal(char *&buffer, T value) +{ +    typedef typename internal::IntTraits<T>::MainType MainType; +    MainType abs_value = static_cast<MainType>(value); +    if (internal::is_negative(value)) +    { +        *buffer++ = '-'; +        abs_value = 0 - abs_value; +    } +    if (abs_value < 100) +    { +        if (abs_value < 10) +        { +            *buffer++ = static_cast<char>('0' + abs_value); +            return; +        } +        unsigned index = static_cast<unsigned>(abs_value * 2); +        *buffer++ = internal::Data::DIGITS[index]; +        *buffer++ = internal::Data::DIGITS[index + 1]; +        return; +    } +    unsigned num_digits = internal::count_digits(abs_value); +    internal::format_decimal(buffer, abs_value, num_digits); +    buffer += num_digits; +} + +/** +  \rst +  Returns a named argument for formatting functions. + +  **Example**:: + +    print("Elapsed time: {s:.2f} seconds", arg("s", 1.23)); + +  \endrst + */ +template<typename T> +inline internal::NamedArgWithType<char, T> arg(StringRef name, const T &arg) +{ +    return internal::NamedArgWithType<char, T>(name, arg); +} + +template<typename T> +inline internal::NamedArgWithType<wchar_t, T> arg(WStringRef name, const T &arg) +{ +    return internal::NamedArgWithType<wchar_t, T>(name, arg); +} + +// The following two functions are deleted intentionally to disable +// nested named arguments as in ``format("{}", arg("a", arg("b", 42)))``. +template<typename Char> +void arg(StringRef, const internal::NamedArg<Char> &) FMT_DELETED_OR_UNDEFINED; +template<typename Char> +void arg(WStringRef, const internal::NamedArg<Char> &) FMT_DELETED_OR_UNDEFINED; +} // namespace fmt + +#if FMT_GCC_VERSION +// Use the system_header pragma to suppress warnings about variadic macros +// because suppressing -Wvariadic-macros with the diagnostic pragma doesn't +// work. It is used at the end because we want to suppress as little warnings +// as possible. +#pragma GCC system_header +#endif + +// This is used to work around VC++ bugs in handling variadic macros. +#define FMT_EXPAND(args) args + +// Returns the number of arguments. +// Based on https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s. +#define FMT_NARG(...) FMT_NARG_(__VA_ARGS__, FMT_RSEQ_N()) +#define FMT_NARG_(...) FMT_EXPAND(FMT_ARG_N(__VA_ARGS__)) +#define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +#define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +#define FMT_FOR_EACH_(N, f, ...) FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__)) +#define FMT_FOR_EACH(f, ...) FMT_EXPAND(FMT_FOR_EACH_(FMT_NARG(__VA_ARGS__), f, __VA_ARGS__)) + +#define FMT_ADD_ARG_NAME(type, index) type arg##index +#define FMT_GET_ARG_NAME(type, index) arg##index + +#if FMT_USE_VARIADIC_TEMPLATES +#define FMT_VARIADIC_(Const, Char, ReturnType, func, call, ...)                                                                            \ +    template<typename... Args>                                                                                                             \ +    ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), const Args &... args) Const                                               \ +    {                                                                                                                                      \ +        typedef fmt::internal::ArgArray<sizeof...(Args)> ArgArray;                                                                         \ +        typename ArgArray::Type array{ArgArray::template make<fmt::BasicFormatter<Char>>(args)...};                                        \ +        call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList(fmt::internal::make_type(args...), array));                         \ +    } +#else +// Defines a wrapper for a function taking __VA_ARGS__ arguments +// and n additional arguments of arbitrary types. +#define FMT_WRAP(Const, Char, ReturnType, func, call, n, ...)                                                                              \ +    template<FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)>                                                                                            \ +    inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), FMT_GEN(n, FMT_MAKE_ARG)) Const                                    \ +    {                                                                                                                                      \ +        fmt::internal::ArgArray<n>::Type arr;                                                                                              \ +        FMT_GEN(n, FMT_ASSIGN_##Char);                                                                                                     \ +        call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList(fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), arr));         \ +    } + +#define FMT_VARIADIC_(Const, Char, ReturnType, func, call, ...)                                                                            \ +    inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) Const                                                              \ +    {                                                                                                                                      \ +        call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList());                                                                 \ +    }                                                                                                                                      \ +    FMT_WRAP(Const, Char, ReturnType, func, call, 1, __VA_ARGS__)                                                                          \ +    FMT_WRAP(Const, Char, ReturnType, func, call, 2, __VA_ARGS__)                                                                          \ +    FMT_WRAP(Const, Char, ReturnType, func, call, 3, __VA_ARGS__)                                                                          \ +    FMT_WRAP(Const, Char, ReturnType, func, call, 4, __VA_ARGS__)                                                                          \ +    FMT_WRAP(Const, Char, ReturnType, func, call, 5, __VA_ARGS__)                                                                          \ +    FMT_WRAP(Const, Char, ReturnType, func, call, 6, __VA_ARGS__)                                                                          \ +    FMT_WRAP(Const, Char, ReturnType, func, call, 7, __VA_ARGS__)                                                                          \ +    FMT_WRAP(Const, Char, ReturnType, func, call, 8, __VA_ARGS__)                                                                          \ +    FMT_WRAP(Const, Char, ReturnType, func, call, 9, __VA_ARGS__)                                                                          \ +    FMT_WRAP(Const, Char, ReturnType, func, call, 10, __VA_ARGS__)                                                                         \ +    FMT_WRAP(Const, Char, ReturnType, func, call, 11, __VA_ARGS__)                                                                         \ +    FMT_WRAP(Const, Char, ReturnType, func, call, 12, __VA_ARGS__)                                                                         \ +    FMT_WRAP(Const, Char, ReturnType, func, call, 13, __VA_ARGS__)                                                                         \ +    FMT_WRAP(Const, Char, ReturnType, func, call, 14, __VA_ARGS__)                                                                         \ +    FMT_WRAP(Const, Char, ReturnType, func, call, 15, __VA_ARGS__) +#endif // FMT_USE_VARIADIC_TEMPLATES + +/** +  \rst +  Defines a variadic function with the specified return type, function name +  and argument types passed as variable arguments to this macro. + +  **Example**:: + +    void print_error(const char *file, int line, const char *format, +                     fmt::ArgList args) { +      fmt::print("{}: {}: ", file, line); +      fmt::print(format, args); +    } +    FMT_VARIADIC(void, print_error, const char *, int, const char *) + +  ``FMT_VARIADIC`` is used for compatibility with legacy C++ compilers that +  don't implement variadic templates. You don't have to use this macro if +  you don't need legacy compiler support and can use variadic templates +  directly:: + +    template <typename... Args> +    void print_error(const char *file, int line, const char *format, +                     const Args & ... args) { +      fmt::print("{}: {}: ", file, line); +      fmt::print(format, args...); +    } +  \endrst + */ +#define FMT_VARIADIC(ReturnType, func, ...) FMT_VARIADIC_(, char, ReturnType, func, return func, __VA_ARGS__) + +#define FMT_VARIADIC_CONST(ReturnType, func, ...) FMT_VARIADIC_(const, char, ReturnType, func, return func, __VA_ARGS__) + +#define FMT_VARIADIC_W(ReturnType, func, ...) FMT_VARIADIC_(, wchar_t, ReturnType, func, return func, __VA_ARGS__) + +#define FMT_VARIADIC_CONST_W(ReturnType, func, ...) FMT_VARIADIC_(const, wchar_t, ReturnType, func, return func, __VA_ARGS__) + +#define FMT_CAPTURE_ARG_(id, index) ::fmt::arg(#id, id) + +#define FMT_CAPTURE_ARG_W_(id, index) ::fmt::arg(L## #id, id) + +/** +  \rst +  Convenient macro to capture the arguments' names and values into several +  ``fmt::arg(name, value)``. + +  **Example**:: + +    int x = 1, y = 2; +    print("point: ({x}, {y})", FMT_CAPTURE(x, y)); +    // same as: +    // print("point: ({x}, {y})", arg("x", x), arg("y", y)); + +  \endrst + */ +#define FMT_CAPTURE(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_, __VA_ARGS__) + +#define FMT_CAPTURE_W(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_W_, __VA_ARGS__) + +namespace fmt { +FMT_VARIADIC(std::string, format, CStringRef) +FMT_VARIADIC_W(std::wstring, format, WCStringRef) +FMT_VARIADIC(void, print, CStringRef) +FMT_VARIADIC(void, print, std::FILE *, CStringRef) +FMT_VARIADIC(void, print_colored, Color, CStringRef) + +namespace internal { +template<typename Char> +inline bool is_name_start(Char c) +{ +    return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; +} + +// Parses an unsigned integer advancing s to the end of the parsed input. +// This function assumes that the first character of s is a digit. +template<typename Char> +unsigned parse_nonnegative_int(const Char *&s) +{ +    assert('0' <= *s && *s <= '9'); +    unsigned value = 0; +    // Convert to unsigned to prevent a warning. +    unsigned max_int = (std::numeric_limits<int>::max)(); +    unsigned big = max_int / 10; +    do +    { +        // Check for overflow. +        if (value > big) +        { +            value = max_int + 1; +            break; +        } +        value = value * 10 + (*s - '0'); +        ++s; +    } while ('0' <= *s && *s <= '9'); +    // Convert to unsigned to prevent a warning. +    if (value > max_int) +        FMT_THROW(FormatError("number is too big")); +    return value; +} + +inline void require_numeric_argument(const Arg &arg, char spec) +{ +    if (arg.type > Arg::LAST_NUMERIC_TYPE) +    { +        std::string message = fmt::format("format specifier '{}' requires numeric argument", spec); +        FMT_THROW(fmt::FormatError(message)); +    } +} + +template<typename Char> +void check_sign(const Char *&s, const Arg &arg) +{ +    char sign = static_cast<char>(*s); +    require_numeric_argument(arg, sign); +    if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) +    { +        FMT_THROW(FormatError(fmt::format("format specifier '{}' requires signed argument", sign))); +    } +    ++s; +} +} // namespace internal + +template<typename Char, typename AF> +inline internal::Arg BasicFormatter<Char, AF>::get_arg(BasicStringRef<Char> arg_name, const char *&error) +{ +    if (check_no_auto_index(error)) +    { +        map_.init(args()); +        const internal::Arg *arg = map_.find(arg_name); +        if (arg) +            return *arg; +        error = "argument not found"; +    } +    return internal::Arg(); +} + +template<typename Char, typename AF> +inline internal::Arg BasicFormatter<Char, AF>::parse_arg_index(const Char *&s) +{ +    const char *error = FMT_NULL; +    internal::Arg arg = *s < '0' || *s > '9' ? next_arg(error) : get_arg(internal::parse_nonnegative_int(s), error); +    if (error) +    { +        FMT_THROW(FormatError(*s != '}' && *s != ':' ? "invalid format string" : error)); +    } +    return arg; +} + +template<typename Char, typename AF> +inline internal::Arg BasicFormatter<Char, AF>::parse_arg_name(const Char *&s) +{ +    assert(internal::is_name_start(*s)); +    const Char *start = s; +    Char c; +    do +    { +        c = *++s; +    } while (internal::is_name_start(c) || ('0' <= c && c <= '9')); +    const char *error = FMT_NULL; +    internal::Arg arg = get_arg(BasicStringRef<Char>(start, s - start), error); +    if (error) +        FMT_THROW(FormatError(error)); +    return arg; +} + +template<typename Char, typename ArgFormatter> +const Char *BasicFormatter<Char, ArgFormatter>::format(const Char *&format_str, const internal::Arg &arg) +{ +    using internal::Arg; +    const Char *s = format_str; +    typename ArgFormatter::SpecType spec; +    if (*s == ':') +    { +        if (arg.type == Arg::CUSTOM) +        { +            arg.custom.format(this, arg.custom.value, &s); +            return s; +        } +        ++s; +        // Parse fill and alignment. +        if (Char c = *s) +        { +            const Char *p = s + 1; +            spec.align_ = ALIGN_DEFAULT; +            do +            { +                switch (*p) +                { +                case '<': +                    spec.align_ = ALIGN_LEFT; +                    break; +                case '>': +                    spec.align_ = ALIGN_RIGHT; +                    break; +                case '=': +                    spec.align_ = ALIGN_NUMERIC; +                    break; +                case '^': +                    spec.align_ = ALIGN_CENTER; +                    break; +                } +                if (spec.align_ != ALIGN_DEFAULT) +                { +                    if (p != s) +                    { +                        if (c == '}') +                            break; +                        if (c == '{') +                            FMT_THROW(FormatError("invalid fill character '{'")); +                        s += 2; +                        spec.fill_ = c; +                    } +                    else +                        ++s; +                    if (spec.align_ == ALIGN_NUMERIC) +                        require_numeric_argument(arg, '='); +                    break; +                } +            } while (--p >= s); +        } + +        // Parse sign. +        switch (*s) +        { +        case '+': +            check_sign(s, arg); +            spec.flags_ |= SIGN_FLAG | PLUS_FLAG; +            break; +        case '-': +            check_sign(s, arg); +            spec.flags_ |= MINUS_FLAG; +            break; +        case ' ': +            check_sign(s, arg); +            spec.flags_ |= SIGN_FLAG; +            break; +        } + +        if (*s == '#') +        { +            require_numeric_argument(arg, '#'); +            spec.flags_ |= HASH_FLAG; +            ++s; +        } + +        // Parse zero flag. +        if (*s == '0') +        { +            require_numeric_argument(arg, '0'); +            spec.align_ = ALIGN_NUMERIC; +            spec.fill_ = '0'; +            ++s; +        } + +        // Parse width. +        if ('0' <= *s && *s <= '9') +        { +            spec.width_ = internal::parse_nonnegative_int(s); +        } +        else if (*s == '{') +        { +            ++s; +            Arg width_arg = internal::is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s); +            if (*s++ != '}') +                FMT_THROW(FormatError("invalid format string")); +            ULongLong value = 0; +            switch (width_arg.type) +            { +            case Arg::INT: +                if (width_arg.int_value < 0) +                    FMT_THROW(FormatError("negative width")); +                value = width_arg.int_value; +                break; +            case Arg::UINT: +                value = width_arg.uint_value; +                break; +            case Arg::LONG_LONG: +                if (width_arg.long_long_value < 0) +                    FMT_THROW(FormatError("negative width")); +                value = width_arg.long_long_value; +                break; +            case Arg::ULONG_LONG: +                value = width_arg.ulong_long_value; +                break; +            default: +                FMT_THROW(FormatError("width is not integer")); +            } +            unsigned max_int = (std::numeric_limits<int>::max)(); +            if (value > max_int) +                FMT_THROW(FormatError("number is too big")); +            spec.width_ = static_cast<int>(value); +        } + +        // Parse precision. +        if (*s == '.') +        { +            ++s; +            spec.precision_ = 0; +            if ('0' <= *s && *s <= '9') +            { +                spec.precision_ = internal::parse_nonnegative_int(s); +            } +            else if (*s == '{') +            { +                ++s; +                Arg precision_arg = internal::is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s); +                if (*s++ != '}') +                    FMT_THROW(FormatError("invalid format string")); +                ULongLong value = 0; +                switch (precision_arg.type) +                { +                case Arg::INT: +                    if (precision_arg.int_value < 0) +                        FMT_THROW(FormatError("negative precision")); +                    value = precision_arg.int_value; +                    break; +                case Arg::UINT: +                    value = precision_arg.uint_value; +                    break; +                case Arg::LONG_LONG: +                    if (precision_arg.long_long_value < 0) +                        FMT_THROW(FormatError("negative precision")); +                    value = precision_arg.long_long_value; +                    break; +                case Arg::ULONG_LONG: +                    value = precision_arg.ulong_long_value; +                    break; +                default: +                    FMT_THROW(FormatError("precision is not integer")); +                } +                unsigned max_int = (std::numeric_limits<int>::max)(); +                if (value > max_int) +                    FMT_THROW(FormatError("number is too big")); +                spec.precision_ = static_cast<int>(value); +            } +            else +            { +                FMT_THROW(FormatError("missing precision specifier")); +            } +            if (arg.type <= Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) +            { +                FMT_THROW(FormatError( +                    fmt::format("precision not allowed in {} format specifier", arg.type == Arg::POINTER ? "pointer" : "integer"))); +            } +        } + +        // Parse type. +        if (*s != '}' && *s) +            spec.type_ = static_cast<char>(*s++); +    } + +    if (*s++ != '}') +        FMT_THROW(FormatError("missing '}' in format string")); + +    // Format argument. +    ArgFormatter(*this, spec, s - 1).visit(arg); +    return s; +} + +template<typename Char, typename AF> +void BasicFormatter<Char, AF>::format(BasicCStringRef<Char> format_str) +{ +    const Char *s = format_str.c_str(); +    const Char *start = s; +    while (*s) +    { +        Char c = *s++; +        if (c != '{' && c != '}') +            continue; +        if (*s == c) +        { +            write(writer_, start, s); +            start = ++s; +            continue; +        } +        if (c == '}') +            FMT_THROW(FormatError("unmatched '}' in format string")); +        write(writer_, start, s - 1); +        internal::Arg arg = internal::is_name_start(*s) ? parse_arg_name(s) : parse_arg_index(s); +        start = s = format(s, arg); +    } +    write(writer_, start, s); +} + +template<typename Char, typename It> +struct ArgJoin +{ +    It first; +    It last; +    BasicCStringRef<Char> sep; + +    ArgJoin(It first, It last, const BasicCStringRef<Char> &sep) +        : first(first) +        , last(last) +        , sep(sep) +    { +    } +}; + +template<typename It> +ArgJoin<char, It> join(It first, It last, const BasicCStringRef<char> &sep) +{ +    return ArgJoin<char, It>(first, last, sep); +} + +template<typename It> +ArgJoin<wchar_t, It> join(It first, It last, const BasicCStringRef<wchar_t> &sep) +{ +    return ArgJoin<wchar_t, It>(first, last, sep); +} + +#if FMT_HAS_GXX_CXX11 +template<typename Range> +auto join(const Range &range, const BasicCStringRef<char> &sep) -> ArgJoin<char, decltype(std::begin(range))> +{ +    return join(std::begin(range), std::end(range), sep); +} + +template<typename Range> +auto join(const Range &range, const BasicCStringRef<wchar_t> &sep) -> ArgJoin<wchar_t, decltype(std::begin(range))> +{ +    return join(std::begin(range), std::end(range), sep); +} +#endif + +template<typename ArgFormatter, typename Char, typename It> +void format_arg(fmt::BasicFormatter<Char, ArgFormatter> &f, const Char *&format_str, const ArgJoin<Char, It> &e) +{ +    const Char *end = format_str; +    if (*end == ':') +        ++end; +    while (*end && *end != '}') +        ++end; +    if (*end != '}') +        FMT_THROW(FormatError("missing '}' in format string")); + +    It it = e.first; +    if (it != e.last) +    { +        const Char *save = format_str; +        f.format(format_str, internal::MakeArg<fmt::BasicFormatter<Char, ArgFormatter>>(*it++)); +        while (it != e.last) +        { +            f.writer().write(e.sep); +            format_str = save; +            f.format(format_str, internal::MakeArg<fmt::BasicFormatter<Char, ArgFormatter>>(*it++)); +        } +    } +    format_str = end + 1; +} +} // namespace fmt + +#if FMT_USE_USER_DEFINED_LITERALS +namespace fmt { +namespace internal { + +template<typename Char> +struct UdlFormat +{ +    const Char *str; + +    template<typename... Args> +    auto operator()(Args &&... args) const -> decltype(format(str, std::forward<Args>(args)...)) +    { +        return format(str, std::forward<Args>(args)...); +    } +}; + +template<typename Char> +struct UdlArg +{ +    const Char *str; + +    template<typename T> +    NamedArgWithType<Char, T> operator=(T &&value) const +    { +        return {str, std::forward<T>(value)}; +    } +}; + +} // namespace internal + +inline namespace literals { + +/** +  \rst +  C++11 literal equivalent of :func:`fmt::format`. + +  **Example**:: + +    using namespace fmt::literals; +    std::string message = "The answer is {}"_format(42); +  \endrst + */ +inline internal::UdlFormat<char> operator"" _format(const char *s, std::size_t) +{ +    return {s}; +} +inline internal::UdlFormat<wchar_t> operator"" _format(const wchar_t *s, std::size_t) +{ +    return {s}; +} + +/** +  \rst +  C++11 literal equivalent of :func:`fmt::arg`. + +  **Example**:: + +    using namespace fmt::literals; +    print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); +  \endrst + */ +inline internal::UdlArg<char> operator"" _a(const char *s, std::size_t) +{ +    return {s}; +} +inline internal::UdlArg<wchar_t> operator"" _a(const wchar_t *s, std::size_t) +{ +    return {s}; +} + +} // namespace literals +} // namespace fmt +#endif // FMT_USE_USER_DEFINED_LITERALS + +// Restore warnings. +#if FMT_GCC_VERSION >= 406 +#pragma GCC diagnostic pop +#endif + +#if defined(__clang__) && !defined(FMT_ICC_VERSION) +#pragma clang diagnostic pop +#endif + +#ifdef FMT_HEADER_ONLY +#define FMT_FUNC inline +#include "format.cc" +#else +#define FMT_FUNC +#endif + +#endif // FMT_FORMAT_H_ diff --git a/lib/spdlog/fmt/bundled/ostream.cc b/lib/spdlog/fmt/bundled/ostream.cc new file mode 100644 index 0000000..2d443f7 --- /dev/null +++ b/lib/spdlog/fmt/bundled/ostream.cc @@ -0,0 +1,35 @@ +/* + Formatting library for C++ - std::ostream support + + Copyright (c) 2012 - 2016, Victor Zverovich + All rights reserved. + + For the license information refer to format.h. + */ + +#include "ostream.h" + +namespace fmt { + +namespace internal { +FMT_FUNC void write(std::ostream &os, Writer &w) { +  const char *data = w.data(); +  typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize; +  UnsignedStreamSize size = w.size(); +  UnsignedStreamSize max_size = +      internal::to_unsigned((std::numeric_limits<std::streamsize>::max)()); +  do { +    UnsignedStreamSize n = size <= max_size ? size : max_size; +    os.write(data, static_cast<std::streamsize>(n)); +    data += n; +    size -= n; +  } while (size != 0); +} +} + +FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) { +  MemoryWriter w; +  w.write(format_str, args); +  internal::write(os, w); +} +}  // namespace fmt diff --git a/lib/spdlog/fmt/bundled/ostream.h b/lib/spdlog/fmt/bundled/ostream.h new file mode 100644 index 0000000..ffa75ab --- /dev/null +++ b/lib/spdlog/fmt/bundled/ostream.h @@ -0,0 +1,117 @@ +/* + Formatting library for C++ - std::ostream support + + Copyright (c) 2012 - 2016, Victor Zverovich + All rights reserved. + + For the license information refer to format.h. + */ + +#ifndef FMT_OSTREAM_H_ +#define FMT_OSTREAM_H_ + +#include "format.h" +#include <ostream> + +namespace fmt { + +namespace internal { + +template<class Char> +class FormatBuf : public std::basic_streambuf<Char> +{ +private: +    typedef typename std::basic_streambuf<Char>::int_type int_type; +    typedef typename std::basic_streambuf<Char>::traits_type traits_type; + +    Buffer<Char> &buffer_; + +public: +    FormatBuf(Buffer<Char> &buffer) +        : buffer_(buffer) +    { +    } + +protected: +    // The put-area is actually 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. The obvious +    // 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. + +    int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE +    { +        if (!traits_type::eq_int_type(ch, traits_type::eof())) +            buffer_.push_back(static_cast<Char>(ch)); +        return ch; +    } + +    std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE +    { +        buffer_.append(s, s + count); +        return count; +    } +}; + +Yes &convert(std::ostream &); + +struct DummyStream : std::ostream +{ +    DummyStream(); // Suppress a bogus warning in MSVC. + +    // Hide all operator<< overloads from std::ostream. +    template<typename T> +    typename EnableIf<sizeof(T) == 0>::type operator<<(const T &); +}; + +No &operator<<(std::ostream &, int); + +template<typename T> +struct ConvertToIntImpl<T, true> +{ +    // Convert to int only if T doesn't have an overloaded operator<<. +    enum +    { +        value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No) +    }; +}; + +// Write the content of w to os. +FMT_API void write(std::ostream &os, Writer &w); +} // namespace internal + +// Formats a value. +template<typename Char, typename ArgFormatter_, typename T> +void format_arg(BasicFormatter<Char, ArgFormatter_> &f, const Char *&format_str, const T &value) +{ +    internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer; + +    internal::FormatBuf<Char> format_buf(buffer); +    std::basic_ostream<Char> output(&format_buf); +    output.exceptions(std::ios_base::failbit | std::ios_base::badbit); +    output << value; + +    BasicStringRef<Char> str(&buffer[0], buffer.size()); +    typedef internal::MakeArg<BasicFormatter<Char>> MakeArg; +    format_str = f.format(format_str, MakeArg(str)); +} + +/** +  \rst +  Prints formatted data to the stream *os*. + +  **Example**:: + +    print(cerr, "Don't {}!", "panic"); +  \endrst + */ +FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args); +FMT_VARIADIC(void, print, std::ostream &, CStringRef) +} // namespace fmt + +#ifdef FMT_HEADER_ONLY +#include "ostream.cc" +#endif + +#endif // FMT_OSTREAM_H_ diff --git a/lib/spdlog/fmt/bundled/posix.cc b/lib/spdlog/fmt/bundled/posix.cc new file mode 100644 index 0000000..356668c --- /dev/null +++ b/lib/spdlog/fmt/bundled/posix.cc @@ -0,0 +1,241 @@ +/* + A C++ interface to POSIX functions. + + Copyright (c) 2012 - 2016, Victor Zverovich + All rights reserved. + + For the license information refer to format.h. + */ + +// Disable bogus MSVC warnings. +#ifndef _CRT_SECURE_NO_WARNINGS +# define _CRT_SECURE_NO_WARNINGS +#endif + +#include "posix.h" + +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> + +#ifndef _WIN32 +# include <unistd.h> +#else +# ifndef WIN32_LEAN_AND_MEAN +#  define WIN32_LEAN_AND_MEAN +# endif +# include <windows.h> +# include <io.h> + +# define O_CREAT _O_CREAT +# define O_TRUNC _O_TRUNC + +# ifndef S_IRUSR +#  define S_IRUSR _S_IREAD +# endif + +# ifndef S_IWUSR +#  define S_IWUSR _S_IWRITE +# endif + +# ifdef __MINGW32__ +#  define _SH_DENYNO 0x40 +# endif + +#endif  // _WIN32 + +#ifdef fileno +# undef fileno +#endif + +namespace { +#ifdef _WIN32 +// Return type of read and write functions. +typedef int RWResult; + +// On Windows the count argument to read and write is unsigned, so convert +// it from size_t preventing integer overflow. +inline unsigned convert_rwcount(std::size_t count) { +  return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX; +} +#else +// Return type of read and write functions. +typedef ssize_t RWResult; + +inline std::size_t convert_rwcount(std::size_t count) { return count; } +#endif +} + +fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT { +  if (file_ && FMT_SYSTEM(fclose(file_)) != 0) +    fmt::report_system_error(errno, "cannot close file"); +} + +fmt::BufferedFile::BufferedFile( +    fmt::CStringRef filename, fmt::CStringRef mode) { +  FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0); +  if (!file_) +    FMT_THROW(SystemError(errno, "cannot open file {}", filename)); +} + +void fmt::BufferedFile::close() { +  if (!file_) +    return; +  int result = FMT_SYSTEM(fclose(file_)); +  file_ = FMT_NULL; +  if (result != 0) +    FMT_THROW(SystemError(errno, "cannot close file")); +} + +// A macro used to prevent expansion of fileno on broken versions of MinGW. +#define FMT_ARGS + +int fmt::BufferedFile::fileno() const { +  int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); +  if (fd == -1) +    FMT_THROW(SystemError(errno, "cannot get file descriptor")); +  return fd; +} + +fmt::File::File(fmt::CStringRef path, int oflag) { +  int mode = S_IRUSR | S_IWUSR; +#if defined(_WIN32) && !defined(__MINGW32__) +  fd_ = -1; +  FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); +#else +  FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); +#endif +  if (fd_ == -1) +    FMT_THROW(SystemError(errno, "cannot open file {}", path)); +} + +fmt::File::~File() FMT_NOEXCEPT { +  // Don't retry close in case of EINTR! +  // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html +  if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) +    fmt::report_system_error(errno, "cannot close file"); +} + +void fmt::File::close() { +  if (fd_ == -1) +    return; +  // Don't retry close in case of EINTR! +  // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html +  int result = FMT_POSIX_CALL(close(fd_)); +  fd_ = -1; +  if (result != 0) +    FMT_THROW(SystemError(errno, "cannot close file")); +} + +fmt::LongLong fmt::File::size() const { +#ifdef _WIN32 +  // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT +  // is less than 0x0500 as is the case with some default MinGW builds. +  // Both functions support large file sizes. +  DWORD size_upper = 0; +  HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_)); +  DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper)); +  if (size_lower == INVALID_FILE_SIZE) { +    DWORD error = GetLastError(); +    if (error != NO_ERROR) +      FMT_THROW(WindowsError(GetLastError(), "cannot get file size")); +  } +  fmt::ULongLong long_size = size_upper; +  return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; +#else +  typedef struct stat Stat; +  Stat file_stat = Stat(); +  if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) +    FMT_THROW(SystemError(errno, "cannot get file attributes")); +  FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size), +      "return type of File::size is not large enough"); +  return file_stat.st_size; +#endif +} + +std::size_t fmt::File::read(void *buffer, std::size_t count) { +  RWResult result = 0; +  FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); +  if (result < 0) +    FMT_THROW(SystemError(errno, "cannot read from file")); +  return internal::to_unsigned(result); +} + +std::size_t fmt::File::write(const void *buffer, std::size_t count) { +  RWResult result = 0; +  FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); +  if (result < 0) +    FMT_THROW(SystemError(errno, "cannot write to file")); +  return internal::to_unsigned(result); +} + +fmt::File fmt::File::dup(int fd) { +  // Don't retry as dup doesn't return EINTR. +  // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html +  int new_fd = FMT_POSIX_CALL(dup(fd)); +  if (new_fd == -1) +    FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {}", fd)); +  return File(new_fd); +} + +void fmt::File::dup2(int fd) { +  int result = 0; +  FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); +  if (result == -1) { +    FMT_THROW(SystemError(errno, +      "cannot duplicate file descriptor {} to {}", fd_, fd)); +  } +} + +void fmt::File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT { +  int result = 0; +  FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); +  if (result == -1) +    ec = ErrorCode(errno); +} + +void fmt::File::pipe(File &read_end, File &write_end) { +  // Close the descriptors first to make sure that assignments don't throw +  // and there are no leaks. +  read_end.close(); +  write_end.close(); +  int fds[2] = {}; +#ifdef _WIN32 +  // Make the default pipe capacity same as on Linux 2.6.11+. +  enum { DEFAULT_CAPACITY = 65536 }; +  int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); +#else +  // Don't retry as the pipe function doesn't return EINTR. +  // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html +  int result = FMT_POSIX_CALL(pipe(fds)); +#endif +  if (result != 0) +    FMT_THROW(SystemError(errno, "cannot create pipe")); +  // The following assignments don't throw because read_fd and write_fd +  // are closed. +  read_end = File(fds[0]); +  write_end = File(fds[1]); +} + +fmt::BufferedFile fmt::File::fdopen(const char *mode) { +  // Don't retry as fdopen doesn't return EINTR. +  FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode)); +  if (!f) +    FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor")); +  BufferedFile file(f); +  fd_ = -1; +  return file; +} + +long fmt::getpagesize() { +#ifdef _WIN32 +  SYSTEM_INFO si; +  GetSystemInfo(&si); +  return si.dwPageSize; +#else +  long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); +  if (size < 0) +    FMT_THROW(SystemError(errno, "cannot get memory page size")); +  return size; +#endif +} diff --git a/lib/spdlog/fmt/bundled/posix.h b/lib/spdlog/fmt/bundled/posix.h new file mode 100644 index 0000000..20af016 --- /dev/null +++ b/lib/spdlog/fmt/bundled/posix.h @@ -0,0 +1,423 @@ +/* + A C++ interface to POSIX functions. + + Copyright (c) 2012 - 2016, Victor Zverovich + All rights reserved. + + For the license information refer to format.h. + */ + +#ifndef FMT_POSIX_H_ +#define FMT_POSIX_H_ + +#if defined(__MINGW32__) || defined(__CYGWIN__) +// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/. +#undef __STRICT_ANSI__ +#endif + +#include <errno.h> +#include <fcntl.h>  // for O_RDONLY +#include <locale.h> // for locale_t +#include <stdio.h> +#include <stdlib.h> // for strtod_l + +#include <cstddef> + +#if defined __APPLE__ || defined(__FreeBSD__) +#include <xlocale.h> // for LC_NUMERIC_MASK on OS X +#endif + +#include "format.h" + +#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) + +namespace fmt { + +// An error code. +class ErrorCode +{ +private: +    int value_; + +public: +    explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {} + +    int get() const FMT_NOEXCEPT +    { +        return value_; +    } +}; + +// A buffered file. +class BufferedFile +{ +private: +    FILE *file_; + +    friend class File; + +    explicit BufferedFile(FILE *f) +        : file_(f) +    { +    } + +public: +    // Constructs a BufferedFile object which doesn't represent any file. +    BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {} + +    // Destroys the object closing the file it represents if any. +    FMT_API ~BufferedFile() FMT_NOEXCEPT; + +#if !FMT_USE_RVALUE_REFERENCES +    // Emulate a move constructor and a move assignment operator if rvalue +    // references are not supported. + +private: +    // A proxy object to emulate a move constructor. +    // It is private to make it impossible call operator Proxy directly. +    struct Proxy +    { +        FILE *file; +    }; + +public: +    // A "move constructor" for moving from a temporary. +    BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {} + +    // A "move constructor" for moving from an lvalue. +    BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) +    { +        f.file_ = FMT_NULL; +    } + +    // A "move assignment operator" for moving from a temporary. +    BufferedFile &operator=(Proxy p) +    { +        close(); +        file_ = p.file; +        return *this; +    } + +    // A "move assignment operator" for moving from an lvalue. +    BufferedFile &operator=(BufferedFile &other) +    { +        close(); +        file_ = other.file_; +        other.file_ = FMT_NULL; +        return *this; +    } + +    // Returns a proxy object for moving from a temporary: +    //   BufferedFile file = BufferedFile(...); +    operator Proxy() FMT_NOEXCEPT +    { +        Proxy p = {file_}; +        file_ = FMT_NULL; +        return p; +    } + +#else +private: +    FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile); + +public: +    BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) +    { +        other.file_ = FMT_NULL; +    } + +    BufferedFile &operator=(BufferedFile &&other) +    { +        close(); +        file_ = other.file_; +        other.file_ = FMT_NULL; +        return *this; +    } +#endif + +    // Opens a file. +    FMT_API BufferedFile(CStringRef filename, CStringRef mode); + +    // Closes the file. +    FMT_API void close(); + +    // Returns the pointer to a FILE object representing this file. +    FILE *get() const FMT_NOEXCEPT +    { +        return file_; +    } + +    // We place parentheses around fileno to workaround a bug in some versions +    // of MinGW that define fileno as a macro. +    FMT_API int(fileno)() const; + +    void print(CStringRef format_str, const ArgList &args) +    { +        fmt::print(file_, format_str, args); +    } +    FMT_VARIADIC(void, print, CStringRef) +}; + +// A file. Closed file is represented by a File object with descriptor -1. +// Methods that are not declared with FMT_NOEXCEPT may throw +// fmt::SystemError 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 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. +    }; + +    // Constructs a File object which doesn't represent any file. +    File() FMT_NOEXCEPT : fd_(-1) {} + +    // Opens a file and constructs a File object representing this file. +    FMT_API File(CStringRef path, int oflag); + +#if !FMT_USE_RVALUE_REFERENCES +    // Emulate a move constructor and a move assignment operator if rvalue +    // references are not supported. + +private: +    // A proxy object to emulate a move constructor. +    // It is private to make it impossible call operator Proxy directly. +    struct Proxy +    { +        int fd; +    }; + +public: +    // A "move constructor" for moving from a temporary. +    File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {} + +    // A "move constructor" for moving from an lvalue. +    File(File &other) FMT_NOEXCEPT : fd_(other.fd_) +    { +        other.fd_ = -1; +    } + +    // A "move assignment operator" for moving from a temporary. +    File &operator=(Proxy p) +    { +        close(); +        fd_ = p.fd; +        return *this; +    } + +    // A "move assignment operator" for moving from an lvalue. +    File &operator=(File &other) +    { +        close(); +        fd_ = other.fd_; +        other.fd_ = -1; +        return *this; +    } + +    // Returns a proxy object for moving from a temporary: +    //   File file = File(...); +    operator Proxy() FMT_NOEXCEPT +    { +        Proxy p = {fd_}; +        fd_ = -1; +        return p; +    } + +#else +private: +    FMT_DISALLOW_COPY_AND_ASSIGN(File); + +public: +    File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) +    { +        other.fd_ = -1; +    } + +    File &operator=(File &&other) +    { +        close(); +        fd_ = other.fd_; +        other.fd_ = -1; +        return *this; +    } +#endif + +    // Destroys the object closing the file it represents if any. +    FMT_API ~File() FMT_NOEXCEPT; + +    // Returns the file descriptor. +    int descriptor() const FMT_NOEXCEPT +    { +        return fd_; +    } + +    // Closes the file. +    FMT_API void close(); + +    // Returns the file size. The size has signed type for consistency with +    // stat::st_size. +    FMT_API LongLong size() const; + +    // Attempts to read count bytes from the file into the specified buffer. +    FMT_API std::size_t read(void *buffer, std::size_t count); + +    // Attempts to write count bytes from the specified buffer to the file. +    FMT_API std::size_t write(const void *buffer, std::size_t count); + +    // Duplicates a file descriptor with the dup function and returns +    // the duplicate as a file object. +    FMT_API static File dup(int fd); + +    // Makes fd be the copy of this file descriptor, closing fd first if +    // necessary. +    FMT_API void dup2(int fd); + +    // Makes fd be the copy of this file descriptor, closing fd first if +    // necessary. +    FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT; + +    // Creates a pipe setting up read_end and write_end file objects for reading +    // and writing respectively. +    FMT_API static void pipe(File &read_end, File &write_end); + +    // Creates a BufferedFile object associated with this file and detaches +    // this File object from the file. +    FMT_API BufferedFile fdopen(const char *mode); +}; + +// Returns the memory page size. +long getpagesize(); + +#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && !defined(__ANDROID__) && !defined(__CYGWIN__) +#define FMT_LOCALE +#endif + +#ifdef FMT_LOCALE +// A "C" numeric locale. +class Locale +{ +private: +#ifdef _MSC_VER +    typedef _locale_t locale_t; + +    enum +    { +        LC_NUMERIC_MASK = LC_NUMERIC +    }; + +    static locale_t newlocale(int category_mask, const char *locale, locale_t) +    { +        return _create_locale(category_mask, locale); +    } + +    static void freelocale(locale_t locale) +    { +        _free_locale(locale); +    } + +    static double strtod_l(const char *nptr, char **endptr, _locale_t locale) +    { +        return _strtod_l(nptr, endptr, locale); +    } +#endif + +    locale_t locale_; + +    FMT_DISALLOW_COPY_AND_ASSIGN(Locale); + +public: +    typedef locale_t Type; + +    Locale() +        : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) +    { +        if (!locale_) +            FMT_THROW(fmt::SystemError(errno, "cannot create locale")); +    } +    ~Locale() +    { +        freelocale(locale_); +    } + +    Type get() const +    { +        return locale_; +    } + +    // Converts string to floating-point number and advances str past the end +    // of the parsed input. +    double strtod(const char *&str) const +    { +        char *end = FMT_NULL; +        double result = strtod_l(str, &end, locale_); +        str = end; +        return result; +    } +}; +#endif // FMT_LOCALE +} // namespace fmt + +#if !FMT_USE_RVALUE_REFERENCES +namespace std { +// For compatibility with C++98. +inline fmt::BufferedFile &move(fmt::BufferedFile &f) +{ +    return f; +} +inline fmt::File &move(fmt::File &f) +{ +    return f; +} +} // namespace std +#endif + +#endif // FMT_POSIX_H_ diff --git a/lib/spdlog/fmt/bundled/printf.cc b/lib/spdlog/fmt/bundled/printf.cc new file mode 100644 index 0000000..95d7a36 --- /dev/null +++ b/lib/spdlog/fmt/bundled/printf.cc @@ -0,0 +1,32 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - 2016, Victor Zverovich + All rights reserved. + + For the license information refer to format.h. + */ + +#include "format.h" +#include "printf.h" + +namespace fmt { + +template <typename Char> +void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args); + +FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) { +  MemoryWriter w; +  printf(w, format, args); +  std::size_t size = w.size(); +  return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size); +} + +#ifndef FMT_HEADER_ONLY + +template void PrintfFormatter<char>::format(CStringRef format); +template void PrintfFormatter<wchar_t>::format(WCStringRef format); + +#endif  // FMT_HEADER_ONLY + +}  // namespace fmt diff --git a/lib/spdlog/fmt/bundled/printf.h b/lib/spdlog/fmt/bundled/printf.h new file mode 100644 index 0000000..679e8fc --- /dev/null +++ b/lib/spdlog/fmt/bundled/printf.h @@ -0,0 +1,723 @@ +/* + Formatting library for C++ + + 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::fill_n +#include <limits>    // std::numeric_limits + +#include "ostream.h" + +namespace fmt { +namespace internal { + +// Checks if a value fits in int - used to avoid warnings about comparing +// signed and unsigned integers. +template<bool IsSigned> +struct IntChecker +{ +    template<typename T> +    static bool fits_in_int(T value) +    { +        unsigned max = std::numeric_limits<int>::max(); +        return value <= max; +    } +    static bool fits_in_int(bool) +    { +        return true; +    } +}; + +template<> +struct IntChecker<true> +{ +    template<typename T> +    static bool fits_in_int(T value) +    { +        return value >= std::numeric_limits<int>::min() && value <= std::numeric_limits<int>::max(); +    } +    static bool fits_in_int(int) +    { +        return true; +    } +}; + +class PrecisionHandler : public ArgVisitor<PrecisionHandler, int> +{ +public: +    void report_unhandled_arg() +    { +        FMT_THROW(FormatError("precision is not integer")); +    } + +    template<typename T> +    int visit_any_int(T value) +    { +        if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) +            FMT_THROW(FormatError("number is too big")); +        return static_cast<int>(value); +    } +}; + +// IsZeroInt::visit(arg) returns true iff arg is a zero integer. +class IsZeroInt : public ArgVisitor<IsZeroInt, bool> +{ +public: +    template<typename T> +    bool visit_any_int(T value) +    { +        return value == 0; +    } +}; + +// returns the default type for format specific "%s" +class DefaultType : public ArgVisitor<DefaultType, char> +{ +public: +    char visit_char(int) +    { +        return 'c'; +    } + +    char visit_bool(bool) +    { +        return 's'; +    } + +    char visit_pointer(const void *) +    { +        return 'p'; +    } + +    template<typename T> +    char visit_any_int(T) +    { +        return 'd'; +    } + +    template<typename T> +    char visit_any_double(T) +    { +        return 'g'; +    } + +    char visit_unhandled_arg() +    { +        return 's'; +    } +}; + +template<typename T, typename U> +struct is_same +{ +    enum +    { +        value = 0 +    }; +}; + +template<typename T> +struct is_same<T, T> +{ +    enum +    { +        value = 1 +    }; +}; + +// An argument visitor that 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 = void> +class ArgConverter : public ArgVisitor<ArgConverter<T>, void> +{ +private: +    internal::Arg &arg_; +    wchar_t type_; + +    FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); + +public: +    ArgConverter(internal::Arg &arg, wchar_t type) +        : arg_(arg) +        , type_(type) +    { +    } + +    void visit_bool(bool value) +    { +        if (type_ != 's') +            visit_any_int(value); +    } + +    void visit_char(int value) +    { +        if (type_ != 's') +            visit_any_int(value); +    } + +    template<typename U> +    void visit_any_int(U value) +    { +        bool is_signed = type_ == 'd' || type_ == 'i'; +        if (type_ == 's') +        { +            is_signed = std::numeric_limits<U>::is_signed; +        } + +        using internal::Arg; +        typedef typename internal::Conditional<is_same<T, void>::value, U, T>::type TargetType; +        if (const_check(sizeof(TargetType) <= sizeof(int))) +        { +            // Extra casts are used to silence warnings. +            if (is_signed) +            { +                arg_.type = Arg::INT; +                arg_.int_value = static_cast<int>(static_cast<TargetType>(value)); +            } +            else +            { +                arg_.type = Arg::UINT; +                typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned; +                arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value)); +            } +        } +        else +        { +            if (is_signed) +            { +                arg_.type = Arg::LONG_LONG; +                // 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_.long_long_value = static_cast<LongLong>(value); +            } +            else +            { +                arg_.type = Arg::ULONG_LONG; +                arg_.ulong_long_value = static_cast<typename internal::MakeUnsigned<U>::Type>(value); +            } +        } +    } +}; + +// Converts an integer argument to char for printf. +class CharConverter : public ArgVisitor<CharConverter, void> +{ +private: +    internal::Arg &arg_; + +    FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); + +public: +    explicit CharConverter(internal::Arg &arg) +        : arg_(arg) +    { +    } + +    template<typename T> +    void visit_any_int(T value) +    { +        arg_.type = internal::Arg::CHAR; +        arg_.int_value = static_cast<char>(value); +    } +}; + +// Checks if an argument is a valid printf width specifier and sets +// left alignment if it is negative. +class WidthHandler : public ArgVisitor<WidthHandler, unsigned> +{ +private: +    FormatSpec &spec_; + +    FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); + +public: +    explicit WidthHandler(FormatSpec &spec) +        : spec_(spec) +    { +    } + +    void report_unhandled_arg() +    { +        FMT_THROW(FormatError("width is not integer")); +    } + +    template<typename T> +    unsigned visit_any_int(T value) +    { +        typedef typename internal::IntTraits<T>::MainType UnsignedType; +        UnsignedType width = static_cast<UnsignedType>(value); +        if (internal::is_negative(value)) +        { +            spec_.align_ = ALIGN_LEFT; +            width = 0 - width; +        } +        unsigned int_max = std::numeric_limits<int>::max(); +        if (width > int_max) +            FMT_THROW(FormatError("number is too big")); +        return static_cast<unsigned>(width); +    } +}; +} // namespace internal + +/** +  \rst +  A ``printf`` argument formatter based on the `curiously recurring template +  pattern <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_. + +  To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some +  or all of the visit methods with the same signatures as the methods in +  `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`. +  Pass the subclass as the *Impl* template parameter. When a formatting +  function processes an argument, it will dispatch to a visit method +  specific to the argument type. For example, if the argument type is +  ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass +  will be called. If the subclass doesn't contain a method with this signature, +  then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its +  superclass will be called. +  \endrst + */ +template<typename Impl, typename Char, typename Spec> +class BasicPrintfArgFormatter : public internal::ArgFormatterBase<Impl, Char, Spec> +{ +private: +    void write_null_pointer() +    { +        this->spec().type_ = 0; +        this->write("(nil)"); +    } + +    typedef internal::ArgFormatterBase<Impl, Char, Spec> Base; + +public: +    /** +      \rst +      Constructs an argument formatter object. +      *writer* is a reference to the output writer and *spec* contains format +      specifier information for standard argument types. +      \endrst +     */ +    BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s) +        : internal::ArgFormatterBase<Impl, Char, Spec>(w, s) +    { +    } + +    /** Formats an argument of type ``bool``. */ +    void visit_bool(bool value) +    { +        Spec &fmt_spec = this->spec(); +        if (fmt_spec.type_ != 's') +            return this->visit_any_int(value); +        fmt_spec.type_ = 0; +        this->write(value); +    } + +    /** Formats a character. */ +    void visit_char(int value) +    { +        const Spec &fmt_spec = this->spec(); +        BasicWriter<Char> &w = this->writer(); +        if (fmt_spec.type_ && fmt_spec.type_ != 'c') +            w.write_int(value, fmt_spec); +        typedef typename BasicWriter<Char>::CharPtr CharPtr; +        CharPtr out = CharPtr(); +        if (fmt_spec.width_ > 1) +        { +            Char fill = ' '; +            out = w.grow_buffer(fmt_spec.width_); +            if (fmt_spec.align_ != ALIGN_LEFT) +            { +                std::fill_n(out, fmt_spec.width_ - 1, fill); +                out += fmt_spec.width_ - 1; +            } +            else +            { +                std::fill_n(out + 1, fmt_spec.width_ - 1, fill); +            } +        } +        else +        { +            out = w.grow_buffer(1); +        } +        *out = static_cast<Char>(value); +    } + +    /** Formats a null-terminated C string. */ +    void visit_cstring(const char *value) +    { +        if (value) +            Base::visit_cstring(value); +        else if (this->spec().type_ == 'p') +            write_null_pointer(); +        else +            this->write("(null)"); +    } + +    /** Formats a pointer. */ +    void visit_pointer(const void *value) +    { +        if (value) +            return Base::visit_pointer(value); +        this->spec().type_ = 0; +        write_null_pointer(); +    } + +    /** Formats an argument of a custom (user-defined) type. */ +    void visit_custom(internal::Arg::CustomValue c) +    { +        BasicFormatter<Char> formatter(ArgList(), this->writer()); +        const Char format_str[] = {'}', 0}; +        const Char *format = format_str; +        c.format(&formatter, c.value, &format); +    } +}; + +/** The default printf argument formatter. */ +template<typename Char> +class PrintfArgFormatter : public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec> +{ +public: +    /** Constructs an argument formatter object. */ +    PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s) +        : BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>(w, s) +    { +    } +}; + +/** This template formats data and writes the output to a writer. */ +template<typename Char, typename ArgFormatter = PrintfArgFormatter<Char>> +class PrintfFormatter : private internal::FormatterBase +{ +private: +    BasicWriter<Char> &writer_; + +    void parse_flags(FormatSpec &spec, const Char *&s); + +    // Returns the argument with specified index or, if arg_index is equal +    // to the maximum unsigned value, the next argument. +    internal::Arg get_arg(const Char *s, unsigned arg_index = (std::numeric_limits<unsigned>::max)()); + +    // Parses argument index, flags and width and returns the argument index. +    unsigned parse_header(const Char *&s, FormatSpec &spec); + +public: +    /** +     \rst +     Constructs a ``PrintfFormatter`` object. References to the arguments and +     the writer are stored in the formatter object so make sure they have +     appropriate lifetimes. +     \endrst +     */ +    explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w) +        : FormatterBase(al) +        , writer_(w) +    { +    } + +    /** Formats stored arguments and writes the output to the writer. */ +    void format(BasicCStringRef<Char> format_str); +}; + +template<typename Char, typename AF> +void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s) +{ +    for (;;) +    { +        switch (*s++) +        { +        case '-': +            spec.align_ = ALIGN_LEFT; +            break; +        case '+': +            spec.flags_ |= SIGN_FLAG | PLUS_FLAG; +            break; +        case '0': +            spec.fill_ = '0'; +            break; +        case ' ': +            spec.flags_ |= SIGN_FLAG; +            break; +        case '#': +            spec.flags_ |= HASH_FLAG; +            break; +        default: +            --s; +            return; +        } +    } +} + +template<typename Char, typename AF> +internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s, unsigned arg_index) +{ +    (void)s; +    const char *error = FMT_NULL; +    internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ? next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); +    if (error) +        FMT_THROW(FormatError(!*s ? "invalid format string" : error)); +    return arg; +} + +template<typename Char, typename AF> +unsigned PrintfFormatter<Char, AF>::parse_header(const Char *&s, FormatSpec &spec) +{ +    unsigned arg_index = std::numeric_limits<unsigned>::max(); +    Char c = *s; +    if (c >= '0' && c <= '9') +    { +        // Parse an argument index (if followed by '$') or a width possibly +        // preceded with '0' flag(s). +        unsigned value = internal::parse_nonnegative_int(s); +        if (*s == '$') // value is an argument index +        { +            ++s; +            arg_index = value; +        } +        else +        { +            if (c == '0') +                spec.fill_ = '0'; +            if (value != 0) +            { +                // Nonzero value means that we parsed width and don't need to +                // parse it or flags again, so return now. +                spec.width_ = value; +                return arg_index; +            } +        } +    } +    parse_flags(spec, s); +    // Parse width. +    if (*s >= '0' && *s <= '9') +    { +        spec.width_ = internal::parse_nonnegative_int(s); +    } +    else if (*s == '*') +    { +        ++s; +        spec.width_ = internal::WidthHandler(spec).visit(get_arg(s)); +    } +    return arg_index; +} + +template<typename Char, typename AF> +void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str) +{ +    const Char *start = format_str.c_str(); +    const Char *s = start; +    while (*s) +    { +        Char c = *s++; +        if (c != '%') +            continue; +        if (*s == c) +        { +            write(writer_, start, s); +            start = ++s; +            continue; +        } +        write(writer_, start, s - 1); + +        FormatSpec spec; +        spec.align_ = ALIGN_RIGHT; + +        // Parse argument index, flags and width. +        unsigned arg_index = parse_header(s, spec); + +        // Parse precision. +        if (*s == '.') +        { +            ++s; +            if ('0' <= *s && *s <= '9') +            { +                spec.precision_ = static_cast<int>(internal::parse_nonnegative_int(s)); +            } +            else if (*s == '*') +            { +                ++s; +                spec.precision_ = internal::PrecisionHandler().visit(get_arg(s)); +            } +            else +            { +                spec.precision_ = 0; +            } +        } + +        using internal::Arg; +        Arg arg = get_arg(s, arg_index); +        if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg)) +            spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG); +        if (spec.fill_ == '0') +        { +            if (arg.type <= Arg::LAST_NUMERIC_TYPE) +                spec.align_ = ALIGN_NUMERIC; +            else +                spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. +        } + +        // Parse length and convert the argument to the required type. +        using internal::ArgConverter; +        switch (*s++) +        { +        case 'h': +            if (*s == 'h') +                ArgConverter<signed char>(arg, *++s).visit(arg); +            else +                ArgConverter<short>(arg, *s).visit(arg); +            break; +        case 'l': +            if (*s == 'l') +                ArgConverter<fmt::LongLong>(arg, *++s).visit(arg); +            else +                ArgConverter<long>(arg, *s).visit(arg); +            break; +        case 'j': +            ArgConverter<intmax_t>(arg, *s).visit(arg); +            break; +        case 'z': +            ArgConverter<std::size_t>(arg, *s).visit(arg); +            break; +        case 't': +            ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg); +            break; +        case 'L': +            // printf produces garbage when 'L' is omitted for long double, no +            // need to do the same. +            break; +        default: +            --s; +            ArgConverter<void>(arg, *s).visit(arg); +        } + +        // Parse type. +        if (!*s) +            FMT_THROW(FormatError("invalid format string")); +        spec.type_ = static_cast<char>(*s++); + +        if (spec.type_ == 's') +        { +            // set the format type to the default if 's' is specified +            spec.type_ = internal::DefaultType().visit(arg); +        } + +        if (arg.type <= Arg::LAST_INTEGER_TYPE) +        { +            // Normalize type. +            switch (spec.type_) +            { +            case 'i': +            case 'u': +                spec.type_ = 'd'; +                break; +            case 'c': +                // TODO: handle wchar_t +                internal::CharConverter(arg).visit(arg); +                break; +            } +        } + +        start = s; + +        // Format argument. +        AF(writer_, spec).visit(arg); +    } +    write(writer_, start, s); +} + +inline void printf(Writer &w, CStringRef format, ArgList args) +{ +    PrintfFormatter<char>(args, w).format(format); +} +FMT_VARIADIC(void, printf, Writer &, CStringRef) + +inline void printf(WWriter &w, WCStringRef format, ArgList args) +{ +    PrintfFormatter<wchar_t>(args, w).format(format); +} +FMT_VARIADIC(void, printf, WWriter &, WCStringRef) + +/** +  \rst +  Formats arguments and returns the result as a string. + +  **Example**:: + +    std::string message = fmt::sprintf("The answer is %d", 42); +  \endrst +*/ +inline std::string sprintf(CStringRef format, ArgList args) +{ +    MemoryWriter w; +    printf(w, format, args); +    return w.str(); +} +FMT_VARIADIC(std::string, sprintf, CStringRef) + +inline std::wstring sprintf(WCStringRef format, ArgList args) +{ +    WMemoryWriter w; +    printf(w, format, args); +    return w.str(); +} +FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef) + +/** +  \rst +  Prints formatted data to the file *f*. + +  **Example**:: + +    fmt::fprintf(stderr, "Don't %s!", "panic"); +  \endrst + */ +FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args); +FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef) + +/** +  \rst +  Prints formatted data to ``stdout``. + +  **Example**:: + +    fmt::printf("Elapsed time: %.2f seconds", 1.23); +  \endrst + */ +inline int printf(CStringRef format, ArgList args) +{ +    return fprintf(stdout, format, args); +} +FMT_VARIADIC(int, printf, CStringRef) + +/** +  \rst +  Prints formatted data to the stream *os*. + +  **Example**:: + +    fprintf(cerr, "Don't %s!", "panic"); +  \endrst + */ +inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args) +{ +    MemoryWriter w; +    printf(w, format_str, args); +    internal::write(os, w); +    return static_cast<int>(w.size()); +} +FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef) +} // namespace fmt + +#ifdef FMT_HEADER_ONLY +#include "printf.cc" +#endif + +#endif // FMT_PRINTF_H_ diff --git a/lib/spdlog/fmt/bundled/time.h b/lib/spdlog/fmt/bundled/time.h new file mode 100644 index 0000000..1d4210c --- /dev/null +++ b/lib/spdlog/fmt/bundled/time.h @@ -0,0 +1,188 @@ +/* + Formatting library for C++ - time formatting + + Copyright (c) 2012 - 2016, Victor Zverovich + All rights reserved. + + For the license information refer to format.h. + */ + +#ifndef FMT_TIME_H_ +#define FMT_TIME_H_ + +#include "format.h" +#include <ctime> + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4702) // unreachable code +#pragma warning(disable : 4996) // "deprecated" functions +#endif + +namespace fmt { +template<typename ArgFormatter> +void format_arg(BasicFormatter<char, ArgFormatter> &f, const char *&format_str, const std::tm &tm) +{ +    if (*format_str == ':') +        ++format_str; +    const char *end = format_str; +    while (*end && *end != '}') +        ++end; +    if (*end != '}') +        FMT_THROW(FormatError("missing '}' in format string")); +    internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format; +    format.append(format_str, end + 1); +    format[format.size() - 1] = '\0'; +    Buffer<char> &buffer = f.writer().buffer(); +    std::size_t start = buffer.size(); +    for (;;) +    { +        std::size_t size = buffer.capacity() - start; +        std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm); +        if (count != 0) +        { +            buffer.resize(start + count); +            break; +        } +        if (size >= format.size() * 256) +        { +            // If the buffer is 256 times larger than the format string, assume +            // that `strftime` gives an empty result. There doesn't seem to be a +            // better way to distinguish the two cases: +            // https://github.com/fmtlib/fmt/issues/367 +            break; +        } +        const std::size_t MIN_GROWTH = 10; +        buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); +    } +    format_str = end + 1; +} + +namespace internal { +inline Null<> localtime_r(...) +{ +    return Null<>(); +} +inline Null<> localtime_s(...) +{ +    return Null<>(); +} +inline Null<> gmtime_r(...) +{ +    return Null<>(); +} +inline Null<> gmtime_s(...) +{ +    return Null<>(); +} +} // namespace internal + +// Thread-safe replacement for std::localtime +inline std::tm localtime(std::time_t time) +{ +    struct LocalTime +    { +        std::time_t time_; +        std::tm tm_; + +        LocalTime(std::time_t t) +            : time_(t) +        { +        } + +        bool run() +        { +            using namespace fmt::internal; +            return handle(localtime_r(&time_, &tm_)); +        } + +        bool handle(std::tm *tm) +        { +            return tm != FMT_NULL; +        } + +        bool handle(internal::Null<>) +        { +            using namespace fmt::internal; +            return fallback(localtime_s(&tm_, &time_)); +        } + +        bool fallback(int res) +        { +            return res == 0; +        } + +        bool fallback(internal::Null<>) +        { +            using namespace fmt::internal; +            std::tm *tm = std::localtime(&time_); +            if (tm) +                tm_ = *tm; +            return tm != FMT_NULL; +        } +    }; +    LocalTime lt(time); +    if (lt.run()) +        return lt.tm_; +    // Too big time values may be unsupported. +    FMT_THROW(fmt::FormatError("time_t value out of range")); +    return std::tm(); +} + +// Thread-safe replacement for std::gmtime +inline std::tm gmtime(std::time_t time) +{ +    struct GMTime +    { +        std::time_t time_; +        std::tm tm_; + +        GMTime(std::time_t t) +            : time_(t) +        { +        } + +        bool run() +        { +            using namespace fmt::internal; +            return handle(gmtime_r(&time_, &tm_)); +        } + +        bool handle(std::tm *tm) +        { +            return tm != FMT_NULL; +        } + +        bool handle(internal::Null<>) +        { +            using namespace fmt::internal; +            return fallback(gmtime_s(&tm_, &time_)); +        } + +        bool fallback(int res) +        { +            return res == 0; +        } + +        bool fallback(internal::Null<>) +        { +            std::tm *tm = std::gmtime(&time_); +            if (tm != FMT_NULL) +                tm_ = *tm; +            return tm != FMT_NULL; +        } +    }; +    GMTime gt(time); +    if (gt.run()) +        return gt.tm_; +    // Too big time values may be unsupported. +    FMT_THROW(fmt::FormatError("time_t value out of range")); +    return std::tm(); +} +} // namespace fmt + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif // FMT_TIME_H_ diff --git a/lib/spdlog/fmt/fmt.h b/lib/spdlog/fmt/fmt.h new file mode 100644 index 0000000..e2e0485 --- /dev/null +++ b/lib/spdlog/fmt/fmt.h @@ -0,0 +1,33 @@ +// +// Copyright(c) 2016 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_FMT_EXTERNAL) + +#ifndef FMT_HEADER_ONLY +#define FMT_HEADER_ONLY +#endif +#ifndef FMT_USE_WINDOWS_H +#define FMT_USE_WINDOWS_H 0 +#endif +#include "bundled/format.h" +#if defined(SPDLOG_FMT_PRINTF) +#include "bundled/printf.h" +#endif + +#else // external fmtlib + +#include <fmt/format.h> +#if defined(SPDLOG_FMT_PRINTF) +#include <fmt/printf.h> +#endif + +#endif diff --git a/lib/spdlog/fmt/ostr.h b/lib/spdlog/fmt/ostr.h new file mode 100644 index 0000000..9902898 --- /dev/null +++ b/lib/spdlog/fmt/ostr.h @@ -0,0 +1,18 @@ +// +// 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_FMT_EXTERNAL) +#ifndef FMT_HEADER_ONLY +#define FMT_HEADER_ONLY +#endif +#include "bundled/ostream.h" +#include "fmt.h" +#else +#include <fmt/ostream.h> +#endif diff --git a/lib/spdlog/formatter.h b/lib/spdlog/formatter.h new file mode 100644 index 0000000..55388ec --- /dev/null +++ b/lib/spdlog/formatter.h @@ -0,0 +1,46 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "details/log_msg.h" + +#include <memory> +#include <string> +#include <vector> + +namespace spdlog { +namespace details { +class flag_formatter; +} + +class formatter +{ +public: +    virtual ~formatter() = default; +    virtual void format(details::log_msg &msg) = 0; +}; + +class pattern_formatter SPDLOG_FINAL : public formatter +{ +public: +    explicit pattern_formatter(const std::string &pattern, pattern_time_type pattern_time = pattern_time_type::local, +        std::string eol = spdlog::details::os::default_eol); +    pattern_formatter(const pattern_formatter &) = delete; +    pattern_formatter &operator=(const pattern_formatter &) = delete; +    void format(details::log_msg &msg) override; + +private: +    const std::string _eol; +    const std::string _pattern; +    const pattern_time_type _pattern_time; +    std::vector<std::unique_ptr<details::flag_formatter>> _formatters; +    std::tm get_time(details::log_msg &msg); +    void handle_flag(char flag); +    void compile_pattern(const std::string &pattern); +}; +} // namespace spdlog + +#include "details/pattern_formatter_impl.h" diff --git a/lib/spdlog/logger.h b/lib/spdlog/logger.h new file mode 100644 index 0000000..aa4a2c5 --- /dev/null +++ b/lib/spdlog/logger.h @@ -0,0 +1,152 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +// Thread safe logger (except for set_pattern(..), set_formatter(..) and 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 +// 2. Format the message using the formatter function +// 3. Pass the formatted message to its sinks to performa the actual logging + +#include "common.h" +#include "sinks/base_sink.h" + +#include <memory> +#include <string> +#include <vector> + +namespace spdlog { + +class logger +{ +public: +    logger(const std::string &name, sink_ptr single_sink); +    logger(const std::string &name, sinks_init_list sinks); + +    template<class It> +    logger(std::string name, const It &begin, const It &end); + +    virtual ~logger(); + +    logger(const logger &) = delete; +    logger &operator=(const logger &) = delete; + +    template<typename... Args> +    void log(level::level_enum lvl, const char *fmt, const Args &... args); + +    template<typename... Args> +    void log(level::level_enum lvl, const char *msg); + +    template<typename Arg1, typename... Args> +    void trace(const char *fmt, const Arg1 &, const Args &... args); + +    template<typename Arg1, typename... Args> +    void debug(const char *fmt, const Arg1 &, const Args &... args); + +    template<typename Arg1, typename... Args> +    void info(const char *fmt, const Arg1 &, const Args &... args); + +    template<typename Arg1, typename... Args> +    void warn(const char *fmt, const Arg1 &, const Args &... args); + +    template<typename Arg1, typename... Args> +    void error(const char *fmt, const Arg1 &, const Args &... args); + +    template<typename Arg1, typename... Args> +    void critical(const char *fmt, const Arg1 &, const Args &... args); + +#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT +    template<typename... Args> +    void log(level::level_enum lvl, const wchar_t *msg); + +    template<typename... Args> +    void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args); + +    template<typename... Args> +    void trace(const wchar_t *fmt, const Args &... args); + +    template<typename... Args> +    void debug(const wchar_t *fmt, const Args &... args); + +    template<typename... Args> +    void info(const wchar_t *fmt, const Args &... args); + +    template<typename... Args> +    void warn(const wchar_t *fmt, const Args &... args); + +    template<typename... Args> +    void error(const wchar_t *fmt, const Args &... args); + +    template<typename... Args> +    void critical(const wchar_t *fmt, const Args &... args); +#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT + +    template<typename T> +    void log(level::level_enum lvl, const T &); + +    template<typename T> +    void trace(const T &msg); + +    template<typename T> +    void debug(const T &msg); + +    template<typename T> +    void info(const T &msg); + +    template<typename T> +    void warn(const T &msg); + +    template<typename T> +    void error(const T &msg); + +    template<typename T> +    void critical(const T &msg); + +    bool should_log(level::level_enum msg_level) const; +    void set_level(level::level_enum log_level); +    level::level_enum level() const; +    const std::string &name() const; +    void set_pattern(const std::string &pattern, pattern_time_type pattern_time = pattern_time_type::local); +    void set_formatter(formatter_ptr msg_formatter); + +    // automatically call flush() if message level >= log_level +    void flush_on(level::level_enum log_level); + +    virtual void flush(); + +    const std::vector<sink_ptr> &sinks() const; + +    // error handler +    virtual void set_error_handler(log_err_handler err_handler); +    virtual log_err_handler error_handler(); + +protected: +    virtual void _sink_it(details::log_msg &msg); +    virtual void _set_pattern(const std::string &pattern, pattern_time_type pattern_time); +    virtual void _set_formatter(formatter_ptr msg_formatter); + +    // default error handler: print the error to stderr with the max rate of 1 message/minute +    virtual void _default_err_handler(const std::string &msg); + +    // return true if the given message level should trigger a flush +    bool _should_flush_on(const details::log_msg &msg); + +    // increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)) +    void _incr_msg_counter(details::log_msg &msg); + +    const std::string _name; +    std::vector<sink_ptr> _sinks; +    formatter_ptr _formatter; +    spdlog::level_t _level; +    spdlog::level_t _flush_level; +    log_err_handler _err_handler; +    std::atomic<time_t> _last_err_time; +    std::atomic<size_t> _msg_counter; +}; +} // namespace spdlog + +#include "details/logger_impl.h" diff --git a/lib/spdlog/sinks/android_sink.h b/lib/spdlog/sinks/android_sink.h new file mode 100644 index 0000000..dd81163 --- /dev/null +++ b/lib/spdlog/sinks/android_sink.h @@ -0,0 +1,91 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#if defined(__ANDROID__) + +#include "../details/os.h" +#include "sink.h" + +#include <android/log.h> +#include <chrono> +#include <mutex> +#include <string> +#include <thread> + +#if !defined(SPDLOG_ANDROID_RETRIES) +#define SPDLOG_ANDROID_RETRIES 2 +#endif + +namespace spdlog { +namespace sinks { + +/* + * Android sink (logging using __android_log_write) + * __android_log_write is thread-safe. No lock is needed. + */ +class android_sink : public sink +{ +public: +    explicit android_sink(const std::string &tag = "spdlog", bool use_raw_msg = false) +        : _tag(tag) +        , _use_raw_msg(use_raw_msg) +    { +    } + +    void log(const details::log_msg &msg) override +    { +        const android_LogPriority priority = convert_to_android(msg.level); +        const char *msg_output = (_use_raw_msg ? msg.raw.c_str() : msg.formatted.c_str()); + +        // See system/core/liblog/logger_write.c for explanation of return value +        int ret = __android_log_write(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_write(priority, _tag.c_str(), msg_output); +            retry_count++; +        } + +        if (ret < 0) +        { +            throw spdlog_ex("__android_log_write() failed", ret); +        } +    } + +    void flush() override {} + +private: +    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; +}; + +} // namespace sinks +} // namespace spdlog + +#endif diff --git a/lib/spdlog/sinks/ansicolor_sink.h b/lib/spdlog/sinks/ansicolor_sink.h new file mode 100644 index 0000000..e74389c --- /dev/null +++ b/lib/spdlog/sinks/ansicolor_sink.h @@ -0,0 +1,150 @@ +// +// Copyright(c) 2017 spdlog authors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "../common.h" +#include "../details/os.h" +#include "base_sink.h" + +#include <string> +#include <unordered_map> + +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<class Mutex> +class ansicolor_sink : public base_sink<Mutex> +{ +public: +    explicit ansicolor_sink(FILE *file) +        : target_file_(file) +    { +        should_do_colors_ = details::os::in_terminal(file) && details::os::is_color_terminal(); +        colors_[level::trace] = white; +        colors_[level::debug] = cyan; +        colors_[level::info] = green; +        colors_[level::warn] = yellow + bold; +        colors_[level::err] = red + bold; +        colors_[level::critical] = bold + on_red; +        colors_[level::off] = reset; +    } + +    ~ansicolor_sink() override +    { +        _flush(); +    } + +    void set_color(level::level_enum color_level, const std::string &color) +    { +        std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex); +        colors_[color_level] = color; +    } + +    /// Formatting codes +    const std::string reset = "\033[m"; +    const std::string bold = "\033[1m"; +    const std::string dark = "\033[2m"; +    const std::string underline = "\033[4m"; +    const std::string blink = "\033[5m"; +    const std::string reverse = "\033[7m"; +    const std::string concealed = "\033[8m"; +    const std::string clear_line = "\033[K"; + +    // Foreground colors +    const std::string black = "\033[30m"; +    const std::string red = "\033[31m"; +    const std::string green = "\033[32m"; +    const std::string yellow = "\033[33m"; +    const std::string blue = "\033[34m"; +    const std::string magenta = "\033[35m"; +    const std::string cyan = "\033[36m"; +    const std::string white = "\033[37m"; + +    /// Background colors +    const std::string on_black = "\033[40m"; +    const std::string on_red = "\033[41m"; +    const std::string on_green = "\033[42m"; +    const std::string on_yellow = "\033[43m"; +    const std::string on_blue = "\033[44m"; +    const std::string on_magenta = "\033[45m"; +    const std::string on_cyan = "\033[46m"; +    const std::string on_white = "\033[47m"; + +protected: +    void _sink_it(const details::log_msg &msg) override +    { +        // Wrap the originally formatted message in color codes. +        // If color is not supported in the terminal, log as is instead. +        if (should_do_colors_ && msg.color_range_end > msg.color_range_start) +        { +            // before color range +            _print_range(msg, 0, msg.color_range_start); +            // in color range +            _print_ccode(colors_[msg.level]); +            _print_range(msg, msg.color_range_start, msg.color_range_end); +            _print_ccode(reset); +            // after color range +            _print_range(msg, msg.color_range_end, msg.formatted.size()); +        } +        else +        { +            _print_range(msg, 0, msg.formatted.size()); +        } +        _flush(); +    } + +    void _flush() override +    { +        fflush(target_file_); +    } + +private: +    void _print_ccode(const std::string &color_code) +    { +        fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_); +    } +    void _print_range(const details::log_msg &msg, size_t start, size_t end) +    { +        fwrite(msg.formatted.data() + start, sizeof(char), end - start, target_file_); +    } +    FILE *target_file_; +    bool should_do_colors_; +    std::unordered_map<level::level_enum, std::string, level::level_hasher> colors_; +}; + +template<class Mutex> +class ansicolor_stdout_sink : public ansicolor_sink<Mutex> +{ +public: +    ansicolor_stdout_sink() +        : ansicolor_sink<Mutex>(stdout) +    { +    } +}; + +using ansicolor_stdout_sink_mt = ansicolor_stdout_sink<std::mutex>; +using ansicolor_stdout_sink_st = ansicolor_stdout_sink<details::null_mutex>; + +template<class Mutex> +class ansicolor_stderr_sink : public ansicolor_sink<Mutex> +{ +public: +    ansicolor_stderr_sink() +        : ansicolor_sink<Mutex>(stderr) +    { +    } +}; + +using ansicolor_stderr_sink_mt = ansicolor_stderr_sink<std::mutex>; +using ansicolor_stderr_sink_st = ansicolor_stderr_sink<details::null_mutex>; + +} // namespace sinks +} // namespace spdlog diff --git a/lib/spdlog/sinks/base_sink.h b/lib/spdlog/sinks/base_sink.h new file mode 100644 index 0000000..96cd001 --- /dev/null +++ b/lib/spdlog/sinks/base_sink.h @@ -0,0 +1,49 @@ +// +// Copyright(c) 2015 Gabi Melman. +// 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 only override the _sink_it method. +// all locking is taken care of here so no locking needed by the implementers.. +// + +#include "../common.h" +#include "../details/log_msg.h" +#include "../formatter.h" +#include "sink.h" + +#include <mutex> + +namespace spdlog { +namespace sinks { +template<class Mutex> +class base_sink : public sink +{ +public: +    base_sink() = default; + +    base_sink(const base_sink &) = delete; +    base_sink &operator=(const base_sink &) = delete; + +    void log(const details::log_msg &msg) SPDLOG_FINAL override +    { +        std::lock_guard<Mutex> lock(_mutex); +        _sink_it(msg); +    } + +    void flush() SPDLOG_FINAL override +    { +        std::lock_guard<Mutex> lock(_mutex); +        _flush(); +    } + +protected: +    virtual void _sink_it(const details::log_msg &msg) = 0; +    virtual void _flush() = 0; +    Mutex _mutex; +}; +} // namespace sinks +} // namespace spdlog diff --git a/lib/spdlog/sinks/dist_sink.h b/lib/spdlog/sinks/dist_sink.h new file mode 100644 index 0000000..b4a7b6a --- /dev/null +++ b/lib/spdlog/sinks/dist_sink.h @@ -0,0 +1,77 @@ +// +// Copyright (c) 2015 David Schury, Gabi Melman +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "../details/log_msg.h" +#include "../details/null_mutex.h" +#include "base_sink.h" +#include "sink.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<class Mutex> +class dist_sink : public base_sink<Mutex> +{ +public: +    explicit dist_sink() +        : _sinks() +    { +    } +    dist_sink(const dist_sink &) = delete; +    dist_sink &operator=(const dist_sink &) = delete; + +protected: +    std::vector<std::shared_ptr<sink>> _sinks; + +    void _sink_it(const details::log_msg &msg) override +    { +        for (auto &sink : _sinks) +        { +            if (sink->should_log(msg.level)) +            { +                sink->log(msg); +            } +        } +    } + +    void _flush() override +    { +        for (auto &sink : _sinks) +            sink->flush(); +    } + +public: +    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 remove_all_sinks() +    { +        std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex); +        _sinks.clear(); +    } +}; + +using dist_sink_mt = dist_sink<std::mutex>; +using dist_sink_st = dist_sink<details::null_mutex>; + +} // namespace sinks +} // namespace spdlog diff --git a/lib/spdlog/sinks/file_sinks.h b/lib/spdlog/sinks/file_sinks.h new file mode 100644 index 0000000..109c493 --- /dev/null +++ b/lib/spdlog/sinks/file_sinks.h @@ -0,0 +1,255 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "../details/file_helper.h" +#include "../details/null_mutex.h" +#include "../fmt/fmt.h" +#include "base_sink.h" + +#include <algorithm> +#include <cerrno> +#include <chrono> +#include <cstdio> +#include <ctime> +#include <mutex> +#include <string> + +namespace spdlog { +namespace sinks { +/* + * Trivial file sink with single file as target + */ +template<class Mutex> +class simple_file_sink SPDLOG_FINAL : public base_sink<Mutex> +{ +public: +    explicit simple_file_sink(const filename_t &filename, bool truncate = false) +        : _force_flush(false) +    { +        _file_helper.open(filename, truncate); +    } + +    void set_force_flush(bool force_flush) +    { +        _force_flush = force_flush; +    } + +protected: +    void _sink_it(const details::log_msg &msg) override +    { +        _file_helper.write(msg); +        if (_force_flush) +        { +            _file_helper.flush(); +        } +    } + +    void _flush() override +    { +        _file_helper.flush(); +    } + +private: +    details::file_helper _file_helper; +    bool _force_flush; +}; + +using simple_file_sink_mt = simple_file_sink<std::mutex>; +using simple_file_sink_st = simple_file_sink<details::null_mutex>; + +/* + * Rotating file sink based on size + */ +template<class Mutex> +class rotating_file_sink SPDLOG_FINAL : public base_sink<Mutex> +{ +public: +    rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files) +        : _base_filename(std::move(base_filename)) +        , _max_size(max_size) +        , _max_files(max_files) +    { +        _file_helper.open(calc_filename(_base_filename, 0)); +        _current_size = _file_helper.size(); // expensive. called only once +    } + +    // calc filename according to index and file extension if exists. +    // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt". +    static filename_t calc_filename(const filename_t &filename, std::size_t index) +    { +        typename std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; +        if (index != 0u) +        { +            filename_t basename, ext; +            std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); +            w.write(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext); +        } +        else +        { +            w.write(SPDLOG_FILENAME_T("{}"), filename); +        } +        return w.str(); +    } + +protected: +    void _sink_it(const details::log_msg &msg) override +    { +        _current_size += msg.formatted.size(); +        if (_current_size > _max_size) +        { +            _rotate(); +            _current_size = msg.formatted.size(); +        } +        _file_helper.write(msg); +    } + +    void _flush() override +    { +        _file_helper.flush(); +    } + +private: +    // Rotate files: +    // log.txt -> log.1.txt +    // log.1.txt -> log.2.txt +    // log.2.txt -> log.3.txt +    // log.3.txt -> delete +    void _rotate() +    { +        using details::os::filename_to_str; +        _file_helper.close(); +        for (auto i = _max_files; i > 0; --i) +        { +            filename_t src = calc_filename(_base_filename, i - 1); +            filename_t target = calc_filename(_base_filename, i); + +            if (details::file_helper::file_exists(target)) +            { +                if (details::os::remove(target) != 0) +                { +                    throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno); +                } +            } +            if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0) +            { +                throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno); +            } +        } +        _file_helper.reopen(true); +    } + +    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>; + +/* + * Default generator of daily log file names. + */ +struct default_daily_file_name_calculator +{ +    // Create filename for the form filename.YYYY-MM-DD_hh-mm.ext +    static filename_t calc_filename(const filename_t &filename) +    { +        std::tm tm = spdlog::details::os::localtime(); +        filename_t basename, ext; +        std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); +        std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; +        w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, +            tm.tm_hour, tm.tm_min, ext); +        return w.str(); +    } +}; + +/* + * Generator of daily log file names in format basename.YYYY-MM-DD.ext + */ +struct dateonly_daily_file_name_calculator +{ +    // Create filename for the form basename.YYYY-MM-DD +    static filename_t calc_filename(const filename_t &filename) +    { +        std::tm tm = spdlog::details::os::localtime(); +        filename_t basename, ext; +        std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); +        std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; +        w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, ext); +        return w.str(); +    } +}; + +/* + * Rotating file sink based on date. rotates at midnight + */ +template<class Mutex, class FileNameCalc = default_daily_file_name_calculator> +class daily_file_sink SPDLOG_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) +        : _base_filename(std::move(base_filename)) +        , _rotation_h(rotation_hour) +        , _rotation_m(rotation_minute) +    { +        if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59) +        { +            throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor"); +        } +        _rotation_tp = _next_rotation_tp(); +        _file_helper.open(FileNameCalc::calc_filename(_base_filename)); +    } + +protected: +    void _sink_it(const details::log_msg &msg) override +    { +        if (std::chrono::system_clock::now() >= _rotation_tp) +        { +            _file_helper.open(FileNameCalc::calc_filename(_base_filename)); +            _rotation_tp = _next_rotation_tp(); +        } +        _file_helper.write(msg); +    } + +    void _flush() override +    { +        _file_helper.flush(); +    } + +private: +    std::chrono::system_clock::time_point _next_rotation_tp() +    { +        auto now = std::chrono::system_clock::now(); +        time_t tnow = std::chrono::system_clock::to_time_t(now); +        tm date = spdlog::details::os::localtime(tnow); +        date.tm_hour = _rotation_h; +        date.tm_min = _rotation_m; +        date.tm_sec = 0; +        auto rotation_time = std::chrono::system_clock::from_time_t(std::mktime(&date)); +        if (rotation_time > now) +        { +            return rotation_time; +        } +        return {rotation_time + std::chrono::hours(24)}; +    } + +    filename_t _base_filename; +    int _rotation_h; +    int _rotation_m; +    std::chrono::system_clock::time_point _rotation_tp; +    details::file_helper _file_helper; +}; + +using daily_file_sink_mt = daily_file_sink<std::mutex>; +using daily_file_sink_st = daily_file_sink<details::null_mutex>; + +} // namespace sinks +} // namespace spdlog diff --git a/lib/spdlog/sinks/msvc_sink.h b/lib/spdlog/sinks/msvc_sink.h new file mode 100644 index 0000000..09a5d67 --- /dev/null +++ b/lib/spdlog/sinks/msvc_sink.h @@ -0,0 +1,44 @@ +// +// Copyright(c) 2016 Alexander Dalshov. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#if defined(_WIN32) + +#include "../details/null_mutex.h" +#include "base_sink.h" + +#include <winbase.h> + +#include <mutex> +#include <string> + +namespace spdlog { +namespace sinks { +/* + * MSVC sink (logging using OutputDebugStringA) + */ +template<class Mutex> +class msvc_sink : public base_sink<Mutex> +{ +public: +    explicit msvc_sink() {} + +protected: +    void _sink_it(const details::log_msg &msg) override +    { +        OutputDebugStringA(msg.formatted.c_str()); +    } + +    void _flush() override {} +}; + +using msvc_sink_mt = msvc_sink<std::mutex>; +using msvc_sink_st = msvc_sink<details::null_mutex>; + +} // namespace sinks +} // namespace spdlog + +#endif diff --git a/lib/spdlog/sinks/null_sink.h b/lib/spdlog/sinks/null_sink.h new file mode 100644 index 0000000..c254bc5 --- /dev/null +++ b/lib/spdlog/sinks/null_sink.h @@ -0,0 +1,29 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "../details/null_mutex.h" +#include "base_sink.h" + +#include <mutex> + +namespace spdlog { +namespace sinks { + +template<class 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 +} // namespace spdlog diff --git a/lib/spdlog/sinks/ostream_sink.h b/lib/spdlog/sinks/ostream_sink.h new file mode 100644 index 0000000..9728138 --- /dev/null +++ b/lib/spdlog/sinks/ostream_sink.h @@ -0,0 +1,49 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "../details/null_mutex.h" +#include "base_sink.h" + +#include <mutex> +#include <ostream> + +namespace spdlog { +namespace sinks { +template<class Mutex> +class ostream_sink : 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 +    { +        _ostream.write(msg.formatted.data(), msg.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/lib/spdlog/sinks/sink.h b/lib/spdlog/sinks/sink.h new file mode 100644 index 0000000..b4e3806 --- /dev/null +++ b/lib/spdlog/sinks/sink.h @@ -0,0 +1,44 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "../details/log_msg.h" + +namespace spdlog { +namespace sinks { +class sink +{ +public: +    virtual ~sink() = default; + +    virtual void log(const details::log_msg &msg) = 0; +    virtual void flush() = 0; + +    bool should_log(level::level_enum msg_level) const; +    void set_level(level::level_enum log_level); +    level::level_enum level() const; + +private: +    level_t _level{level::trace}; +}; + +inline bool sink::should_log(level::level_enum msg_level) const +{ +    return msg_level >= _level.load(std::memory_order_relaxed); +} + +inline void sink::set_level(level::level_enum log_level) +{ +    _level.store(log_level); +} + +inline level::level_enum sink::level() const +{ +    return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed)); +} + +} // namespace sinks +} // namespace spdlog diff --git a/lib/spdlog/sinks/stdout_sinks.h b/lib/spdlog/sinks/stdout_sinks.h new file mode 100644 index 0000000..b15d080 --- /dev/null +++ b/lib/spdlog/sinks/stdout_sinks.h @@ -0,0 +1,79 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "../details/null_mutex.h" +#include "base_sink.h" + +#include <cstdio> +#include <memory> +#include <mutex> + +namespace spdlog { +namespace sinks { + +template<class Mutex> +class stdout_sink SPDLOG_FINAL : public base_sink<Mutex> +{ +    using MyType = stdout_sink<Mutex>; + +public: +    explicit stdout_sink() = default; + +    static std::shared_ptr<MyType> instance() +    { +        static std::shared_ptr<MyType> instance = std::make_shared<MyType>(); +        return instance; +    } + +protected: +    void _sink_it(const details::log_msg &msg) override +    { +        fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stdout); +        _flush(); +    } + +    void _flush() override +    { +        fflush(stdout); +    } +}; + +using stdout_sink_mt = stdout_sink<std::mutex>; +using stdout_sink_st = stdout_sink<details::null_mutex>; + +template<class Mutex> +class stderr_sink SPDLOG_FINAL : public base_sink<Mutex> +{ +    using MyType = stderr_sink<Mutex>; + +public: +    explicit stderr_sink() = default; + +    static std::shared_ptr<MyType> instance() +    { +        static std::shared_ptr<MyType> instance = std::make_shared<MyType>(); +        return instance; +    } + +protected: +    void _sink_it(const details::log_msg &msg) override +    { +        fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stderr); +        _flush(); +    } + +    void _flush() override +    { +        fflush(stderr); +    } +}; + +using stderr_sink_mt = stderr_sink<std::mutex>; +using stderr_sink_st = stderr_sink<details::null_mutex>; + +} // namespace sinks +} // namespace spdlog diff --git a/lib/spdlog/sinks/syslog_sink.h b/lib/spdlog/sinks/syslog_sink.h new file mode 100644 index 0000000..17bbb1d --- /dev/null +++ b/lib/spdlog/sinks/syslog_sink.h @@ -0,0 +1,76 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "../common.h" + +#ifdef SPDLOG_ENABLE_SYSLOG + +#include "../details/log_msg.h" +#include "sink.h" + +#include <array> +#include <string> +#include <syslog.h> + +namespace spdlog { +namespace sinks { +/** + * Sink that write to syslog using the `syscall()` library call. + * + * Locking is not needed, as `syslog()` itself is thread-safe. + */ +class syslog_sink : public sink +{ +public: +    // +    syslog_sink(const std::string &ident = "", int syslog_option = 0, int syslog_facility = LOG_USER) +        : _ident(ident) +    { +        _priorities[static_cast<size_t>(level::trace)] = LOG_DEBUG; +        _priorities[static_cast<size_t>(level::debug)] = LOG_DEBUG; +        _priorities[static_cast<size_t>(level::info)] = LOG_INFO; +        _priorities[static_cast<size_t>(level::warn)] = LOG_WARNING; +        _priorities[static_cast<size_t>(level::err)] = LOG_ERR; +        _priorities[static_cast<size_t>(level::critical)] = LOG_CRIT; +        _priorities[static_cast<size_t>(level::off)] = LOG_INFO; + +        // 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; + +    void log(const details::log_msg &msg) override +    { +        ::syslog(syslog_prio_from_level(msg), "%s", msg.raw.str().c_str()); +    } + +    void flush() override {} + +private: +    std::array<int, 7> _priorities; +    // 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 _priorities[static_cast<size_t>(msg.level)]; +    } +}; +} // namespace sinks +} // namespace spdlog + +#endif diff --git a/lib/spdlog/sinks/wincolor_sink.h b/lib/spdlog/sinks/wincolor_sink.h new file mode 100644 index 0000000..402fa12 --- /dev/null +++ b/lib/spdlog/sinks/wincolor_sink.h @@ -0,0 +1,144 @@ +// +// Copyright(c) 2016 spdlog +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "../common.h" +#include "../details/null_mutex.h" +#include "base_sink.h" + +#include <mutex> +#include <string> +#include <unordered_map> +#include <wincon.h> + +namespace spdlog { +namespace sinks { +/* + * Windows color console sink. Uses WriteConsoleA to write to the console with colors + */ +template<class Mutex> +class wincolor_sink : public base_sink<Mutex> +{ +public: +    const WORD BOLD = FOREGROUND_INTENSITY; +    const WORD RED = FOREGROUND_RED; +    const WORD GREEN = FOREGROUND_GREEN; +    const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE; +    const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; +    const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN; + +    wincolor_sink(HANDLE std_handle) +        : out_handle_(std_handle) +    { +        colors_[level::trace] = WHITE; +        colors_[level::debug] = CYAN; +        colors_[level::info] = GREEN; +        colors_[level::warn] = YELLOW | BOLD; +        colors_[level::err] = RED | BOLD;                         // red bold +        colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background +        colors_[level::off] = 0; +    } + +    ~wincolor_sink() override +    { +        this->flush(); +    } + +    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, WORD color) +    { +        std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex); +        colors_[level] = color; +    } + +protected: +    void _sink_it(const details::log_msg &msg) override +    { +        if (msg.color_range_end > msg.color_range_start) +        { +            // before color range +            _print_range(msg, 0, msg.color_range_start); + +            // in color range +            auto orig_attribs = set_console_attribs(colors_[msg.level]); +            _print_range(msg, msg.color_range_start, msg.color_range_end); +            ::SetConsoleTextAttribute(out_handle_, orig_attribs); // reset to orig colors +            // after color range +            _print_range(msg, msg.color_range_end, msg.formatted.size()); +        } +        else // print without colors if color range is invalid +        { +            _print_range(msg, 0, msg.formatted.size()); +        } +    } + +    void _flush() override +    { +        // windows console always flushed? +    } + +private: +    HANDLE out_handle_; +    std::unordered_map<level::level_enum, WORD, level::level_hasher> colors_; + +    // set color and return the orig console attributes (for resetting later) +    WORD set_console_attribs(WORD attribs) +    { +        CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info; +        GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info); +        WORD back_color = orig_buffer_info.wAttributes; +        // retrieve the current background color +        back_color &= static_cast<WORD>(~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY)); +        // keep the background color unchanged +        SetConsoleTextAttribute(out_handle_, attribs | back_color); +        return orig_buffer_info.wAttributes; // return orig attribs +    } + +    // print a range of formatted message to console +    void _print_range(const details::log_msg &msg, size_t start, size_t end) +    { +        DWORD size = static_cast<DWORD>(end - start); +        WriteConsoleA(out_handle_, msg.formatted.data() + start, size, nullptr, nullptr); +    } +}; + +// +// windows color console to stdout +// +template<class Mutex> +class wincolor_stdout_sink : public wincolor_sink<Mutex> +{ +public: +    wincolor_stdout_sink() +        : wincolor_sink<Mutex>(GetStdHandle(STD_OUTPUT_HANDLE)) +    { +    } +}; + +using wincolor_stdout_sink_mt = wincolor_stdout_sink<std::mutex>; +using wincolor_stdout_sink_st = wincolor_stdout_sink<details::null_mutex>; + +// +// windows color console to stderr +// +template<class Mutex> +class wincolor_stderr_sink : public wincolor_sink<Mutex> +{ +public: +    wincolor_stderr_sink() +        : wincolor_sink<Mutex>(GetStdHandle(STD_ERROR_HANDLE)) +    { +    } +}; + +using wincolor_stderr_sink_mt = wincolor_stderr_sink<std::mutex>; +using wincolor_stderr_sink_st = wincolor_stderr_sink<details::null_mutex>; + +} // namespace sinks +} // namespace spdlog diff --git a/lib/spdlog/sinks/windebug_sink.h b/lib/spdlog/sinks/windebug_sink.h new file mode 100644 index 0000000..2c7f1bc --- /dev/null +++ b/lib/spdlog/sinks/windebug_sink.h @@ -0,0 +1,27 @@ +// +// Copyright(c) 2017 Alexander Dalshov. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#if defined(_WIN32) + +#include "msvc_sink.h" + +namespace spdlog { +namespace sinks { + +/* + * Windows debug sink (logging using OutputDebugStringA, synonym for msvc_sink) + */ +template<class Mutex> +using windebug_sink = msvc_sink<Mutex>; + +using windebug_sink_mt = msvc_sink_mt; +using windebug_sink_st = msvc_sink_st; + +} // namespace sinks +} // namespace spdlog + +#endif diff --git a/lib/spdlog/spdlog.h b/lib/spdlog/spdlog.h new file mode 100644 index 0000000..21f5951 --- /dev/null +++ b/lib/spdlog/spdlog.h @@ -0,0 +1,204 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// +// spdlog main header file. +// see example.cpp for usage example + +#pragma once + +#include "common.h" +#include "logger.h" + +#include <chrono> +#include <functional> +#include <memory> +#include <string> + +namespace spdlog { + +// +// Return an existing logger or nullptr if a logger with such name doesn't exist. +// example: spdlog::get("my_logger")->info("hello {}", "world"); +// +std::shared_ptr<logger> get(const std::string &name); + +// +// Set global formatting +// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v"); +// +void set_pattern(const std::string &format_string); +void set_formatter(formatter_ptr f); + +// +// Set global logging level +// +void set_level(level::level_enum log_level); + +// +// Set global flush level +// +void flush_on(level::level_enum log_level); + +// +// Set global error handler +// +void set_error_handler(log_err_handler handler); + +// +// Turn on async mode (off by default) and set the queue size for each async_logger. +// effective only for loggers created after this call. +// queue_size: size of queue (must be power of 2): +//    Each logger will pre-allocate a dedicated queue with queue_size entries upon construction. +// +// async_overflow_policy (optional, block_retry by default): +//    async_overflow_policy::block_retry - if queue is full, block until queue has room for the new log entry. +//    async_overflow_policy::discard_log_msg - never block and discard any new messages when queue overflows. +// +// worker_warmup_cb (optional): +//     callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity) +// +// worker_teardown_cb (optional): +//     callback function that will be called in worker thread upon exit +// +void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, +    const std::function<void()> &worker_warmup_cb = nullptr, +    const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), +    const std::function<void()> &worker_teardown_cb = nullptr); + +// Turn off async mode +void set_sync_mode(); + +// +// Create and register multi/single threaded basic file logger. +// Basic logger simply writes to given file without any limitations or rotations. +// +std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false); +std::shared_ptr<logger> basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false); + +// +// Create and register multi/single threaded rotating file logger +// +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); + +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); + +// +// Create file logger which creates new file on the given time (default in midnight): +// +std::shared_ptr<logger> daily_logger_mt(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0); +std::shared_ptr<logger> daily_logger_st(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0); + +// +// Create and register stdout/stderr loggers +// +std::shared_ptr<logger> stdout_logger_mt(const std::string &logger_name); +std::shared_ptr<logger> stdout_logger_st(const std::string &logger_name); +std::shared_ptr<logger> stderr_logger_mt(const std::string &logger_name); +std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name); +// +// Create and register colored stdout/stderr loggers +// +std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name); +std::shared_ptr<logger> stdout_color_st(const std::string &logger_name); +std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name); +std::shared_ptr<logger> stderr_color_st(const std::string &logger_name); + +// +// Create and register a syslog logger +// +#ifdef SPDLOG_ENABLE_SYSLOG +std::shared_ptr<logger> syslog_logger( +    const std::string &logger_name, const std::string &ident = "", int syslog_option = 0, int syslog_facilty = (1 << 3)); +#endif + +#if defined(__ANDROID__) +std::shared_ptr<logger> android_logger(const std::string &logger_name, const std::string &tag = "spdlog"); +#endif + +// Create and register a logger with a single sink +std::shared_ptr<logger> create(const std::string &logger_name, const sink_ptr &sink); + +// Create and register a logger with multiple sinks +std::shared_ptr<logger> create(const std::string &logger_name, sinks_init_list sinks); + +template<class It> +std::shared_ptr<logger> create(const std::string &logger_name, const It &sinks_begin, const It &sinks_end); + +// Create and register a logger with templated sink type +// Example: +// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename"); +template<typename Sink, typename... Args> +std::shared_ptr<spdlog::logger> create(const std::string &logger_name, Args... args); + +// Create and register an async logger with a single sink +std::shared_ptr<logger> create_async(const std::string &logger_name, const sink_ptr &sink, size_t queue_size, +    const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, +    const std::function<void()> &worker_warmup_cb = nullptr, +    const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), +    const std::function<void()> &worker_teardown_cb = nullptr); + +// Create and register an async logger with multiple sinks +std::shared_ptr<logger> create_async(const std::string &logger_name, sinks_init_list sinks, size_t queue_size, +    const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, +    const std::function<void()> &worker_warmup_cb = nullptr, +    const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), +    const std::function<void()> &worker_teardown_cb = nullptr); + +template<class It> +std::shared_ptr<logger> create_async(const std::string &logger_name, const It &sinks_begin, const It &sinks_end, size_t queue_size, +    const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, +    const std::function<void()> &worker_warmup_cb = nullptr, +    const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(), +    const std::function<void()> &worker_teardown_cb = nullptr); + +// Register the given logger with the given name +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();}); +void apply_all(std::function<void(std::shared_ptr<logger>)> fun); + +// Drop the reference to the given logger +void drop(const std::string &name); + +// Drop all references from the registry +void drop_all(); + +/////////////////////////////////////////////////////////////////////////////// +// +// Trace & Debug can be switched on/off at compile time for zero cost debug statements. +// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in tweakme.h to enable. +// SPDLOG_TRACE(..) will also print current file and line. +// +// Example: +// spdlog::set_level(spdlog::level::trace); +// SPDLOG_TRACE(my_logger, "some trace message"); +// SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2); +// SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4); +/////////////////////////////////////////////////////////////////////////////// + +#ifdef SPDLOG_TRACE_ON +#define SPDLOG_STR_H(x) #x +#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x) +#ifdef _MSC_VER +#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__) +#else +#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__) +#endif +#else +#define SPDLOG_TRACE(logger, ...) (void)0 +#endif + +#ifdef SPDLOG_DEBUG_ON +#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__) +#else +#define SPDLOG_DEBUG(logger, ...) (void)0 +#endif + +} // namespace spdlog + +#include "details/spdlog_impl.h" diff --git a/lib/spdlog/tweakme.h b/lib/spdlog/tweakme.h new file mode 100644 index 0000000..801e13e --- /dev/null +++ b/lib/spdlog/tweakme.h @@ -0,0 +1,143 @@ +// +// Copyright(c) 2015 Gabi Melman. +// 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 date/time logging is not needed and never appear in the log pattern. +// This will prevent spdlog from querying the clock on each log call. +// +// WARNING: If the log pattern contains any date/time while this flag is on, the result is undefined. +//          You must set new pattern(spdlog::set_pattern(..") without any date/time in it +// +// #define SPDLOG_NO_DATETIME +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// 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, the result is undefined. +// +#define SPDLOG_NO_THREAD_ID +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Uncomment to prevent spdlog from caching thread ids in thread local storage. +// By default spdlog saves thread ids in tls to gain a few micros for each call. +// +// WARNING: if your program forks, UNCOMMENT this flag to prevent undefined thread ids in the children logs. +// +#define SPDLOG_DISABLE_TID_CACHING +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Uncomment if logger name logging is not needed. +// This will prevent spdlog from copying the logger name on each log call. +// +#define SPDLOG_NO_NAME +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Uncomment to enable the SPDLOG_DEBUG/SPDLOG_TRACE macros. +// +// #define SPDLOG_DEBUG_ON +// #define SPDLOG_TRACE_ON +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()). +// Use only if your code never modifies concurrently the registry. +// Note that upon creating a logger the registry is modified by spdlog.. +// +// #define SPDLOG_NO_REGISTRY_MUTEX +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// 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 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 printf-style messages in your logs instead of the usual +// format-style used by default. +// +// #define SPDLOG_FMT_PRINTF +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Uncomment to enable syslog (disabled by default) +// +// #define SPDLOG_ENABLE_SYSLOG +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// 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 if your compiler doesn't support the "final" keyword. +// The final keyword allows more optimizations in release +// mode with recent compilers. See GCC's documentation for -Wsuggest-final-types +// for instance. +// +// #define SPDLOG_NO_FINAL +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Uncomment to enable message counting feature. +// Use the %i in the logger pattern to display log message sequence id. +// +// #define SPDLOG_ENABLE_MESSAGE_COUNTER +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Uncomment to customize level names (e.g. "MT TRACE") +// +// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", "MY ERROR", "MY CRITICAL", "OFF" } +/////////////////////////////////////////////////////////////////////////////// diff --git a/modules/modpy/Makefile.am b/modules/modpy/Makefile.am index eccb2a2..231031a 100644 --- a/modules/modpy/Makefile.am +++ b/modules/modpy/Makefile.am @@ -10,8 +10,8 @@ mod_py_la_SOURCES       = \  	py.h py.cpp \  	interface.h interface.cpp -AM_CPPFLAGS             = -I$(top_srcdir)/lib -I$(top_srcdir)/src \ -	${PYTHON_INCLUDES} ${libconfig_CFLAGS} ${log4cpp_CFLAGS} +AM_CPPFLAGS             = -std=c++11 -I$(top_srcdir)/lib -I$(top_srcdir)/src \ +	${PYTHON_INCLUDES} ${libconfig_CFLAGS}  mod_py_la_LIBADD        = ${PYTHON_LIBS}  endif diff --git a/modules/modpy/interface.cpp b/modules/modpy/interface.cpp index 948fa5c..60e1c7b 100644 --- a/modules/modpy/interface.cpp +++ b/modules/modpy/interface.cpp @@ -19,10 +19,11 @@  #include "interface.h" -#include <bitz/logger.h>  #include <icap/request.h>  #include <icap/response.h> -#include <iostream> +#include <spdlog/spdlog.h> + +static auto _logger = spdlog::get( "bitz-server" );  PyObject * bitz_get_request( PyObject * self, PyObject * pyrequest ) { @@ -31,9 +32,7 @@ PyObject * bitz_get_request( PyObject * self, PyObject * pyrequest ) {  	PyObject * pypayload;  	icap::Request * request; -	// logger -	bitz::Logger &logger = bitz::Logger::instance(); -	logger.debug( "[modpy.interface] get_request()" ); +	_logger->debug( "[modpy.interface] get_request()" );  	// initialise return dictionary  	pyreturn = PyDict_New(); @@ -65,7 +64,7 @@ PyObject * bitz_get_request( PyObject * self, PyObject * pyrequest ) {  		Py_DECREF( pypayload );  	} else { -		logger.warn( "[modpy.interface] failed to get request object pointer" ); +		_logger->warn( "[modpy.interface] failed to get request object pointer" );  	}  	return pyreturn; @@ -80,15 +79,13 @@ PyObject * bitz_get_response_from_status( PyObject * self, PyObject * args ) {  	unsigned int resp_status; -	// logger -	bitz::Logger &logger = bitz::Logger::instance(); -	logger.debug( "[modpy.interface] get_response_from_status()" ); +	_logger->debug( "[modpy.interface] get_response_from_status()" );  	// parse args  	if ( PyArg_ParseTuple( args, "I", &resp_status ) ) {  		response = new icap::Response( (icap::ResponseHeader::status_t) resp_status );  	} else { -		logger.warn( "[modpy.interface] failed to parse arguments" ); +		_logger->warn( "[modpy.interface] failed to parse arguments" );  		response = new icap::Response( icap::ResponseHeader::SERVER_ERROR );  	} @@ -114,9 +111,7 @@ PyObject * bitz_get_response( PyObject * self, PyObject * args ) {  	Py_ssize_t pybuflen; -	// logger -	bitz::Logger &logger = bitz::Logger::instance(); -	logger.debug( "[modpy.interface] get_response()" ); +	_logger->debug( "[modpy.interface] get_response()" );  	// parse args  	if ( PyArg_ParseTuple( args, "IO!", &resp_status, &PyDict_Type, &pypayload ) ) { @@ -142,7 +137,7 @@ PyObject * bitz_get_response( PyObject * self, PyObject * args ) {  		response->payload( payload );  	} else { -		logger.warn( "[modpy.interface] failed to parse arguments" ); +		_logger->warn( "[modpy.interface] failed to parse arguments" );  	}  	// sanity check diff --git a/modules/modpy/py.cpp b/modules/modpy/py.cpp index 51cb38f..a1d330d 100644 --- a/modules/modpy/py.cpp +++ b/modules/modpy/py.cpp @@ -21,7 +21,6 @@  #include "interface.h"  #include <bitz/config.h> -#include <bitz/logger.h>  namespace bitz { @@ -35,6 +34,9 @@ namespace bitz {  		_config.module_name = "modpy";  		_config.module_path = ""; +		// logger +		_logger = spdlog::get( "bitz-server" ); +  		// load configs  		load_configs(); @@ -92,21 +94,19 @@ namespace bitz {  		PyObject * pymethod;  		PyObject * pyreturn; -		// logger -		Logger &logger = Logger::instance();  		// python core  		Py_Initialize();  		// bitz python module  		if ( Py_InitModule( "bitz", bitz_methods ) == NULL ) { -			logger.warn( "[modpy] failed to init C interface module: bitz" ); +			_logger->warn( "[modpy] failed to init C interface module: bitz" );  		}  		// setup python environment  		if ( _config.module_path != "" ) { -			logger.debug( std::string( "[modpy] appending to sys.path, module_path: " ).append( _config.module_path ) ); +			_logger->debug( std::string( "[modpy] appending to sys.path, module_path: " ).append( _config.module_path ) );  			sys_path      = PySys_GetObject( (char *) "path" );  			pymodule_path = PyString_FromString( _config.module_path.c_str() ); @@ -115,14 +115,14 @@ namespace bitz {  		}  		// load the interface module -		logger.debug( std::string( "[modpy] interface module: " ).append( _config.module_name ) ); +		_logger->debug( std::string( "[modpy] interface module: " ).append( _config.module_name ) );  		pymodule_name = PyString_FromString( _config.module_name.c_str() );  		_pymodule     = PyImport_Import( pymodule_name );  		if ( _pymodule != NULL ) { -			logger.debug( "[modpy] interface module loaded successfully" ); +			_logger->debug( "[modpy] interface module loaded successfully" );  			// call init() in the interface module  			pymethod = PyObject_GetAttrString( _pymodule, "init" ); @@ -131,13 +131,13 @@ namespace bitz {  				pyreturn = PyObject_CallObject( pymethod, NULL );  				Py_DECREF( pyreturn );  			} else { -				logger.warn ( "[modpy] failed to call init() in interface module" ); +				_logger->warn ( "[modpy] failed to call init() in interface module" );  			}  			Py_DECREF( pymethod );  		} else { -			logger.warn( "[modpy] failed to load interface module" ); +			_logger->warn( "[modpy] failed to load interface module" );  		} @@ -154,9 +154,6 @@ namespace bitz {  		PyObject * pymethod;  		PyObject * pyreturn; -		// logger -		Logger &logger = Logger::instance(); -  		// cleanup  		if ( _pymodule != NULL ) { @@ -168,7 +165,7 @@ namespace bitz {  				pyreturn = PyObject_CallObject( pymethod, NULL );  				Py_DECREF( pyreturn );  			} else { -				logger.warn ( "[modpy] failed to call cleanup() in interface module" ); +				_logger->warn ( "[modpy] failed to call cleanup() in interface module" );  			}  			Py_DECREF( pymethod ); @@ -199,9 +196,6 @@ namespace bitz {  		PyObject * pyargs;  		PyObject * pyrequest, * pyresponse; -		// logger -		Logger &logger = Logger::instance(); -  		// initialise the response object  		response = NULL; @@ -231,18 +225,18 @@ namespace bitz {  						response = static_cast<icap::Response *>(p);  					} else { -						logger.warn( std::string( "[modpy] invalid capsule response, method: " ).append( method ) ); +						_logger->warn( std::string( "[modpy] invalid capsule response, method: " ).append( method ) );  					}  					Py_DECREF( pyresponse );  				} else { -					logger.warn( std::string( "[modpy] response is NULL, method: " ).append( method ) ); +					_logger->warn( std::string( "[modpy] response is NULL, method: " ).append( method ) );  				}  			} else { -				logger.warn ( std::string( "[modpy] failed to call the method in interface module, method: " ).append( method ) ); +				_logger->warn ( std::string( "[modpy] failed to call the method in interface module, method: " ).append( method ) );  			}  			// cleanup diff --git a/modules/modpy/py.h b/modules/modpy/py.h index 358c36f..f171063 100644 --- a/modules/modpy/py.h +++ b/modules/modpy/py.h @@ -23,6 +23,7 @@  #include <Python.h>  #include <bitz/modifier.h> +#include <spdlog/spdlog.h>  namespace bitz { @@ -44,6 +45,7 @@ namespace bitz {  	private:  		config_t _config;  		PyObject * _pymodule; +		std::shared_ptr<spdlog::logger> _logger;  		void load_configs() throw();  		void init_python() throw(); diff --git a/src/Makefile.am b/src/Makefile.am index b3038cd..15a6efc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,12 +1,15 @@  ## src/  AUTOMAKE_OPTIONS        = subdir-objects -AM_CPPFLAGS             = -I$(top_srcdir)/lib ${libconfig_CFLAGS} ${log4cpp_CFLAGS} ${psocksxx_CFLAGS} +AM_CPPFLAGS             = -std=c++11 \ +	-I$(top_srcdir)/lib \ +	${libconfig_CFLAGS} \ +	${psocksxx_CFLAGS}  bitzincludedir          = $(pkgincludedir)  sbin_PROGRAMS           = bitz-server -bitz_server_LDFLAGS     = -ldl -export-dynamic +bitz_server_LDFLAGS     = -ldl -lpthread -export-dynamic  bitz_server_LDADD       = $(top_builddir)/lib/icap/libicap.la \ -	${libconfig_LIBS} ${log4cpp_LIBS} \ +	${libconfig_LIBS} \  	${psocksxx_LIBS}  bitz_server_SOURCES     = main.cpp \ @@ -14,7 +17,6 @@ bitz_server_SOURCES     = main.cpp \  	bitz/exception.h bitz/exception.cpp \  	bitz/manager_exception.h bitz/manager_exception.cpp \  	bitz/config.h bitz/config.cpp \ -	bitz/logger.h bitz/logger.cpp \  	bitz/common.h \  	bitz/util.h bitz/util.cpp \  	bitz/manager.h bitz/manager.cpp \ @@ -27,6 +29,5 @@ bitz_server_SOURCES     = main.cpp \  bitzinclude_HEADERS     = \  	bitz/config.h \ -	bitz/logger.h \  	bitz/modifier.h diff --git a/src/bitz-server.cpp b/src/bitz-server.cpp index d6257fd..e99078e 100644 --- a/src/bitz-server.cpp +++ b/src/bitz-server.cpp @@ -29,6 +29,8 @@  #include <sys/stat.h>  #include <sys/wait.h> +#include <spdlog/spdlog.h> +  #include <config.h>  #include "bitz-server.h" @@ -41,15 +43,12 @@ namespace bitz {  		void init() {  			// initialise defaults -			globals.pid_handle  = -1; +			globals.pid         = -1; +			globals.pidfd       = -1;  			globals.manager     = NULL;  			globals.terminating = 0;  			globals.daemon      = false; -			// logger (syslog) -			setlogmask( LOG_UPTO( LOG_INFO ) ); -			openlog( PACKAGE_NAME, LOG_CONS, LOG_USER ); -  			// signal handlers  			init_signal_handlers(); @@ -100,9 +99,10 @@ namespace bitz {  			pid_t worker_pid;  			int status; -			std::cout << "[" << getpid() << "] inside zombie deleter: "; +			auto logger = spdlog::get( "bitz-server" ); +			logger->trace( "[{}] inside zombie reaper", getpid() );  			while ( ( worker_pid = waitpid( WAIT_ANY, &status, WNOHANG ) ) > 0 ) { -				std::cout << "child " << worker_pid << " terminated with status " << status << std::endl; +				logger->trace( "[reaper] child {} terminated with status {}", worker_pid, status );  				if ( globals.manager != NULL ) {  					globals.manager->reap_worker( worker_pid ); @@ -134,7 +134,8 @@ namespace bitz {  		void sigterm_handler( int sig, siginfo_t *siginfo, void *context ) { -			std::cout << "[" << getpid() << "] inside SIGTERM handler" << std::endl; +			auto logger = spdlog::get( "bitz-server" ); +			logger->trace( "[{}] inside SIGTERM handler", getpid() );  			termination_handler( sig, siginfo, context );  		} @@ -162,7 +163,8 @@ namespace bitz {  		void sigquit_handler( int sig, siginfo_t *siginfo, void *context ) { -			std::cout << "[" << getpid() << "] inside SIGQUIT handler" << std::endl; +			auto logger = spdlog::get( "bitz-server" ); +			logger->trace( "[{}] inside SIGQUIT handler", getpid() );  			termination_handler( sig, siginfo, context );  		} @@ -190,7 +192,8 @@ namespace bitz {  		void sigint_handler(  int sig, siginfo_t *siginfo, void *context ) { -			std::cout << "[" << getpid() << "] inside SIGQINT handler" << std::endl; +			auto logger = spdlog::get( "bitz-server" ); +			logger->trace( "[{}] inside SIGQINT handler", getpid() );  			termination_handler( sig, siginfo, context );  		} @@ -198,20 +201,28 @@ namespace bitz {  		void daemonize( const char *rundir, const char *pidfile ) { -			pid_t pid, sid; +			pid_t pid;  			long i;  			char str[10]; -			// notify -			syslog( LOG_NOTICE, "starting daemon (version %s)", PACKAGE_VERSION ); +			/* fork daemon */ +			pid = fork(); +			if ( pid < 0 ) { +				exit( EXIT_FAILURE ); +			} -			// check parent process id value -			if ( getppid() == 1 ) { -				// we are already a daemon -				return; +			// exit the parent +			if ( pid > 0 ) { +				exit( EXIT_SUCCESS );  			} -			/* fork daemon */ +			// get a new process group +			if ( setsid() < 0 ) { +				exit(EXIT_FAILURE); +			} + + +			// 2nd fork (to make PID != SID)  			pid = fork();  			if ( pid < 0 ) {  				exit( EXIT_FAILURE ); @@ -224,63 +235,67 @@ namespace bitz {  			/* child (a.k.a daemon) continues */ -  			// set file permissions (750)  			umask( 027 ); -			// get a new process group -			sid = setsid(); -			if ( sid < 0 ) { -				exit(EXIT_FAILURE); +			// change running directory +			chdir( rundir ); + +			// close all open file descriptors +			for ( int fd = sysconf( _SC_OPEN_MAX ); fd > 0; fd-- ) { +				close( fd );  			} -			// route I/O connections -			close( STDIN_FILENO ); -			close( STDOUT_FILENO ); -			close( STDERR_FILENO ); -			// change running directory -			chdir( rundir ); +			// logger (syslog) +			setlogmask( LOG_UPTO( LOG_INFO ) ); +			openlog( PACKAGE_NAME, LOG_CONS, LOG_USER ); + +			// notify +			syslog( LOG_NOTICE, "starting daemon (version %s)", PACKAGE_VERSION );  			/* lock pid file to ensure we have only one copy */ -			globals.pid_handle = open( pidfile, O_RDWR | O_CREAT, 0600 ); -			if ( globals.pid_handle == -1 ) { +			globals.pidfd = open( pidfile, O_RDWR | O_CREAT, 0600 ); +			if ( globals.pidfd == -1 ) {  				syslog( LOG_ERR, "could not open pid lock file: %s", pidfile );  				exit( EXIT_FAILURE );  			} -			if ( lockf( globals.pid_handle, F_TLOCK, 0 ) == -1 ) { +			if ( lockf( globals.pidfd, F_TLOCK, 0 ) == -1 ) {  				syslog( LOG_ERR, "could not lock pid lock file: %s", pidfile);  				exit( EXIT_FAILURE );  			}  			// get and format pid -			sprintf( str, "%d\n", getpid() ); +			globals.pid = getpid(); +			sprintf( str, "%d\n", globals.pid );  			// write pid to lockfile -			write( globals.pid_handle, str, strlen( str ) ); +			write( globals.pidfd, str, strlen( str ) );  			// update status  			globals.daemon = true; -  		}  		void shutdown() {  			// notify -			if ( globals.daemon && ( getppid() == 1 ) ) { +			if ( globals.daemon && ( getpid() == globals.pid ) ) {  				syslog( LOG_NOTICE, "shutting down daemon (version %s)", PACKAGE_VERSION );  			}  			// close pid file -			if ( globals.pid_handle != -1 ) { -				close( globals.pid_handle ); +			if ( globals.pidfd != -1 ) { +				close( globals.pidfd );  			} +			// flush logs +			spdlog::get( "bitz-server" )->flush(); +  			// close logger (syslog)  			closelog(); @@ -289,12 +304,15 @@ namespace bitz {  		void termination_handler( int sig, siginfo_t * sig_info, void * context ) { -			std::cout << "[" << getpid() << "] inside termination handler" << std::endl; +			pid_t pid = getpid(); +			auto logger = spdlog::get( "bitz-server" ); +			logger->trace( "[{}] inside termination handler", pid );  			// exit by re-raising the signal if termination  			// already in progress  			if ( globals.terminating ) { -				std::cout << "[" << getpid() << "] already terminating" << std::endl; +				logger->warn( "[{}] already terminating", pid ); +  				raise( sig );  			} @@ -455,7 +473,7 @@ namespace bitz {  				sigprocmask( SIG_UNBLOCK, &mask, NULL );  				// manage workers -				globals.manager->manager_workers(); +				globals.manager->manage_workers();  				// block termination signals  				sigprocmask( SIG_BLOCK, &mask, &oldmask ); diff --git a/src/bitz-server.h b/src/bitz-server.h index c7dda05..2a4b57f 100644 --- a/src/bitz-server.h +++ b/src/bitz-server.h @@ -23,6 +23,7 @@  #include "bitz/manager.h"  #include <csignal> +#include <sys/types.h>  namespace bitz { @@ -34,7 +35,8 @@ namespace bitz {  		*   by the init() method.  		*/  		struct server_t { -			int pid_handle; +			pid_t pid; +			int pidfd;  			volatile sig_atomic_t terminating;  			bool daemon; diff --git a/src/bitz/config.cpp b/src/bitz/config.cpp index aaa247f..f5ef810 100644 --- a/src/bitz/config.cpp +++ b/src/bitz/config.cpp @@ -18,9 +18,7 @@   */  #include "config.h" -#include "logger.h" -#include <iostream>  #include <cstdlib> @@ -32,7 +30,6 @@ namespace bitz {  		_config.port            = 1344;  		_config.pid_file        = "/dev/null";  		_config.log_file        = "/dev/null"; -		_config.log_category    = "bitz";  		_config.req_handlers_count = 0;  		_config.req_handlers       = NULL; @@ -44,6 +41,9 @@ namespace bitz {  		// defaults  		_lconfig = NULL; +		// logger +		_logger = spdlog::get( "bitz-server" ); +  	}  	Config::~Config() { @@ -72,12 +72,10 @@ namespace bitz {  		try {  			config->readFile( config_file.c_str() );  		} catch ( const libconfig::FileIOException &ex ) { -			std::cerr << "[config] failed to read config file: " << config_file -					<< ", exception: " << ex.what() << std::endl; +			_logger->error( "[config] failed to read config file: {}, exception: {}",  config_file, ex.what() );  			exit( EXIT_FAILURE );  		} catch ( const libconfig::ParseException &pex ) { -			std::cerr << "[config] parse error at " << pex.getFile() -					<< ":" << pex.getLine() << " - " << pex.getError() << std::endl; +			_logger->error( "[config] parse error at {}:{} - {}", pex.getFile(), pex.getLine(), pex.getError() );  			exit( EXIT_FAILURE );  		} @@ -87,7 +85,6 @@ namespace bitz {  			config->lookupValue( "port", _config.port );  			config->lookupValue( "pid_file", _config.pid_file );  			config->lookupValue( "log_file", _config.log_file ); -			config->lookupValue( "log_category", _config.log_category );  			config->lookupValue( "max_workers", _config.max_workers );  			config->lookupValue( "max_worker_requests", _config.max_worker_requests ); @@ -95,8 +92,7 @@ namespace bitz {  			config->lookupValue( "comm_timeout", _config.comm_timeout );  		} catch ( const libconfig::SettingNotFoundException &e ) { -			std::cerr << "[config] failed to load core configs, " -					<< e.getPath() << " : " << e.what() << std::endl; +			_logger->error( "[config] failed to load core configs, {} : {}", e.getPath(), e.what() );  		}  		// cache configs @@ -125,12 +121,11 @@ namespace bitz {  				libconfig::Setting &setting = _lconfig->lookup( std::string( "modules." ).append( module ) );  				setting.lookupValue( config, config_value );  			} catch ( const libconfig::SettingNotFoundException &e ) { -				// TODO: log errors ?? -				std::cerr << "[config] " << e.getPath() << " : " << e.what() << std::endl; +				_logger->error( "[config] {} : {}", e.getPath(), e.what() );  			}  		} else { -			std::cout << "[config] 'modules' configs not found" << std::endl; +			_logger->info( "[config] 'modules' configs not found" );  		}  		return config_value; @@ -143,16 +138,14 @@ namespace bitz {  		int i, j;  		std::string s; -		std::cout << "[config] looking for req_handlers... "; +		_logger->trace( "[config] looking for req_handlers" );  		if ( _lconfig->exists( "req_handlers" ) ) { -			std::cout << "found "; -  			libconfig::Setting &req_handlers = _lconfig->lookup( "req_handlers" );  			_config.req_handlers_count       = req_handlers.getLength();  			_config.req_handlers             = new req_handlers_config_t[_config.req_handlers_count]; -			std::cout << "(" << _config.req_handlers_count << ")" << std::endl; +			_logger->debug( "[config] found {} request handlers", _config.req_handlers_count );  			try { @@ -162,29 +155,28 @@ namespace bitz {  					_config.req_handlers[i].class_name = (const char *) req_handlers[i]["class"];  					// read request handler modules config -					std::cout << "[config] looking for " << _config.req_handlers[i].name << " modules... "; +					_logger->debug( "[config] looking for {} modules", _config.req_handlers[i].name );  					if ( req_handlers[i].exists( "modules" ) ) { -						std::cout << "found ";  						_config.req_handlers[i].modules_count   = req_handlers[i]["modules"].getLength();  						_config.req_handlers[i].modules         = new modules_config_t[_config.req_handlers[i].modules_count]; -						std::cout << "(" << _config.req_handlers[i].modules_count << ")" << std::endl; +						_logger->debug( "[config] found {} modules for {}", _config.req_handlers[i].modules_count, _config.req_handlers[i].name );  						for ( j = 0; j < _config.req_handlers[i].modules_count; j++ ) {  							_config.req_handlers[i].modules[j].name   = (const char *) req_handlers[i]["modules"][j]["name"];  							_config.req_handlers[i].modules[j].module = (const char *) req_handlers[i]["modules"][j]["module"];  						}  					} else { -						std::cout << "not found" << std::endl; +						_logger->info( "[config] no modules found for {}", _config.req_handlers[i].name );  					}  				}  			} catch ( const libconfig::SettingNotFoundException &ex ) { -				std::cerr << "[config] Error: " << ex.getPath() << ex.what() << std::endl; +				_logger->error( "[config] {} : {}", ex.getPath(), ex.what() );  			}  		} else { -			std::cout << "not found" << std::endl; +			_logger->info( "[config] no request handlers found" );  		}  	} diff --git a/src/bitz/config.h b/src/bitz/config.h index 021384b..06cd4df 100644 --- a/src/bitz/config.h +++ b/src/bitz/config.h @@ -22,6 +22,7 @@  #include <string>  #include <libconfig.h++> +#include <spdlog/spdlog.h>  #ifndef BITZ_SERVER_CONFIG_FILE @@ -50,7 +51,6 @@ namespace bitz {  		int comm_timeout;  		std::string pid_file;  		std::string log_file; -		std::string log_category;  		unsigned int req_handlers_count;  		req_handlers_config_t * req_handlers; @@ -80,6 +80,7 @@ namespace bitz {  	private:  		config_t _config;  		libconfig::Config * _lconfig; +		std::shared_ptr<spdlog::logger> _logger;  		Config();  		~Config(); diff --git a/src/bitz/logger.cpp b/src/bitz/logger.cpp deleted file mode 100644 index 1d7d833..0000000 --- a/src/bitz/logger.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - *	bitz-server, An ICAP server implementation in C++ - *	Copyright (C) 2012	Uditha Atukorala - * - *	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, write to the Free Software - *	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA - */ - -#include "logger.h" - -#include <log4cpp/FileAppender.hh> -#include <log4cpp/PatternLayout.hh> - -namespace bitz { - -	Logger::Logger( std::string log_file, std::string category ) { -		this->LOGGER = NULL; -		this->initialise( log_file, category ); -	} - -	Logger::~Logger() { -		this->LOGGER->debug( "closing down logger" ); -	} - -	void Logger::initialise( std::string log_file, std::string category ) { - -		this->LOGGER = &log4cpp::Category::getInstance( category ); - -		// setting up appender, layout and category -		log4cpp::Appender * log_appender    = new log4cpp::FileAppender( "FileAppender", log_file ); -		log4cpp::PatternLayout * log_layout = new log4cpp::PatternLayout(); -		log_layout->setConversionPattern( "%d %p %c %x: %m%n" ); - -		log_appender->setLayout( log_layout ); -		this->LOGGER->setAppender( log_appender ); -		this->LOGGER->setPriority( this->getPriorityValue( "DEBUG" ) ); - -		this->LOGGER->debug( "logger initialised, log_file: " + log_file ); - -	} - -	int Logger::getPriorityValue( const std::string &priority ) { -		return ( log4cpp::Priority::getPriorityValue( priority ) ); -	} - -	void Logger::log( log4cpp::Priority::Value priority, const std::string &message ) { -		this->LOGGER->log( priority, message ); -	} - -	void Logger::fatal( const std::string& message ) { -		this->LOGGER->fatal( message ); -	} - -	void Logger::emerg( const std::string& message ) { -		this->LOGGER->emerg( message ); -	} - -	void Logger::alert( const std::string& message ) { -		this->LOGGER->alert( message ); -	} - -	void Logger::crit( const std::string& message ) { -		this->LOGGER->crit( message ); -	} - -	void Logger::error( const std::string& message ) { -		this->LOGGER->error( message ); -	} - -	void Logger::warn( const std::string& message ) { -		this->LOGGER->warn( message ); -	} - -	void Logger::notice( const std::string& message ) { -		this->LOGGER->notice( message ); -	} - -	void Logger::info( const std::string& message ) { -		this->LOGGER->info( message ); -	} - -	void Logger::debug( const std::string& message ) { -		this->LOGGER->debug( message ); -	} - -} /* end of namespace bitz */ - diff --git a/src/bitz/logger.h b/src/bitz/logger.h deleted file mode 100644 index f1c9c6c..0000000 --- a/src/bitz/logger.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - *	bitz-server, An ICAP server implementation in C++ - *	Copyright (C) 2012	Uditha Atukorala - * - *	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, write to the Free Software - *	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA - */ - -#ifndef BITZ_LOGGER_H -#define BITZ_LOGGER_H - -#include <string> -#include <log4cpp/Category.hh> - -namespace bitz { - -	class Logger { -	public: -		static Logger &instance( std::string log_file = "/dev/null" , std::string category = "logger" ) { -			static Logger logger( log_file, category ); -			return logger; -		} - -		void initialise( std::string log_file, std::string category ); - -		static int getPriorityValue( const std::string& priority ); -		void log( int priority, const std::string &message ); - -		void fatal( const std::string& message ); -		void emerg( const std::string& message ); -		void alert( const std::string& message ); -		void crit( const std::string& message ); -		void error( const std::string& message ); -		void warn( const std::string& message ); -		void notice( const std::string& message ); -		void info( const std::string& message ); -		void debug( const std::string& message ); - -	private: -		log4cpp::Category * LOGGER; - -		Logger( std::string log_file, std::string category ); -		~Logger(); -		Logger( Logger const © ); -		Logger &operator=( const Logger © ); -	}; - -} /* end of namespace bitz */ - -#endif /* !BITZ_LOGGER_H */ - diff --git a/src/bitz/manager.cpp b/src/bitz/manager.cpp index 14033a5..38b431f 100644 --- a/src/bitz/manager.cpp +++ b/src/bitz/manager.cpp @@ -18,7 +18,6 @@   */  #include "manager.h" -#include "logger.h"  #include "util.h"  #include <cstdlib> @@ -55,20 +54,19 @@ namespace bitz {  			throw ManagerException( std::string( "failed to initialise socket, " ).append( e.what() ) );  		} -		Logger &logger = Logger::instance(); -		logger.debug( "manager initialised" ); +		// logger +		_logger = spdlog::get( "bitz-server" ); +		_logger->debug( "manager initialised" );  	}  	Manager::~Manager() { -		Logger &logger = Logger::instance(); -  		if ( _manager.worker ) { -			logger.debug( "[worker] cleaning up manager" ); +			_logger->debug( "[worker] cleaning up manager" );  		} else { -			logger.debug( "[manager] shutting down manager" ); +			_logger->debug( "[manager] shutting down manager" );  		}  		delete [] _manager.worker_pool; @@ -102,7 +100,6 @@ namespace bitz {  	void Manager::spawn_worker( unsigned int worker_id ) throw( ManagerException ) { -		Logger &logger = Logger::instance();  		pid_t worker_pid;  		// create a worker child @@ -122,7 +119,7 @@ namespace bitz {  			_manager.worker_pool[worker_id].worker_pid = worker_pid;  			_manager.worker_pool[worker_id].worker->run( _manager.socket, _manager.max_worker_requests, _manager.comm_timeout ); -			logger.info( std::string( "end of cycle, worker[" ).append( util::itoa( worker_id ) ).append( "]" ) ); +			_logger->info( std::string( "end of cycle, worker[" ).append( util::itoa( worker_id ) ).append( "]" ) );  			delete _manager.worker_pool[worker_id].worker;  			_exit( EXIT_SUCCESS ); @@ -130,7 +127,7 @@ namespace bitz {  		} else {  			/* manager */ -			logger.info( std::string( "[manager] worker spawned with pid: " ).append( util::itoa( worker_pid) ) ); +			_logger->info( std::string( "[manager] worker spawned with pid: " ).append( util::itoa( worker_pid) ) );  			_manager.workers_count++; @@ -146,12 +143,9 @@ namespace bitz {  	void Manager::shutdown( bool graceful ) throw() { -		// logger -		Logger &logger = Logger::instance(); -  		if ( _manager.worker ) { -			logger.info( "[worker] manager shutdown request received" ); +			_logger->info( "[worker] manager shutdown request received" );  			/* worker: cleanup */  			delete _manager.worker_pool[_manager.worker_id].worker; @@ -159,21 +153,21 @@ namespace bitz {  		} else {  			/* manager: stop all child processes */ -			logger.info( "[manager] shutdown request received" ); +			_logger->info( "[manager] shutdown request received" );  			for (unsigned int i = 0; i < _manager.max_workers; i++ ) {  				if ( _manager.worker_pool[i].worker_pid != 0 ) {  					if ( graceful ) {  						kill( _manager.worker_pool[i].worker_pid, SIGTERM ); -						logger.debug( std::string( "[manager] sending SIGTERM to worker[" ).append( util::itoa( i ) ) +						_logger->debug( std::string( "[manager] sending SIGTERM to worker[" ).append( util::itoa( i ) )  								.append( "], pid: " ).append( util::itoa( _manager.worker_pool[i].worker_pid ) ) );  					} else {  						kill( _manager.worker_pool[i].worker_pid, SIGKILL ); -						logger.debug( std::string( "[manager] sending SIGKILL to worker[" ).append( util::itoa( i ) ) +						_logger->debug( std::string( "[manager] sending SIGKILL to worker[" ).append( util::itoa( i ) )  								.append( "], pid: " ).append( util::itoa( _manager.worker_pool[i].worker_pid ) ) );  					}  				} else { -					logger.debug( std::string( "[manager] worker[" ).append( util::itoa( i ) ).append( "] already closed" ) ); +					_logger->debug( std::string( "[manager] worker[" ).append( util::itoa( i ) ).append( "] already closed" ) );  				}  			}  		} @@ -183,9 +177,7 @@ namespace bitz {  	void Manager::reap_worker( pid_t worker_pid ) throw() { -		// logger -		Logger &logger = Logger::instance(); -		logger.debug( std::string( "reaping worker, pid: " ).append( util::itoa( worker_pid ) ) ); +		_logger->debug( std::string( "reaping worker, pid: " ).append( util::itoa( worker_pid ) ) );  		if (! _manager.worker ) {  			for (unsigned int i = 0; i < _manager.max_workers; i++ ) { @@ -205,10 +197,7 @@ namespace bitz {  	} -	void Manager::manager_workers() throw() { - -		// logger -		Logger &logger = Logger::instance(); +	void Manager::manage_workers() throw() {  		if (! _manager.worker ) { @@ -222,7 +211,7 @@ namespace bitz {  							// spawn a worker for the missing  							spawn_worker( i );  						} catch ( ManagerException &mex ) { -							logger.warn( std::string( "[manager] failed to spawn worker[" ).append( util::itoa( i ) ).append( "], exception: ").append( mex.what() ) ); +							_logger->warn( std::string( "[manager] failed to spawn worker[" ).append( util::itoa( i ) ).append( "], exception: ").append( mex.what() ) );  						}  					}  				} diff --git a/src/bitz/manager.h b/src/bitz/manager.h index ce49e9e..8ad23e9 100644 --- a/src/bitz/manager.h +++ b/src/bitz/manager.h @@ -21,6 +21,7 @@  #define BITZ_MANAGER_H  #include <unistd.h> +#include <spdlog/spdlog.h>  #include <psocksxx/tcpnsockstream.h>  #include "manager_exception.h" @@ -75,11 +76,12 @@ namespace bitz {  		virtual void shutdown( bool graceful = true ) throw();  		virtual void reap_worker( pid_t worker_pid ) throw(); -		virtual void manager_workers() throw(); +		virtual void manage_workers() throw();  	private: -		manager_t         _manager; +		manager_t _manager; +		std::shared_ptr<spdlog::logger> _logger;  		virtual void spawn_worker( unsigned int worker_id ) throw( ManagerException ); diff --git a/src/bitz/request_handler.cpp b/src/bitz/request_handler.cpp index 56f9bc0..aa906f9 100644 --- a/src/bitz/request_handler.cpp +++ b/src/bitz/request_handler.cpp @@ -19,7 +19,6 @@  #include "request_handler.h"  #include "config.h" -#include "logger.h"  #include "util.h"  #include <dlfcn.h> @@ -37,6 +36,9 @@ namespace bitz {  		// update variables  		_req_handler.method = method; +		// logger +		_logger = spdlog::get( "bitz-server" ); +  		// load modifier modules  		load_modules(); @@ -52,8 +54,7 @@ namespace bitz {  			delete [] _handlers;  		} -		Logger &logger = Logger::instance(); -		logger.debug( std::string( "[req] exiting request handler [" ).append( _req_handler.method ).append( "]" ) ); +		_logger->debug( std::string( "[req] exiting request handler [" ).append( _req_handler.method ).append( "]" ) );  	} @@ -65,13 +66,9 @@ namespace bitz {  	icap::Response * RequestHandler::process( icap::RequestHeader * req_header, psocksxx::iosockstream * socket ) throw() { -  		icap::Request  * request;  		icap::Response * response = NULL; -		// logger -		Logger &logger = Logger::instance(); -  		// request  		request = new icap::Request( req_header ); @@ -79,22 +76,22 @@ namespace bitz {  		// read request data  		if (! icap::util::read_req_data( request, socket ) ) { -			logger.warn( "[req] failed to read request data" ); +			_logger->warn( "[req] failed to read request data" );  			response = new icap::Response( icap::ResponseHeader::SERVER_ERROR );  		} else { -			logger.debug( std::string( "[req] payload.req-hdr:\r\n").append( request->payload().req_header ) ); -			logger.debug( std::string( "[req] payload.req-body:\r\n").append( request->payload().req_body ) ); -			logger.debug( std::string( "[req] payload.res-hdr:\r\n").append( request->payload().res_header ) ); -			logger.debug( std::string( "[req] payload.res-body:\r\n").append( request->payload().res_body ) ); +			_logger->debug( std::string( "[req] payload.req-hdr:\r\n").append( request->payload().req_header ) ); +			_logger->debug( std::string( "[req] payload.req-body:\r\n").append( request->payload().req_body ) ); +			_logger->debug( std::string( "[req] payload.res-hdr:\r\n").append( request->payload().res_header ) ); +			_logger->debug( std::string( "[req] payload.res-body:\r\n").append( request->payload().res_body ) );  			// check for message preview  			if ( request->preview_size() >= 0 ) {  				// process preview -				logger.debug( std::string( "[req] message preview request, preview: " ).append( util::itoa( request->preview_size() ) ) ); +				_logger->debug( std::string( "[req] message preview request, preview: " ).append( util::itoa( request->preview_size() ) ) );  				response = process_preview( request, socket );  			} @@ -108,7 +105,7 @@ namespace bitz {  			if ( response == NULL ) {  				// process modify -				logger.debug( "[req] modify request" ); +				_logger->debug( "[req] modify request" );  				response = process_modify( request );  			} @@ -120,7 +117,7 @@ namespace bitz {  		// sanity check  		if ( response == NULL ) { -			logger.warn( "[req] no valid response from modifiers, creating a server error (500) response" ); +			_logger->warn( "[req] no valid response from modifiers, creating a server error (500) response" );  			response = new icap::Response( icap::ResponseHeader::SERVER_ERROR );  		} @@ -131,18 +128,15 @@ namespace bitz {  	bool RequestHandler::load_modifier( const std::string &file, Modifier::symbols_t &symbols ) throw() { -		// logger -		Logger &logger = Logger::instance(); -  		// vars  		const char* dlsym_error;  		// load the modifier module -		logger.debug( "[req] loading modifier: " + file ); +		_logger->debug( "[req] loading modifier: " + file );  		symbols.modifier = dlopen( file.c_str(), RTLD_LAZY | RTLD_LOCAL );  		if (! symbols.modifier ) { -			logger.warn( std::string( "[req] failed to load modifier: " ).append( file ).append( dlerror() ) ); +			_logger->warn( std::string( "[req] failed to load modifier: " ).append( file ).append( dlerror() ) );  			return false;  		} @@ -154,7 +148,7 @@ namespace bitz {  		dlsym_error    = dlerror();  		if ( dlsym_error ) { -			logger.warn( std::string( "[req] failed to load create symbol: " ).append( dlsym_error ) ); +			_logger->warn( std::string( "[req] failed to load create symbol: " ).append( dlsym_error ) );  			return false;  		} @@ -162,7 +156,7 @@ namespace bitz {  		dlsym_error     = dlerror();  		if ( dlsym_error ) { -			logger.warn( std::string( "[req] failed to load destroy symbol: " ).append( dlsym_error ) ); +			_logger->warn( std::string( "[req] failed to load destroy symbol: " ).append( dlsym_error ) );  			return false;  		} @@ -223,12 +217,9 @@ namespace bitz {  		int i = 0; -		// logger -		Logger &logger = Logger::instance(); -  		for ( i = 0; i < _handlers_count; i++ ) { -			logger.debug( std::string( "[req] unloading module: " ).append( _handlers[i].name ) ); +			_logger->debug( std::string( "[req] unloading module: " ).append( _handlers[i].name ) );  			// unload  			unload_modifier( _handlers[i].symbols.modifier ); @@ -246,9 +237,6 @@ namespace bitz {  		int i = 0;  		bool continue_status = false; -		// logger -		Logger &logger = Logger::instance(); -  		/*  		*  Loop through loaded modifier modules and grab responses @@ -262,17 +250,17 @@ namespace bitz {  			// sanity check  			if ( _handlers[i].name == "" ) { -				logger.info( "[req] modifier not loaded, not trying to get a response" ); +				_logger->info( "[req] modifier not loaded, not trying to get a response" );  				continue;  			}  			// grab the response from modifier -			logger.debug( std::string( "[req] getting preview response from modifier: " ).append( _handlers[i].name ) ); +			_logger->debug( std::string( "[req] getting preview response from modifier: " ).append( _handlers[i].name ) );  			modifier = _handlers[i].symbols.create();  			response = modifier->preview( request );  			// cleanup -			logger.debug( std::string( "[req] cleaning up modifier: " ).append( _handlers[i].name ) ); +			_logger->debug( std::string( "[req] cleaning up modifier: " ).append( _handlers[i].name ) );  			_handlers[i].symbols.destroy( modifier );  			// check response status @@ -309,7 +297,7 @@ namespace bitz {  			}  			// we shouldn't have got this far -			logger.info( std::string( "[req] unrecognised preview response from modifier: " ).append( _handlers[i].name ) ); +			_logger->info( std::string( "[req] unrecognised preview response from modifier: " ).append( _handlers[i].name ) );  		} @@ -325,9 +313,6 @@ namespace bitz {  		int i = 0; -		// logger -		Logger &logger = Logger::instance(); -  		/*  		*  Loop through loaded modifier modules and grab responses @@ -339,22 +324,22 @@ namespace bitz {  			// sanity check  			if ( _handlers[i].name == "" ) { -				logger.info( "[req] modifier not loaded, not trying to get a response" ); +				_logger->info( "[req] modifier not loaded, not trying to get a response" );  				continue;  			}  			// grab the response from modifier -			logger.debug( std::string( "[req] getting modify response from modifier: " ).append( _handlers[i].name ) ); +			_logger->debug( std::string( "[req] getting modify response from modifier: " ).append( _handlers[i].name ) );  			modifier = _handlers[i].symbols.create();  			response = modifier->modify( request );  			// cleanup -			logger.debug( std::string( "[req] cleaning up modifier: " ).append( _handlers[i].name ) ); +			_logger->debug( std::string( "[req] cleaning up modifier: " ).append( _handlers[i].name ) );  			_handlers[i].symbols.destroy( modifier );  			// status 200 OK means content modified  			if ( response->header()->status() == icap::ResponseHeader::OK ) { -				logger.debug( "[req] OK response received, not getting responses from other modifiers" ); +				_logger->debug( "[req] OK response received, not getting responses from other modifiers" );  				break;  			} @@ -369,15 +354,11 @@ namespace bitz {  		bool status = false; -		// logger -		Logger &logger = Logger::instance(); - -  		// sanity check  		if ( request->payload().ieof ) {  			// we can process a '100 Continue' only if an 'ieof' is not received -			logger.warn( "[req] illegal '100 Continue' response" ); +			_logger->warn( "[req] illegal '100 Continue' response" );  		} else { diff --git a/src/bitz/request_handler.h b/src/bitz/request_handler.h index 2c31090..e05832c 100644 --- a/src/bitz/request_handler.h +++ b/src/bitz/request_handler.h @@ -25,6 +25,7 @@  #include <icap/response.h>  #include <icap/request.h>  #include <psocksxx/iosockstream.h> +#include <spdlog/spdlog.h>  namespace bitz { @@ -124,6 +125,7 @@ namespace bitz {  	private:  		req_handler_t _req_handler; +		std::shared_ptr<spdlog::logger> _logger;  	}; diff --git a/src/bitz/worker.cpp b/src/bitz/worker.cpp index 42fb3d0..202f531 100644 --- a/src/bitz/worker.cpp +++ b/src/bitz/worker.cpp @@ -18,7 +18,6 @@   */  #include "worker.h" -#include "logger.h"  #include "util.h"  #include "options_request_handler.h"  #include "reqmod_request_handler.h" @@ -32,6 +31,9 @@ namespace bitz {  	Worker::Worker() { +		// logger +		_logger = spdlog::get( "bitz-server" ); +  		// load request handlers  		load_req_handlers(); @@ -40,9 +42,7 @@ namespace bitz {  	Worker::~Worker() { -		// logger -		Logger &logger = Logger::instance(); -		logger.debug( "[worker] exiting" ); +		_logger->debug( "[worker] exiting" );  		// cleanup request handlers  		util::delete_req_handlers( _req_handlers ); @@ -54,15 +54,12 @@ namespace bitz {  	void Worker::run( psocksxx::tcpnsockstream * server_sock, unsigned int max_requests,  			unsigned int comm_timeout ) throw() { -		// logger -		Logger &logger = Logger::instance(); -  		// client socket stream  		psocksxx::nsockstream * client_sock;  		while ( max_requests > 0 ) { -			logger.debug( std::string( "[worker] waiting for a connection" ) ); +			_logger->debug( std::string( "[worker] waiting for a connection" ) );  			try { @@ -70,7 +67,7 @@ namespace bitz {  				client_sock = server_sock->accept();  				// FIXME: log accepted client details (e.g. address, port etc.) -				logger.debug( std::string( "[worker] new connection accepted on [...]" ) ); +				_logger->debug( std::string( "[worker] new connection accepted on [...]" ) );  				// set timeout value  				if ( comm_timeout > 0 ) { @@ -81,7 +78,7 @@ namespace bitz {  			} catch ( psocksxx::sockexception &e ) {  				// failed to accept client connection -				logger.error( std::string( "[worker] failed to accept connection: " ).append( e.what() ) ); +				_logger->error( std::string( "[worker] failed to accept connection: " ).append( e.what() ) );  				// update request count  				max_requests--; @@ -123,9 +120,6 @@ namespace bitz {  	unsigned int Worker::serve_client( psocksxx::nsockstream * client_sock, unsigned int max_requests ) throw() { -		// logger -		Logger &logger = Logger::instance(); -  		icap::RequestHeader * req_header = NULL;  		icap::Response * response;  		RequestHandler * req_handler; @@ -140,11 +134,11 @@ namespace bitz {  			// request header  			req_header  = icap::util::read_req_header( client_sock ); -			logger.debug( std::string( "[worker] request header:\r\n" ).append( req_header->raw_data() ) ); +			_logger->debug( std::string( "[worker] request header:\r\n" ).append( req_header->raw_data() ) );  			// check timeout  			if ( client_sock->timedout() ) { -				logger.warn( "[worker] communication timed out..." ); +				_logger->warn( "[worker] communication timed out..." );  				return --max_requests;  			} @@ -154,7 +148,7 @@ namespace bitz {  			// sanity check  			if ( req_handler != NULL ) { -				logger.debug( std::string( "[worker] handling request: " ).append( req_header->method() ) ); +				_logger->debug( std::string( "[worker] handling request: " ).append( req_header->method() ) );  				// process the request and grab the response  				response = req_handler->process( req_header, client_sock ); @@ -162,7 +156,7 @@ namespace bitz {  			} else {  				// unsupported request -				logger.info( std::string( "[worker] unsupported request: " ).append( req_header->method() ) ); +				_logger->info( std::string( "[worker] unsupported request: " ).append( req_header->method() ) );  				response = new icap::Response( new icap::ResponseHeader( icap::ResponseHeader::NOT_ALLOWED ) );  			} diff --git a/src/bitz/worker.h b/src/bitz/worker.h index 25b7bac..a3c3951 100644 --- a/src/bitz/worker.h +++ b/src/bitz/worker.h @@ -20,6 +20,7 @@  #ifndef BITZ_WORKER_H  #define BITZ_WORKER_H +#include <spdlog/spdlog.h>  #include <psocksxx/tcpnsockstream.h>  #include "common.h" @@ -36,7 +37,8 @@ namespace bitz {  				unsigned int max_requests, unsigned int comm_timeout = 0 ) throw();  	private: -		req_handlers_t    _req_handlers; +		req_handlers_t _req_handlers; +		std::shared_ptr<spdlog::logger> _logger;  		virtual void load_req_handlers() throw();  		virtual unsigned int serve_client( psocksxx::nsockstream * client_sock, unsigned int max_requests ) throw(); diff --git a/src/main.cpp b/src/main.cpp index 7570e3a..c65ccc4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,8 +22,8 @@  #include <config.h>  #include "bitz-server.h"  #include "bitz/config.h" -#include "bitz/logger.h"  #include "bitz/util.h" +#include "spdlog/spdlog.h"  int main( int argc, char **argv ) { @@ -34,6 +34,12 @@ int main( int argc, char **argv ) {  	// read command line options  	bitz::server::options_t opt = bitz::server::read_options( argc, argv ); +	// logger +	auto logger = spdlog::stdout_color_st( "bitz-server" ); +	if ( opt.debug_flag == 1 ) { +		spdlog::set_level( spdlog::level::debug ); +	} +  	// initialise configurations  	bitz::Config &server_config = bitz::Config::instance(); @@ -48,28 +54,39 @@ int main( int argc, char **argv ) {  	// create directories  	if ( ( opt.debug_flag != 1 ) && (! bitz::util::mkdirp( bitz::util::dirpath( config.pid_file ) ) ) ) { -		std::cerr << "[core] failed to create run dir" << std::endl; +		logger->critical( "[core] failed to create run dir" );  		exit( EXIT_FAILURE );  	}  	if (! bitz::util::mkdirp( bitz::util::dirpath( config.log_file ) ) ) { -		std::cerr << "[core] failed to create log dir" << std::endl; +		logger->critical( "[core] failed to create log dir" );  		exit( EXIT_FAILURE );  	}  	// daemonize  	if ( opt.debug_flag != 1 ) { + +		// close stdout logger +		spdlog::drop( "bitz-server" ); + +		// daemonize  		bitz::server::daemonize( bitz::util::dirpath( config.pid_file ).c_str(), config.pid_file.c_str() ); + +		// open a file logger +		logger = spdlog::rotating_logger_mt( "bitz-server", config.log_file, ( 1048576 * 1024 ), 7 ); + +	} else { +		logger->info( "{} initialised", PACKAGE_STRING );  	} -	// initialise the logger -	bitz::Logger &logger = bitz::Logger::instance( config.log_file, config.log_category ); -	logger.info( std::string( PACKAGE_STRING ) + " initialised" );  	// start the server  	bitz::server::start( config.port, config.max_workers, config.max_worker_requests, config.comm_timeout ); +	// flush logs +	logger->flush(); +  	// run the server  	bitz::server::run();  | 
