Commit 6de00aa9 authored by Philip Trettner's avatar Philip Trettner
Browse files
parents bbea44f7 55c5bfa9
......@@ -10,6 +10,6 @@ if (MSVC)
target_compile_options(aion PUBLIC /MP)
else()
target_compile_options(aion PRIVATE -Wall -Werror -fPIC)
target_compile_options(aion PUBLIC
target_compile_options(aion PRIVATE
$<$<COMPILE_LANGUAGE:CXX>:-std=c++11>)
endif()
/*
Formatting library for C++
Copyright (c) 2012 - 2015, Victor Zverovich
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
......@@ -34,129 +34,83 @@
#include <climits>
#include <cmath>
#include <cstdarg>
#include <cstddef> // for std::ptrdiff_t
#include <cstddef> // for std::ptrdiff_t
#if defined(_WIN32) && defined(__MINGW32__)
#include <cstring>
# include <cstring>
#endif
#if FMT_USE_WINDOWS_H
#if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
#include <windows.h>
#else
#define NOMINMAX
#include <windows.h>
#undef NOMINMAX
#endif
#endif
using aion_fmt::internal::Arg;
// Check if exceptions are disabled.
#if defined(__GNUC__) && !defined(__EXCEPTIONS)
#define FMT_EXCEPTIONS 0
#endif
#if defined(_MSC_VER) && !_HAS_EXCEPTIONS
#define FMT_EXCEPTIONS 0
#endif
#ifndef FMT_EXCEPTIONS
#define FMT_EXCEPTIONS 1
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
# define WIN32_LEAN_AND_MEAN
# endif
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
# include <windows.h>
# else
# define NOMINMAX
# include <windows.h>
# undef NOMINMAX
# endif
#endif
#if FMT_EXCEPTIONS
#define FMT_TRY try
#define FMT_CATCH(x) catch (x)
#else
#define FMT_TRY if (true)
#define FMT_CATCH(x) if (false)
#endif
#ifndef FMT_THROW
#if FMT_EXCEPTIONS
#define FMT_THROW(x) throw x
#else
#define FMT_THROW(x) assert(false)
#endif
#endif
#ifdef FMT_HEADER_ONLY
#define FMT_FUNC inline
# define FMT_TRY try
# define FMT_CATCH(x) catch (x)
#else
#define FMT_FUNC
# define FMT_TRY if (true)
# define FMT_CATCH(x) if (false)
#endif
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4127) // conditional expression is constant
#pragma warning(disable : 4702) // unreachable code
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
# pragma warning(disable: 4702) // unreachable code
// Disable deprecation warning for strerror. The latter is not called but
// MSVC fails to detect it.
#pragma warning(disable : 4996)
# pragma warning(disable: 4996)
#endif
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
static inline aion_fmt::internal::Null<> strerror_r(int, char *, ...)
{
return aion_fmt::internal::Null<>();
FMT_MAYBE_UNUSED
static inline aion_fmt::internal::Null<> strerror_r(int, char *, ...) {
return aion_fmt::internal::Null<>();
}
static inline aion_fmt::internal::Null<> strerror_s(char *, std::size_t, ...)
{
return aion_fmt::internal::Null<>();
FMT_MAYBE_UNUSED
static inline aion_fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
return aion_fmt::internal::Null<>();
}
namespace aion_fmt
{
namespace
{
namespace aion_fmt {
FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {}
FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {}
FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {}
namespace {
#ifndef _MSC_VER
#define FMT_SNPRINTF snprintf
#else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...)
{
va_list args;
va_start(args, format);
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
va_end(args);
return result;
}
#define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
# define FMT_SNPRINTF snprintf
#else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
va_list args;
va_start(args, format);
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
va_end(args);
return result;
}
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
#define FMT_SWPRINTF snwprintf
# define FMT_SWPRINTF snwprintf
#else
#define FMT_SWPRINTF swprintf
# define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned>
struct IntChecker
{
template <typename T>
static bool fits_in_int(T value)
{
unsigned max = INT_MAX;
return value <= max;
}
static bool fits_in_int(bool) { return true; }
};
template <>
struct IntChecker<true>
{
template <typename T>
static bool fits_in_int(T value)
{
return value >= INT_MIN && value <= INT_MAX;
}
static bool fits_in_int(int) { return true; }
};
const char RESET_COLOR[] = "\x1b[0m";
typedef void (*FormatFunc)(aion_fmt::Writer &, int, aion_fmt::StringRef);
typedef void (*FormatFunc)(Writer &, int, StringRef);
// Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
......@@ -167,1311 +121,375 @@ typedef void (*FormatFunc)(aion_fmt::Writer &, int, aion_fmt::StringRef);
// ERANGE - buffer is not large enough to store the error message
// other - failure
// Buffer should be at least of size 1.
int safe_strerror(int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT
{
FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
class StrError
{
private:
int error_code_;
char *&buffer_;
std::size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings.
void operator=(const StrError &) {}
// Handle the result of XSI-compliant version of strerror_r.
int handle(int result)
{
// glibc versions before 2.13 return result in errno.
return result == -1 ? errno : result;
}
int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
// Handle the result of GNU-specific version of strerror_r.
int handle(char *message)
{
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
return ERANGE;
buffer_ = message;
return 0;
}
class StrError {
private:
int error_code_;
char *&buffer_;
std::size_t buffer_size_;
// Handle the case when strerror_r is not available.
int handle(aion_fmt::internal::Null<>) { return fallback(strerror_s(buffer_, buffer_size_, error_code_)); }
// Fallback to strerror_s when strerror_r is not available.
int fallback(int result)
{
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE : result;
}
// A noop assignment operator to avoid bogus warnings.
void operator=(const StrError &) {}
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(aion_fmt::internal::Null<>)
{
errno = 0;
buffer_ = strerror(error_code_);
return errno;
}
public:
StrError(int err_code, char *&buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size)
{
}
int run()
{
strerror_r(0, 0, ""); // Suppress a warning about unused strerror_r.
return handle(strerror_r(error_code_, buffer_, buffer_size_));
}
};
return StrError(error_code, buffer, buffer_size).run();
}
void format_error_code(aion_fmt::Writer &out, int error_code, aion_fmt::StringRef message) FMT_NOEXCEPT
{
// Report error code making sure that the output fits into
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
// bad_alloc.
out.clear();
static const char SEP[] = ": ";
static const char ERROR_STR[] = "error ";
aion_fmt::internal::IntTraits<int>::MainType ec_value = error_code;
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
error_code_size += aion_fmt::internal::count_digits(ec_value);
if (message.size() <= aion_fmt::internal::INLINE_BUFFER_SIZE - error_code_size)
out << message << SEP;
out << ERROR_STR << error_code;
assert(out.size() <= aion_fmt::internal::INLINE_BUFFER_SIZE);
}
void report_error(FormatFunc func, int error_code, aion_fmt::StringRef message) FMT_NOEXCEPT
{
aion_fmt::MemoryWriter full_message;
func(full_message, error_code, message);
// Use Writer::data instead of Writer::c_str to avoid potential memory
// allocation.
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public aion_fmt::internal::ArgVisitor<IsZeroInt, bool>
{
public:
template <typename T>
bool visit_any_int(T value)
{
return value == 0;
}
};
// Parses an unsigned integer advancing s to the end of the parsed input.
// This function assumes that the first character of s is a digit.
template <typename Char>
int parse_nonnegative_int(const Char *&s)
{
assert('0' <= *s && *s <= '9');
unsigned value = 0;
do
{
unsigned new_value = value * 10 + (*s++ - '0');
// Check if value wrapped around.
if (new_value < value)
{
value = UINT_MAX;
break;
}
value = new_value;
} while ('0' <= *s && *s <= '9');
if (value > INT_MAX)
FMT_THROW(aion_fmt::FormatError("number is too big"));
return value;
}
template <typename Char>
inline bool is_name_start(Char c)
{
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c;
}
inline void require_numeric_argument(const Arg &arg, char spec)
{
if (arg.type > Arg::LAST_NUMERIC_TYPE)
{
std::string message = aion_fmt::format("format specifier '{}' requires numeric argument", spec);
FMT_THROW(aion_fmt::FormatError(message));
// Handle the result of XSI-compliant version of strerror_r.
int handle(int result) {
// glibc versions before 2.13 return result in errno.
return result == -1 ? errno : result;
}
}
template <typename Char>
void check_sign(const Char *&s, const Arg &arg)
{
char sign = static_cast<char>(*s);
require_numeric_argument(arg, sign);
if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG)
{
FMT_THROW(aion_fmt::FormatError(aion_fmt::format("format specifier '{}' requires signed argument", sign)));
// Handle the result of GNU-specific version of strerror_r.
int handle(char *message) {
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
return ERANGE;
buffer_ = message;
return 0;
}
++s;
}
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class WidthHandler : public aion_fmt::internal::ArgVisitor<WidthHandler, unsigned>
{
private:
aion_fmt::FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public:
explicit WidthHandler(aion_fmt::FormatSpec &spec) : spec_(spec) {}
void report_unhandled_arg() { FMT_THROW(aion_fmt::FormatError("width is not integer")); }
template <typename T>
unsigned visit_any_int(T value)
{
typedef typename aion_fmt::internal::IntTraits<T>::MainType UnsignedType;
UnsignedType width = value;
if (aion_fmt::internal::is_negative(value))
{
spec_.align_ = aion_fmt::ALIGN_LEFT;
width = 0 - width;
}
if (width > INT_MAX)
FMT_THROW(aion_fmt::FormatError("number is too big"));
return static_cast<unsigned>(width);
// Handle the case when strerror_r is not available.
int handle(internal::Null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
};
class PrecisionHandler : public aion_fmt::internal::ArgVisitor<PrecisionHandler, int>
{
public:
void report_unhandled_arg() { FMT_THROW(aion_fmt::FormatError("precision is not integer")); }
template <typename T>
int visit_any_int(T value)
{
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(aion_fmt::FormatError("number is too big"));
return static_cast<int>(value);
// Fallback to strerror_s when strerror_r is not available.
int fallback(int result) {
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
ERANGE : result;
}
};
// Converts an integer argument to an integral type T for printf.
template <typename T>
class ArgConverter : public aion_fmt::internal::ArgVisitor<ArgConverter<T>, void>
{
private:
aion_fmt::internal::Arg &arg_;
wchar_t type_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
public:
ArgConverter(aion_fmt::internal::Arg &arg, wchar_t type) : arg_(arg), type_(type) {}
void visit_bool(bool value)
{
if (type_ != 's')
visit_any_int(value);
}
template <typename U>
void visit_any_int(U value)
{
bool is_signed = type_ == 'd' || type_ == 'i';
using aion_fmt::internal::Arg;
if (sizeof(T) <= sizeof(int))
{
// Extra casts are used to silence warnings.
if (is_signed)
{
arg_.type = Arg::INT;
arg_.int_value = static_cast<int>(static_cast<T>(value));
}
else
{
arg_.type = Arg::UINT;
arg_.uint_value = static_cast<unsigned>(static_cast<typename aion_fmt::internal::MakeUnsigned<T>::Type>(value));
}
}
else
{
if (is_signed)
{
arg_.type = Arg::LONG_LONG;
arg_.long_long_value = static_cast<typename aion_fmt::internal::MakeUnsigned<U>::Type>(value);
}
else
{
arg_.type = Arg::ULONG_LONG;
arg_.ulong_long_value = static_cast<typename aion_fmt::internal::MakeUnsigned<U>::Type>(value);
}
}
}
};
// Converts an integer argument to char for printf.
class CharConverter : public aion_fmt::internal::ArgVisitor<CharConverter, void>
{
private:
aion_fmt::internal::Arg &arg_;
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public:
explicit CharConverter(aion_fmt::internal::Arg &arg) : arg_(arg) {}
template <typename T>
void visit_any_int(T value)
{
arg_.type = Arg::CHAR;
arg_.int_value = static_cast<char>(value);
}
};
} // namespace
namespace internal
{
template <typename Impl, typename Char>
class BasicArgFormatter : public ArgVisitor<Impl, void>
{
private:
BasicWriter<Char> &writer_;
FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(BasicArgFormatter);
void write_pointer(const void *p)
{
spec_.flags_ = HASH_FLAG;
spec_.type_ = 'x';
writer_.write_int(reinterpret_cast<uintptr_t>(p), spec_);
}
protected:
BasicWriter<Char> &writer() { return writer_; }
FormatSpec &spec() { return spec_; }
void write(bool value)
{
const char *str_value = value ? "true" : "false";
Arg::StringValue<char> str = {str_value, strlen(str_value)};
writer_.write_str(str, spec_);
}
void write(const char *value)
{
Arg::StringValue<char> str = {value, value != 0 ? strlen(value) : 0};
writer_.write_str(str, spec_);
}
public:
BasicArgFormatter(BasicWriter<Char> &w, FormatSpec &s) : writer_(w), spec_(s) {}
template <typename T>
void visit_any_int(T value)
{
writer_.write_int(value, spec_);
}
template <typename T>
void visit_any_double(T value)
{
writer_.write_double(value, spec_);
}
void visit_bool(bool value)
{
if (spec_.type_)
return visit_any_int(value);
write(value);
}
void visit_char(int value)
{
if (spec_.type_ && spec_.type_ != 'c')
{
spec_.flags_ |= CHAR_FLAG;
writer_.write_int(value, spec_);
return;
}
if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0)
FMT_THROW(FormatError("invalid format specifier for char"));
typedef typename BasicWriter<Char>::CharPtr CharPtr;
Char fill = internal::CharTraits<Char>::cast(spec_.fill());
CharPtr out = CharPtr();
const unsigned CHAR_WIDTH = 1;
if (spec_.width_ > CHAR_WIDTH)
{
out = writer_.grow_buffer(spec_.width_);
if (spec_.align_ == ALIGN_RIGHT)
{
std::fill_n(out, spec_.width_ - CHAR_WIDTH, fill);
out += spec_.width_ - CHAR_WIDTH;
}
else if (spec_.align_ == ALIGN_CENTER)
{