diff options
| author | untodesu <kirill@untode.su> | 2025-03-15 16:22:09 +0500 |
|---|---|---|
| committer | untodesu <kirill@untode.su> | 2025-03-15 16:22:09 +0500 |
| commit | 3bf42c6ff3805a0d42bbc661794a95ff31bedc26 (patch) | |
| tree | 05049955847504808d6bed2bb7b155f8b03807bb /deps/include/spdlog/details | |
| parent | 02294547dcde0d4ad76e229106702261e9f10a51 (diff) | |
| download | voxelius-3bf42c6ff3805a0d42bbc661794a95ff31bedc26.tar.bz2 voxelius-3bf42c6ff3805a0d42bbc661794a95ff31bedc26.zip | |
Add whatever I was working on for the last month
Diffstat (limited to 'deps/include/spdlog/details')
27 files changed, 2896 insertions, 0 deletions
diff --git a/deps/include/spdlog/details/backtracer-inl.h b/deps/include/spdlog/details/backtracer-inl.h new file mode 100644 index 0000000..e36c328 --- /dev/null +++ b/deps/include/spdlog/details/backtracer-inl.h @@ -0,0 +1,63 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include <spdlog/details/backtracer.h>
+#endif
+namespace spdlog {
+namespace details {
+SPDLOG_INLINE backtracer::backtracer(const backtracer &other) {
+ std::lock_guard<std::mutex> lock(other.mutex_);
+ enabled_ = other.enabled();
+ messages_ = other.messages_;
+}
+
+SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT {
+ std::lock_guard<std::mutex> lock(other.mutex_);
+ enabled_ = other.enabled();
+ messages_ = std::move(other.messages_);
+}
+
+SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ enabled_ = other.enabled();
+ messages_ = std::move(other.messages_);
+ return *this;
+}
+
+SPDLOG_INLINE void backtracer::enable(size_t size) {
+ std::lock_guard<std::mutex> lock{mutex_};
+ enabled_.store(true, std::memory_order_relaxed);
+ messages_ = circular_q<log_msg_buffer>{size};
+}
+
+SPDLOG_INLINE void backtracer::disable() {
+ std::lock_guard<std::mutex> lock{mutex_};
+ enabled_.store(false, std::memory_order_relaxed);
+}
+
+SPDLOG_INLINE bool backtracer::enabled() const { return enabled_.load(std::memory_order_relaxed); }
+
+SPDLOG_INLINE void backtracer::push_back(const log_msg &msg) {
+ std::lock_guard<std::mutex> lock{mutex_};
+ messages_.push_back(log_msg_buffer{msg});
+}
+
+SPDLOG_INLINE bool backtracer::empty() const {
+ std::lock_guard<std::mutex> lock{mutex_};
+ return messages_.empty();
+}
+
+// pop all items in the q and apply the given fun on each of them.
+SPDLOG_INLINE void backtracer::foreach_pop(std::function<void(const details::log_msg &)> fun) {
+ std::lock_guard<std::mutex> lock{mutex_};
+ while (!messages_.empty()) {
+ auto &front_msg = messages_.front();
+ fun(front_msg);
+ messages_.pop_front();
+ }
+}
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/backtracer.h b/deps/include/spdlog/details/backtracer.h new file mode 100644 index 0000000..fd7c155 --- /dev/null +++ b/deps/include/spdlog/details/backtracer.h @@ -0,0 +1,45 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/details/circular_q.h>
+#include <spdlog/details/log_msg_buffer.h>
+
+#include <atomic>
+#include <functional>
+#include <mutex>
+
+// Store log messages in circular buffer.
+// Useful for storing debug data in case of error/warning happens.
+
+namespace spdlog {
+namespace details {
+class SPDLOG_API backtracer {
+ mutable std::mutex mutex_;
+ std::atomic<bool> enabled_{false};
+ circular_q<log_msg_buffer> messages_;
+
+public:
+ backtracer() = default;
+ backtracer(const backtracer &other);
+
+ backtracer(backtracer &&other) SPDLOG_NOEXCEPT;
+ backtracer &operator=(backtracer other);
+
+ void enable(size_t size);
+ void disable();
+ bool enabled() const;
+ void push_back(const log_msg &msg);
+ bool empty() const;
+
+ // pop all items in the q and apply the given fun on each of them.
+ void foreach_pop(std::function<void(const details::log_msg &)> fun);
+};
+
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "backtracer-inl.h"
+#endif
diff --git a/deps/include/spdlog/details/circular_q.h b/deps/include/spdlog/details/circular_q.h new file mode 100644 index 0000000..780a4dd --- /dev/null +++ b/deps/include/spdlog/details/circular_q.h @@ -0,0 +1,115 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+// circular q view of std::vector.
+#pragma once
+
+#include <cassert>
+#include <vector>
+
+#include "spdlog/common.h"
+
+namespace spdlog {
+namespace details {
+template <typename T>
+class circular_q {
+ size_t max_items_ = 0;
+ typename std::vector<T>::size_type head_ = 0;
+ typename std::vector<T>::size_type tail_ = 0;
+ size_t overrun_counter_ = 0;
+ std::vector<T> v_;
+
+public:
+ using value_type = T;
+
+ // empty ctor - create a disabled queue with no elements allocated at all
+ circular_q() = default;
+
+ explicit circular_q(size_t max_items)
+ : max_items_(max_items + 1) // one item is reserved as marker for full q
+ ,
+ v_(max_items_) {}
+
+ circular_q(const circular_q &) = default;
+ circular_q &operator=(const circular_q &) = default;
+
+ // move cannot be default,
+ // since we need to reset head_, tail_, etc to zero in the moved object
+ circular_q(circular_q &&other) SPDLOG_NOEXCEPT { copy_moveable(std::move(other)); }
+
+ circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT {
+ copy_moveable(std::move(other));
+ return *this;
+ }
+
+ // push back, overrun (oldest) item if no room left
+ void push_back(T &&item) {
+ if (max_items_ > 0) {
+ v_[tail_] = std::move(item);
+ tail_ = (tail_ + 1) % max_items_;
+
+ if (tail_ == head_) // overrun last item if full
+ {
+ head_ = (head_ + 1) % max_items_;
+ ++overrun_counter_;
+ }
+ }
+ }
+
+ // Return reference to the front item.
+ // If there are no elements in the container, the behavior is undefined.
+ const T &front() const { return v_[head_]; }
+
+ T &front() { return v_[head_]; }
+
+ // Return number of elements actually stored
+ size_t size() const {
+ if (tail_ >= head_) {
+ return tail_ - head_;
+ } else {
+ return max_items_ - (head_ - tail_);
+ }
+ }
+
+ // Return const reference to item by index.
+ // If index is out of range 0…size()-1, the behavior is undefined.
+ const T &at(size_t i) const {
+ assert(i < size());
+ return v_[(head_ + i) % max_items_];
+ }
+
+ // Pop item from front.
+ // If there are no elements in the container, the behavior is undefined.
+ void pop_front() { head_ = (head_ + 1) % max_items_; }
+
+ bool empty() const { return tail_ == head_; }
+
+ bool full() const {
+ // head is ahead of the tail by 1
+ if (max_items_ > 0) {
+ return ((tail_ + 1) % max_items_) == head_;
+ }
+ return false;
+ }
+
+ size_t overrun_counter() const { return overrun_counter_; }
+
+ void reset_overrun_counter() { overrun_counter_ = 0; }
+
+private:
+ // copy from other&& and reset it to disabled state
+ void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT {
+ max_items_ = other.max_items_;
+ head_ = other.head_;
+ tail_ = other.tail_;
+ overrun_counter_ = other.overrun_counter_;
+ v_ = std::move(other.v_);
+
+ // put &&other in disabled, but valid state
+ other.max_items_ = 0;
+ other.head_ = other.tail_ = 0;
+ other.overrun_counter_ = 0;
+ }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/console_globals.h b/deps/include/spdlog/details/console_globals.h new file mode 100644 index 0000000..06368fb --- /dev/null +++ b/deps/include/spdlog/details/console_globals.h @@ -0,0 +1,28 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <mutex>
+#include <spdlog/details/null_mutex.h>
+
+namespace spdlog {
+namespace details {
+
+struct console_mutex {
+ using mutex_t = std::mutex;
+ static mutex_t &mutex() {
+ static mutex_t s_mutex;
+ return s_mutex;
+ }
+};
+
+struct console_nullmutex {
+ using mutex_t = null_mutex;
+ static mutex_t &mutex() {
+ static mutex_t s_mutex;
+ return s_mutex;
+ }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/file_helper-inl.h b/deps/include/spdlog/details/file_helper-inl.h new file mode 100644 index 0000000..f3609fb --- /dev/null +++ b/deps/include/spdlog/details/file_helper-inl.h @@ -0,0 +1,152 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include <spdlog/details/file_helper.h>
+#endif
+
+#include <spdlog/common.h>
+#include <spdlog/details/os.h>
+
+#include <cerrno>
+#include <chrono>
+#include <cstdio>
+#include <string>
+#include <thread>
+#include <tuple>
+
+namespace spdlog {
+namespace details {
+
+SPDLOG_INLINE file_helper::file_helper(const file_event_handlers &event_handlers)
+ : event_handlers_(event_handlers) {}
+
+SPDLOG_INLINE file_helper::~file_helper() { close(); }
+
+SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate) {
+ close();
+ filename_ = fname;
+
+ auto *mode = SPDLOG_FILENAME_T("ab");
+ auto *trunc_mode = SPDLOG_FILENAME_T("wb");
+
+ if (event_handlers_.before_open) {
+ event_handlers_.before_open(filename_);
+ }
+ for (int tries = 0; tries < open_tries_; ++tries) {
+ // create containing folder if not exists already.
+ os::create_dir(os::dir_name(fname));
+ if (truncate) {
+ // Truncate by opening-and-closing a tmp file in "wb" mode, always
+ // opening the actual log-we-write-to in "ab" mode, since that
+ // interacts more politely with eternal processes that might
+ // rotate/truncate the file underneath us.
+ std::FILE *tmp;
+ if (os::fopen_s(&tmp, fname, trunc_mode)) {
+ continue;
+ }
+ std::fclose(tmp);
+ }
+ if (!os::fopen_s(&fd_, fname, mode)) {
+ if (event_handlers_.after_open) {
+ event_handlers_.after_open(filename_, fd_);
+ }
+ return;
+ }
+
+ details::os::sleep_for_millis(open_interval_);
+ }
+
+ throw_spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing",
+ errno);
+}
+
+SPDLOG_INLINE void file_helper::reopen(bool truncate) {
+ if (filename_.empty()) {
+ throw_spdlog_ex("Failed re opening file - was not opened before");
+ }
+ this->open(filename_, truncate);
+}
+
+SPDLOG_INLINE void file_helper::flush() {
+ if (std::fflush(fd_) != 0) {
+ throw_spdlog_ex("Failed flush to file " + os::filename_to_str(filename_), errno);
+ }
+}
+
+SPDLOG_INLINE void file_helper::sync() {
+ if (!os::fsync(fd_)) {
+ throw_spdlog_ex("Failed to fsync file " + os::filename_to_str(filename_), errno);
+ }
+}
+
+SPDLOG_INLINE void file_helper::close() {
+ if (fd_ != nullptr) {
+ if (event_handlers_.before_close) {
+ event_handlers_.before_close(filename_, fd_);
+ }
+
+ std::fclose(fd_);
+ fd_ = nullptr;
+
+ if (event_handlers_.after_close) {
+ event_handlers_.after_close(filename_);
+ }
+ }
+}
+
+SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf) {
+ if (fd_ == nullptr) return;
+ size_t msg_size = buf.size();
+ auto data = buf.data();
+ if (std::fwrite(data, 1, msg_size, fd_) != msg_size) {
+ throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno);
+ }
+}
+
+SPDLOG_INLINE size_t file_helper::size() const {
+ if (fd_ == nullptr) {
+ throw_spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_));
+ }
+ return os::filesize(fd_);
+}
+
+SPDLOG_INLINE const filename_t &file_helper::filename() const { return filename_; }
+
+//
+// return file path and its extension:
+//
+// "mylog.txt" => ("mylog", ".txt")
+// "mylog" => ("mylog", "")
+// "mylog." => ("mylog.", "")
+// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
+//
+// the starting dot in filenames is ignored (hidden files):
+//
+// ".mylog" => (".mylog". "")
+// "my_folder/.mylog" => ("my_folder/.mylog", "")
+// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
+SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension(
+ const filename_t &fname) {
+ auto ext_index = fname.rfind('.');
+
+ // no valid extension found - return whole path and empty string as
+ // extension
+ if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1) {
+ return std::make_tuple(fname, filename_t());
+ }
+
+ // treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
+ auto folder_index = fname.find_last_of(details::os::folder_seps_filename);
+ if (folder_index != filename_t::npos && folder_index >= ext_index - 1) {
+ return std::make_tuple(fname, filename_t());
+ }
+
+ // finally - return a valid base and extension tuple
+ return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
+}
+
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/file_helper.h b/deps/include/spdlog/details/file_helper.h new file mode 100644 index 0000000..6dfb486 --- /dev/null +++ b/deps/include/spdlog/details/file_helper.h @@ -0,0 +1,61 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/common.h>
+#include <tuple>
+
+namespace spdlog {
+namespace details {
+
+// Helper class for file sinks.
+// When failing to open a file, retry several times(5) with a delay interval(10 ms).
+// Throw spdlog_ex exception on errors.
+
+class SPDLOG_API file_helper {
+public:
+ file_helper() = default;
+ explicit file_helper(const file_event_handlers &event_handlers);
+
+ file_helper(const file_helper &) = delete;
+ file_helper &operator=(const file_helper &) = delete;
+ ~file_helper();
+
+ void open(const filename_t &fname, bool truncate = false);
+ void reopen(bool truncate);
+ void flush();
+ void sync();
+ void close();
+ void write(const memory_buf_t &buf);
+ size_t size() const;
+ const filename_t &filename() const;
+
+ //
+ // return file path and its extension:
+ //
+ // "mylog.txt" => ("mylog", ".txt")
+ // "mylog" => ("mylog", "")
+ // "mylog." => ("mylog.", "")
+ // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
+ //
+ // the starting dot in filenames is ignored (hidden files):
+ //
+ // ".mylog" => (".mylog". "")
+ // "my_folder/.mylog" => ("my_folder/.mylog", "")
+ // "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
+ static std::tuple<filename_t, filename_t> split_by_extension(const filename_t &fname);
+
+private:
+ const int open_tries_ = 5;
+ const unsigned int open_interval_ = 10;
+ std::FILE *fd_{nullptr};
+ filename_t filename_;
+ file_event_handlers event_handlers_;
+};
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "file_helper-inl.h"
+#endif
diff --git a/deps/include/spdlog/details/fmt_helper.h b/deps/include/spdlog/details/fmt_helper.h new file mode 100644 index 0000000..dcb887c --- /dev/null +++ b/deps/include/spdlog/details/fmt_helper.h @@ -0,0 +1,141 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+#pragma once
+
+#include <chrono>
+#include <iterator>
+#include <spdlog/common.h>
+#include <spdlog/fmt/fmt.h>
+#include <type_traits>
+
+#ifdef SPDLOG_USE_STD_FORMAT
+ #include <charconv>
+ #include <limits>
+#endif
+
+// Some fmt helpers to efficiently format and pad ints and strings
+namespace spdlog {
+namespace details {
+namespace fmt_helper {
+
+inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest) {
+ auto *buf_ptr = view.data();
+ dest.append(buf_ptr, buf_ptr + view.size());
+}
+
+#ifdef SPDLOG_USE_STD_FORMAT
+template <typename T>
+inline void append_int(T n, memory_buf_t &dest) {
+ // Buffer should be large enough to hold all digits (digits10 + 1) and a sign
+ SPDLOG_CONSTEXPR const auto BUF_SIZE = std::numeric_limits<T>::digits10 + 2;
+ char buf[BUF_SIZE];
+
+ auto [ptr, ec] = std::to_chars(buf, buf + BUF_SIZE, n, 10);
+ if (ec == std::errc()) {
+ dest.append(buf, ptr);
+ } else {
+ throw_spdlog_ex("Failed to format int", static_cast<int>(ec));
+ }
+}
+#else
+template <typename T>
+inline void append_int(T n, memory_buf_t &dest) {
+ fmt::format_int i(n);
+ dest.append(i.data(), i.data() + i.size());
+}
+#endif
+
+template <typename T>
+SPDLOG_CONSTEXPR_FUNC unsigned int count_digits_fallback(T n) {
+ // taken from fmt: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/format.h#L899-L912
+ unsigned int count = 1;
+ for (;;) {
+ // Integer division is slow so do it for a group of four digits instead
+ // of for every digit. The idea comes from the talk by Alexandrescu
+ // "Three Optimization Tips for C++". See speed-test for a comparison.
+ if (n < 10) return count;
+ if (n < 100) return count + 1;
+ if (n < 1000) return count + 2;
+ if (n < 10000) return count + 3;
+ n /= 10000u;
+ count += 4;
+ }
+}
+
+template <typename T>
+inline unsigned int count_digits(T n) {
+ using count_type =
+ typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
+#ifdef SPDLOG_USE_STD_FORMAT
+ return count_digits_fallback(static_cast<count_type>(n));
+#else
+ return static_cast<unsigned int>(fmt::
+ // fmt 7.0.0 renamed the internal namespace to detail.
+ // See: https://github.com/fmtlib/fmt/issues/1538
+ #if FMT_VERSION < 70000
+ internal
+ #else
+ detail
+ #endif
+ ::count_digits(static_cast<count_type>(n)));
+#endif
+}
+
+inline void pad2(int n, memory_buf_t &dest) {
+ if (n >= 0 && n < 100) // 0-99
+ {
+ dest.push_back(static_cast<char>('0' + n / 10));
+ dest.push_back(static_cast<char>('0' + n % 10));
+ } else // unlikely, but just in case, let fmt deal with it
+ {
+ fmt_lib::format_to(std::back_inserter(dest), SPDLOG_FMT_STRING("{:02}"), n);
+ }
+}
+
+template <typename T>
+inline void pad_uint(T n, unsigned int width, memory_buf_t &dest) {
+ static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
+ for (auto digits = count_digits(n); digits < width; digits++) {
+ dest.push_back('0');
+ }
+ append_int(n, dest);
+}
+
+template <typename T>
+inline void pad3(T n, memory_buf_t &dest) {
+ static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T");
+ if (n < 1000) {
+ dest.push_back(static_cast<char>(n / 100 + '0'));
+ n = n % 100;
+ dest.push_back(static_cast<char>((n / 10) + '0'));
+ dest.push_back(static_cast<char>((n % 10) + '0'));
+ } else {
+ append_int(n, dest);
+ }
+}
+
+template <typename T>
+inline void pad6(T n, memory_buf_t &dest) {
+ pad_uint(n, 6, dest);
+}
+
+template <typename T>
+inline void pad9(T n, memory_buf_t &dest) {
+ pad_uint(n, 9, dest);
+}
+
+// return fraction of a second of the given time_point.
+// e.g.
+// fraction<std::milliseconds>(tp) -> will return the millis part of the second
+template <typename ToDuration>
+inline ToDuration time_fraction(log_clock::time_point tp) {
+ using std::chrono::duration_cast;
+ using std::chrono::seconds;
+ auto duration = tp.time_since_epoch();
+ auto secs = duration_cast<seconds>(duration);
+ return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
+}
+
+} // namespace fmt_helper
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/log_msg-inl.h b/deps/include/spdlog/details/log_msg-inl.h new file mode 100644 index 0000000..c6fc0de --- /dev/null +++ b/deps/include/spdlog/details/log_msg-inl.h @@ -0,0 +1,44 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include <spdlog/details/log_msg.h>
+#endif
+
+#include <spdlog/details/os.h>
+
+namespace spdlog {
+namespace details {
+
+SPDLOG_INLINE log_msg::log_msg(spdlog::log_clock::time_point log_time,
+ spdlog::source_loc loc,
+ string_view_t a_logger_name,
+ spdlog::level::level_enum lvl,
+ spdlog::string_view_t msg)
+ : logger_name(a_logger_name),
+ level(lvl),
+ time(log_time)
+#ifndef SPDLOG_NO_THREAD_ID
+ ,
+ thread_id(os::thread_id())
+#endif
+ ,
+ source(loc),
+ payload(msg) {
+}
+
+SPDLOG_INLINE log_msg::log_msg(spdlog::source_loc loc,
+ string_view_t a_logger_name,
+ spdlog::level::level_enum lvl,
+ spdlog::string_view_t msg)
+ : log_msg(os::now(), loc, a_logger_name, lvl, msg) {}
+
+SPDLOG_INLINE log_msg::log_msg(string_view_t a_logger_name,
+ spdlog::level::level_enum lvl,
+ spdlog::string_view_t msg)
+ : log_msg(os::now(), source_loc{}, a_logger_name, lvl, msg) {}
+
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/log_msg.h b/deps/include/spdlog/details/log_msg.h new file mode 100644 index 0000000..a674c47 --- /dev/null +++ b/deps/include/spdlog/details/log_msg.h @@ -0,0 +1,40 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/common.h>
+#include <string>
+
+namespace spdlog {
+namespace details {
+struct SPDLOG_API log_msg {
+ log_msg() = default;
+ log_msg(log_clock::time_point log_time,
+ source_loc loc,
+ string_view_t logger_name,
+ level::level_enum lvl,
+ string_view_t msg);
+ log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
+ log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
+ log_msg(const log_msg &other) = default;
+ log_msg &operator=(const log_msg &other) = default;
+
+ string_view_t logger_name;
+ level::level_enum level{level::off};
+ log_clock::time_point time;
+ size_t thread_id{0};
+
+ // wrapping the formatted text with color (updated by pattern_formatter).
+ mutable size_t color_range_start{0};
+ mutable size_t color_range_end{0};
+
+ source_loc source;
+ string_view_t payload;
+};
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "log_msg-inl.h"
+#endif
diff --git a/deps/include/spdlog/details/log_msg_buffer-inl.h b/deps/include/spdlog/details/log_msg_buffer-inl.h new file mode 100644 index 0000000..8d3023b --- /dev/null +++ b/deps/include/spdlog/details/log_msg_buffer-inl.h @@ -0,0 +1,54 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include <spdlog/details/log_msg_buffer.h>
+#endif
+
+namespace spdlog {
+namespace details {
+
+SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg)
+ : log_msg{orig_msg} {
+ buffer.append(logger_name.begin(), logger_name.end());
+ buffer.append(payload.begin(), payload.end());
+ update_string_views();
+}
+
+SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
+ : log_msg{other} {
+ buffer.append(logger_name.begin(), logger_name.end());
+ buffer.append(payload.begin(), payload.end());
+ update_string_views();
+}
+
+SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT
+ : log_msg{other},
+ buffer{std::move(other.buffer)} {
+ update_string_views();
+}
+
+SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other) {
+ log_msg::operator=(other);
+ buffer.clear();
+ buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size());
+ update_string_views();
+ return *this;
+}
+
+SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT {
+ log_msg::operator=(other);
+ buffer = std::move(other.buffer);
+ update_string_views();
+ return *this;
+}
+
+SPDLOG_INLINE void log_msg_buffer::update_string_views() {
+ logger_name = string_view_t{buffer.data(), logger_name.size()};
+ payload = string_view_t{buffer.data() + logger_name.size(), payload.size()};
+}
+
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/log_msg_buffer.h b/deps/include/spdlog/details/log_msg_buffer.h new file mode 100644 index 0000000..e1c0412 --- /dev/null +++ b/deps/include/spdlog/details/log_msg_buffer.h @@ -0,0 +1,32 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/details/log_msg.h>
+
+namespace spdlog {
+namespace details {
+
+// Extend log_msg with internal buffer to store its payload.
+// This is needed since log_msg holds string_views that points to stack data.
+
+class SPDLOG_API log_msg_buffer : public log_msg {
+ memory_buf_t buffer;
+ void update_string_views();
+
+public:
+ log_msg_buffer() = default;
+ explicit log_msg_buffer(const log_msg &orig_msg);
+ log_msg_buffer(const log_msg_buffer &other);
+ log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
+ log_msg_buffer &operator=(const log_msg_buffer &other);
+ log_msg_buffer &operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
+};
+
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "log_msg_buffer-inl.h"
+#endif
diff --git a/deps/include/spdlog/details/mpmc_blocking_q.h b/deps/include/spdlog/details/mpmc_blocking_q.h new file mode 100644 index 0000000..a65b7f8 --- /dev/null +++ b/deps/include/spdlog/details/mpmc_blocking_q.h @@ -0,0 +1,177 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+// multi producer-multi consumer blocking queue.
+// enqueue(..) - will block until room found to put the new message.
+// enqueue_nowait(..) - will return immediately with false if no room left in
+// the queue.
+// dequeue_for(..) - will block until the queue is not empty or timeout have
+// passed.
+
+#include <spdlog/details/circular_q.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+
+namespace spdlog {
+namespace details {
+
+template <typename T>
+class mpmc_blocking_queue {
+public:
+ using item_type = T;
+ explicit mpmc_blocking_queue(size_t max_items)
+ : q_(max_items) {}
+
+#ifndef __MINGW32__
+ // try to enqueue and block if no room left
+ void enqueue(T &&item) {
+ {
+ std::unique_lock<std::mutex> lock(queue_mutex_);
+ pop_cv_.wait(lock, [this] { return !this->q_.full(); });
+ q_.push_back(std::move(item));
+ }
+ push_cv_.notify_one();
+ }
+
+ // enqueue immediately. overrun oldest message in the queue if no room left.
+ void enqueue_nowait(T &&item) {
+ {
+ std::unique_lock<std::mutex> lock(queue_mutex_);
+ q_.push_back(std::move(item));
+ }
+ push_cv_.notify_one();
+ }
+
+ void enqueue_if_have_room(T &&item) {
+ bool pushed = false;
+ {
+ std::unique_lock<std::mutex> lock(queue_mutex_);
+ if (!q_.full()) {
+ q_.push_back(std::move(item));
+ pushed = true;
+ }
+ }
+
+ if (pushed) {
+ push_cv_.notify_one();
+ } else {
+ ++discard_counter_;
+ }
+ }
+
+ // dequeue with a timeout.
+ // Return true, if succeeded dequeue item, false otherwise
+ bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) {
+ {
+ std::unique_lock<std::mutex> lock(queue_mutex_);
+ if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) {
+ return false;
+ }
+ popped_item = std::move(q_.front());
+ q_.pop_front();
+ }
+ pop_cv_.notify_one();
+ return true;
+ }
+
+ // blocking dequeue without a timeout.
+ void dequeue(T &popped_item) {
+ {
+ std::unique_lock<std::mutex> lock(queue_mutex_);
+ push_cv_.wait(lock, [this] { return !this->q_.empty(); });
+ popped_item = std::move(q_.front());
+ q_.pop_front();
+ }
+ pop_cv_.notify_one();
+ }
+
+#else
+ // apparently mingw deadlocks if the mutex is released before cv.notify_one(),
+ // so release the mutex at the very end each function.
+
+ // try to enqueue and block if no room left
+ void enqueue(T &&item) {
+ std::unique_lock<std::mutex> lock(queue_mutex_);
+ pop_cv_.wait(lock, [this] { return !this->q_.full(); });
+ q_.push_back(std::move(item));
+ push_cv_.notify_one();
+ }
+
+ // enqueue immediately. overrun oldest message in the queue if no room left.
+ void enqueue_nowait(T &&item) {
+ std::unique_lock<std::mutex> lock(queue_mutex_);
+ q_.push_back(std::move(item));
+ push_cv_.notify_one();
+ }
+
+ void enqueue_if_have_room(T &&item) {
+ bool pushed = false;
+ std::unique_lock<std::mutex> lock(queue_mutex_);
+ if (!q_.full()) {
+ q_.push_back(std::move(item));
+ pushed = true;
+ }
+
+ if (pushed) {
+ push_cv_.notify_one();
+ } else {
+ ++discard_counter_;
+ }
+ }
+
+ // dequeue with a timeout.
+ // Return true, if succeeded dequeue item, false otherwise
+ bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) {
+ std::unique_lock<std::mutex> lock(queue_mutex_);
+ if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) {
+ return false;
+ }
+ popped_item = std::move(q_.front());
+ q_.pop_front();
+ pop_cv_.notify_one();
+ return true;
+ }
+
+ // blocking dequeue without a timeout.
+ void dequeue(T &popped_item) {
+ std::unique_lock<std::mutex> lock(queue_mutex_);
+ push_cv_.wait(lock, [this] { return !this->q_.empty(); });
+ popped_item = std::move(q_.front());
+ q_.pop_front();
+ pop_cv_.notify_one();
+ }
+
+#endif
+
+ size_t overrun_counter() {
+ std::lock_guard<std::mutex> lock(queue_mutex_);
+ return q_.overrun_counter();
+ }
+
+ size_t discard_counter() { return discard_counter_.load(std::memory_order_relaxed); }
+
+ size_t size() {
+ std::lock_guard<std::mutex> lock(queue_mutex_);
+ return q_.size();
+ }
+
+ void reset_overrun_counter() {
+ std::lock_guard<std::mutex> lock(queue_mutex_);
+ q_.reset_overrun_counter();
+ }
+
+ void reset_discard_counter() { discard_counter_.store(0, std::memory_order_relaxed); }
+
+private:
+ std::mutex queue_mutex_;
+ std::condition_variable push_cv_;
+ std::condition_variable pop_cv_;
+ spdlog::details::circular_q<T> q_;
+ std::atomic<size_t> discard_counter_{0};
+};
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/null_mutex.h b/deps/include/spdlog/details/null_mutex.h new file mode 100644 index 0000000..e771686 --- /dev/null +++ b/deps/include/spdlog/details/null_mutex.h @@ -0,0 +1,35 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <atomic>
+#include <utility>
+// null, no cost dummy "mutex" and dummy "atomic" int
+
+namespace spdlog {
+namespace details {
+struct null_mutex {
+ void lock() const {}
+ void unlock() const {}
+};
+
+struct null_atomic_int {
+ int value;
+ null_atomic_int() = default;
+
+ explicit null_atomic_int(int new_value)
+ : value(new_value) {}
+
+ int load(std::memory_order = std::memory_order_relaxed) const { return value; }
+
+ void store(int new_value, std::memory_order = std::memory_order_relaxed) { value = new_value; }
+
+ int exchange(int new_value, std::memory_order = std::memory_order_relaxed) {
+ std::swap(new_value, value);
+ return new_value; // return value before the call
+ }
+};
+
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/os-inl.h b/deps/include/spdlog/details/os-inl.h new file mode 100644 index 0000000..d2ee7cf --- /dev/null +++ b/deps/include/spdlog/details/os-inl.h @@ -0,0 +1,594 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include <spdlog/details/os.h>
+#endif
+
+#include <spdlog/common.h>
+
+#include <algorithm>
+#include <array>
+#include <chrono>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <string>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <thread>
+
+#ifdef _WIN32
+ #include <spdlog/details/windows_include.h>
+ #include <fileapi.h> // for FlushFileBuffers
+ #include <io.h> // for _get_osfhandle, _isatty, _fileno
+ #include <process.h> // for _get_pid
+
+ #ifdef __MINGW32__
+ #include <share.h>
+ #endif
+
+ #if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
+ #include <cassert>
+ #include <limits>
+ #endif
+
+ #include <direct.h> // for _mkdir/_wmkdir
+
+#else // unix
+
+ #include <fcntl.h>
+ #include <unistd.h>
+
+ #ifdef __linux__
+ #include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
+
+ #elif defined(_AIX)
+ #include <pthread.h> // for pthread_getthrds_np
+
+ #elif defined(__DragonFly__) || defined(__FreeBSD__)
+ #include <pthread_np.h> // for pthread_getthreadid_np
+
+ #elif defined(__NetBSD__)
+ #include <lwp.h> // for _lwp_self
+
+ #elif defined(__sun)
+ #include <thread.h> // for thr_self
+ #endif
+
+#endif // unix
+
+#if defined __APPLE__
+ #include <AvailabilityMacros.h>
+#endif
+
+#ifndef __has_feature // Clang - feature checking macros.
+ #define __has_feature(x) 0 // Compatibility with non-clang compilers.
+#endif
+
+namespace spdlog {
+namespace details {
+namespace os {
+
+SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT {
+#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
+ timespec ts;
+ ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
+ return std::chrono::time_point<log_clock, typename log_clock::duration>(
+ std::chrono::duration_cast<typename log_clock::duration>(
+ std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
+
+#else
+ return log_clock::now();
+#endif
+}
+SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT {
+#ifdef _WIN32
+ std::tm tm;
+ ::localtime_s(&tm, &time_tt);
+#else
+ std::tm tm;
+ ::localtime_r(&time_tt, &tm);
+#endif
+ return tm;
+}
+
+SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT {
+ std::time_t now_t = ::time(nullptr);
+ return localtime(now_t);
+}
+
+SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT {
+#ifdef _WIN32
+ std::tm tm;
+ ::gmtime_s(&tm, &time_tt);
+#else
+ std::tm tm;
+ ::gmtime_r(&time_tt, &tm);
+#endif
+ return tm;
+}
+
+SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT {
+ std::time_t now_t = ::time(nullptr);
+ return gmtime(now_t);
+}
+
+// fopen_s on non windows for writing
+SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) {
+#ifdef _WIN32
+ #ifdef SPDLOG_WCHAR_FILENAMES
+ *fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
+ #else
+ *fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
+ #endif
+ #if defined(SPDLOG_PREVENT_CHILD_FD)
+ if (*fp != nullptr) {
+ auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
+ if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) {
+ ::fclose(*fp);
+ *fp = nullptr;
+ }
+ }
+ #endif
+#else // unix
+ #if defined(SPDLOG_PREVENT_CHILD_FD)
+ const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
+ const int fd =
+ ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
+ if (fd == -1) {
+ return true;
+ }
+ *fp = ::fdopen(fd, mode.c_str());
+ if (*fp == nullptr) {
+ ::close(fd);
+ }
+ #else
+ *fp = ::fopen((filename.c_str()), mode.c_str());
+ #endif
+#endif
+
+ return *fp == nullptr;
+}
+
+SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT {
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+ return ::_wremove(filename.c_str());
+#else
+ return std::remove(filename.c_str());
+#endif
+}
+
+SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT {
+ return path_exists(filename) ? remove(filename) : 0;
+}
+
+SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT {
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+ return ::_wrename(filename1.c_str(), filename2.c_str());
+#else
+ return std::rename(filename1.c_str(), filename2.c_str());
+#endif
+}
+
+// Return true if path exists (file or directory)
+SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT {
+#ifdef _WIN32
+ struct _stat buffer;
+ #ifdef SPDLOG_WCHAR_FILENAMES
+ return (::_wstat(filename.c_str(), &buffer) == 0);
+ #else
+ return (::_stat(filename.c_str(), &buffer) == 0);
+ #endif
+#else // common linux/unix all have the stat system call
+ struct stat buffer;
+ return (::stat(filename.c_str(), &buffer) == 0);
+#endif
+}
+
+#ifdef _MSC_VER
+ // avoid warning about unreachable statement at the end of filesize()
+ #pragma warning(push)
+ #pragma warning(disable : 4702)
+#endif
+
+// Return file size according to open FILE* object
+SPDLOG_INLINE size_t filesize(FILE *f) {
+ if (f == nullptr) {
+ throw_spdlog_ex("Failed getting file size. fd is null");
+ }
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ int fd = ::_fileno(f);
+ #if defined(_WIN64) // 64 bits
+ __int64 ret = ::_filelengthi64(fd);
+ if (ret >= 0) {
+ return static_cast<size_t>(ret);
+ }
+
+ #else // windows 32 bits
+ long ret = ::_filelength(fd);
+ if (ret >= 0) {
+ return static_cast<size_t>(ret);
+ }
+ #endif
+
+#else // unix
+ // OpenBSD and AIX doesn't compile with :: before the fileno(..)
+ #if defined(__OpenBSD__) || defined(_AIX)
+ int fd = fileno(f);
+ #else
+ int fd = ::fileno(f);
+ #endif
+ // 64 bits(but not in osx, linux/musl or cygwin, where fstat64 is deprecated)
+ #if ((defined(__linux__) && defined(__GLIBC__)) || defined(__sun) || defined(_AIX)) && \
+ (defined(__LP64__) || defined(_LP64))
+ struct stat64 st;
+ if (::fstat64(fd, &st) == 0) {
+ return static_cast<size_t>(st.st_size);
+ }
+ #else // other unix or linux 32 bits or cygwin
+ struct stat st;
+ if (::fstat(fd, &st) == 0) {
+ return static_cast<size_t>(st.st_size);
+ }
+ #endif
+#endif
+ throw_spdlog_ex("Failed getting file size from fd", errno);
+ return 0; // will not be reached.
+}
+
+#ifdef _MSC_VER
+ #pragma warning(pop)
+#endif
+
+// Return utc offset in minutes or throw spdlog_ex on failure
+SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) {
+#ifdef _WIN32
+ #if _WIN32_WINNT < _WIN32_WINNT_WS08
+ TIME_ZONE_INFORMATION tzinfo;
+ auto rv = ::GetTimeZoneInformation(&tzinfo);
+ #else
+ DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
+ auto rv = ::GetDynamicTimeZoneInformation(&tzinfo);
+ #endif
+ if (rv == TIME_ZONE_ID_INVALID) throw_spdlog_ex("Failed getting timezone info. ", errno);
+
+ int offset = -tzinfo.Bias;
+ if (tm.tm_isdst) {
+ offset -= tzinfo.DaylightBias;
+ } else {
+ offset -= tzinfo.StandardBias;
+ }
+ return offset;
+#else
+
+ #if defined(sun) || defined(__sun) || defined(_AIX) || \
+ (defined(__NEWLIB__) && !defined(__TM_GMTOFF)) || \
+ (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE))
+ // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
+ struct helper {
+ static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(),
+ const std::tm &gmtm = details::os::gmtime()) {
+ int local_year = localtm.tm_year + (1900 - 1);
+ int gmt_year = gmtm.tm_year + (1900 - 1);
+
+ long int days = (
+ // difference in day of year
+ localtm.tm_yday -
+ gmtm.tm_yday
+
+ // + intervening leap days
+ + ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
+ ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
+
+ // + difference in years * 365 */
+ + static_cast<long int>(local_year - gmt_year) * 365);
+
+ long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
+ long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
+ long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
+
+ return secs;
+ }
+ };
+
+ auto offset_seconds = helper::calculate_gmt_offset(tm);
+ #else
+ auto offset_seconds = tm.tm_gmtoff;
+ #endif
+
+ return static_cast<int>(offset_seconds / 60);
+#endif
+}
+
+// Return current thread id as size_t
+// It exists because the std::this_thread::get_id() is much slower(especially
+// under VS 2013)
+SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT {
+#ifdef _WIN32
+ return static_cast<size_t>(::GetCurrentThreadId());
+#elif defined(__linux__)
+ #if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
+ #define SYS_gettid __NR_gettid
+ #endif
+ return static_cast<size_t>(::syscall(SYS_gettid));
+#elif defined(_AIX)
+ struct __pthrdsinfo buf;
+ int reg_size = 0;
+ pthread_t pt = pthread_self();
+ int retval = pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_TID, &buf, sizeof(buf), NULL, ®_size);
+ int tid = (!retval) ? buf.__pi_tid : 0;
+ return static_cast<size_t>(tid);
+#elif defined(__DragonFly__) || defined(__FreeBSD__)
+ return static_cast<size_t>(::pthread_getthreadid_np());
+#elif defined(__NetBSD__)
+ return static_cast<size_t>(::_lwp_self());
+#elif defined(__OpenBSD__)
+ return static_cast<size_t>(::getthrid());
+#elif defined(__sun)
+ return static_cast<size_t>(::thr_self());
+#elif __APPLE__
+ uint64_t tid;
+ // There is no pthread_threadid_np prior to Mac OS X 10.6, and it is not supported on any PPC,
+ // including 10.6.8 Rosetta. __POWERPC__ is Apple-specific define encompassing ppc and ppc64.
+ #ifdef MAC_OS_X_VERSION_MAX_ALLOWED
+ {
+ #if (MAC_OS_X_VERSION_MAX_ALLOWED < 1060) || defined(__POWERPC__)
+ tid = pthread_mach_thread_np(pthread_self());
+ #elif MAC_OS_X_VERSION_MIN_REQUIRED < 1060
+ if (&pthread_threadid_np) {
+ pthread_threadid_np(nullptr, &tid);
+ } else {
+ tid = pthread_mach_thread_np(pthread_self());
+ }
+ #else
+ pthread_threadid_np(nullptr, &tid);
+ #endif
+ }
+ #else
+ pthread_threadid_np(nullptr, &tid);
+ #endif
+ return static_cast<size_t>(tid);
+#else // Default to standard C++11 (other Unix)
+ return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
+#endif
+}
+
+// Return current thread id as size_t (from thread local storage)
+SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT {
+#if defined(SPDLOG_NO_TLS)
+ return _thread_id();
+#else // cache thread id in tls
+ static thread_local const size_t tid = _thread_id();
+ return tid;
+#endif
+}
+
+// This is avoid msvc issue in sleep_for that happens if the clock changes.
+// See https://github.com/gabime/spdlog/issues/609
+SPDLOG_INLINE void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT {
+#if defined(_WIN32)
+ ::Sleep(milliseconds);
+#else
+ std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
+#endif
+}
+
+// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) {
+ memory_buf_t buf;
+ wstr_to_utf8buf(filename, buf);
+ return SPDLOG_BUF_TO_STRING(buf);
+}
+#else
+SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) { return filename; }
+#endif
+
+SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT {
+#ifdef _WIN32
+ return conditional_static_cast<int>(::GetCurrentProcessId());
+#else
+ return conditional_static_cast<int>(::getpid());
+#endif
+}
+
+// Determine if the terminal supports colors
+// Based on: https://github.com/agauniyal/rang/
+SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT {
+#ifdef _WIN32
+ return true;
+#else
+
+ static const bool result = []() {
+ const char *env_colorterm_p = std::getenv("COLORTERM");
+ if (env_colorterm_p != nullptr) {
+ return true;
+ }
+
+ static constexpr std::array<const char *, 16> terms = {
+ {"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys",
+ "putty", "rxvt", "screen", "vt100", "xterm", "alacritty", "vt102"}};
+
+ const char *env_term_p = std::getenv("TERM");
+ if (env_term_p == nullptr) {
+ return false;
+ }
+
+ return std::any_of(terms.begin(), terms.end(), [&](const char *term) {
+ return std::strstr(env_term_p, term) != nullptr;
+ });
+ }();
+
+ return result;
+#endif
+}
+
+// Determine if the terminal attached
+// Source: https://github.com/agauniyal/rang/
+SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT {
+#ifdef _WIN32
+ return ::_isatty(_fileno(file)) != 0;
+#else
+ return ::isatty(fileno(file)) != 0;
+#endif
+}
+
+#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
+SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) {
+ if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 4 - 1) {
+ throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
+ }
+
+ int wstr_size = static_cast<int>(wstr.size());
+ if (wstr_size == 0) {
+ target.resize(0);
+ return;
+ }
+
+ int result_size = static_cast<int>(target.capacity());
+ if ((wstr_size + 1) * 4 > result_size) {
+ result_size =
+ ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
+ }
+
+ if (result_size > 0) {
+ target.resize(result_size);
+ result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(),
+ result_size, NULL, NULL);
+
+ if (result_size > 0) {
+ target.resize(result_size);
+ return;
+ }
+ }
+
+ throw_spdlog_ex(
+ fmt_lib::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
+}
+
+SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) {
+ if (str.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) - 1) {
+ throw_spdlog_ex("UTF-8 string is too big to be converted to UTF-16");
+ }
+
+ int str_size = static_cast<int>(str.size());
+ if (str_size == 0) {
+ target.resize(0);
+ return;
+ }
+
+ // find the size to allocate for the result buffer
+ int result_size =
+ ::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, NULL, 0);
+
+ if (result_size > 0) {
+ target.resize(result_size);
+ result_size = ::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, target.data(),
+ result_size);
+ if (result_size > 0) {
+ assert(result_size == target.size());
+ return;
+ }
+ }
+
+ throw_spdlog_ex(
+ fmt_lib::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError()));
+}
+#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) &&
+ // defined(_WIN32)
+
+// return true on success
+static SPDLOG_INLINE bool mkdir_(const filename_t &path) {
+#ifdef _WIN32
+ #ifdef SPDLOG_WCHAR_FILENAMES
+ return ::_wmkdir(path.c_str()) == 0;
+ #else
+ return ::_mkdir(path.c_str()) == 0;
+ #endif
+#else
+ return ::mkdir(path.c_str(), mode_t(0755)) == 0;
+#endif
+}
+
+// create the given directory - and all directories leading to it
+// return true on success or if the directory already exists
+SPDLOG_INLINE bool create_dir(const filename_t &path) {
+ if (path_exists(path)) {
+ return true;
+ }
+
+ if (path.empty()) {
+ return false;
+ }
+
+ size_t search_offset = 0;
+ do {
+ auto token_pos = path.find_first_of(folder_seps_filename, search_offset);
+ // treat the entire path as a folder if no folder separator not found
+ if (token_pos == filename_t::npos) {
+ token_pos = path.size();
+ }
+
+ auto subdir = path.substr(0, token_pos);
+#ifdef _WIN32
+ // if subdir is just a drive letter, add a slash e.g. "c:"=>"c:\",
+ // otherwise path_exists(subdir) returns false (issue #3079)
+ const bool is_drive = subdir.length() == 2 && subdir[1] == ':';
+ if (is_drive) {
+ subdir += '\\';
+ token_pos++;
+ }
+#endif
+
+ if (!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir)) {
+ return false; // return error if failed creating dir
+ }
+ search_offset = token_pos + 1;
+ } while (search_offset < path.size());
+
+ return true;
+}
+
+// Return directory name from given path or empty string
+// "abc/file" => "abc"
+// "abc/" => "abc"
+// "abc" => ""
+// "abc///" => "abc//"
+SPDLOG_INLINE filename_t dir_name(const filename_t &path) {
+ auto pos = path.find_last_of(folder_seps_filename);
+ return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
+}
+
+std::string SPDLOG_INLINE getenv(const char *field) {
+#if defined(_MSC_VER)
+ #if defined(__cplusplus_winrt)
+ return std::string{}; // not supported under uwp
+ #else
+ size_t len = 0;
+ char buf[128];
+ bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
+ return ok ? buf : std::string{};
+ #endif
+#else // revert to getenv
+ char *buf = ::getenv(field);
+ return buf ? buf : std::string{};
+#endif
+}
+
+// Do fsync by FILE handlerpointer
+// Return true on success
+SPDLOG_INLINE bool fsync(FILE *fp) {
+#ifdef _WIN32
+ return FlushFileBuffers(reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(fp)))) != 0;
+#else
+ return ::fsync(fileno(fp)) == 0;
+#endif
+}
+
+} // namespace os
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/os.h b/deps/include/spdlog/details/os.h new file mode 100644 index 0000000..fa4fdcd --- /dev/null +++ b/deps/include/spdlog/details/os.h @@ -0,0 +1,123 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <ctime> // std::time_t
+#include <spdlog/common.h>
+
+namespace spdlog {
+namespace details {
+namespace os {
+
+SPDLOG_API spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
+
+SPDLOG_API std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
+
+SPDLOG_API std::tm localtime() SPDLOG_NOEXCEPT;
+
+SPDLOG_API std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
+
+SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
+
+// eol definition
+#if !defined(SPDLOG_EOL)
+ #ifdef _WIN32
+ #define SPDLOG_EOL "\r\n"
+ #else
+ #define SPDLOG_EOL "\n"
+ #endif
+#endif
+
+SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
+
+// folder separator
+#if !defined(SPDLOG_FOLDER_SEPS)
+ #ifdef _WIN32
+ #define SPDLOG_FOLDER_SEPS "\\/"
+ #else
+ #define SPDLOG_FOLDER_SEPS "/"
+ #endif
+#endif
+
+SPDLOG_CONSTEXPR static const char folder_seps[] = SPDLOG_FOLDER_SEPS;
+SPDLOG_CONSTEXPR static const filename_t::value_type folder_seps_filename[] =
+ SPDLOG_FILENAME_T(SPDLOG_FOLDER_SEPS);
+
+// fopen_s on non windows for writing
+SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
+
+// Remove filename. return 0 on success
+SPDLOG_API int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
+
+// Remove file if exists. return 0 on success
+// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
+SPDLOG_API int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
+
+SPDLOG_API int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
+
+// Return if file exists.
+SPDLOG_API bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
+
+// Return file size according to open FILE* object
+SPDLOG_API size_t filesize(FILE *f);
+
+// Return utc offset in minutes or throw spdlog_ex on failure
+SPDLOG_API int utc_minutes_offset(const std::tm &tm = details::os::localtime());
+
+// Return current thread id as size_t
+// It exists because the std::this_thread::get_id() is much slower(especially
+// under VS 2013)
+SPDLOG_API size_t _thread_id() SPDLOG_NOEXCEPT;
+
+// Return current thread id as size_t (from thread local storage)
+SPDLOG_API size_t thread_id() SPDLOG_NOEXCEPT;
+
+// This is avoid msvc issue in sleep_for that happens if the clock changes.
+// See https://github.com/gabime/spdlog/issues/609
+SPDLOG_API void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT;
+
+SPDLOG_API std::string filename_to_str(const filename_t &filename);
+
+SPDLOG_API int pid() SPDLOG_NOEXCEPT;
+
+// Determine if the terminal supports colors
+// Source: https://github.com/agauniyal/rang/
+SPDLOG_API bool is_color_terminal() SPDLOG_NOEXCEPT;
+
+// Determine if the terminal attached
+// Source: https://github.com/agauniyal/rang/
+SPDLOG_API bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
+
+#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
+SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
+
+SPDLOG_API void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target);
+#endif
+
+// Return directory name from given path or empty string
+// "abc/file" => "abc"
+// "abc/" => "abc"
+// "abc" => ""
+// "abc///" => "abc//"
+SPDLOG_API filename_t dir_name(const filename_t &path);
+
+// Create a dir from the given path.
+// Return true if succeeded or if this dir already exists.
+SPDLOG_API bool create_dir(const filename_t &path);
+
+// non thread safe, cross platform getenv/getenv_s
+// return empty string if field not found
+SPDLOG_API std::string getenv(const char *field);
+
+// Do fsync by FILE objectpointer.
+// Return true on success.
+SPDLOG_API bool fsync(FILE *fp);
+
+} // namespace os
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "os-inl.h"
+#endif
diff --git a/deps/include/spdlog/details/periodic_worker-inl.h b/deps/include/spdlog/details/periodic_worker-inl.h new file mode 100644 index 0000000..3c91b40 --- /dev/null +++ b/deps/include/spdlog/details/periodic_worker-inl.h @@ -0,0 +1,26 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include <spdlog/details/periodic_worker.h>
+#endif
+
+namespace spdlog {
+namespace details {
+
+// stop the worker thread and join it
+SPDLOG_INLINE periodic_worker::~periodic_worker() {
+ if (worker_thread_.joinable()) {
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ active_ = false;
+ }
+ cv_.notify_one();
+ worker_thread_.join();
+ }
+}
+
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/periodic_worker.h b/deps/include/spdlog/details/periodic_worker.h new file mode 100644 index 0000000..bf54886 --- /dev/null +++ b/deps/include/spdlog/details/periodic_worker.h @@ -0,0 +1,58 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+// periodic worker thread - periodically executes the given callback function.
+//
+// RAII over the owned thread:
+// creates the thread on construction.
+// stops and joins the thread on destruction (if the thread is executing a callback, wait for it
+// to finish first).
+
+#include <chrono>
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <thread>
+namespace spdlog {
+namespace details {
+
+class SPDLOG_API periodic_worker {
+public:
+ template <typename Rep, typename Period>
+ periodic_worker(const std::function<void()> &callback_fun,
+ std::chrono::duration<Rep, Period> interval) {
+ active_ = (interval > std::chrono::duration<Rep, Period>::zero());
+ if (!active_) {
+ return;
+ }
+
+ worker_thread_ = std::thread([this, callback_fun, interval]() {
+ for (;;) {
+ std::unique_lock<std::mutex> lock(this->mutex_);
+ if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; })) {
+ return; // active_ == false, so exit this thread
+ }
+ callback_fun();
+ }
+ });
+ }
+ std::thread &get_thread() { return worker_thread_; }
+ periodic_worker(const periodic_worker &) = delete;
+ periodic_worker &operator=(const periodic_worker &) = delete;
+ // stop the worker thread and join it
+ ~periodic_worker();
+
+private:
+ bool active_;
+ std::thread worker_thread_;
+ std::mutex mutex_;
+ std::condition_variable cv_;
+};
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "periodic_worker-inl.h"
+#endif
diff --git a/deps/include/spdlog/details/registry-inl.h b/deps/include/spdlog/details/registry-inl.h new file mode 100644 index 0000000..9ae56ae --- /dev/null +++ b/deps/include/spdlog/details/registry-inl.h @@ -0,0 +1,261 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include <spdlog/details/registry.h>
+#endif
+
+#include <spdlog/common.h>
+#include <spdlog/details/periodic_worker.h>
+#include <spdlog/logger.h>
+#include <spdlog/pattern_formatter.h>
+
+#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
+ // support for the default stdout color logger
+ #ifdef _WIN32
+ #include <spdlog/sinks/wincolor_sink.h>
+ #else
+ #include <spdlog/sinks/ansicolor_sink.h>
+ #endif
+#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+namespace spdlog {
+namespace details {
+
+SPDLOG_INLINE registry::registry()
+ : formatter_(new pattern_formatter()) {
+#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
+ // create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows).
+ #ifdef _WIN32
+ auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
+ #else
+ auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
+ #endif
+
+ const char *default_logger_name = "";
+ default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
+ loggers_[default_logger_name] = default_logger_;
+
+#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
+}
+
+SPDLOG_INLINE registry::~registry() = default;
+
+SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger) {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ register_logger_(std::move(new_logger));
+}
+
+SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger) {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ new_logger->set_formatter(formatter_->clone());
+
+ if (err_handler_) {
+ new_logger->set_error_handler(err_handler_);
+ }
+
+ // set new level according to previously configured level or default level
+ auto it = log_levels_.find(new_logger->name());
+ auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
+ new_logger->set_level(new_level);
+
+ new_logger->flush_on(flush_level_);
+
+ if (backtrace_n_messages_ > 0) {
+ new_logger->enable_backtrace(backtrace_n_messages_);
+ }
+
+ if (automatic_registration_) {
+ register_logger_(std::move(new_logger));
+ }
+}
+
+SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_name) {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ auto found = loggers_.find(logger_name);
+ return found == loggers_.end() ? nullptr : found->second;
+}
+
+SPDLOG_INLINE std::shared_ptr<logger> registry::default_logger() {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ return default_logger_;
+}
+
+// Return raw ptr to the default logger.
+// To be used directly by the spdlog default api (e.g. spdlog::info)
+// This make the default API faster, but cannot be used concurrently with set_default_logger().
+// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
+SPDLOG_INLINE logger *registry::get_default_raw() { return default_logger_.get(); }
+
+// set default logger.
+// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
+SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger) {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ if (new_default_logger != nullptr) {
+ loggers_[new_default_logger->name()] = new_default_logger;
+ }
+ default_logger_ = std::move(new_default_logger);
+}
+
+SPDLOG_INLINE void registry::set_tp(std::shared_ptr<thread_pool> tp) {
+ std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
+ tp_ = std::move(tp);
+}
+
+SPDLOG_INLINE std::shared_ptr<thread_pool> registry::get_tp() {
+ std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
+ return tp_;
+}
+
+// Set global formatter. Each sink in each logger will get a clone of this object
+SPDLOG_INLINE void registry::set_formatter(std::unique_ptr<formatter> formatter) {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ formatter_ = std::move(formatter);
+ for (auto &l : loggers_) {
+ l.second->set_formatter(formatter_->clone());
+ }
+}
+
+SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages) {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ backtrace_n_messages_ = n_messages;
+
+ for (auto &l : loggers_) {
+ l.second->enable_backtrace(n_messages);
+ }
+}
+
+SPDLOG_INLINE void registry::disable_backtrace() {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ backtrace_n_messages_ = 0;
+ for (auto &l : loggers_) {
+ l.second->disable_backtrace();
+ }
+}
+
+SPDLOG_INLINE void registry::set_level(level::level_enum log_level) {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ for (auto &l : loggers_) {
+ l.second->set_level(log_level);
+ }
+ global_log_level_ = log_level;
+}
+
+SPDLOG_INLINE void registry::flush_on(level::level_enum log_level) {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ for (auto &l : loggers_) {
+ l.second->flush_on(log_level);
+ }
+ flush_level_ = log_level;
+}
+
+SPDLOG_INLINE void registry::set_error_handler(err_handler handler) {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ for (auto &l : loggers_) {
+ l.second->set_error_handler(handler);
+ }
+ err_handler_ = std::move(handler);
+}
+
+SPDLOG_INLINE void registry::apply_all(
+ const std::function<void(const std::shared_ptr<logger>)> &fun) {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ for (auto &l : loggers_) {
+ fun(l.second);
+ }
+}
+
+SPDLOG_INLINE void registry::flush_all() {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ for (auto &l : loggers_) {
+ l.second->flush();
+ }
+}
+
+SPDLOG_INLINE void registry::drop(const std::string &logger_name) {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ auto is_default_logger = default_logger_ && default_logger_->name() == logger_name;
+ loggers_.erase(logger_name);
+ if (is_default_logger) {
+ default_logger_.reset();
+ }
+}
+
+SPDLOG_INLINE void registry::drop_all() {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ loggers_.clear();
+ default_logger_.reset();
+}
+
+// clean all resources and threads started by the registry
+SPDLOG_INLINE void registry::shutdown() {
+ {
+ std::lock_guard<std::mutex> lock(flusher_mutex_);
+ periodic_flusher_.reset();
+ }
+
+ drop_all();
+
+ {
+ std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
+ tp_.reset();
+ }
+}
+
+SPDLOG_INLINE std::recursive_mutex ®istry::tp_mutex() { return tp_mutex_; }
+
+SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registration) {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ automatic_registration_ = automatic_registration;
+}
+
+SPDLOG_INLINE void registry::set_levels(log_levels levels, level::level_enum *global_level) {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ log_levels_ = std::move(levels);
+ auto global_level_requested = global_level != nullptr;
+ global_log_level_ = global_level_requested ? *global_level : global_log_level_;
+
+ for (auto &logger : loggers_) {
+ auto logger_entry = log_levels_.find(logger.first);
+ if (logger_entry != log_levels_.end()) {
+ logger.second->set_level(logger_entry->second);
+ } else if (global_level_requested) {
+ logger.second->set_level(*global_level);
+ }
+ }
+}
+
+SPDLOG_INLINE registry ®istry::instance() {
+ static registry s_instance;
+ return s_instance;
+}
+
+SPDLOG_INLINE void registry::apply_logger_env_levels(std::shared_ptr<logger> new_logger) {
+ std::lock_guard<std::mutex> lock(logger_map_mutex_);
+ auto it = log_levels_.find(new_logger->name());
+ auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
+ new_logger->set_level(new_level);
+}
+
+SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name) {
+ if (loggers_.find(logger_name) != loggers_.end()) {
+ throw_spdlog_ex("logger with name '" + logger_name + "' already exists");
+ }
+}
+
+SPDLOG_INLINE void registry::register_logger_(std::shared_ptr<logger> new_logger) {
+ auto logger_name = new_logger->name();
+ throw_if_exists_(logger_name);
+ loggers_[logger_name] = std::move(new_logger);
+}
+
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/registry.h b/deps/include/spdlog/details/registry.h new file mode 100644 index 0000000..4dfddc4 --- /dev/null +++ b/deps/include/spdlog/details/registry.h @@ -0,0 +1,129 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+// Loggers registry of unique name->logger pointer
+// An attempt to create a logger with an already existing name will result with spdlog_ex exception.
+// If user requests a non existing logger, nullptr will be returned
+// This class is thread safe
+
+#include <spdlog/common.h>
+#include <spdlog/details/periodic_worker.h>
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+
+namespace spdlog {
+class logger;
+
+namespace details {
+class thread_pool;
+
+class SPDLOG_API registry {
+public:
+ using log_levels = std::unordered_map<std::string, level::level_enum>;
+ registry(const registry &) = delete;
+ registry &operator=(const registry &) = delete;
+
+ void register_logger(std::shared_ptr<logger> new_logger);
+ void initialize_logger(std::shared_ptr<logger> new_logger);
+ std::shared_ptr<logger> get(const std::string &logger_name);
+ std::shared_ptr<logger> default_logger();
+
+ // Return raw ptr to the default logger.
+ // To be used directly by the spdlog default api (e.g. spdlog::info)
+ // This make the default API faster, but cannot be used concurrently with set_default_logger().
+ // e.g do not call set_default_logger() from one thread while calling spdlog::info() from
+ // another.
+ logger *get_default_raw();
+
+ // set default logger and add it to the registry if not registered already.
+ // default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
+ // Note: Make sure to unregister it when no longer needed or before calling again with a new
+ // logger.
+ void set_default_logger(std::shared_ptr<logger> new_default_logger);
+
+ void set_tp(std::shared_ptr<thread_pool> tp);
+
+ std::shared_ptr<thread_pool> get_tp();
+
+ // Set global formatter. Each sink in each logger will get a clone of this object
+ void set_formatter(std::unique_ptr<formatter> formatter);
+
+ void enable_backtrace(size_t n_messages);
+
+ void disable_backtrace();
+
+ void set_level(level::level_enum log_level);
+
+ void flush_on(level::level_enum log_level);
+
+ template <typename Rep, typename Period>
+ void flush_every(std::chrono::duration<Rep, Period> interval) {
+ std::lock_guard<std::mutex> lock(flusher_mutex_);
+ auto clbk = [this]() { this->flush_all(); };
+ periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
+ }
+
+ std::unique_ptr<periodic_worker> &get_flusher() {
+ std::lock_guard<std::mutex> lock(flusher_mutex_);
+ return periodic_flusher_;
+ }
+
+ void set_error_handler(err_handler handler);
+
+ void apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun);
+
+ void flush_all();
+
+ void drop(const std::string &logger_name);
+
+ void drop_all();
+
+ // clean all resources and threads started by the registry
+ void shutdown();
+
+ std::recursive_mutex &tp_mutex();
+
+ void set_automatic_registration(bool automatic_registration);
+
+ // set levels for all existing/future loggers. global_level can be null if should not set.
+ void set_levels(log_levels levels, level::level_enum *global_level);
+
+ static registry &instance();
+
+ void apply_logger_env_levels(std::shared_ptr<logger> new_logger);
+
+private:
+ registry();
+ ~registry();
+
+ void throw_if_exists_(const std::string &logger_name);
+ void register_logger_(std::shared_ptr<logger> new_logger);
+ bool set_level_from_cfg_(logger *logger);
+ std::mutex logger_map_mutex_, flusher_mutex_;
+ std::recursive_mutex tp_mutex_;
+ std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
+ log_levels log_levels_;
+ std::unique_ptr<formatter> formatter_;
+ spdlog::level::level_enum global_log_level_ = level::info;
+ level::level_enum flush_level_ = level::off;
+ err_handler err_handler_;
+ std::shared_ptr<thread_pool> tp_;
+ std::unique_ptr<periodic_worker> periodic_flusher_;
+ std::shared_ptr<logger> default_logger_;
+ bool automatic_registration_ = true;
+ size_t backtrace_n_messages_ = 0;
+};
+
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "registry-inl.h"
+#endif
diff --git a/deps/include/spdlog/details/synchronous_factory.h b/deps/include/spdlog/details/synchronous_factory.h new file mode 100644 index 0000000..0962f72 --- /dev/null +++ b/deps/include/spdlog/details/synchronous_factory.h @@ -0,0 +1,22 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include "registry.h"
+
+namespace spdlog {
+
+// Default logger factory- creates synchronous loggers
+class logger;
+
+struct synchronous_factory {
+ template <typename Sink, typename... SinkArgs>
+ static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&...args) {
+ auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
+ auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink));
+ details::registry::instance().initialize_logger(new_logger);
+ return new_logger;
+ }
+};
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/tcp_client-windows.h b/deps/include/spdlog/details/tcp_client-windows.h new file mode 100644 index 0000000..93d7331 --- /dev/null +++ b/deps/include/spdlog/details/tcp_client-windows.h @@ -0,0 +1,135 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN
+// tcp client helper
+#include <spdlog/common.h>
+#include <spdlog/details/os.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#pragma comment(lib, "Ws2_32.lib")
+#pragma comment(lib, "Mswsock.lib")
+#pragma comment(lib, "AdvApi32.lib")
+
+namespace spdlog {
+namespace details {
+class tcp_client {
+ SOCKET socket_ = INVALID_SOCKET;
+
+ static void init_winsock_() {
+ WSADATA wsaData;
+ auto rv = WSAStartup(MAKEWORD(2, 2), &wsaData);
+ if (rv != 0) {
+ throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
+ }
+ }
+
+ static void throw_winsock_error_(const std::string &msg, int last_error) {
+ char buf[512];
+ ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
+ last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf,
+ (sizeof(buf) / sizeof(char)), NULL);
+
+ throw_spdlog_ex(fmt_lib::format("tcp_sink - {}: {}", msg, buf));
+ }
+
+public:
+ tcp_client() { init_winsock_(); }
+
+ ~tcp_client() {
+ close();
+ ::WSACleanup();
+ }
+
+ bool is_connected() const { return socket_ != INVALID_SOCKET; }
+
+ void close() {
+ ::closesocket(socket_);
+ socket_ = INVALID_SOCKET;
+ }
+
+ SOCKET fd() const { return socket_; }
+
+ // try to connect or throw on failure
+ void connect(const std::string &host, int port) {
+ if (is_connected()) {
+ close();
+ }
+ struct addrinfo hints {};
+ ZeroMemory(&hints, sizeof(hints));
+
+ hints.ai_family = AF_UNSPEC; // To work with IPv4, IPv6, and so on
+ hints.ai_socktype = SOCK_STREAM; // TCP
+ hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
+ hints.ai_protocol = 0;
+
+ auto port_str = std::to_string(port);
+ struct addrinfo *addrinfo_result;
+ auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
+ int last_error = 0;
+ if (rv != 0) {
+ last_error = ::WSAGetLastError();
+ WSACleanup();
+ throw_winsock_error_("getaddrinfo failed", last_error);
+ }
+
+ // Try each address until we successfully connect(2).
+
+ for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next) {
+ socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if (socket_ == INVALID_SOCKET) {
+ last_error = ::WSAGetLastError();
+ WSACleanup();
+ continue;
+ }
+ if (::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen) == 0) {
+ break;
+ } else {
+ last_error = ::WSAGetLastError();
+ close();
+ }
+ }
+ ::freeaddrinfo(addrinfo_result);
+ if (socket_ == INVALID_SOCKET) {
+ WSACleanup();
+ throw_winsock_error_("connect failed", last_error);
+ }
+
+ // set TCP_NODELAY
+ int enable_flag = 1;
+ ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag),
+ sizeof(enable_flag));
+ }
+
+ // Send exactly n_bytes of the given data.
+ // On error close the connection and throw.
+ void send(const char *data, size_t n_bytes) {
+ size_t bytes_sent = 0;
+ while (bytes_sent < n_bytes) {
+ const int send_flags = 0;
+ auto write_result =
+ ::send(socket_, data + bytes_sent, (int)(n_bytes - bytes_sent), send_flags);
+ if (write_result == SOCKET_ERROR) {
+ int last_error = ::WSAGetLastError();
+ close();
+ throw_winsock_error_("send failed", last_error);
+ }
+
+ if (write_result == 0) // (probably should not happen but in any case..)
+ {
+ break;
+ }
+ bytes_sent += static_cast<size_t>(write_result);
+ }
+ }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/tcp_client.h b/deps/include/spdlog/details/tcp_client.h new file mode 100644 index 0000000..7de44e2 --- /dev/null +++ b/deps/include/spdlog/details/tcp_client.h @@ -0,0 +1,127 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifdef _WIN32
+ #error include tcp_client-windows.h instead
+#endif
+
+// tcp client helper
+#include <spdlog/common.h>
+#include <spdlog/details/os.h>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <string>
+
+namespace spdlog {
+namespace details {
+class tcp_client {
+ int socket_ = -1;
+
+public:
+ bool is_connected() const { return socket_ != -1; }
+
+ void close() {
+ if (is_connected()) {
+ ::close(socket_);
+ socket_ = -1;
+ }
+ }
+
+ int fd() const { return socket_; }
+
+ ~tcp_client() { close(); }
+
+ // try to connect or throw on failure
+ void connect(const std::string &host, int port) {
+ close();
+ struct addrinfo hints {};
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC; // To work with IPv4, IPv6, and so on
+ hints.ai_socktype = SOCK_STREAM; // TCP
+ hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
+ hints.ai_protocol = 0;
+
+ auto port_str = std::to_string(port);
+ struct addrinfo *addrinfo_result;
+ auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
+ if (rv != 0) {
+ throw_spdlog_ex(fmt_lib::format("::getaddrinfo failed: {}", gai_strerror(rv)));
+ }
+
+ // Try each address until we successfully connect(2).
+ int last_errno = 0;
+ for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next) {
+#if defined(SOCK_CLOEXEC)
+ const int flags = SOCK_CLOEXEC;
+#else
+ const int flags = 0;
+#endif
+ socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
+ if (socket_ == -1) {
+ last_errno = errno;
+ continue;
+ }
+ rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
+ if (rv == 0) {
+ break;
+ }
+ last_errno = errno;
+ ::close(socket_);
+ socket_ = -1;
+ }
+ ::freeaddrinfo(addrinfo_result);
+ if (socket_ == -1) {
+ throw_spdlog_ex("::connect failed", last_errno);
+ }
+
+ // set TCP_NODELAY
+ int enable_flag = 1;
+ ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag),
+ sizeof(enable_flag));
+
+ // prevent sigpipe on systems where MSG_NOSIGNAL is not available
+#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
+ ::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char *>(&enable_flag),
+ sizeof(enable_flag));
+#endif
+
+#if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
+ #error "tcp_sink would raise SIGPIPE since neither SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
+#endif
+ }
+
+ // Send exactly n_bytes of the given data.
+ // On error close the connection and throw.
+ void send(const char *data, size_t n_bytes) {
+ size_t bytes_sent = 0;
+ while (bytes_sent < n_bytes) {
+#if defined(MSG_NOSIGNAL)
+ const int send_flags = MSG_NOSIGNAL;
+#else
+ const int send_flags = 0;
+#endif
+ auto write_result =
+ ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
+ if (write_result < 0) {
+ close();
+ throw_spdlog_ex("write(2) failed", errno);
+ }
+
+ if (write_result == 0) // (probably should not happen but in any case..)
+ {
+ break;
+ }
+ bytes_sent += static_cast<size_t>(write_result);
+ }
+ }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/thread_pool-inl.h b/deps/include/spdlog/details/thread_pool-inl.h new file mode 100644 index 0000000..6283753 --- /dev/null +++ b/deps/include/spdlog/details/thread_pool-inl.h @@ -0,0 +1,127 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include <spdlog/details/thread_pool.h>
+#endif
+
+#include <cassert>
+#include <spdlog/common.h>
+
+namespace spdlog {
+namespace details {
+
+SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items,
+ size_t threads_n,
+ std::function<void()> on_thread_start,
+ std::function<void()> on_thread_stop)
+ : q_(q_max_items) {
+ if (threads_n == 0 || threads_n > 1000) {
+ throw_spdlog_ex(
+ "spdlog::thread_pool(): invalid threads_n param (valid "
+ "range is 1-1000)");
+ }
+ for (size_t i = 0; i < threads_n; i++) {
+ threads_.emplace_back([this, on_thread_start, on_thread_stop] {
+ on_thread_start();
+ this->thread_pool::worker_loop_();
+ on_thread_stop();
+ });
+ }
+}
+
+SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items,
+ size_t threads_n,
+ std::function<void()> on_thread_start)
+ : thread_pool(q_max_items, threads_n, on_thread_start, [] {}) {}
+
+SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n)
+ : thread_pool(
+ q_max_items, threads_n, [] {}, [] {}) {}
+
+// message all threads to terminate gracefully join them
+SPDLOG_INLINE thread_pool::~thread_pool() {
+ SPDLOG_TRY {
+ for (size_t i = 0; i < threads_.size(); i++) {
+ post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
+ }
+
+ for (auto &t : threads_) {
+ t.join();
+ }
+ }
+ SPDLOG_CATCH_STD
+}
+
+void SPDLOG_INLINE thread_pool::post_log(async_logger_ptr &&worker_ptr,
+ const details::log_msg &msg,
+ async_overflow_policy overflow_policy) {
+ async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
+ post_async_msg_(std::move(async_m), overflow_policy);
+}
+
+void SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr &&worker_ptr,
+ async_overflow_policy overflow_policy) {
+ post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
+}
+
+size_t SPDLOG_INLINE thread_pool::overrun_counter() { return q_.overrun_counter(); }
+
+void SPDLOG_INLINE thread_pool::reset_overrun_counter() { q_.reset_overrun_counter(); }
+
+size_t SPDLOG_INLINE thread_pool::discard_counter() { return q_.discard_counter(); }
+
+void SPDLOG_INLINE thread_pool::reset_discard_counter() { q_.reset_discard_counter(); }
+
+size_t SPDLOG_INLINE thread_pool::queue_size() { return q_.size(); }
+
+void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg,
+ async_overflow_policy overflow_policy) {
+ if (overflow_policy == async_overflow_policy::block) {
+ q_.enqueue(std::move(new_msg));
+ } else if (overflow_policy == async_overflow_policy::overrun_oldest) {
+ q_.enqueue_nowait(std::move(new_msg));
+ } else {
+ assert(overflow_policy == async_overflow_policy::discard_new);
+ q_.enqueue_if_have_room(std::move(new_msg));
+ }
+}
+
+void SPDLOG_INLINE thread_pool::worker_loop_() {
+ while (process_next_msg_()) {
+ }
+}
+
+// process next message in the queue
+// return true if this thread should still be active (while no terminate msg
+// was received)
+bool SPDLOG_INLINE thread_pool::process_next_msg_() {
+ async_msg incoming_async_msg;
+ q_.dequeue(incoming_async_msg);
+
+ switch (incoming_async_msg.msg_type) {
+ case async_msg_type::log: {
+ incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg);
+ return true;
+ }
+ case async_msg_type::flush: {
+ incoming_async_msg.worker_ptr->backend_flush_();
+ return true;
+ }
+
+ case async_msg_type::terminate: {
+ return false;
+ }
+
+ default: {
+ assert(false);
+ }
+ }
+
+ return true;
+}
+
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/thread_pool.h b/deps/include/spdlog/details/thread_pool.h new file mode 100644 index 0000000..9ff7fc5 --- /dev/null +++ b/deps/include/spdlog/details/thread_pool.h @@ -0,0 +1,117 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include <spdlog/details/log_msg_buffer.h>
+#include <spdlog/details/mpmc_blocking_q.h>
+#include <spdlog/details/os.h>
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <thread>
+#include <vector>
+
+namespace spdlog {
+class async_logger;
+
+namespace details {
+
+using async_logger_ptr = std::shared_ptr<spdlog::async_logger>;
+
+enum class async_msg_type { log, flush, terminate };
+
+// Async msg to move to/from the queue
+// Movable only. should never be copied
+struct async_msg : log_msg_buffer {
+ async_msg_type msg_type{async_msg_type::log};
+ async_logger_ptr worker_ptr;
+
+ async_msg() = default;
+ ~async_msg() = default;
+
+ // should only be moved in or out of the queue..
+ async_msg(const async_msg &) = delete;
+
+// support for vs2013 move
+#if defined(_MSC_VER) && _MSC_VER <= 1800
+ async_msg(async_msg &&other)
+ : log_msg_buffer(std::move(other)),
+ msg_type(other.msg_type),
+ worker_ptr(std::move(other.worker_ptr)) {}
+
+ async_msg &operator=(async_msg &&other) {
+ *static_cast<log_msg_buffer *>(this) = std::move(other);
+ msg_type = other.msg_type;
+ worker_ptr = std::move(other.worker_ptr);
+ return *this;
+ }
+#else // (_MSC_VER) && _MSC_VER <= 1800
+ async_msg(async_msg &&) = default;
+ async_msg &operator=(async_msg &&) = default;
+#endif
+
+ // construct from log_msg with given type
+ async_msg(async_logger_ptr &&worker, async_msg_type the_type, const details::log_msg &m)
+ : log_msg_buffer{m},
+ msg_type{the_type},
+ worker_ptr{std::move(worker)} {}
+
+ async_msg(async_logger_ptr &&worker, async_msg_type the_type)
+ : log_msg_buffer{},
+ msg_type{the_type},
+ worker_ptr{std::move(worker)} {}
+
+ explicit async_msg(async_msg_type the_type)
+ : async_msg{nullptr, the_type} {}
+};
+
+class SPDLOG_API thread_pool {
+public:
+ using item_type = async_msg;
+ using q_type = details::mpmc_blocking_queue<item_type>;
+
+ thread_pool(size_t q_max_items,
+ size_t threads_n,
+ std::function<void()> on_thread_start,
+ std::function<void()> on_thread_stop);
+ thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start);
+ thread_pool(size_t q_max_items, size_t threads_n);
+
+ // message all threads to terminate gracefully and join them
+ ~thread_pool();
+
+ thread_pool(const thread_pool &) = delete;
+ thread_pool &operator=(thread_pool &&) = delete;
+
+ void post_log(async_logger_ptr &&worker_ptr,
+ const details::log_msg &msg,
+ async_overflow_policy overflow_policy);
+ void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
+ size_t overrun_counter();
+ void reset_overrun_counter();
+ size_t discard_counter();
+ void reset_discard_counter();
+ size_t queue_size();
+
+private:
+ q_type q_;
+
+ std::vector<std::thread> threads_;
+
+ void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy);
+ void worker_loop_();
+
+ // process next message in the queue
+ // return true if this thread should still be active (while no terminate msg
+ // was received)
+ bool process_next_msg_();
+};
+
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "thread_pool-inl.h"
+#endif
diff --git a/deps/include/spdlog/details/udp_client-windows.h b/deps/include/spdlog/details/udp_client-windows.h new file mode 100644 index 0000000..4473807 --- /dev/null +++ b/deps/include/spdlog/details/udp_client-windows.h @@ -0,0 +1,98 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+// Helper RAII over winsock udp client socket.
+// Will throw on construction if socket creation failed.
+
+#include <spdlog/common.h>
+#include <spdlog/details/os.h>
+#include <spdlog/details/windows_include.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#if defined(_MSC_VER)
+ #pragma comment(lib, "Ws2_32.lib")
+ #pragma comment(lib, "Mswsock.lib")
+ #pragma comment(lib, "AdvApi32.lib")
+#endif
+
+namespace spdlog {
+namespace details {
+class udp_client {
+ static constexpr int TX_BUFFER_SIZE = 1024 * 10;
+ SOCKET socket_ = INVALID_SOCKET;
+ sockaddr_in addr_ = {};
+
+ static void init_winsock_() {
+ WSADATA wsaData;
+ auto rv = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
+ if (rv != 0) {
+ throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
+ }
+ }
+
+ static void throw_winsock_error_(const std::string &msg, int last_error) {
+ char buf[512];
+ ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
+ last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf,
+ (sizeof(buf) / sizeof(char)), NULL);
+
+ throw_spdlog_ex(fmt_lib::format("udp_sink - {}: {}", msg, buf));
+ }
+
+ void cleanup_() {
+ if (socket_ != INVALID_SOCKET) {
+ ::closesocket(socket_);
+ }
+ socket_ = INVALID_SOCKET;
+ ::WSACleanup();
+ }
+
+public:
+ udp_client(const std::string &host, uint16_t port) {
+ init_winsock_();
+
+ addr_.sin_family = PF_INET;
+ addr_.sin_port = htons(port);
+ addr_.sin_addr.s_addr = INADDR_ANY;
+ if (InetPtonA(PF_INET, host.c_str(), &addr_.sin_addr.s_addr) != 1) {
+ int last_error = ::WSAGetLastError();
+ ::WSACleanup();
+ throw_winsock_error_("error: Invalid address!", last_error);
+ }
+
+ socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
+ if (socket_ == INVALID_SOCKET) {
+ int last_error = ::WSAGetLastError();
+ ::WSACleanup();
+ throw_winsock_error_("error: Create Socket failed", last_error);
+ }
+
+ int option_value = TX_BUFFER_SIZE;
+ if (::setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
+ reinterpret_cast<const char *>(&option_value), sizeof(option_value)) < 0) {
+ int last_error = ::WSAGetLastError();
+ cleanup_();
+ throw_winsock_error_("error: setsockopt(SO_SNDBUF) Failed!", last_error);
+ }
+ }
+
+ ~udp_client() { cleanup_(); }
+
+ SOCKET fd() const { return socket_; }
+
+ void send(const char *data, size_t n_bytes) {
+ socklen_t tolen = sizeof(struct sockaddr);
+ if (::sendto(socket_, data, static_cast<int>(n_bytes), 0, (struct sockaddr *)&addr_,
+ tolen) == -1) {
+ throw_spdlog_ex("sendto(2) failed", errno);
+ }
+ }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/udp_client.h b/deps/include/spdlog/details/udp_client.h new file mode 100644 index 0000000..07fe2fd --- /dev/null +++ b/deps/include/spdlog/details/udp_client.h @@ -0,0 +1,81 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+// Helper RAII over unix udp client socket.
+// Will throw on construction if the socket creation failed.
+
+#ifdef _WIN32
+ #error "include udp_client-windows.h instead"
+#endif
+
+#include <arpa/inet.h>
+#include <cstring>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <spdlog/common.h>
+#include <spdlog/details/os.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <string>
+
+namespace spdlog {
+namespace details {
+
+class udp_client {
+ static constexpr int TX_BUFFER_SIZE = 1024 * 10;
+ int socket_ = -1;
+ struct sockaddr_in sockAddr_;
+
+ void cleanup_() {
+ if (socket_ != -1) {
+ ::close(socket_);
+ socket_ = -1;
+ }
+ }
+
+public:
+ udp_client(const std::string &host, uint16_t port) {
+ socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
+ if (socket_ < 0) {
+ throw_spdlog_ex("error: Create Socket Failed!");
+ }
+
+ int option_value = TX_BUFFER_SIZE;
+ if (::setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
+ reinterpret_cast<const char *>(&option_value), sizeof(option_value)) < 0) {
+ cleanup_();
+ throw_spdlog_ex("error: setsockopt(SO_SNDBUF) Failed!");
+ }
+
+ sockAddr_.sin_family = AF_INET;
+ sockAddr_.sin_port = htons(port);
+
+ if (::inet_aton(host.c_str(), &sockAddr_.sin_addr) == 0) {
+ cleanup_();
+ throw_spdlog_ex("error: Invalid address!");
+ }
+
+ ::memset(sockAddr_.sin_zero, 0x00, sizeof(sockAddr_.sin_zero));
+ }
+
+ ~udp_client() { cleanup_(); }
+
+ int fd() const { return socket_; }
+
+ // Send exactly n_bytes of the given data.
+ // On error close the connection and throw.
+ void send(const char *data, size_t n_bytes) {
+ ssize_t toslen = 0;
+ socklen_t tolen = sizeof(struct sockaddr);
+ if ((toslen = ::sendto(socket_, data, n_bytes, 0, (struct sockaddr *)&sockAddr_, tolen)) ==
+ -1) {
+ throw_spdlog_ex("sendto(2) failed", errno);
+ }
+ }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/deps/include/spdlog/details/windows_include.h b/deps/include/spdlog/details/windows_include.h new file mode 100644 index 0000000..8150d4e --- /dev/null +++ b/deps/include/spdlog/details/windows_include.h @@ -0,0 +1,11 @@ +#pragma once
+
+#ifndef NOMINMAX
+ #define NOMINMAX // prevent windows redefining min/max
+#endif
+
+#ifndef WIN32_LEAN_AND_MEAN
+ #define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <windows.h>
|
