mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-03-12 22:56:52 +01:00

Allows us to migrate off of printf specifiers and have more type-safe formatting facilities. It also allows for custom type support as well. fmt is also on track to have part of it standardized within C++2a, so this will also lessen the transitional work necessary later on by allowing new code to use it. This simply adds the library but doesn't do anything with it yet.
161 lines
4.2 KiB
C++
Executable File
161 lines
4.2 KiB
C++
Executable File
// Formatting library for C++ - time formatting
|
|
//
|
|
// Copyright (c) 2012 - present, Victor Zverovich
|
|
// All rights reserved.
|
|
//
|
|
// For the license information refer to format.h.
|
|
|
|
#ifndef FMT_TIME_H_
|
|
#define FMT_TIME_H_
|
|
|
|
#include "format.h"
|
|
#include <ctime>
|
|
#include <locale>
|
|
|
|
FMT_BEGIN_NAMESPACE
|
|
|
|
// Prevents expansion of a preceding token as a function-style macro.
|
|
// Usage: f FMT_NOMACRO()
|
|
#define FMT_NOMACRO
|
|
|
|
namespace internal{
|
|
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
|
|
inline null<> localtime_s(...) { return null<>(); }
|
|
inline null<> gmtime_r(...) { return null<>(); }
|
|
inline null<> gmtime_s(...) { return null<>(); }
|
|
} // namespace internal
|
|
|
|
// Thread-safe replacement for std::localtime
|
|
inline std::tm localtime(std::time_t time) {
|
|
struct dispatcher {
|
|
std::time_t time_;
|
|
std::tm tm_;
|
|
|
|
dispatcher(std::time_t t): time_(t) {}
|
|
|
|
bool run() {
|
|
using namespace fmt::internal;
|
|
return handle(localtime_r(&time_, &tm_));
|
|
}
|
|
|
|
bool handle(std::tm *tm) { return tm != FMT_NULL; }
|
|
|
|
bool handle(internal::null<>) {
|
|
using namespace fmt::internal;
|
|
return fallback(localtime_s(&tm_, &time_));
|
|
}
|
|
|
|
bool fallback(int res) { return res == 0; }
|
|
|
|
#if !FMT_MSC_VER
|
|
bool fallback(internal::null<>) {
|
|
using namespace fmt::internal;
|
|
std::tm *tm = std::localtime(&time_);
|
|
if (tm) tm_ = *tm;
|
|
return tm != FMT_NULL;
|
|
}
|
|
#endif
|
|
};
|
|
dispatcher lt(time);
|
|
// Too big time values may be unsupported.
|
|
if (!lt.run())
|
|
FMT_THROW(format_error("time_t value out of range"));
|
|
return lt.tm_;
|
|
}
|
|
|
|
// Thread-safe replacement for std::gmtime
|
|
inline std::tm gmtime(std::time_t time) {
|
|
struct dispatcher {
|
|
std::time_t time_;
|
|
std::tm tm_;
|
|
|
|
dispatcher(std::time_t t): time_(t) {}
|
|
|
|
bool run() {
|
|
using namespace fmt::internal;
|
|
return handle(gmtime_r(&time_, &tm_));
|
|
}
|
|
|
|
bool handle(std::tm *tm) { return tm != FMT_NULL; }
|
|
|
|
bool handle(internal::null<>) {
|
|
using namespace fmt::internal;
|
|
return fallback(gmtime_s(&tm_, &time_));
|
|
}
|
|
|
|
bool fallback(int res) { return res == 0; }
|
|
|
|
#if !FMT_MSC_VER
|
|
bool fallback(internal::null<>) {
|
|
std::tm *tm = std::gmtime(&time_);
|
|
if (tm) tm_ = *tm;
|
|
return tm != FMT_NULL;
|
|
}
|
|
#endif
|
|
};
|
|
dispatcher gt(time);
|
|
// Too big time values may be unsupported.
|
|
if (!gt.run())
|
|
FMT_THROW(format_error("time_t value out of range"));
|
|
return gt.tm_;
|
|
}
|
|
|
|
namespace internal {
|
|
inline std::size_t strftime(char *str, std::size_t count, const char *format,
|
|
const std::tm *time) {
|
|
return std::strftime(str, count, format, time);
|
|
}
|
|
|
|
inline std::size_t strftime(wchar_t *str, std::size_t count,
|
|
const wchar_t *format, const std::tm *time) {
|
|
return std::wcsftime(str, count, format, time);
|
|
}
|
|
}
|
|
|
|
template <typename Char>
|
|
struct formatter<std::tm, Char> {
|
|
template <typename ParseContext>
|
|
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
|
auto it = ctx.begin();
|
|
if (it != ctx.end() && *it == ':')
|
|
++it;
|
|
auto end = it;
|
|
while (end != ctx.end() && *end != '}')
|
|
++end;
|
|
tm_format.reserve(internal::to_unsigned(end - it + 1));
|
|
tm_format.append(it, end);
|
|
tm_format.push_back('\0');
|
|
return end;
|
|
}
|
|
|
|
template <typename FormatContext>
|
|
auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) {
|
|
basic_memory_buffer<Char> buf;
|
|
std::size_t start = buf.size();
|
|
for (;;) {
|
|
std::size_t size = buf.capacity() - start;
|
|
std::size_t count =
|
|
internal::strftime(&buf[start], size, &tm_format[0], &tm);
|
|
if (count != 0) {
|
|
buf.resize(start + count);
|
|
break;
|
|
}
|
|
if (size >= tm_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;
|
|
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
|
}
|
|
return std::copy(buf.begin(), buf.end(), ctx.out());
|
|
}
|
|
|
|
basic_memory_buffer<Char> tm_format;
|
|
};
|
|
FMT_END_NAMESPACE
|
|
|
|
#endif // FMT_TIME_H_
|