Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-20 10:28:54

0001 // Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
0002 // Distributed under the MIT License (http://opensource.org/licenses/MIT)
0003 
0004 #pragma once
0005 
0006 #ifndef SPDLOG_HEADER_ONLY
0007     #include <spdlog/details/os.h>
0008 #endif
0009 
0010 #include <spdlog/common.h>
0011 
0012 #include <algorithm>
0013 #include <array>
0014 #include <chrono>
0015 #include <cstdio>
0016 #include <cstdlib>
0017 #include <cstring>
0018 #include <ctime>
0019 #include <string>
0020 #include <sys/stat.h>
0021 #include <sys/types.h>
0022 #include <thread>
0023 
0024 #ifdef _WIN32
0025     #include <spdlog/details/windows_include.h>
0026     #include <fileapi.h>  // for FlushFileBuffers
0027     #include <io.h>       // for _get_osfhandle, _isatty, _fileno
0028     #include <process.h>  // for _get_pid
0029 
0030     #ifdef __MINGW32__
0031         #include <share.h>
0032     #endif
0033 
0034     #if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
0035         #include <cassert>
0036         #include <limits>
0037     #endif
0038 
0039     #include <direct.h>  // for _mkdir/_wmkdir
0040 
0041 #else  // unix
0042 
0043     #include <fcntl.h>
0044     #include <unistd.h>
0045 
0046     #ifdef __linux__
0047         #include <sys/syscall.h>  //Use gettid() syscall under linux to get thread id
0048 
0049     #elif defined(_AIX)
0050         #include <pthread.h>  // for pthread_getthrds_np
0051 
0052     #elif defined(__DragonFly__) || defined(__FreeBSD__)
0053         #include <pthread_np.h>  // for pthread_getthreadid_np
0054 
0055     #elif defined(__NetBSD__)
0056         #include <lwp.h>  // for _lwp_self
0057 
0058     #elif defined(__sun)
0059         #include <thread.h>  // for thr_self
0060     #endif
0061 
0062 #endif  // unix
0063 
0064 #if defined __APPLE__
0065     #include <AvailabilityMacros.h>
0066 #endif
0067 
0068 #ifndef __has_feature           // Clang - feature checking macros.
0069     #define __has_feature(x) 0  // Compatibility with non-clang compilers.
0070 #endif
0071 
0072 namespace spdlog {
0073 namespace details {
0074 namespace os {
0075 
0076 SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT {
0077 #if defined __linux__ && defined SPDLOG_CLOCK_COARSE
0078     timespec ts;
0079     ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
0080     return std::chrono::time_point<log_clock, typename log_clock::duration>(
0081         std::chrono::duration_cast<typename log_clock::duration>(
0082             std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
0083 
0084 #else
0085     return log_clock::now();
0086 #endif
0087 }
0088 SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT {
0089 #ifdef _WIN32
0090     std::tm tm;
0091     ::localtime_s(&tm, &time_tt);
0092 #else
0093     std::tm tm;
0094     ::localtime_r(&time_tt, &tm);
0095 #endif
0096     return tm;
0097 }
0098 
0099 SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT {
0100     std::time_t now_t = ::time(nullptr);
0101     return localtime(now_t);
0102 }
0103 
0104 SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT {
0105 #ifdef _WIN32
0106     std::tm tm;
0107     ::gmtime_s(&tm, &time_tt);
0108 #else
0109     std::tm tm;
0110     ::gmtime_r(&time_tt, &tm);
0111 #endif
0112     return tm;
0113 }
0114 
0115 SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT {
0116     std::time_t now_t = ::time(nullptr);
0117     return gmtime(now_t);
0118 }
0119 
0120 // fopen_s on non windows for writing
0121 SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) {
0122 #ifdef _WIN32
0123     #ifdef SPDLOG_WCHAR_FILENAMES
0124     *fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
0125     #else
0126     *fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
0127     #endif
0128     #if defined(SPDLOG_PREVENT_CHILD_FD)
0129     if (*fp != nullptr) {
0130         auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
0131         if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) {
0132             ::fclose(*fp);
0133             *fp = nullptr;
0134         }
0135     }
0136     #endif
0137 #else  // unix
0138     #if defined(SPDLOG_PREVENT_CHILD_FD)
0139     const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
0140     const int fd =
0141         ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
0142     if (fd == -1) {
0143         return true;
0144     }
0145     *fp = ::fdopen(fd, mode.c_str());
0146     if (*fp == nullptr) {
0147         ::close(fd);
0148     }
0149     #else
0150     *fp = ::fopen((filename.c_str()), mode.c_str());
0151     #endif
0152 #endif
0153 
0154     return *fp == nullptr;
0155 }
0156 
0157 SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT {
0158 #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
0159     return ::_wremove(filename.c_str());
0160 #else
0161     return std::remove(filename.c_str());
0162 #endif
0163 }
0164 
0165 SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT {
0166     return path_exists(filename) ? remove(filename) : 0;
0167 }
0168 
0169 SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT {
0170 #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
0171     return ::_wrename(filename1.c_str(), filename2.c_str());
0172 #else
0173     return std::rename(filename1.c_str(), filename2.c_str());
0174 #endif
0175 }
0176 
0177 // Return true if path exists (file or directory)
0178 SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT {
0179 #ifdef _WIN32
0180     struct _stat buffer;
0181     #ifdef SPDLOG_WCHAR_FILENAMES
0182     return (::_wstat(filename.c_str(), &buffer) == 0);
0183     #else
0184     return (::_stat(filename.c_str(), &buffer) == 0);
0185     #endif
0186 #else  // common linux/unix all have the stat system call
0187     struct stat buffer;
0188     return (::stat(filename.c_str(), &buffer) == 0);
0189 #endif
0190 }
0191 
0192 #ifdef _MSC_VER
0193     // avoid warning about unreachable statement at the end of filesize()
0194     #pragma warning(push)
0195     #pragma warning(disable : 4702)
0196 #endif
0197 
0198 // Return file size according to open FILE* object
0199 SPDLOG_INLINE size_t filesize(FILE *f) {
0200     if (f == nullptr) {
0201         throw_spdlog_ex("Failed getting file size. fd is null");
0202     }
0203 #if defined(_WIN32) && !defined(__CYGWIN__)
0204     int fd = ::_fileno(f);
0205     #if defined(_WIN64)  // 64 bits
0206     __int64 ret = ::_filelengthi64(fd);
0207     if (ret >= 0) {
0208         return static_cast<size_t>(ret);
0209     }
0210 
0211     #else  // windows 32 bits
0212     long ret = ::_filelength(fd);
0213     if (ret >= 0) {
0214         return static_cast<size_t>(ret);
0215     }
0216     #endif
0217 
0218 #else  // unix
0219     // OpenBSD and AIX doesn't compile with :: before the fileno(..)
0220     #if defined(__OpenBSD__) || defined(_AIX)
0221     int fd = fileno(f);
0222     #else
0223     int fd = ::fileno(f);
0224     #endif
0225     // 64 bits(but not in osx, linux/musl or cygwin, where fstat64 is deprecated)
0226     #if ((defined(__linux__) && defined(__GLIBC__)) || defined(__sun) || defined(_AIX)) && \
0227         (defined(__LP64__) || defined(_LP64))
0228     struct stat64 st;
0229     if (::fstat64(fd, &st) == 0) {
0230         return static_cast<size_t>(st.st_size);
0231     }
0232     #else  // other unix or linux 32 bits or cygwin
0233     struct stat st;
0234     if (::fstat(fd, &st) == 0) {
0235         return static_cast<size_t>(st.st_size);
0236     }
0237     #endif
0238 #endif
0239     throw_spdlog_ex("Failed getting file size from fd", errno);
0240     return 0;  // will not be reached.
0241 }
0242 
0243 #ifdef _MSC_VER
0244     #pragma warning(pop)
0245 #endif
0246 
0247 // Return utc offset in minutes or throw spdlog_ex on failure
0248 SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) {
0249 #ifdef _WIN32
0250     #if _WIN32_WINNT < _WIN32_WINNT_WS08
0251     TIME_ZONE_INFORMATION tzinfo;
0252     auto rv = ::GetTimeZoneInformation(&tzinfo);
0253     #else
0254     DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
0255     auto rv = ::GetDynamicTimeZoneInformation(&tzinfo);
0256     #endif
0257     if (rv == TIME_ZONE_ID_INVALID) throw_spdlog_ex("Failed getting timezone info. ", errno);
0258 
0259     int offset = -tzinfo.Bias;
0260     if (tm.tm_isdst) {
0261         offset -= tzinfo.DaylightBias;
0262     } else {
0263         offset -= tzinfo.StandardBias;
0264     }
0265     return offset;
0266 #else
0267 
0268     #if defined(sun) || defined(__sun) || defined(_AIX) || \
0269         (defined(__NEWLIB__) && !defined(__TM_GMTOFF)) ||  \
0270         (!defined(__APPLE__) && !defined(_BSD_SOURCE) && !defined(_GNU_SOURCE) && \
0271             (!defined(_POSIX_VERSION) || (_POSIX_VERSION < 202405L)))
0272     // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
0273     struct helper {
0274         static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(),
0275                                              const std::tm &gmtm = details::os::gmtime()) {
0276             int local_year = localtm.tm_year + (1900 - 1);
0277             int gmt_year = gmtm.tm_year + (1900 - 1);
0278 
0279             long int days = (
0280                 // difference in day of year
0281                 localtm.tm_yday -
0282                 gmtm.tm_yday
0283 
0284                 // + intervening leap days
0285                 + ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
0286                 ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
0287 
0288                 // + difference in years * 365 */
0289                 + static_cast<long int>(local_year - gmt_year) * 365);
0290 
0291             long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
0292             long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
0293             long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
0294 
0295             return secs;
0296         }
0297     };
0298 
0299     auto offset_seconds = helper::calculate_gmt_offset(tm);
0300     #else
0301     auto offset_seconds = tm.tm_gmtoff;
0302     #endif
0303 
0304     return static_cast<int>(offset_seconds / 60);
0305 #endif
0306 }
0307 
0308 // Return current thread id as size_t
0309 // It exists because the std::this_thread::get_id() is much slower(especially
0310 // under VS 2013)
0311 SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT {
0312 #ifdef _WIN32
0313     return static_cast<size_t>(::GetCurrentThreadId());
0314 #elif defined(__linux__)
0315     #if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
0316         #define SYS_gettid __NR_gettid
0317     #endif
0318     return static_cast<size_t>(::syscall(SYS_gettid));
0319 #elif defined(_AIX)
0320     struct __pthrdsinfo buf;
0321     int reg_size = 0;
0322     pthread_t pt = pthread_self();
0323     int retval = pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_TID, &buf, sizeof(buf), NULL, &reg_size);
0324     int tid = (!retval) ? buf.__pi_tid : 0;
0325     return static_cast<size_t>(tid);
0326 #elif defined(__DragonFly__) || defined(__FreeBSD__)
0327     return static_cast<size_t>(::pthread_getthreadid_np());
0328 #elif defined(__NetBSD__)
0329     return static_cast<size_t>(::_lwp_self());
0330 #elif defined(__OpenBSD__)
0331     return static_cast<size_t>(::getthrid());
0332 #elif defined(__sun)
0333     return static_cast<size_t>(::thr_self());
0334 #elif __APPLE__
0335     uint64_t tid;
0336     // There is no pthread_threadid_np prior to Mac OS X 10.6, and it is not supported on any PPC,
0337     // including 10.6.8 Rosetta. __POWERPC__ is Apple-specific define encompassing ppc and ppc64.
0338     #ifdef MAC_OS_X_VERSION_MAX_ALLOWED
0339     {
0340         #if (MAC_OS_X_VERSION_MAX_ALLOWED < 1060) || defined(__POWERPC__)
0341         tid = pthread_mach_thread_np(pthread_self());
0342         #elif MAC_OS_X_VERSION_MIN_REQUIRED < 1060
0343         if (&pthread_threadid_np) {
0344             pthread_threadid_np(nullptr, &tid);
0345         } else {
0346             tid = pthread_mach_thread_np(pthread_self());
0347         }
0348         #else
0349         pthread_threadid_np(nullptr, &tid);
0350         #endif
0351     }
0352     #else
0353     pthread_threadid_np(nullptr, &tid);
0354     #endif
0355     return static_cast<size_t>(tid);
0356 #else  // Default to standard C++11 (other Unix)
0357     return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
0358 #endif
0359 }
0360 
0361 // Return current thread id as size_t (from thread local storage)
0362 SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT {
0363 #if defined(SPDLOG_NO_TLS)
0364     return _thread_id();
0365 #else  // cache thread id in tls
0366     static thread_local const size_t tid = _thread_id();
0367     return tid;
0368 #endif
0369 }
0370 
0371 // This is avoid msvc issue in sleep_for that happens if the clock changes.
0372 // See https://github.com/gabime/spdlog/issues/609
0373 SPDLOG_INLINE void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT {
0374 #if defined(_WIN32)
0375     ::Sleep(milliseconds);
0376 #else
0377     std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
0378 #endif
0379 }
0380 
0381 // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
0382 #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
0383 SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) {
0384     memory_buf_t buf;
0385     wstr_to_utf8buf(filename, buf);
0386     return SPDLOG_BUF_TO_STRING(buf);
0387 }
0388 #else
0389 SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) { return filename; }
0390 #endif
0391 
0392 SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT {
0393 #ifdef _WIN32
0394     return conditional_static_cast<int>(::GetCurrentProcessId());
0395 #else
0396     return conditional_static_cast<int>(::getpid());
0397 #endif
0398 }
0399 
0400 // Determine if the terminal supports colors
0401 // Based on: https://github.com/agauniyal/rang/
0402 SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT {
0403 #ifdef _WIN32
0404     return true;
0405 #else
0406 
0407     static const bool result = []() {
0408         const char *env_colorterm_p = std::getenv("COLORTERM");
0409         if (env_colorterm_p != nullptr) {
0410             return true;
0411         }
0412 
0413         static constexpr std::array<const char *, 16> terms = {
0414             {"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys",
0415              "putty", "rxvt", "screen", "vt100", "xterm", "alacritty", "vt102"}};
0416 
0417         const char *env_term_p = std::getenv("TERM");
0418         if (env_term_p == nullptr) {
0419             return false;
0420         }
0421 
0422         return std::any_of(terms.begin(), terms.end(), [&](const char *term) {
0423             return std::strstr(env_term_p, term) != nullptr;
0424         });
0425     }();
0426 
0427     return result;
0428 #endif
0429 }
0430 
0431 // Determine if the terminal attached
0432 // Source: https://github.com/agauniyal/rang/
0433 SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT {
0434 #ifdef _WIN32
0435     return ::_isatty(_fileno(file)) != 0;
0436 #else
0437     return ::isatty(fileno(file)) != 0;
0438 #endif
0439 }
0440 
0441 #if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
0442 SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) {
0443     if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 4 - 1) {
0444         throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
0445     }
0446 
0447     int wstr_size = static_cast<int>(wstr.size());
0448     if (wstr_size == 0) {
0449         target.resize(0);
0450         return;
0451     }
0452 
0453     int result_size = static_cast<int>(target.capacity());
0454     if ((wstr_size + 1) * 4 > result_size) {
0455         result_size =
0456             ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
0457     }
0458 
0459     if (result_size > 0) {
0460         target.resize(result_size);
0461         result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(),
0462                                             result_size, NULL, NULL);
0463 
0464         if (result_size > 0) {
0465             target.resize(result_size);
0466             return;
0467         }
0468     }
0469 
0470     throw_spdlog_ex(
0471         fmt_lib::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
0472 }
0473 
0474 SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) {
0475     if (str.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) - 1) {
0476         throw_spdlog_ex("UTF-8 string is too big to be converted to UTF-16");
0477     }
0478 
0479     int str_size = static_cast<int>(str.size());
0480     if (str_size == 0) {
0481         target.resize(0);
0482         return;
0483     }
0484 
0485     // find the size to allocate for the result buffer
0486     int result_size =
0487         ::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, NULL, 0);
0488 
0489     if (result_size > 0) {
0490         target.resize(result_size);
0491         result_size = ::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, target.data(),
0492                                             result_size);
0493         if (result_size > 0) {
0494             assert(result_size == target.size());
0495             return;
0496         }
0497     }
0498 
0499     throw_spdlog_ex(
0500         fmt_lib::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError()));
0501 }
0502 #endif  // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) &&
0503         // defined(_WIN32)
0504 
0505 // return true on success
0506 static SPDLOG_INLINE bool mkdir_(const filename_t &path) {
0507 #ifdef _WIN32
0508     #ifdef SPDLOG_WCHAR_FILENAMES
0509     return ::_wmkdir(path.c_str()) == 0;
0510     #else
0511     return ::_mkdir(path.c_str()) == 0;
0512     #endif
0513 #else
0514     return ::mkdir(path.c_str(), mode_t(0755)) == 0;
0515 #endif
0516 }
0517 
0518 // create the given directory - and all directories leading to it
0519 // return true on success or if the directory already exists
0520 SPDLOG_INLINE bool create_dir(const filename_t &path) {
0521     if (path_exists(path)) {
0522         return true;
0523     }
0524 
0525     if (path.empty()) {
0526         return false;
0527     }
0528 
0529     size_t search_offset = 0;
0530     do {
0531         auto token_pos = path.find_first_of(folder_seps_filename, search_offset);
0532         // treat the entire path as a folder if no folder separator not found
0533         if (token_pos == filename_t::npos) {
0534             token_pos = path.size();
0535         }
0536 
0537         auto subdir = path.substr(0, token_pos);
0538 #ifdef _WIN32
0539         // if subdir is just a drive letter, add a slash e.g. "c:"=>"c:\",
0540         // otherwise path_exists(subdir) returns false (issue #3079)
0541         const bool is_drive = subdir.length() == 2 && subdir[1] == ':';
0542         if (is_drive) {
0543             subdir += '\\';
0544             token_pos++;
0545         }
0546 #endif
0547 
0548         if (!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir)) {
0549             return false;  // return error if failed creating dir
0550         }
0551         search_offset = token_pos + 1;
0552     } while (search_offset < path.size());
0553 
0554     return true;
0555 }
0556 
0557 // Return directory name from given path or empty string
0558 // "abc/file" => "abc"
0559 // "abc/" => "abc"
0560 // "abc" => ""
0561 // "abc///" => "abc//"
0562 SPDLOG_INLINE filename_t dir_name(const filename_t &path) {
0563     auto pos = path.find_last_of(folder_seps_filename);
0564     return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
0565 }
0566 
0567 std::string SPDLOG_INLINE getenv(const char *field) {
0568 #if defined(_MSC_VER)
0569     #if defined(__cplusplus_winrt)
0570     return std::string{};  // not supported under uwp
0571     #else
0572     size_t len = 0;
0573     char buf[128];
0574     bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
0575     return ok ? buf : std::string{};
0576     #endif
0577 #else  // revert to getenv
0578     char *buf = ::getenv(field);
0579     return buf ? buf : std::string{};
0580 #endif
0581 }
0582 
0583 // Do fsync by FILE handlerpointer
0584 // Return true on success
0585 SPDLOG_INLINE bool fsync(FILE *fp) {
0586 #ifdef _WIN32
0587     return FlushFileBuffers(reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(fp)))) != 0;
0588 #else
0589     return ::fsync(fileno(fp)) == 0;
0590 #endif
0591 }
0592 
0593 // Do non-locking fwrite if possible by the os or use the regular locking fwrite
0594 // Return true on success.
0595 SPDLOG_INLINE bool fwrite_bytes(const void *ptr, const size_t n_bytes, FILE *fp) {
0596     #if defined(_WIN32) && defined(SPDLOG_FWRITE_UNLOCKED)
0597     return _fwrite_nolock(ptr, 1, n_bytes, fp) == n_bytes;
0598     #elif defined(SPDLOG_FWRITE_UNLOCKED)
0599     return ::fwrite_unlocked(ptr, 1, n_bytes, fp) == n_bytes;
0600     #else
0601     return std::fwrite(ptr, 1, n_bytes, fp) == n_bytes;
0602     #endif
0603 }
0604 
0605 }  // namespace os
0606 }  // namespace details
0607 }  // namespace spdlog