This commit is contained in:
gabime 2017-11-06 12:39:04 +02:00
parent 6ab2f0e099
commit 93be7713e0
11 changed files with 4457 additions and 3587 deletions

View File

@ -1,168 +1,176 @@
/* /*
A modified version of Bounded MPMC queue by Dmitry Vyukov. A modified version of Bounded MPMC queue by Dmitry Vyukov.
Original code from: Original code from:
http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
licensed by Dmitry Vyukov under the terms below: licensed by Dmitry Vyukov under the terms below:
Simplified BSD license Simplified BSD license
Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met: are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of 1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer. conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list 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 of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution. provided with the distribution.
THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those of the authors and The views and conclusions contained in the software and documentation are those of the authors and
should not be interpreted as representing official policies, either expressed or implied, of Dmitry Vyukov. should not be interpreted as representing official policies, either expressed or implied, of Dmitry Vyukov.
*/ */
/* /*
The code in its current form adds the license below: The code in its current form adds the license below:
Copyright(c) 2015 Gabi Melman. Copyright(c) 2015 Gabi Melman.
Distributed under the MIT License (http://opensource.org/licenses/MIT) Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/ */
#pragma once #pragma once
#include "spdlog/common.h" #include "spdlog/common.h"
#include <atomic> #include <atomic>
#include <utility> #include <utility>
namespace spdlog namespace spdlog
{ {
namespace details namespace details
{ {
template<typename T> template<typename T>
class mpmc_bounded_queue class mpmc_bounded_queue
{ {
public: public:
using item_type = T; using item_type = T;
mpmc_bounded_queue(size_t buffer_size) mpmc_bounded_queue(size_t buffer_size)
:max_size_(buffer_size), :max_size_(buffer_size),
buffer_(new cell_t[buffer_size]), buffer_(new cell_t[buffer_size]),
buffer_mask_(buffer_size - 1) buffer_mask_(buffer_size - 1)
{ {
//queue size must be power of two //queue size must be power of two
if (!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0))) if (!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0)))
throw spdlog_ex("async logger queue size must be power of two"); throw spdlog_ex("async logger queue size must be power of two");
for (size_t i = 0; i != buffer_size; i += 1) for (size_t i = 0; i != buffer_size; i += 1)
buffer_[i].sequence_.store(i, std::memory_order_relaxed); buffer_[i].sequence_.store(i, std::memory_order_relaxed);
enqueue_pos_.store(0, std::memory_order_relaxed); enqueue_pos_.store(0, std::memory_order_relaxed);
dequeue_pos_.store(0, std::memory_order_relaxed); dequeue_pos_.store(0, std::memory_order_relaxed);
} }
~mpmc_bounded_queue() ~mpmc_bounded_queue()
{ {
delete[] buffer_; delete[] buffer_;
} }
bool enqueue(T&& data) bool enqueue(T&& data)
{ {
cell_t* cell; cell_t* cell;
size_t pos = enqueue_pos_.load(std::memory_order_relaxed); size_t pos = enqueue_pos_.load(std::memory_order_relaxed);
for (;;) { for (;;)
cell = &buffer_[pos & buffer_mask_]; {
size_t seq = cell->sequence_.load(std::memory_order_acquire); cell = &buffer_[pos & buffer_mask_];
intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos); size_t seq = cell->sequence_.load(std::memory_order_acquire);
if (dif == 0) { intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos);
if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) if (dif == 0)
break; {
} if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
else if (dif < 0) { break;
return false; }
} else if (dif < 0)
else { {
pos = enqueue_pos_.load(std::memory_order_relaxed); return false;
} }
} else
cell->data_ = std::move(data); {
cell->sequence_.store(pos + 1, std::memory_order_release); pos = enqueue_pos_.load(std::memory_order_relaxed);
return true; }
} }
cell->data_ = std::move(data);
bool dequeue(T& data) cell->sequence_.store(pos + 1, std::memory_order_release);
{ return true;
cell_t* cell; }
size_t pos = dequeue_pos_.load(std::memory_order_relaxed);
for (;;) { bool dequeue(T& data)
cell = &buffer_[pos & buffer_mask_]; {
size_t seq = cell_t* cell;
cell->sequence_.load(std::memory_order_acquire); size_t pos = dequeue_pos_.load(std::memory_order_relaxed);
intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos + 1); for (;;)
if (dif == 0) { {
if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) cell = &buffer_[pos & buffer_mask_];
break; size_t seq =
} cell->sequence_.load(std::memory_order_acquire);
else if (dif < 0) intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos + 1);
return false; if (dif == 0)
else {
pos = dequeue_pos_.load(std::memory_order_relaxed); if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
} break;
data = std::move(cell->data_); }
cell->sequence_.store(pos + buffer_mask_ + 1, std::memory_order_release); else if (dif < 0)
return true; return false;
} else
pos = dequeue_pos_.load(std::memory_order_relaxed);
bool is_empty() }
{ data = std::move(cell->data_);
size_t front, front1, back; cell->sequence_.store(pos + buffer_mask_ + 1, std::memory_order_release);
// try to take a consistent snapshot of front/tail. return true;
do { }
front = enqueue_pos_.load(std::memory_order_acquire);
back = dequeue_pos_.load(std::memory_order_acquire); bool is_empty()
front1 = enqueue_pos_.load(std::memory_order_relaxed); {
} while (front != front1); size_t front, front1, back;
return back == front; // try to take a consistent snapshot of front/tail.
} do
{
private: front = enqueue_pos_.load(std::memory_order_acquire);
struct cell_t back = dequeue_pos_.load(std::memory_order_acquire);
{ front1 = enqueue_pos_.load(std::memory_order_relaxed);
std::atomic<size_t> sequence_; }
T data_; while (front != front1);
}; return back == front;
}
size_t const max_size_;
private:
static size_t const cacheline_size = 64; struct cell_t
typedef char cacheline_pad_t[cacheline_size]; {
std::atomic<size_t> sequence_;
cacheline_pad_t pad0_; T data_;
cell_t* const buffer_; };
size_t const buffer_mask_;
cacheline_pad_t pad1_; size_t const max_size_;
std::atomic<size_t> enqueue_pos_;
cacheline_pad_t pad2_; static size_t const cacheline_size = 64;
std::atomic<size_t> dequeue_pos_; typedef char cacheline_pad_t[cacheline_size];
cacheline_pad_t pad3_;
cacheline_pad_t pad0_;
mpmc_bounded_queue(mpmc_bounded_queue const&) = delete; cell_t* const buffer_;
void operator= (mpmc_bounded_queue const&) = delete; size_t const buffer_mask_;
}; cacheline_pad_t pad1_;
std::atomic<size_t> enqueue_pos_;
} // ns details cacheline_pad_t pad2_;
} // ns spdlog std::atomic<size_t> dequeue_pos_;
cacheline_pad_t pad3_;
mpmc_bounded_queue(mpmc_bounded_queue const&) = delete;
void operator= (mpmc_bounded_queue const&) = delete;
};
} // ns details
} // ns spdlog

View File

@ -1,469 +1,469 @@
// //
// Copyright(c) 2015 Gabi Melman. // Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#pragma once #pragma once
#include "spdlog/common.h" #include "spdlog/common.h"
#include <cstdio> #include <cstdio>
#include <ctime> #include <ctime>
#include <functional> #include <functional>
#include <string> #include <string>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
#include <cstdlib> #include <cstdlib>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#ifdef _WIN32 #ifdef _WIN32
#ifndef NOMINMAX #ifndef NOMINMAX
#define NOMINMAX //prevent windows redefining min/max #define NOMINMAX //prevent windows redefining min/max
#endif #endif
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
#include <windows.h> #include <windows.h>
#include <process.h> // _get_pid support #include <process.h> // _get_pid support
#include <io.h> // _get_osfhandle and _isatty support #include <io.h> // _get_osfhandle and _isatty support
#ifdef __MINGW32__ #ifdef __MINGW32__
#include <share.h> #include <share.h>
#endif #endif
#else // unix #else // unix
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#ifdef __linux__ #ifdef __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id #include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#elif __FreeBSD__ #elif __FreeBSD__
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id #include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
#endif #endif
#endif //unix #endif //unix
#ifndef __has_feature // Clang - feature checking macros. #ifndef __has_feature // Clang - feature checking macros.
#define __has_feature(x) 0 // Compatibility with non-clang compilers. #define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif #endif
namespace spdlog namespace spdlog
{ {
namespace details namespace details
{ {
namespace os namespace os
{ {
inline spdlog::log_clock::time_point now() inline spdlog::log_clock::time_point now()
{ {
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE #if defined __linux__ && defined SPDLOG_CLOCK_COARSE
timespec ts; timespec ts;
::clock_gettime(CLOCK_REALTIME_COARSE, &ts); ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
return std::chrono::time_point<log_clock, typename log_clock::duration>( return std::chrono::time_point<log_clock, typename log_clock::duration>(
std::chrono::duration_cast<typename log_clock::duration>( std::chrono::duration_cast<typename log_clock::duration>(
std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec))); std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
#else #else
return log_clock::now(); return log_clock::now();
#endif #endif
} }
inline std::tm localtime(const std::time_t &time_tt) inline std::tm localtime(const std::time_t &time_tt)
{ {
#ifdef _WIN32 #ifdef _WIN32
std::tm tm; std::tm tm;
localtime_s(&tm, &time_tt); localtime_s(&tm, &time_tt);
#else #else
std::tm tm; std::tm tm;
localtime_r(&time_tt, &tm); localtime_r(&time_tt, &tm);
#endif #endif
return tm; return tm;
} }
inline std::tm localtime() inline std::tm localtime()
{ {
std::time_t now_t = time(nullptr); std::time_t now_t = time(nullptr);
return localtime(now_t); return localtime(now_t);
} }
inline std::tm gmtime(const std::time_t &time_tt) inline std::tm gmtime(const std::time_t &time_tt)
{ {
#ifdef _WIN32 #ifdef _WIN32
std::tm tm; std::tm tm;
gmtime_s(&tm, &time_tt); gmtime_s(&tm, &time_tt);
#else #else
std::tm tm; std::tm tm;
gmtime_r(&time_tt, &tm); gmtime_r(&time_tt, &tm);
#endif #endif
return tm; return tm;
} }
inline std::tm gmtime() inline std::tm gmtime()
{ {
std::time_t now_t = time(nullptr); std::time_t now_t = time(nullptr);
return gmtime(now_t); return gmtime(now_t);
} }
inline bool operator==(const std::tm& tm1, const std::tm& tm2) inline bool operator==(const std::tm& tm1, const std::tm& tm2)
{ {
return (tm1.tm_sec == tm2.tm_sec && return (tm1.tm_sec == tm2.tm_sec &&
tm1.tm_min == tm2.tm_min && tm1.tm_min == tm2.tm_min &&
tm1.tm_hour == tm2.tm_hour && tm1.tm_hour == tm2.tm_hour &&
tm1.tm_mday == tm2.tm_mday && tm1.tm_mday == tm2.tm_mday &&
tm1.tm_mon == tm2.tm_mon && tm1.tm_mon == tm2.tm_mon &&
tm1.tm_year == tm2.tm_year && tm1.tm_year == tm2.tm_year &&
tm1.tm_isdst == tm2.tm_isdst); tm1.tm_isdst == tm2.tm_isdst);
} }
inline bool operator!=(const std::tm& tm1, const std::tm& tm2) inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
{ {
return !(tm1 == tm2); return !(tm1 == tm2);
} }
// eol definition // eol definition
#if !defined (SPDLOG_EOL) #if !defined (SPDLOG_EOL)
#ifdef _WIN32 #ifdef _WIN32
#define SPDLOG_EOL "\r\n" #define SPDLOG_EOL "\r\n"
#else #else
#define SPDLOG_EOL "\n" #define SPDLOG_EOL "\n"
#endif #endif
#endif #endif
SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL; SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL;
SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1; SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;
inline void prevent_child_fd(FILE *f) inline void prevent_child_fd(FILE *f)
{ {
#ifdef _WIN32 #ifdef _WIN32
auto file_handle = (HANDLE)_get_osfhandle(_fileno(f)); auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
throw spdlog_ex("SetHandleInformation failed", errno); throw spdlog_ex("SetHandleInformation failed", errno);
#else #else
auto fd = fileno(f); auto fd = fileno(f);
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno); throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
#endif #endif
} }
//fopen_s on non windows for writing //fopen_s on non windows for writing
inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode) inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode)
{ {
#ifdef _WIN32 #ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES #ifdef SPDLOG_WCHAR_FILENAMES
*fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
#else #else
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
#endif #endif
#else //unix #else //unix
*fp = fopen((filename.c_str()), mode.c_str()); *fp = fopen((filename.c_str()), mode.c_str());
#endif #endif
#ifdef SPDLOG_PREVENT_CHILD_FD #ifdef SPDLOG_PREVENT_CHILD_FD
if (*fp != nullptr) if (*fp != nullptr)
prevent_child_fd(*fp); prevent_child_fd(*fp);
#endif #endif
return *fp == nullptr; return *fp == nullptr;
} }
inline int remove(const filename_t &filename) inline int remove(const filename_t &filename)
{ {
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
return _wremove(filename.c_str()); return _wremove(filename.c_str());
#else #else
return std::remove(filename.c_str()); return std::remove(filename.c_str());
#endif #endif
} }
inline int rename(const filename_t& filename1, const filename_t& filename2) inline int rename(const filename_t& filename1, const filename_t& filename2)
{ {
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
return _wrename(filename1.c_str(), filename2.c_str()); return _wrename(filename1.c_str(), filename2.c_str());
#else #else
return std::rename(filename1.c_str(), filename2.c_str()); return std::rename(filename1.c_str(), filename2.c_str());
#endif #endif
} }
//Return if file exists //Return if file exists
inline bool file_exists(const filename_t& filename) inline bool file_exists(const filename_t& filename)
{ {
#ifdef _WIN32 #ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES #ifdef SPDLOG_WCHAR_FILENAMES
auto attribs = GetFileAttributesW(filename.c_str()); auto attribs = GetFileAttributesW(filename.c_str());
#else #else
auto attribs = GetFileAttributesA(filename.c_str()); auto attribs = GetFileAttributesA(filename.c_str());
#endif #endif
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY)); return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
#else //common linux/unix all have the stat system call #else //common linux/unix all have the stat system call
struct stat buffer; struct stat buffer;
return (stat(filename.c_str(), &buffer) == 0); return (stat(filename.c_str(), &buffer) == 0);
#endif #endif
} }
//Return file size according to open FILE* object //Return file size according to open FILE* object
inline size_t filesize(FILE *f) inline size_t filesize(FILE *f)
{ {
if (f == nullptr) if (f == nullptr)
throw spdlog_ex("Failed getting file size. fd is null"); throw spdlog_ex("Failed getting file size. fd is null");
#if defined ( _WIN32) && !defined(__CYGWIN__) #if defined ( _WIN32) && !defined(__CYGWIN__)
int fd = _fileno(f); int fd = _fileno(f);
#if _WIN64 //64 bits #if _WIN64 //64 bits
struct _stat64 st; struct _stat64 st;
if (_fstat64(fd, &st) == 0) if (_fstat64(fd, &st) == 0)
return st.st_size; return st.st_size;
#else //windows 32 bits #else //windows 32 bits
long ret = _filelength(fd); long ret = _filelength(fd);
if (ret >= 0) if (ret >= 0)
return static_cast<size_t>(ret); return static_cast<size_t>(ret);
#endif #endif
#else // unix #else // unix
int fd = fileno(f); int fd = fileno(f);
//64 bits(but not in osx or cygwin, where fstat64 is deprecated) //64 bits(but not in osx or cygwin, where fstat64 is deprecated)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__) #if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__)
struct stat64 st; struct stat64 st;
if (fstat64(fd, &st) == 0) if (fstat64(fd, &st) == 0)
return static_cast<size_t>(st.st_size); return static_cast<size_t>(st.st_size);
#else // unix 32 bits or cygwin #else // unix 32 bits or cygwin
struct stat st; struct stat st;
if (fstat(fd, &st) == 0) if (fstat(fd, &st) == 0)
return static_cast<size_t>(st.st_size); return static_cast<size_t>(st.st_size);
#endif #endif
#endif #endif
throw spdlog_ex("Failed getting file size from fd", errno); throw spdlog_ex("Failed getting file size from fd", errno);
} }
//Return utc offset in minutes or throw spdlog_ex on failure //Return utc offset in minutes or throw spdlog_ex on failure
inline int utc_minutes_offset(const std::tm& tm = details::os::localtime()) inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
{ {
#ifdef _WIN32 #ifdef _WIN32
#if _WIN32_WINNT < _WIN32_WINNT_WS08 #if _WIN32_WINNT < _WIN32_WINNT_WS08
TIME_ZONE_INFORMATION tzinfo; TIME_ZONE_INFORMATION tzinfo;
auto rv = GetTimeZoneInformation(&tzinfo); auto rv = GetTimeZoneInformation(&tzinfo);
#else #else
DYNAMIC_TIME_ZONE_INFORMATION tzinfo; DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
auto rv = GetDynamicTimeZoneInformation(&tzinfo); auto rv = GetDynamicTimeZoneInformation(&tzinfo);
#endif #endif
if (rv == TIME_ZONE_ID_INVALID) if (rv == TIME_ZONE_ID_INVALID)
throw spdlog::spdlog_ex("Failed getting timezone info. ", errno); throw spdlog::spdlog_ex("Failed getting timezone info. ", errno);
int offset = -tzinfo.Bias; int offset = -tzinfo.Bias;
if (tm.tm_isdst) if (tm.tm_isdst)
offset -= tzinfo.DaylightBias; offset -= tzinfo.DaylightBias;
else else
offset -= tzinfo.StandardBias; offset -= tzinfo.StandardBias;
return offset; return offset;
#else #else
#if defined(sun) || defined(__sun) #if defined(sun) || defined(__sun)
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
struct helper struct helper
{ {
static long int calculate_gmt_offset(const std::tm & localtm = details::os::localtime(), const std::tm & gmtm = details::os::gmtime()) 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 local_year = localtm.tm_year + (1900 - 1);
int gmt_year = gmtm.tm_year + (1900 - 1); int gmt_year = gmtm.tm_year + (1900 - 1);
long int days = ( long int days = (
// difference in day of year // difference in day of year
localtm.tm_yday - gmtm.tm_yday localtm.tm_yday - gmtm.tm_yday
// + intervening leap days // + intervening leap days
+ ((local_year >> 2) - (gmt_year >> 2)) + ((local_year >> 2) - (gmt_year >> 2))
- (local_year / 100 - gmt_year / 100) - (local_year / 100 - gmt_year / 100)
+ ((local_year / 100 >> 2) - (gmt_year / 100 >> 2)) + ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
// + difference in years * 365 */ // + difference in years * 365 */
+ (long int)(local_year - gmt_year) * 365 + (long int)(local_year - gmt_year) * 365
); );
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour); long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min); long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec); long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
return secs; return secs;
} }
}; };
long int offset_seconds = helper::calculate_gmt_offset(tm); long int offset_seconds = helper::calculate_gmt_offset(tm);
#else #else
long int offset_seconds = tm.tm_gmtoff; long int offset_seconds = tm.tm_gmtoff;
#endif #endif
return static_cast<int>(offset_seconds / 60); return static_cast<int>(offset_seconds / 60);
#endif #endif
} }
//Return current thread id as size_t //Return current thread id as size_t
//It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013) //It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013)
inline size_t _thread_id() inline size_t _thread_id()
{ {
#ifdef _WIN32 #ifdef _WIN32
return static_cast<size_t>(::GetCurrentThreadId()); return static_cast<size_t>(::GetCurrentThreadId());
#elif __linux__ #elif __linux__
# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) # if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
# define SYS_gettid __NR_gettid # define SYS_gettid __NR_gettid
# endif # endif
return static_cast<size_t>(syscall(SYS_gettid)); return static_cast<size_t>(syscall(SYS_gettid));
#elif __FreeBSD__ #elif __FreeBSD__
long tid; long tid;
thr_self(&tid); thr_self(&tid);
return static_cast<size_t>(tid); return static_cast<size_t>(tid);
#elif __APPLE__ #elif __APPLE__
uint64_t tid; uint64_t tid;
pthread_threadid_np(nullptr, &tid); pthread_threadid_np(nullptr, &tid);
return static_cast<size_t>(tid); return static_cast<size_t>(tid);
#else //Default to standard C++11 (other Unix) #else //Default to standard C++11 (other Unix)
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id())); return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
#endif #endif
} }
//Return current thread id as size_t (from thread local storage) //Return current thread id as size_t (from thread local storage)
inline size_t thread_id() inline size_t thread_id()
{ {
#if defined(_MSC_VER) && (_MSC_VER < 1900) || defined(__clang__) && !__has_feature(cxx_thread_local) #if defined(_MSC_VER) && (_MSC_VER < 1900) || defined(__clang__) && !__has_feature(cxx_thread_local)
return _thread_id(); return _thread_id();
#else #else
static thread_local const size_t tid = _thread_id(); static thread_local const size_t tid = _thread_id();
return tid; return tid;
#endif #endif
} }
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
#define SPDLOG_FILENAME_T(s) L ## s #define SPDLOG_FILENAME_T(s) L ## s
inline std::string filename_to_str(const filename_t& filename) inline std::string filename_to_str(const filename_t& filename)
{ {
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c; std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c;
return c.to_bytes(filename); return c.to_bytes(filename);
} }
#else #else
#define SPDLOG_FILENAME_T(s) s #define SPDLOG_FILENAME_T(s) s
inline std::string filename_to_str(const filename_t& filename) inline std::string filename_to_str(const filename_t& filename)
{ {
return filename; return filename;
} }
#endif #endif
inline std::string errno_to_string(char[256], char* res) inline std::string errno_to_string(char[256], char* res)
{ {
return std::string(res); return std::string(res);
} }
inline std::string errno_to_string(char buf[256], int res) inline std::string errno_to_string(char buf[256], int res)
{ {
if (res == 0) if (res == 0)
{ {
return std::string(buf); return std::string(buf);
} }
else else
{ {
return "Unknown error"; return "Unknown error";
} }
} }
// Return errno string (thread safe) // Return errno string (thread safe)
inline std::string errno_str(int err_num) inline std::string errno_str(int err_num)
{ {
char buf[256]; char buf[256];
SPDLOG_CONSTEXPR auto buf_size = sizeof(buf); SPDLOG_CONSTEXPR auto buf_size = sizeof(buf);
#ifdef _WIN32 #ifdef _WIN32
if (strerror_s(buf, buf_size, err_num) == 0) if (strerror_s(buf, buf_size, err_num) == 0)
return std::string(buf); return std::string(buf);
else else
return "Unknown error"; return "Unknown error";
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \ #elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version ((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version
if (strerror_r(err_num, buf, buf_size) == 0) if (strerror_r(err_num, buf, buf_size) == 0)
return std::string(buf); return std::string(buf);
else else
return "Unknown error"; return "Unknown error";
#else // gnu version (might not use the given buf, so its retval pointer must be used) #else // gnu version (might not use the given buf, so its retval pointer must be used)
auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type
return errno_to_string(buf, err); // use overloading to select correct stringify function return errno_to_string(buf, err); // use overloading to select correct stringify function
#endif #endif
} }
inline int pid() inline int pid()
{ {
#ifdef _WIN32 #ifdef _WIN32
return ::_getpid(); return ::_getpid();
#else #else
return static_cast<int>(::getpid()); return static_cast<int>(::getpid());
#endif #endif
} }
// Detrmine if the terminal supports colors // Detrmine if the terminal supports colors
// Source: https://github.com/agauniyal/rang/ // Source: https://github.com/agauniyal/rang/
inline bool is_color_terminal() inline bool is_color_terminal()
{ {
#ifdef _WIN32 #ifdef _WIN32
return true; return true;
#else #else
static constexpr const char* Terms[] = static constexpr const char* Terms[] =
{ {
"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm",
"linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm" "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"
}; };
const char *env_p = std::getenv("TERM"); const char *env_p = std::getenv("TERM");
if (env_p == nullptr) if (env_p == nullptr)
{ {
return false; return false;
} }
static const bool result = std::any_of( static const bool result = std::any_of(
std::begin(Terms), std::end(Terms), [&](const char* term) std::begin(Terms), std::end(Terms), [&](const char* term)
{ {
return std::strstr(env_p, term) != nullptr; return std::strstr(env_p, term) != nullptr;
}); });
return result; return result;
#endif #endif
} }
// Detrmine if the terminal attached // Detrmine if the terminal attached
// Source: https://github.com/agauniyal/rang/ // Source: https://github.com/agauniyal/rang/
inline bool in_terminal(FILE* file) inline bool in_terminal(FILE* file)
{ {
#ifdef _WIN32 #ifdef _WIN32
return _isatty(_fileno(file)) ? true : false; return _isatty(_fileno(file)) ? true : false;
#else #else
return isatty(fileno(file)) ? true : false; return isatty(fileno(file)) ? true : false;
#endif #endif
} }
} //os } //os
} //details } //details
} //spdlog } //spdlog

File diff suppressed because it is too large Load Diff

View File

@ -13,57 +13,65 @@
#include "format.h" #include "format.h"
#include <ostream> #include <ostream>
namespace fmt { namespace fmt
{
namespace internal { namespace internal
{
template <class Char> template <class Char>
class FormatBuf : public std::basic_streambuf<Char> { class FormatBuf : public std::basic_streambuf<Char>
private: {
typedef typename std::basic_streambuf<Char>::int_type int_type; private:
typedef typename std::basic_streambuf<Char>::traits_type traits_type; typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
Buffer<Char> &buffer_; Buffer<Char> &buffer_;
public: public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {} FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {}
protected: protected:
// The put-area is actually always empty. This makes the implementation // The put-area is actually always empty. This makes the implementation
// simpler and has the advantage that the streambuf and the buffer are always // simpler and has the advantage that the streambuf and the buffer are always
// in sync and sputc never writes into uninitialized memory. The obvious // in sync and sputc never writes into uninitialized memory. The obvious
// disadvantage is that each call to sputc always results in a (virtual) call // 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 // to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn. // results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { 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)); if (!traits_type::eq_int_type(ch, traits_type::eof()))
return ch; buffer_.push_back(static_cast<Char>(ch));
} return ch;
}
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE { std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE
buffer_.append(s, s + count); {
return count; buffer_.append(s, s + count);
} return count;
}
}; };
Yes &convert(std::ostream &); Yes &convert(std::ostream &);
struct DummyStream : std::ostream { struct DummyStream : std::ostream
DummyStream(); // Suppress a bogus warning in MSVC. {
// Hide all operator<< overloads from std::ostream. DummyStream(); // Suppress a bogus warning in MSVC.
void operator<<(Null<>); // Hide all operator<< overloads from std::ostream.
void operator<<(Null<>);
}; };
No &operator<<(std::ostream &, int); No &operator<<(std::ostream &, int);
template<typename T> template<typename T>
struct ConvertToIntImpl<T, true> { struct ConvertToIntImpl<T, true>
// Convert to int only if T doesn't have an overloaded operator<<. {
enum { // Convert to int only if T doesn't have an overloaded operator<<.
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No) enum
}; {
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
}; };
// Write the content of w to os. // Write the content of w to os.
@ -73,16 +81,17 @@ FMT_API void write(std::ostream &os, Writer &w);
// Formats a value. // Formats a value.
template <typename Char, typename ArgFormatter_, typename T> template <typename Char, typename ArgFormatter_, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter_> &f, void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
const Char *&format_str, const T &value) { const Char *&format_str, const T &value)
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer; {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
internal::FormatBuf<Char> format_buf(buffer); internal::FormatBuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf); std::basic_ostream<Char> output(&format_buf);
output << value; output << value;
BasicStringRef<Char> str(&buffer[0], buffer.size()); BasicStringRef<Char> str(&buffer[0], buffer.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg; typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
format_str = f.format(format_str, MakeArg(str)); format_str = f.format(format_str, MakeArg(str));
} }
/** /**

View File

@ -64,112 +64,134 @@
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
namespace fmt { namespace fmt
{
// An error code. // An error code.
class ErrorCode { class ErrorCode
private: {
int value_; private:
int value_;
public: public:
explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {} explicit ErrorCode(int value = 0) FMT_NOEXCEPT :
value_(value) {}
int get() const FMT_NOEXCEPT { return value_; } int get() const FMT_NOEXCEPT
{
return value_;
}
}; };
// A buffered file. // A buffered file.
class BufferedFile { class BufferedFile
private: {
FILE *file_; private:
FILE *file_;
friend class File; friend class File;
explicit BufferedFile(FILE *f) : file_(f) {} 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: public:
// A "move constructor" for moving from a temporary. // Constructs a BufferedFile object which doesn't represent any file.
BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {} BufferedFile() FMT_NOEXCEPT :
file_(FMT_NULL) {}
// A "move constructor" for moving from an lvalue. // Destroys the object closing the file it represents if any.
BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) { FMT_API ~BufferedFile() FMT_NOEXCEPT;
f.file_ = FMT_NULL;
}
// A "move assignment operator" for moving from a temporary. #if !FMT_USE_RVALUE_REFERENCES
BufferedFile &operator=(Proxy p) { // Emulate a move constructor and a move assignment operator if rvalue
close(); // references are not supported.
file_ = p.file;
return *this;
}
// A "move assignment operator" for moving from an lvalue. private:
BufferedFile &operator=(BufferedFile &other) { // A proxy object to emulate a move constructor.
close(); // It is private to make it impossible call operator Proxy directly.
file_ = other.file_; struct Proxy
other.file_ = FMT_NULL; {
return *this; FILE *file;
} };
// Returns a proxy object for moving from a temporary: public:
// BufferedFile file = BufferedFile(...); // A "move constructor" for moving from a temporary.
operator Proxy() FMT_NOEXCEPT { BufferedFile(Proxy p) FMT_NOEXCEPT :
Proxy p = {file_}; file_(p.file) {}
file_ = FMT_NULL;
return p; // 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 #else
private: private:
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile); FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
public: public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) { BufferedFile(BufferedFile &&other) FMT_NOEXCEPT :
other.file_ = FMT_NULL; file_(other.file_)
} {
other.file_ = FMT_NULL;
}
BufferedFile& operator=(BufferedFile &&other) { BufferedFile& operator=(BufferedFile &&other)
close(); {
file_ = other.file_; close();
other.file_ = FMT_NULL; file_ = other.file_;
return *this; other.file_ = FMT_NULL;
} return *this;
}
#endif #endif
// Opens a file. // Opens a file.
FMT_API BufferedFile(CStringRef filename, CStringRef mode); FMT_API BufferedFile(CStringRef filename, CStringRef mode);
// Closes the file. // Closes the file.
FMT_API void close(); FMT_API void close();
// Returns the pointer to a FILE object representing this file. // Returns the pointer to a FILE object representing this file.
FILE *get() const FMT_NOEXCEPT { return file_; } FILE *get() const FMT_NOEXCEPT
{
return file_;
}
// We place parentheses around fileno to workaround a bug in some versions // We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro. // of MinGW that define fileno as a macro.
FMT_API int (fileno)() const; FMT_API int (fileno)() const;
void print(CStringRef format_str, const ArgList &args) { void print(CStringRef format_str, const ArgList &args)
fmt::print(file_, format_str, args); {
} fmt::print(file_, format_str, args);
FMT_VARIADIC(void, print, CStringRef) }
FMT_VARIADIC(void, print, CStringRef)
}; };
// A file. Closed file is represented by a File object with descriptor -1. // A file. Closed file is represented by a File object with descriptor -1.
@ -178,125 +200,141 @@ public:
// closing the file multiple times will cause a crash on Windows rather // closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the // than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler. // invalid parameter handler with _set_invalid_parameter_handler.
class File { class File
private: {
int fd_; // File descriptor. private:
int fd_; // File descriptor.
// Constructs a File object with a given descriptor. // Constructs a File object with a given descriptor.
explicit File(int fd) : fd_(fd) {} explicit File(int fd) : fd_(fd) {}
public: public:
// Possible values for the oflag argument to the constructor. // Possible values for the oflag argument to the constructor.
enum { enum
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. {
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. 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. // Constructs a File object which doesn't represent any file.
File() FMT_NOEXCEPT : fd_(-1) {} File() FMT_NOEXCEPT :
fd_(-1) {}
// Opens a file and constructs a File object representing this file. // Opens a file and constructs a File object representing this file.
FMT_API File(CStringRef path, int oflag); FMT_API File(CStringRef path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES #if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue // Emulate a move constructor and a move assignment operator if rvalue
// references are not supported. // references are not supported.
private: private:
// A proxy object to emulate a move constructor. // A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly. // It is private to make it impossible call operator Proxy directly.
struct Proxy { struct Proxy
int fd; {
}; int fd;
};
public: public:
// A "move constructor" for moving from a temporary. // A "move constructor" for moving from a temporary.
File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {} File(Proxy p) FMT_NOEXCEPT :
fd_(p.fd) {}
// A "move constructor" for moving from an lvalue. // A "move constructor" for moving from an lvalue.
File(File &other) FMT_NOEXCEPT : fd_(other.fd_) { File(File &other) FMT_NOEXCEPT :
other.fd_ = -1; fd_(other.fd_)
} {
other.fd_ = -1;
}
// A "move assignment operator" for moving from a temporary. // A "move assignment operator" for moving from a temporary.
File &operator=(Proxy p) { File &operator=(Proxy p)
close(); {
fd_ = p.fd; close();
return *this; fd_ = p.fd;
} return *this;
}
// A "move assignment operator" for moving from an lvalue. // A "move assignment operator" for moving from an lvalue.
File &operator=(File &other) { File &operator=(File &other)
close(); {
fd_ = other.fd_; close();
other.fd_ = -1; fd_ = other.fd_;
return *this; other.fd_ = -1;
} return *this;
}
// Returns a proxy object for moving from a temporary: // Returns a proxy object for moving from a temporary:
// File file = File(...); // File file = File(...);
operator Proxy() FMT_NOEXCEPT { operator Proxy() FMT_NOEXCEPT
Proxy p = {fd_}; {
fd_ = -1; Proxy p = {fd_};
return p; fd_ = -1;
} return p;
}
#else #else
private: private:
FMT_DISALLOW_COPY_AND_ASSIGN(File); FMT_DISALLOW_COPY_AND_ASSIGN(File);
public: public:
File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) { File(File &&other) FMT_NOEXCEPT :
other.fd_ = -1; fd_(other.fd_)
} {
other.fd_ = -1;
}
File& operator=(File &&other) { File& operator=(File &&other)
close(); {
fd_ = other.fd_; close();
other.fd_ = -1; fd_ = other.fd_;
return *this; other.fd_ = -1;
} return *this;
}
#endif #endif
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~File() FMT_NOEXCEPT; FMT_API ~File() FMT_NOEXCEPT;
// Returns the file descriptor. // Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; } int descriptor() const FMT_NOEXCEPT
{
return fd_;
}
// Closes the file. // Closes the file.
FMT_API void close(); FMT_API void close();
// Returns the file size. The size has signed type for consistency with // Returns the file size. The size has signed type for consistency with
// stat::st_size. // stat::st_size.
FMT_API LongLong size() const; FMT_API LongLong size() const;
// Attempts to read count bytes from the file into the specified buffer. // Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void *buffer, std::size_t count); FMT_API std::size_t read(void *buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file. // 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); FMT_API std::size_t write(const void *buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns // Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object. // the duplicate as a file object.
FMT_API static File dup(int fd); FMT_API static File dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd); FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT; FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading // Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively. // and writing respectively.
FMT_API static void pipe(File &read_end, File &write_end); FMT_API static void pipe(File &read_end, File &write_end);
// Creates a BufferedFile object associated with this file and detaches // Creates a BufferedFile object associated with this file and detaches
// this File object from the file. // this File object from the file.
FMT_API BufferedFile fdopen(const char *mode); FMT_API BufferedFile fdopen(const char *mode);
}; };
// Returns the memory page size. // Returns the memory page size.
@ -309,58 +347,77 @@ long getpagesize();
#ifdef FMT_LOCALE #ifdef FMT_LOCALE
// A "C" numeric locale. // A "C" numeric locale.
class Locale { class Locale
private: {
private:
# ifdef _MSC_VER # ifdef _MSC_VER
typedef _locale_t locale_t; typedef _locale_t locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC }; enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char *locale, locale_t) { static locale_t newlocale(int category_mask, const char *locale, locale_t)
return _create_locale(category_mask, locale); {
} return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale) { static void freelocale(locale_t locale)
_free_locale(locale); {
} _free_locale(locale);
}
static double strtod_l(const char *nptr, char **endptr, _locale_t locale) { static double strtod_l(const char *nptr, char **endptr, _locale_t locale)
return _strtod_l(nptr, endptr, locale); {
} return _strtod_l(nptr, endptr, locale);
}
# endif # endif
locale_t locale_; locale_t locale_;
FMT_DISALLOW_COPY_AND_ASSIGN(Locale); FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
public: public:
typedef locale_t Type; typedef locale_t Type;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) { Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL))
if (!locale_) {
FMT_THROW(fmt::SystemError(errno, "cannot create locale")); if (!locale_)
} FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
~Locale() { freelocale(locale_); } }
~Locale()
{
freelocale(locale_);
}
Type get() const { return locale_; } Type get() const
{
return locale_;
}
// Converts string to floating-point number and advances str past the end // Converts string to floating-point number and advances str past the end
// of the parsed input. // of the parsed input.
double strtod(const char *&str) const { double strtod(const char *&str) const
char *end = FMT_NULL; {
double result = strtod_l(str, &end, locale_); char *end = FMT_NULL;
str = end; double result = strtod_l(str, &end, locale_);
return result; str = end;
} return result;
}
}; };
#endif // FMT_LOCALE #endif // FMT_LOCALE
} // namespace fmt } // namespace fmt
#if !FMT_USE_RVALUE_REFERENCES #if !FMT_USE_RVALUE_REFERENCES
namespace std { namespace std
{
// For compatibility with C++98. // For compatibility with C++98.
inline fmt::BufferedFile &move(fmt::BufferedFile &f) { return f; } inline fmt::BufferedFile &move(fmt::BufferedFile &f)
inline fmt::File &move(fmt::File &f) { return f; } {
return f;
}
inline fmt::File &move(fmt::File &f)
{
return f;
}
} }
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -19,120 +19,160 @@
# pragma warning(disable: 4996) // "deprecated" functions # pragma warning(disable: 4996) // "deprecated" functions
#endif #endif
namespace fmt { namespace fmt
{
template <typename ArgFormatter> template <typename ArgFormatter>
void format_arg(BasicFormatter<char, ArgFormatter> &f, void format_arg(BasicFormatter<char, ArgFormatter> &f,
const char *&format_str, const std::tm &tm) { const char *&format_str, const std::tm &tm)
if (*format_str == ':') {
++format_str; if (*format_str == ':')
const char *end = format_str; ++format_str;
while (*end && *end != '}') const char *end = format_str;
++end; while (*end && *end != '}')
if (*end != '}') ++end;
FMT_THROW(FormatError("missing '}' in format string")); if (*end != '}')
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format; FMT_THROW(FormatError("missing '}' in format string"));
format.append(format_str, end + 1); internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
format[format.size() - 1] = '\0'; format.append(format_str, end + 1);
Buffer<char> &buffer = f.writer().buffer(); format[format.size() - 1] = '\0';
std::size_t start = buffer.size(); Buffer<char> &buffer = f.writer().buffer();
for (;;) { std::size_t start = buffer.size();
std::size_t size = buffer.capacity() - start; for (;;)
std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm); {
if (count != 0) { std::size_t size = buffer.capacity() - start;
buffer.resize(start + count); std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm);
break; 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));
} }
if (size >= format.size() * 256) { format_str = end + 1;
// 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{ namespace internal
inline Null<> localtime_r(...) { return Null<>(); } {
inline Null<> localtime_s(...) { return Null<>(); } inline Null<> localtime_r(...)
inline Null<> gmtime_r(...) { return Null<>(); } {
inline Null<> gmtime_s(...) { return Null<>(); } return Null<>();
}
inline Null<> localtime_s(...)
{
return Null<>();
}
inline Null<> gmtime_r(...)
{
return Null<>();
}
inline Null<> gmtime_s(...)
{
return Null<>();
}
} }
// Thread-safe replacement for std::localtime // Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) { inline std::tm localtime(std::time_t time)
struct LocalTime { {
std::time_t time_; struct LocalTime
std::tm tm_; {
std::time_t time_;
std::tm tm_;
LocalTime(std::time_t t): time_(t) {} LocalTime(std::time_t t): time_(t) {}
bool run() { bool run()
using namespace fmt::internal; {
return handle(localtime_r(&time_, &tm_)); using namespace fmt::internal;
} return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; } bool handle(std::tm *tm)
{
return tm != FMT_NULL;
}
bool handle(internal::Null<>) { bool handle(internal::Null<>)
using namespace fmt::internal; {
return fallback(localtime_s(&tm_, &time_)); using namespace fmt::internal;
} return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; } bool fallback(int res)
{
return res == 0;
}
bool fallback(internal::Null<>) { bool fallback(internal::Null<>)
using namespace fmt::internal; {
std::tm *tm = std::localtime(&time_); using namespace fmt::internal;
if (tm) tm_ = *tm; std::tm *tm = std::localtime(&time_);
return tm != FMT_NULL; if (tm) tm_ = *tm;
} return tm != FMT_NULL;
}; }
LocalTime lt(time); };
if (lt.run()) LocalTime lt(time);
return lt.tm_; if (lt.run())
// Too big time values may be unsupported. return lt.tm_;
FMT_THROW(fmt::FormatError("time_t value out of range")); // Too big time values may be unsupported.
return std::tm(); FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
} }
// Thread-safe replacement for std::gmtime // Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) { inline std::tm gmtime(std::time_t time)
struct GMTime { {
std::time_t time_; struct GMTime
std::tm tm_; {
std::time_t time_;
std::tm tm_;
GMTime(std::time_t t): time_(t) {} GMTime(std::time_t t): time_(t) {}
bool run() { bool run()
using namespace fmt::internal; {
return handle(gmtime_r(&time_, &tm_)); using namespace fmt::internal;
} return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; } bool handle(std::tm *tm)
{
return tm != FMT_NULL;
}
bool handle(internal::Null<>) { bool handle(internal::Null<>)
using namespace fmt::internal; {
return fallback(gmtime_s(&tm_, &time_)); using namespace fmt::internal;
} return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; } bool fallback(int res)
{
return res == 0;
}
bool fallback(internal::Null<>) { bool fallback(internal::Null<>)
std::tm *tm = std::gmtime(&time_); {
if (tm != FMT_NULL) tm_ = *tm; std::tm *tm = std::gmtime(&time_);
return tm != FMT_NULL; if (tm != FMT_NULL) tm_ = *tm;
} return tm != FMT_NULL;
}; }
GMTime gt(time); };
if (gt.run()) GMTime gt(time);
return gt.tm_; if (gt.run())
// Too big time values may be unsupported. return gt.tm_;
FMT_THROW(fmt::FormatError("time_t value out of range")); // Too big time values may be unsupported.
return std::tm(); FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
} }
} //namespace fmt } //namespace fmt

View File

@ -169,11 +169,11 @@ void drop_all();
#define SPDLOG_STR_H(x) #x #define SPDLOG_STR_H(x) #x
#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x) #define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x)
#ifdef _MSC_VER #ifdef _MSC_VER
#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__) #define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__)
#define SPDLOG_TRACE_IF(logger, flag, ...) logger->trace_if(flag, "[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__) #define SPDLOG_TRACE_IF(logger, flag, ...) logger->trace_if(flag, "[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__)
#else #else
#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__) #define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__)
#define SPDLOG_TRACE_IF(logger, flag, ...) logger->trace_if(flag, "[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__) #define SPDLOG_TRACE_IF(logger, flag, ...) logger->trace_if(flag, "[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__)
#endif #endif
#else #else
#define SPDLOG_TRACE(logger, ...) #define SPDLOG_TRACE(logger, ...)

View File

@ -10,8 +10,8 @@
#include "catch.hpp" #include "catch.hpp"
#include "utils.h" #include "utils.h"
#define SPDLOG_TRACE_ON #define SPDLOG_TRACE_ON
#define SPDLOG_DEBUG_ON #define SPDLOG_DEBUG_ON
#include "../include/spdlog/spdlog.h" #include "../include/spdlog/spdlog.h"
#include "../include/spdlog/sinks/null_sink.h" #include "../include/spdlog/sinks/null_sink.h"

View File

@ -6,45 +6,45 @@
TEST_CASE("debug and trace w/o format string", "[macros]]") TEST_CASE("debug and trace w/o format string", "[macros]]")
{ {
prepare_logdir(); prepare_logdir();
std::string filename = "logs/simple_log"; std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename); auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v"); logger->set_pattern("%v");
logger->set_level(spdlog::level::trace); logger->set_level(spdlog::level::trace);
SPDLOG_TRACE(logger, "Test message 1"); SPDLOG_TRACE(logger, "Test message 1");
//SPDLOG_DEBUG(logger, "Test message 2"); //SPDLOG_DEBUG(logger, "Test message 2");
SPDLOG_DEBUG(logger, "Test message 2"); SPDLOG_DEBUG(logger, "Test message 2");
logger->flush(); logger->flush();
REQUIRE(ends_with(file_contents(filename), "Test message 2\n")); REQUIRE(ends_with(file_contents(filename), "Test message 2\n"));
REQUIRE(count_lines(filename) == 2); REQUIRE(count_lines(filename) == 2);
} }
TEST_CASE("debug and trace with format strings", "[macros]]") TEST_CASE("debug and trace with format strings", "[macros]]")
{ {
prepare_logdir(); prepare_logdir();
std::string filename = "logs/simple_log"; std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename); auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v"); logger->set_pattern("%v");
logger->set_level(spdlog::level::trace); logger->set_level(spdlog::level::trace);
#if !defined(SPDLOG_FMT_PRINTF) #if !defined(SPDLOG_FMT_PRINTF)
SPDLOG_TRACE(logger, "Test message {}", 1); SPDLOG_TRACE(logger, "Test message {}", 1);
//SPDLOG_DEBUG(logger, "Test message 2"); //SPDLOG_DEBUG(logger, "Test message 2");
SPDLOG_DEBUG(logger, "Test message {}", 222); SPDLOG_DEBUG(logger, "Test message {}", 222);
#else #else
SPDLOG_TRACE(logger, "Test message %d", 1); SPDLOG_TRACE(logger, "Test message %d", 1);
//SPDLOG_DEBUG(logger, "Test message 2"); //SPDLOG_DEBUG(logger, "Test message 2");
SPDLOG_DEBUG(logger, "Test message %d", 222); SPDLOG_DEBUG(logger, "Test message %d", 222);
#endif #endif
logger->flush(); logger->flush();
REQUIRE(ends_with(file_contents(filename), "Test message 222\n")); REQUIRE(ends_with(file_contents(filename), "Test message 222\n"));
REQUIRE(count_lines(filename) == 2); REQUIRE(count_lines(filename) == 2);
} }

View File

@ -49,8 +49,8 @@ std::size_t get_filesize(const std::string& filename)
// source: https://stackoverflow.com/a/2072890/192001 // source: https://stackoverflow.com/a/2072890/192001
bool ends_with(std::string const & value, std::string const & ending) bool ends_with(std::string const & value, std::string const & ending)
{ {
if (ending.size() > value.size()) return false; if (ending.size() > value.size()) return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
} }