mirror of
https://github.com/deepinsight/insightface.git
synced 2026-07-19 04:17:49 +00:00
Add the inspireface project to cpp-package.
This commit is contained in:
17970
cpp-package/inspireface/cpp/test/3rd/catch2/catch2/catch.hpp
Normal file
17970
cpp-package/inspireface/cpp/test/3rd/catch2/catch2/catch.hpp
Normal file
File diff suppressed because it is too large
Load Diff
600
cpp-package/inspireface/cpp/test/3rd/cnpy/cnpy/npy.hpp
Normal file
600
cpp-package/inspireface/cpp/test/3rd/cnpy/cnpy/npy.hpp
Normal file
@@ -0,0 +1,600 @@
|
||||
/*
|
||||
Copyright 2017-2023 Leon Merten Lohse
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef NPY_HPP_
|
||||
#define NPY_HPP_
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <complex>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace npy {
|
||||
|
||||
/* Compile-time test for byte order.
|
||||
If your compiler does not define these per default, you may want to define
|
||||
one of these constants manually.
|
||||
Defaults to little endian order. */
|
||||
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || defined(__BIG_ENDIAN__) || defined(__ARMEB__) || \
|
||||
defined(__THUMBEB__) || defined(__AARCH64EB__) || defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__)
|
||||
const bool big_endian = true;
|
||||
#else
|
||||
const bool big_endian = false;
|
||||
#endif
|
||||
|
||||
const size_t magic_string_length = 6;
|
||||
const std::array<char, magic_string_length> magic_string = {'\x93', 'N', 'U', 'M', 'P', 'Y'};
|
||||
|
||||
const char little_endian_char = '<';
|
||||
const char big_endian_char = '>';
|
||||
const char no_endian_char = '|';
|
||||
|
||||
constexpr std::array<char, 3> endian_chars = {little_endian_char, big_endian_char, no_endian_char};
|
||||
constexpr std::array<char, 4> numtype_chars = {'f', 'i', 'u', 'c'};
|
||||
|
||||
constexpr char host_endian_char = (big_endian ? big_endian_char : little_endian_char);
|
||||
|
||||
/* npy array length */
|
||||
using ndarray_len_t = unsigned long int;
|
||||
using shape_t = std::vector<ndarray_len_t>;
|
||||
|
||||
using version_t = std::pair<char, char>;
|
||||
|
||||
struct dtype_t {
|
||||
char byteorder;
|
||||
char kind;
|
||||
unsigned int itemsize;
|
||||
|
||||
inline std::string str() const {
|
||||
std::stringstream ss;
|
||||
ss << byteorder << kind << itemsize;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
inline std::tuple<const char, const char, const unsigned int> tie() const {
|
||||
return std::tie(byteorder, kind, itemsize);
|
||||
}
|
||||
};
|
||||
|
||||
struct header_t {
|
||||
dtype_t dtype;
|
||||
bool fortran_order;
|
||||
shape_t shape;
|
||||
};
|
||||
|
||||
inline void write_magic(std::ostream &ostream, version_t version) {
|
||||
ostream.write(magic_string.data(), magic_string_length);
|
||||
ostream.put(version.first);
|
||||
ostream.put(version.second);
|
||||
}
|
||||
|
||||
inline version_t read_magic(std::istream &istream) {
|
||||
std::array<char, magic_string_length + 2> buf{};
|
||||
istream.read(buf.data(), sizeof(buf));
|
||||
|
||||
if (!istream) {
|
||||
throw std::runtime_error("io error: failed reading file");
|
||||
}
|
||||
|
||||
if (!std::equal(magic_string.begin(), magic_string.end(), buf.begin()))
|
||||
throw std::runtime_error("this file does not have a valid npy format.");
|
||||
|
||||
version_t version;
|
||||
version.first = buf[magic_string_length];
|
||||
version.second = buf[magic_string_length + 1];
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
const std::unordered_map<std::type_index, dtype_t> dtype_map = {
|
||||
{std::type_index(typeid(float)), {host_endian_char, 'f', sizeof(float)}},
|
||||
{std::type_index(typeid(double)), {host_endian_char, 'f', sizeof(double)}},
|
||||
{std::type_index(typeid(long double)), {host_endian_char, 'f', sizeof(long double)}},
|
||||
{std::type_index(typeid(char)), {no_endian_char, 'i', sizeof(char)}},
|
||||
{std::type_index(typeid(signed char)), {no_endian_char, 'i', sizeof(signed char)}},
|
||||
{std::type_index(typeid(short)), {host_endian_char, 'i', sizeof(short)}},
|
||||
{std::type_index(typeid(int)), {host_endian_char, 'i', sizeof(int)}},
|
||||
{std::type_index(typeid(long)), {host_endian_char, 'i', sizeof(long)}},
|
||||
{std::type_index(typeid(long long)), {host_endian_char, 'i', sizeof(long long)}},
|
||||
{std::type_index(typeid(unsigned char)), {no_endian_char, 'u', sizeof(unsigned char)}},
|
||||
{std::type_index(typeid(unsigned short)), {host_endian_char, 'u', sizeof(unsigned short)}},
|
||||
{std::type_index(typeid(unsigned int)), {host_endian_char, 'u', sizeof(unsigned int)}},
|
||||
{std::type_index(typeid(unsigned long)), {host_endian_char, 'u', sizeof(unsigned long)}},
|
||||
{std::type_index(typeid(unsigned long long)), {host_endian_char, 'u', sizeof(unsigned long long)}},
|
||||
{std::type_index(typeid(std::complex<float>)), {host_endian_char, 'c', sizeof(std::complex<float>)}},
|
||||
{std::type_index(typeid(std::complex<double>)), {host_endian_char, 'c', sizeof(std::complex<double>)}},
|
||||
{std::type_index(typeid(std::complex<long double>)), {host_endian_char, 'c', sizeof(std::complex<long double>)}}};
|
||||
|
||||
// helpers
|
||||
inline bool is_digits(const std::string &str) { return std::all_of(str.begin(), str.end(), ::isdigit); }
|
||||
|
||||
template <typename T, size_t N>
|
||||
inline bool in_array(T val, const std::array<T, N> &arr) {
|
||||
return std::find(std::begin(arr), std::end(arr), val) != std::end(arr);
|
||||
}
|
||||
|
||||
inline dtype_t parse_descr(std::string typestring) {
|
||||
if (typestring.length() < 3) {
|
||||
throw std::runtime_error("invalid typestring (length)");
|
||||
}
|
||||
|
||||
char byteorder_c = typestring.at(0);
|
||||
char kind_c = typestring.at(1);
|
||||
std::string itemsize_s = typestring.substr(2);
|
||||
|
||||
if (!in_array(byteorder_c, endian_chars)) {
|
||||
throw std::runtime_error("invalid typestring (byteorder)");
|
||||
}
|
||||
|
||||
if (!in_array(kind_c, numtype_chars)) {
|
||||
throw std::runtime_error("invalid typestring (kind)");
|
||||
}
|
||||
|
||||
if (!is_digits(itemsize_s)) {
|
||||
throw std::runtime_error("invalid typestring (itemsize)");
|
||||
}
|
||||
unsigned int itemsize = std::stoul(itemsize_s);
|
||||
|
||||
return {byteorder_c, kind_c, itemsize};
|
||||
}
|
||||
|
||||
namespace pyparse {
|
||||
|
||||
/**
|
||||
Removes leading and trailing whitespaces
|
||||
*/
|
||||
inline std::string trim(const std::string &str) {
|
||||
const std::string whitespace = " \t";
|
||||
auto begin = str.find_first_not_of(whitespace);
|
||||
|
||||
if (begin == std::string::npos) return "";
|
||||
|
||||
auto end = str.find_last_not_of(whitespace);
|
||||
|
||||
return str.substr(begin, end - begin + 1);
|
||||
}
|
||||
|
||||
inline std::string get_value_from_map(const std::string &mapstr) {
|
||||
size_t sep_pos = mapstr.find_first_of(":");
|
||||
if (sep_pos == std::string::npos) return "";
|
||||
|
||||
std::string tmp = mapstr.substr(sep_pos + 1);
|
||||
return trim(tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
Parses the string representation of a Python dict
|
||||
|
||||
The keys need to be known and may not appear anywhere else in the data.
|
||||
*/
|
||||
inline std::unordered_map<std::string, std::string> parse_dict(std::string in, const std::vector<std::string> &keys) {
|
||||
std::unordered_map<std::string, std::string> map;
|
||||
|
||||
if (keys.size() == 0) return map;
|
||||
|
||||
in = trim(in);
|
||||
|
||||
// unwrap dictionary
|
||||
if ((in.front() == '{') && (in.back() == '}'))
|
||||
in = in.substr(1, in.length() - 2);
|
||||
else
|
||||
throw std::runtime_error("Not a Python dictionary.");
|
||||
|
||||
std::vector<std::pair<size_t, std::string>> positions;
|
||||
|
||||
for (auto const &value : keys) {
|
||||
size_t pos = in.find("'" + value + "'");
|
||||
|
||||
if (pos == std::string::npos) throw std::runtime_error("Missing '" + value + "' key.");
|
||||
|
||||
std::pair<size_t, std::string> position_pair{pos, value};
|
||||
positions.push_back(position_pair);
|
||||
}
|
||||
|
||||
// sort by position in dict
|
||||
std::sort(positions.begin(), positions.end());
|
||||
|
||||
for (size_t i = 0; i < positions.size(); ++i) {
|
||||
std::string raw_value;
|
||||
size_t begin{positions[i].first};
|
||||
size_t end{std::string::npos};
|
||||
|
||||
std::string key = positions[i].second;
|
||||
|
||||
if (i + 1 < positions.size()) end = positions[i + 1].first;
|
||||
|
||||
raw_value = in.substr(begin, end - begin);
|
||||
|
||||
raw_value = trim(raw_value);
|
||||
|
||||
if (raw_value.back() == ',') raw_value.pop_back();
|
||||
|
||||
map[key] = get_value_from_map(raw_value);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
Parses the string representation of a Python boolean
|
||||
*/
|
||||
inline bool parse_bool(const std::string &in) {
|
||||
if (in == "True") return true;
|
||||
if (in == "False") return false;
|
||||
|
||||
throw std::runtime_error("Invalid python boolan.");
|
||||
}
|
||||
|
||||
/**
|
||||
Parses the string representation of a Python str
|
||||
*/
|
||||
inline std::string parse_str(const std::string &in) {
|
||||
if ((in.front() == '\'') && (in.back() == '\'')) return in.substr(1, in.length() - 2);
|
||||
|
||||
throw std::runtime_error("Invalid python string.");
|
||||
}
|
||||
|
||||
/**
|
||||
Parses the string represenatation of a Python tuple into a vector of its items
|
||||
*/
|
||||
inline std::vector<std::string> parse_tuple(std::string in) {
|
||||
std::vector<std::string> v;
|
||||
const char seperator = ',';
|
||||
|
||||
in = trim(in);
|
||||
|
||||
if ((in.front() == '(') && (in.back() == ')'))
|
||||
in = in.substr(1, in.length() - 2);
|
||||
else
|
||||
throw std::runtime_error("Invalid Python tuple.");
|
||||
|
||||
std::istringstream iss(in);
|
||||
|
||||
for (std::string token; std::getline(iss, token, seperator);) {
|
||||
v.push_back(token);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::string write_tuple(const std::vector<T> &v) {
|
||||
if (v.size() == 0) return "()";
|
||||
|
||||
std::ostringstream ss;
|
||||
ss.imbue(std::locale("C"));
|
||||
|
||||
if (v.size() == 1) {
|
||||
ss << "(" << v.front() << ",)";
|
||||
} else {
|
||||
const std::string delimiter = ", ";
|
||||
// v.size() > 1
|
||||
ss << "(";
|
||||
std::copy(v.begin(), v.end() - 1, std::ostream_iterator<T>(ss, delimiter.c_str()));
|
||||
ss << v.back();
|
||||
ss << ")";
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
inline std::string write_boolean(bool b) {
|
||||
if (b)
|
||||
return "True";
|
||||
else
|
||||
return "False";
|
||||
}
|
||||
|
||||
} // namespace pyparse
|
||||
|
||||
inline header_t parse_header(std::string header) {
|
||||
/*
|
||||
The first 6 bytes are a magic string: exactly "x93NUMPY".
|
||||
The next 1 byte is an unsigned byte: the major version number of the file
|
||||
format, e.g. x01. The next 1 byte is an unsigned byte: the minor version
|
||||
number of the file format, e.g. x00. Note: the version of the file format
|
||||
is not tied to the version of the numpy package. The next 2 bytes form a
|
||||
little-endian unsigned short int: the length of the header data HEADER_LEN.
|
||||
The next HEADER_LEN bytes form the header data describing the array's
|
||||
format. It is an ASCII string which contains a Python literal expression of
|
||||
a dictionary. It is terminated by a newline ('n') and padded with spaces
|
||||
('x20') to make the total length of the magic string + 4 + HEADER_LEN be
|
||||
evenly divisible by 16 for alignment purposes. The dictionary contains
|
||||
three keys:
|
||||
|
||||
"descr" : dtype.descr
|
||||
An object that can be passed as an argument to the numpy.dtype()
|
||||
constructor to create the array's dtype. "fortran_order" : bool Whether the
|
||||
array data is Fortran-contiguous or not. Since Fortran-contiguous arrays
|
||||
are a common form of non-C-contiguity, we allow them to be written directly
|
||||
to disk for efficiency. "shape" : tuple of int The shape of the array. For
|
||||
repeatability and readability, this dictionary is formatted using
|
||||
pprint.pformat() so the keys are in alphabetic order.
|
||||
*/
|
||||
|
||||
// remove trailing newline
|
||||
if (header.back() != '\n') throw std::runtime_error("invalid header");
|
||||
header.pop_back();
|
||||
|
||||
// parse the dictionary
|
||||
std::vector<std::string> keys{"descr", "fortran_order", "shape"};
|
||||
auto dict_map = npy::pyparse::parse_dict(header, keys);
|
||||
|
||||
if (dict_map.size() == 0) throw std::runtime_error("invalid dictionary in header");
|
||||
|
||||
std::string descr_s = dict_map["descr"];
|
||||
std::string fortran_s = dict_map["fortran_order"];
|
||||
std::string shape_s = dict_map["shape"];
|
||||
|
||||
std::string descr = npy::pyparse::parse_str(descr_s);
|
||||
dtype_t dtype = parse_descr(descr);
|
||||
|
||||
// convert literal Python bool to C++ bool
|
||||
bool fortran_order = npy::pyparse::parse_bool(fortran_s);
|
||||
|
||||
// parse the shape tuple
|
||||
auto shape_v = npy::pyparse::parse_tuple(shape_s);
|
||||
|
||||
shape_t shape;
|
||||
for (auto item : shape_v) {
|
||||
auto dim = static_cast<ndarray_len_t>(std::stoul(item));
|
||||
shape.push_back(dim);
|
||||
}
|
||||
|
||||
return {dtype, fortran_order, shape};
|
||||
}
|
||||
|
||||
inline std::string write_header_dict(const std::string &descr, bool fortran_order, const shape_t &shape) {
|
||||
std::string s_fortran_order = npy::pyparse::write_boolean(fortran_order);
|
||||
std::string shape_s = npy::pyparse::write_tuple(shape);
|
||||
|
||||
return "{'descr': '" + descr + "', 'fortran_order': " + s_fortran_order + ", 'shape': " + shape_s + ", }";
|
||||
}
|
||||
|
||||
inline void write_header(std::ostream &out, const header_t &header) {
|
||||
std::string header_dict = write_header_dict(header.dtype.str(), header.fortran_order, header.shape);
|
||||
|
||||
size_t length = magic_string_length + 2 + 2 + header_dict.length() + 1;
|
||||
|
||||
version_t version{1, 0};
|
||||
if (length >= 255 * 255) {
|
||||
length = magic_string_length + 2 + 4 + header_dict.length() + 1;
|
||||
version = {2, 0};
|
||||
}
|
||||
size_t padding_len = 16 - length % 16;
|
||||
std::string padding(padding_len, ' ');
|
||||
|
||||
// write magic
|
||||
write_magic(out, version);
|
||||
|
||||
// write header length
|
||||
if (version == version_t{1, 0}) {
|
||||
auto header_len = static_cast<uint16_t>(header_dict.length() + padding.length() + 1);
|
||||
|
||||
std::array<uint8_t, 2> header_len_le16{static_cast<uint8_t>((header_len >> 0) & 0xff),
|
||||
static_cast<uint8_t>((header_len >> 8) & 0xff)};
|
||||
out.write(reinterpret_cast<char *>(header_len_le16.data()), 2);
|
||||
} else {
|
||||
auto header_len = static_cast<uint32_t>(header_dict.length() + padding.length() + 1);
|
||||
|
||||
std::array<uint8_t, 4> header_len_le32{
|
||||
static_cast<uint8_t>((header_len >> 0) & 0xff), static_cast<uint8_t>((header_len >> 8) & 0xff),
|
||||
static_cast<uint8_t>((header_len >> 16) & 0xff), static_cast<uint8_t>((header_len >> 24) & 0xff)};
|
||||
out.write(reinterpret_cast<char *>(header_len_le32.data()), 4);
|
||||
}
|
||||
|
||||
out << header_dict << padding << '\n';
|
||||
}
|
||||
|
||||
inline std::string read_header(std::istream &istream) {
|
||||
// check magic bytes an version number
|
||||
version_t version = read_magic(istream);
|
||||
|
||||
uint32_t header_length = 0;
|
||||
if (version == version_t{1, 0}) {
|
||||
std::array<uint8_t, 2> header_len_le16{};
|
||||
istream.read(reinterpret_cast<char *>(header_len_le16.data()), 2);
|
||||
header_length = (header_len_le16[0] << 0) | (header_len_le16[1] << 8);
|
||||
|
||||
if ((magic_string_length + 2 + 2 + header_length) % 16 != 0) {
|
||||
// TODO(llohse): display warning
|
||||
}
|
||||
} else if (version == version_t{2, 0}) {
|
||||
std::array<uint8_t, 4> header_len_le32{};
|
||||
istream.read(reinterpret_cast<char *>(header_len_le32.data()), 4);
|
||||
|
||||
header_length =
|
||||
(header_len_le32[0] << 0) | (header_len_le32[1] << 8) | (header_len_le32[2] << 16) | (header_len_le32[3] << 24);
|
||||
|
||||
if ((magic_string_length + 2 + 4 + header_length) % 16 != 0) {
|
||||
// TODO(llohse): display warning
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("unsupported file format version");
|
||||
}
|
||||
|
||||
auto buf_v = std::vector<char>(header_length);
|
||||
istream.read(buf_v.data(), header_length);
|
||||
std::string header(buf_v.data(), header_length);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
inline ndarray_len_t comp_size(const shape_t &shape) {
|
||||
ndarray_len_t size = 1;
|
||||
for (ndarray_len_t i : shape) size *= i;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
struct npy_data {
|
||||
std::vector<Scalar> data = {};
|
||||
shape_t shape = {};
|
||||
bool fortran_order = false;
|
||||
};
|
||||
|
||||
template <typename Scalar>
|
||||
struct npy_data_ptr {
|
||||
const Scalar *data_ptr = nullptr;
|
||||
shape_t shape = {};
|
||||
bool fortran_order = false;
|
||||
};
|
||||
|
||||
template <typename Scalar>
|
||||
inline npy_data<Scalar> read_npy(std::istream &in) {
|
||||
std::string header_s = read_header(in);
|
||||
|
||||
// parse header
|
||||
header_t header = parse_header(header_s);
|
||||
|
||||
// check if the typestring matches the given one
|
||||
const dtype_t dtype = dtype_map.at(std::type_index(typeid(Scalar)));
|
||||
|
||||
if (header.dtype.tie() != dtype.tie()) {
|
||||
throw std::runtime_error("formatting error: typestrings not matching");
|
||||
}
|
||||
|
||||
// compute the data size based on the shape
|
||||
auto size = static_cast<size_t>(comp_size(header.shape));
|
||||
|
||||
npy_data<Scalar> data;
|
||||
|
||||
data.shape = header.shape;
|
||||
data.fortran_order = header.fortran_order;
|
||||
|
||||
data.data.resize(size);
|
||||
|
||||
// read the data
|
||||
in.read(reinterpret_cast<char *>(data.data.data()), sizeof(Scalar) * size);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
inline npy_data<Scalar> read_npy(const std::string &filename) {
|
||||
std::ifstream stream(filename, std::ifstream::binary);
|
||||
if (!stream) {
|
||||
throw std::runtime_error("io error: failed to open a file.");
|
||||
}
|
||||
|
||||
return read_npy<Scalar>(stream);
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
inline void write_npy(std::ostream &out, const npy_data<Scalar> &data) {
|
||||
// static_assert(has_typestring<Scalar>::value, "scalar type not
|
||||
// understood");
|
||||
const dtype_t dtype = dtype_map.at(std::type_index(typeid(Scalar)));
|
||||
|
||||
header_t header{dtype, data.fortran_order, data.shape};
|
||||
write_header(out, header);
|
||||
|
||||
auto size = static_cast<size_t>(comp_size(data.shape));
|
||||
|
||||
out.write(reinterpret_cast<const char *>(data.data.data()), sizeof(Scalar) * size);
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
inline void write_npy(const std::string &filename, const npy_data<Scalar> &data) {
|
||||
std::ofstream stream(filename, std::ofstream::binary);
|
||||
if (!stream) {
|
||||
throw std::runtime_error("io error: failed to open a file.");
|
||||
}
|
||||
|
||||
write_npy<Scalar>(stream, data);
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
inline void write_npy(std::ostream &out, const npy_data_ptr<Scalar> &data_ptr) {
|
||||
const dtype_t dtype = dtype_map.at(std::type_index(typeid(Scalar)));
|
||||
|
||||
header_t header{dtype, data_ptr.fortran_order, data_ptr.shape};
|
||||
write_header(out, header);
|
||||
|
||||
auto size = static_cast<size_t>(comp_size(data_ptr.shape));
|
||||
|
||||
out.write(reinterpret_cast<const char *>(data_ptr.data_ptr), sizeof(Scalar) * size);
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
inline void write_npy(const std::string &filename, const npy_data_ptr<Scalar> &data_ptr) {
|
||||
std::ofstream stream(filename, std::ofstream::binary);
|
||||
if (!stream) {
|
||||
throw std::runtime_error("io error: failed to open a file.");
|
||||
}
|
||||
|
||||
write_npy<Scalar>(stream, data_ptr);
|
||||
}
|
||||
|
||||
// old interface
|
||||
|
||||
// NOLINTBEGIN(*-avoid-c-arrays)
|
||||
template <typename Scalar>
|
||||
inline void SaveArrayAsNumpy(const std::string &filename, bool fortran_order, unsigned int n_dims,
|
||||
const unsigned long shape[], const Scalar *data) {
|
||||
const npy_data_ptr<Scalar> ptr{data, {shape, shape + n_dims}, fortran_order};
|
||||
|
||||
write_npy<Scalar>(filename, ptr);
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
inline void SaveArrayAsNumpy(const std::string &filename, bool fortran_order, unsigned int n_dims,
|
||||
const unsigned long shape[], const std::vector<Scalar> &data) {
|
||||
SaveArrayAsNumpy(filename, fortran_order, n_dims, shape, data.data());
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
inline void LoadArrayFromNumpy(const std::string &filename, std::vector<unsigned long> &shape, bool &fortran_order,
|
||||
std::vector<Scalar> &data) {
|
||||
const npy_data<Scalar> n_data = read_npy<Scalar>(filename);
|
||||
|
||||
shape = n_data.shape;
|
||||
fortran_order = n_data.fortran_order;
|
||||
|
||||
std::copy(n_data.data.begin(), n_data.data.end(), std::back_inserter(data));
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
inline void LoadArrayFromNumpy(const std::string &filename, std::vector<unsigned long> &shape,
|
||||
std::vector<Scalar> &data) {
|
||||
bool fortran_order = false;
|
||||
LoadArrayFromNumpy<Scalar>(filename, shape, fortran_order, data);
|
||||
}
|
||||
// NOLINTEND(*-avoid-c-arrays)
|
||||
|
||||
} // namespace npy
|
||||
|
||||
#endif // NPY_HPP_
|
||||
@@ -0,0 +1,290 @@
|
||||
|
||||
#ifndef INDICATORS_BLOCK_PROGRESS_BAR
|
||||
#define INDICATORS_BLOCK_PROGRESS_BAR
|
||||
|
||||
#include <indicators/color.hpp>
|
||||
#include <indicators/details/stream_helper.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <indicators/setting.hpp>
|
||||
#include <indicators/terminal_size.hpp>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace indicators {
|
||||
|
||||
class BlockProgressBar {
|
||||
using Settings = std::tuple<option::ForegroundColor, option::BarWidth, option::Start, option::End,
|
||||
option::PrefixText, option::PostfixText, option::ShowPercentage,
|
||||
option::ShowElapsedTime, option::ShowRemainingTime, option::Completed,
|
||||
option::SavedStartTime, option::MaxPostfixTextLen, option::FontStyles,
|
||||
option::MaxProgress, option::Stream>;
|
||||
|
||||
public:
|
||||
template <typename... Args,
|
||||
typename std::enable_if<details::are_settings_from_tuple<
|
||||
Settings, typename std::decay<Args>::type...>::value,
|
||||
void *>::type = nullptr>
|
||||
explicit BlockProgressBar(Args &&... args)
|
||||
: settings_(details::get<details::ProgressBarOption::foreground_color>(
|
||||
option::ForegroundColor{Color::unspecified}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::bar_width>(option::BarWidth{100},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::start>(option::Start{"["},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::end>(option::End{"]"},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::prefix_text>(
|
||||
option::PrefixText{""}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::postfix_text>(
|
||||
option::PostfixText{""}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::show_percentage>(
|
||||
option::ShowPercentage{true}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::show_elapsed_time>(
|
||||
option::ShowElapsedTime{false}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::show_remaining_time>(
|
||||
option::ShowRemainingTime{false}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::completed>(option::Completed{false},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::saved_start_time>(
|
||||
option::SavedStartTime{false}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::max_postfix_text_len>(
|
||||
option::MaxPostfixTextLen{0}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::font_styles>(
|
||||
option::FontStyles{std::vector<FontStyle>{}}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::max_progress>(
|
||||
option::MaxProgress{100}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::stream>(option::Stream{std::cout},
|
||||
std::forward<Args>(args)...)) {}
|
||||
|
||||
template <typename T, details::ProgressBarOption id>
|
||||
void set_option(details::Setting<T, id> &&setting) {
|
||||
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
|
||||
std::declval<Settings>()))>::type>::value,
|
||||
"Setting has wrong type!");
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<id>() = std::move(setting).value;
|
||||
}
|
||||
|
||||
template <typename T, details::ProgressBarOption id>
|
||||
void set_option(const details::Setting<T, id> &setting) {
|
||||
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
|
||||
std::declval<Settings>()))>::type>::value,
|
||||
"Setting has wrong type!");
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<id>() = setting.value;
|
||||
}
|
||||
|
||||
void set_option(
|
||||
const details::Setting<std::string, details::ProgressBarOption::postfix_text> &setting) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<details::ProgressBarOption::postfix_text>() = setting.value;
|
||||
if (setting.value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
|
||||
get_value<details::ProgressBarOption::max_postfix_text_len>() = setting.value.length();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_option(details::Setting<std::string, details::ProgressBarOption::postfix_text> &&setting) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<details::ProgressBarOption::postfix_text>() = std::move(setting).value;
|
||||
auto &new_value = get_value<details::ProgressBarOption::postfix_text>();
|
||||
if (new_value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
|
||||
get_value<details::ProgressBarOption::max_postfix_text_len>() = new_value.length();
|
||||
}
|
||||
}
|
||||
|
||||
void set_progress(float value) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
progress_ = value;
|
||||
}
|
||||
save_start_time();
|
||||
print_progress();
|
||||
}
|
||||
|
||||
void tick() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
progress_ += 1;
|
||||
}
|
||||
save_start_time();
|
||||
print_progress();
|
||||
}
|
||||
|
||||
size_t current() {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
return (std::min)(static_cast<size_t>(progress_),
|
||||
size_t(get_value<details::ProgressBarOption::max_progress>()));
|
||||
}
|
||||
|
||||
bool is_completed() const { return get_value<details::ProgressBarOption::completed>(); }
|
||||
|
||||
void mark_as_completed() {
|
||||
get_value<details::ProgressBarOption::completed>() = true;
|
||||
print_progress();
|
||||
}
|
||||
|
||||
private:
|
||||
template <details::ProgressBarOption id>
|
||||
auto get_value() -> decltype((details::get_value<id>(std::declval<Settings &>()).value)) {
|
||||
return details::get_value<id>(settings_).value;
|
||||
}
|
||||
|
||||
template <details::ProgressBarOption id>
|
||||
auto get_value() const
|
||||
-> decltype((details::get_value<id>(std::declval<const Settings &>()).value)) {
|
||||
return details::get_value<id>(settings_).value;
|
||||
}
|
||||
|
||||
Settings settings_;
|
||||
float progress_{0.0};
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> start_time_point_;
|
||||
std::mutex mutex_;
|
||||
|
||||
template <typename Indicator, size_t count> friend class MultiProgress;
|
||||
template <typename Indicator> friend class DynamicProgress;
|
||||
std::atomic<bool> multi_progress_mode_{false};
|
||||
|
||||
void save_start_time() {
|
||||
auto &show_elapsed_time = get_value<details::ProgressBarOption::show_elapsed_time>();
|
||||
auto &saved_start_time = get_value<details::ProgressBarOption::saved_start_time>();
|
||||
auto &show_remaining_time = get_value<details::ProgressBarOption::show_remaining_time>();
|
||||
if ((show_elapsed_time || show_remaining_time) && !saved_start_time) {
|
||||
start_time_point_ = std::chrono::high_resolution_clock::now();
|
||||
saved_start_time = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::string, size_t> get_prefix_text() {
|
||||
std::stringstream os;
|
||||
os << get_value<details::ProgressBarOption::prefix_text>();
|
||||
const auto result = os.str();
|
||||
const auto result_size = unicode::display_width(result);
|
||||
return {result, result_size};
|
||||
}
|
||||
|
||||
std::pair<std::string, size_t> get_postfix_text() {
|
||||
std::stringstream os;
|
||||
const auto max_progress = get_value<details::ProgressBarOption::max_progress>();
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(now - start_time_point_);
|
||||
|
||||
if (get_value<details::ProgressBarOption::show_percentage>()) {
|
||||
os << " " << (std::min)(static_cast<size_t>(progress_ / max_progress * 100.0), size_t(100))
|
||||
<< "%";
|
||||
}
|
||||
|
||||
auto &saved_start_time = get_value<details::ProgressBarOption::saved_start_time>();
|
||||
|
||||
if (get_value<details::ProgressBarOption::show_elapsed_time>()) {
|
||||
os << " [";
|
||||
if (saved_start_time)
|
||||
details::write_duration(os, elapsed);
|
||||
else
|
||||
os << "00:00s";
|
||||
}
|
||||
|
||||
if (get_value<details::ProgressBarOption::show_remaining_time>()) {
|
||||
if (get_value<details::ProgressBarOption::show_elapsed_time>())
|
||||
os << "<";
|
||||
else
|
||||
os << " [";
|
||||
|
||||
if (saved_start_time) {
|
||||
auto eta = std::chrono::nanoseconds(
|
||||
progress_ > 0
|
||||
? static_cast<long long>(std::ceil(float(elapsed.count()) *
|
||||
max_progress / progress_))
|
||||
: 0);
|
||||
auto remaining = eta > elapsed ? (eta - elapsed) : (elapsed - eta);
|
||||
details::write_duration(os, remaining);
|
||||
} else {
|
||||
os << "00:00s";
|
||||
}
|
||||
|
||||
os << "]";
|
||||
} else {
|
||||
if (get_value<details::ProgressBarOption::show_elapsed_time>())
|
||||
os << "]";
|
||||
}
|
||||
|
||||
os << " " << get_value<details::ProgressBarOption::postfix_text>();
|
||||
|
||||
const auto result = os.str();
|
||||
const auto result_size = unicode::display_width(result);
|
||||
return {result, result_size};
|
||||
}
|
||||
|
||||
public:
|
||||
void print_progress(bool from_multi_progress = false) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
|
||||
auto &os = get_value<details::ProgressBarOption::stream>();
|
||||
|
||||
const auto max_progress = get_value<details::ProgressBarOption::max_progress>();
|
||||
if (multi_progress_mode_ && !from_multi_progress) {
|
||||
if (progress_ > max_progress) {
|
||||
get_value<details::ProgressBarOption::completed>() = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (get_value<details::ProgressBarOption::foreground_color>() != Color::unspecified)
|
||||
details::set_stream_color(os, get_value<details::ProgressBarOption::foreground_color>());
|
||||
|
||||
for (auto &style : get_value<details::ProgressBarOption::font_styles>())
|
||||
details::set_font_style(os, style);
|
||||
|
||||
const auto prefix_pair = get_prefix_text();
|
||||
const auto prefix_text = prefix_pair.first;
|
||||
const auto prefix_length = prefix_pair.second;
|
||||
os << "\r" << prefix_text;
|
||||
|
||||
os << get_value<details::ProgressBarOption::start>();
|
||||
|
||||
details::BlockProgressScaleWriter writer{os,
|
||||
get_value<details::ProgressBarOption::bar_width>()};
|
||||
writer.write(progress_ / max_progress * 100);
|
||||
|
||||
os << get_value<details::ProgressBarOption::end>();
|
||||
|
||||
const auto postfix_pair = get_postfix_text();
|
||||
const auto postfix_text = postfix_pair.first;
|
||||
const auto postfix_length = postfix_pair.second;
|
||||
os << postfix_text;
|
||||
|
||||
// Get length of prefix text and postfix text
|
||||
const auto start_length = get_value<details::ProgressBarOption::start>().size();
|
||||
const auto bar_width = get_value<details::ProgressBarOption::bar_width>();
|
||||
const auto end_length = get_value<details::ProgressBarOption::end>().size();
|
||||
const auto terminal_width = terminal_size().second;
|
||||
// prefix + bar_width + postfix should be <= terminal_width
|
||||
const int remaining = terminal_width - (prefix_length + start_length + bar_width + end_length + postfix_length);
|
||||
if (remaining > 0) {
|
||||
os << std::string(remaining, ' ') << "\r";
|
||||
} else if (remaining < 0) {
|
||||
// Do nothing. Maybe in the future truncate postfix with ...
|
||||
}
|
||||
os.flush();
|
||||
|
||||
if (progress_ > max_progress) {
|
||||
get_value<details::ProgressBarOption::completed>() = true;
|
||||
}
|
||||
if (get_value<details::ProgressBarOption::completed>() &&
|
||||
!from_multi_progress) // Don't std::endl if calling from MultiProgress
|
||||
os << termcolor::reset << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace indicators
|
||||
|
||||
#endif
|
||||
9
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/color.hpp
Executable file
9
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/color.hpp
Executable file
@@ -0,0 +1,9 @@
|
||||
|
||||
#ifndef INDICATORS_COLOR
|
||||
#define INDICATORS_COLOR
|
||||
|
||||
namespace indicators {
|
||||
enum class Color { grey, red, green, yellow, blue, magenta, cyan, white, unspecified };
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,66 @@
|
||||
|
||||
#ifndef INDICATORS_CURSOR_CONTROL
|
||||
#define INDICATORS_CURSOR_CONTROL
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#if !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <io.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <cstdio>
|
||||
#endif
|
||||
|
||||
namespace indicators {
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
static inline void show_console_cursor(bool const show) {
|
||||
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
CONSOLE_CURSOR_INFO cursorInfo;
|
||||
|
||||
GetConsoleCursorInfo(out, &cursorInfo);
|
||||
cursorInfo.bVisible = show; // set the cursor visibility
|
||||
SetConsoleCursorInfo(out, &cursorInfo);
|
||||
}
|
||||
|
||||
static inline void erase_line() {
|
||||
auto hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (!hStdout)
|
||||
return;
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
|
||||
GetConsoleScreenBufferInfo(hStdout, &csbiInfo);
|
||||
|
||||
COORD cursor;
|
||||
|
||||
cursor.X = 0;
|
||||
cursor.Y = csbiInfo.dwCursorPosition.Y;
|
||||
|
||||
DWORD count = 0;
|
||||
|
||||
FillConsoleOutputCharacterA(hStdout, ' ', csbiInfo.dwSize.X, cursor, &count);
|
||||
|
||||
FillConsoleOutputAttribute(hStdout, csbiInfo.wAttributes, csbiInfo.dwSize.X,
|
||||
cursor, &count);
|
||||
|
||||
SetConsoleCursorPosition(hStdout, cursor);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline void show_console_cursor(bool const show) {
|
||||
std::fputs(show ? "\033[?25h" : "\033[?25l", stdout);
|
||||
}
|
||||
|
||||
static inline void erase_line() {
|
||||
std::fputs("\r\033[K", stdout);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace indicators
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
#ifndef INDICATORS_CURSOR_MOVEMENT
|
||||
#define INDICATORS_CURSOR_MOVEMENT
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#if !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <io.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
namespace indicators {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
static inline void move(int x, int y) {
|
||||
auto hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (!hStdout)
|
||||
return;
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
|
||||
GetConsoleScreenBufferInfo(hStdout, &csbiInfo);
|
||||
|
||||
COORD cursor;
|
||||
|
||||
cursor.X = csbiInfo.dwCursorPosition.X + x;
|
||||
cursor.Y = csbiInfo.dwCursorPosition.Y + y;
|
||||
SetConsoleCursorPosition(hStdout, cursor);
|
||||
}
|
||||
|
||||
static inline void move_up(int lines) { move(0, -lines); }
|
||||
static inline void move_down(int lines) { move(0, -lines); }
|
||||
static inline void move_right(int cols) { move(cols, 0); }
|
||||
static inline void move_left(int cols) { move(-cols, 0); }
|
||||
|
||||
#else
|
||||
|
||||
static inline void move_up(int lines) { std::cout << "\033[" << lines << "A"; }
|
||||
static inline void move_down(int lines) { std::cout << "\033[" << lines << "B"; }
|
||||
static inline void move_right(int cols) { std::cout << "\033[" << cols << "C"; }
|
||||
static inline void move_left(int cols) { std::cout << "\033[" << cols << "D"; }
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace indicators
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,223 @@
|
||||
|
||||
#ifndef INDICATORS_STREAM_HELPER
|
||||
#define INDICATORS_STREAM_HELPER
|
||||
|
||||
#include <indicators/display_width.hpp>
|
||||
#include <indicators/setting.hpp>
|
||||
#include <indicators/termcolor.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
namespace indicators {
|
||||
namespace details {
|
||||
|
||||
inline void set_stream_color(std::ostream &os, Color color) {
|
||||
switch (color) {
|
||||
case Color::grey:
|
||||
os << termcolor::grey;
|
||||
break;
|
||||
case Color::red:
|
||||
os << termcolor::red;
|
||||
break;
|
||||
case Color::green:
|
||||
os << termcolor::green;
|
||||
break;
|
||||
case Color::yellow:
|
||||
os << termcolor::yellow;
|
||||
break;
|
||||
case Color::blue:
|
||||
os << termcolor::blue;
|
||||
break;
|
||||
case Color::magenta:
|
||||
os << termcolor::magenta;
|
||||
break;
|
||||
case Color::cyan:
|
||||
os << termcolor::cyan;
|
||||
break;
|
||||
case Color::white:
|
||||
os << termcolor::white;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
inline void set_font_style(std::ostream &os, FontStyle style) {
|
||||
switch (style) {
|
||||
case FontStyle::bold:
|
||||
os << termcolor::bold;
|
||||
break;
|
||||
case FontStyle::dark:
|
||||
os << termcolor::dark;
|
||||
break;
|
||||
case FontStyle::italic:
|
||||
os << termcolor::italic;
|
||||
break;
|
||||
case FontStyle::underline:
|
||||
os << termcolor::underline;
|
||||
break;
|
||||
case FontStyle::blink:
|
||||
os << termcolor::blink;
|
||||
break;
|
||||
case FontStyle::reverse:
|
||||
os << termcolor::reverse;
|
||||
break;
|
||||
case FontStyle::concealed:
|
||||
os << termcolor::concealed;
|
||||
break;
|
||||
case FontStyle::crossed:
|
||||
os << termcolor::crossed;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::ostream &write_duration(std::ostream &os, std::chrono::nanoseconds ns) {
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
using days = duration<int, ratio<86400>>;
|
||||
char fill = os.fill();
|
||||
os.fill('0');
|
||||
auto d = duration_cast<days>(ns);
|
||||
ns -= d;
|
||||
auto h = duration_cast<hours>(ns);
|
||||
ns -= h;
|
||||
auto m = duration_cast<minutes>(ns);
|
||||
ns -= m;
|
||||
auto s = duration_cast<seconds>(ns);
|
||||
if (d.count() > 0)
|
||||
os << setw(2) << d.count() << "d:";
|
||||
if (h.count() > 0)
|
||||
os << setw(2) << h.count() << "h:";
|
||||
os << setw(2) << m.count() << "m:" << setw(2) << s.count() << 's';
|
||||
os.fill(fill);
|
||||
return os;
|
||||
}
|
||||
|
||||
class BlockProgressScaleWriter {
|
||||
public:
|
||||
BlockProgressScaleWriter(std::ostream &os, size_t bar_width) : os(os), bar_width(bar_width) {}
|
||||
|
||||
std::ostream &write(float progress) {
|
||||
std::string fill_text{"█"};
|
||||
std::vector<std::string> lead_characters{" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"};
|
||||
auto value = (std::min)(1.0f, (std::max)(0.0f, progress / 100.0f));
|
||||
auto whole_width = std::floor(value * bar_width);
|
||||
auto remainder_width = fmod((value * bar_width), 1.0f);
|
||||
auto part_width = std::floor(remainder_width * lead_characters.size());
|
||||
std::string lead_text = lead_characters[size_t(part_width)];
|
||||
if ((bar_width - whole_width - 1) < 0)
|
||||
lead_text = "";
|
||||
for (size_t i = 0; i < whole_width; ++i)
|
||||
os << fill_text;
|
||||
os << lead_text;
|
||||
for (size_t i = 0; i < (bar_width - whole_width - 1); ++i)
|
||||
os << " ";
|
||||
return os;
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostream &os;
|
||||
size_t bar_width = 0;
|
||||
};
|
||||
|
||||
class ProgressScaleWriter {
|
||||
public:
|
||||
ProgressScaleWriter(std::ostream &os, size_t bar_width, const std::string &fill,
|
||||
const std::string &lead, const std::string &remainder)
|
||||
: os(os), bar_width(bar_width), fill(fill), lead(lead), remainder(remainder) {}
|
||||
|
||||
std::ostream &write(float progress) {
|
||||
auto pos = static_cast<size_t>(progress * bar_width / 100.0);
|
||||
for (size_t i = 0, current_display_width = 0; i < bar_width;) {
|
||||
std::string next;
|
||||
|
||||
if (i < pos) {
|
||||
next = fill;
|
||||
current_display_width = unicode::display_width(fill);
|
||||
} else if (i == pos) {
|
||||
next = lead;
|
||||
current_display_width = unicode::display_width(lead);
|
||||
} else {
|
||||
next = remainder;
|
||||
current_display_width = unicode::display_width(remainder);
|
||||
}
|
||||
|
||||
i += current_display_width;
|
||||
|
||||
if (i > bar_width) {
|
||||
// `next` is larger than the allowed bar width
|
||||
// fill with empty space instead
|
||||
os << std::string((bar_width - (i - current_display_width)), ' ');
|
||||
break;
|
||||
}
|
||||
|
||||
os << next;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostream &os;
|
||||
size_t bar_width = 0;
|
||||
std::string fill;
|
||||
std::string lead;
|
||||
std::string remainder;
|
||||
};
|
||||
|
||||
class IndeterminateProgressScaleWriter {
|
||||
public:
|
||||
IndeterminateProgressScaleWriter(std::ostream &os, size_t bar_width, const std::string &fill,
|
||||
const std::string &lead)
|
||||
: os(os), bar_width(bar_width), fill(fill), lead(lead) {}
|
||||
|
||||
std::ostream &write(size_t progress) {
|
||||
for (size_t i = 0; i < bar_width;) {
|
||||
std::string next;
|
||||
size_t current_display_width = 0;
|
||||
|
||||
if (i < progress) {
|
||||
next = fill;
|
||||
current_display_width = unicode::display_width(fill);
|
||||
} else if (i == progress) {
|
||||
next = lead;
|
||||
current_display_width = unicode::display_width(lead);
|
||||
} else {
|
||||
next = fill;
|
||||
current_display_width = unicode::display_width(fill);
|
||||
}
|
||||
|
||||
i += current_display_width;
|
||||
|
||||
if (i > bar_width) {
|
||||
// `next` is larger than the allowed bar width
|
||||
// fill with empty space instead
|
||||
os << std::string((bar_width - (i - current_display_width)), ' ');
|
||||
break;
|
||||
}
|
||||
|
||||
os << next;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostream &os;
|
||||
size_t bar_width = 0;
|
||||
std::string fill;
|
||||
std::string lead;
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace indicators
|
||||
|
||||
#endif
|
||||
353
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/display_width.hpp
Executable file
353
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/display_width.hpp
Executable file
@@ -0,0 +1,353 @@
|
||||
|
||||
#ifndef INDICATORS_DISPLAY_WIDTH
|
||||
#define INDICATORS_DISPLAY_WIDTH
|
||||
|
||||
#include <clocale>
|
||||
#include <cstdlib>
|
||||
#include <locale>
|
||||
#include <string>
|
||||
#include <wchar.h>
|
||||
|
||||
namespace unicode {
|
||||
|
||||
namespace details {
|
||||
|
||||
/*
|
||||
* This is an implementation of wcwidth() and wcswidth() (defined in
|
||||
* IEEE Std 1002.1-2001) for Unicode.
|
||||
*
|
||||
* http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
|
||||
* http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
|
||||
*
|
||||
* In fixed-width output devices, Latin characters all occupy a single
|
||||
* "cell" position of equal width, whereas ideographic CJK characters
|
||||
* occupy two such cells. Interoperability between terminal-line
|
||||
* applications and (teletype-style) character terminals using the
|
||||
* UTF-8 encoding requires agreement on which character should advance
|
||||
* the cursor by how many cell positions. No established formal
|
||||
* standards exist at present on which Unicode character shall occupy
|
||||
* how many cell positions on character terminals. These routines are
|
||||
* a first attempt of defining such behavior based on simple rules
|
||||
* applied to data provided by the Unicode Consortium.
|
||||
*
|
||||
* For some graphical characters, the Unicode standard explicitly
|
||||
* defines a character-cell width via the definition of the East Asian
|
||||
* FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
|
||||
* In all these cases, there is no ambiguity about which width a
|
||||
* terminal shall use. For characters in the East Asian Ambiguous (A)
|
||||
* class, the width choice depends purely on a preference of backward
|
||||
* compatibility with either historic CJK or Western practice.
|
||||
* Choosing single-width for these characters is easy to justify as
|
||||
* the appropriate long-term solution, as the CJK practice of
|
||||
* displaying these characters as double-width comes from historic
|
||||
* implementation simplicity (8-bit encoded characters were displayed
|
||||
* single-width and 16-bit ones double-width, even for Greek,
|
||||
* Cyrillic, etc.) and not any typographic considerations.
|
||||
*
|
||||
* Much less clear is the choice of width for the Not East Asian
|
||||
* (Neutral) class. Existing practice does not dictate a width for any
|
||||
* of these characters. It would nevertheless make sense
|
||||
* typographically to allocate two character cells to characters such
|
||||
* as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
|
||||
* represented adequately with a single-width glyph. The following
|
||||
* routines at present merely assign a single-cell width to all
|
||||
* neutral characters, in the interest of simplicity. This is not
|
||||
* entirely satisfactory and should be reconsidered before
|
||||
* establishing a formal standard in this area. At the moment, the
|
||||
* decision which Not East Asian (Neutral) characters should be
|
||||
* represented by double-width glyphs cannot yet be answered by
|
||||
* applying a simple rule from the Unicode database content. Setting
|
||||
* up a proper standard for the behavior of UTF-8 character terminals
|
||||
* will require a careful analysis not only of each Unicode character,
|
||||
* but also of each presentation form, something the author of these
|
||||
* routines has avoided to do so far.
|
||||
*
|
||||
* http://www.unicode.org/unicode/reports/tr11/
|
||||
*
|
||||
* Markus Kuhn -- 2007-05-26 (Unicode 5.0)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software
|
||||
* for any purpose and without fee is hereby granted. The author
|
||||
* disclaims all warranties with regard to this software.
|
||||
*
|
||||
* Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
|
||||
*/
|
||||
|
||||
struct interval {
|
||||
int first;
|
||||
int last;
|
||||
};
|
||||
|
||||
/* auxiliary function for binary search in interval table */
|
||||
static inline int bisearch(wchar_t ucs, const struct interval *table, int max) {
|
||||
int min = 0;
|
||||
int mid;
|
||||
|
||||
if (ucs < table[0].first || ucs > table[max].last)
|
||||
return 0;
|
||||
while (max >= min) {
|
||||
mid = (min + max) / 2;
|
||||
if (ucs > table[mid].last)
|
||||
min = mid + 1;
|
||||
else if (ucs < table[mid].first)
|
||||
max = mid - 1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The following two functions define the column width of an ISO 10646
|
||||
* character as follows:
|
||||
*
|
||||
* - The null character (U+0000) has a column width of 0.
|
||||
*
|
||||
* - Other C0/C1 control characters and DEL will lead to a return
|
||||
* value of -1.
|
||||
*
|
||||
* - Non-spacing and enclosing combining characters (general
|
||||
* category code Mn or Me in the Unicode database) have a
|
||||
* column width of 0.
|
||||
*
|
||||
* - SOFT HYPHEN (U+00AD) has a column width of 1.
|
||||
*
|
||||
* - Other format characters (general category code Cf in the Unicode
|
||||
* database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
|
||||
*
|
||||
* - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
|
||||
* have a column width of 0.
|
||||
*
|
||||
* - Spacing characters in the East Asian Wide (W) or East Asian
|
||||
* Full-width (F) category as defined in Unicode Technical
|
||||
* Report #11 have a column width of 2.
|
||||
*
|
||||
* - All remaining characters (including all printable
|
||||
* ISO 8859-1 and WGL4 characters, Unicode control characters,
|
||||
* etc.) have a column width of 1.
|
||||
*
|
||||
* This implementation assumes that wchar_t characters are encoded
|
||||
* in ISO 10646.
|
||||
*/
|
||||
|
||||
static inline int mk_wcwidth(wchar_t ucs) {
|
||||
/* sorted list of non-overlapping intervals of non-spacing characters */
|
||||
/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
|
||||
static const struct interval combining[] = {
|
||||
{0x0300, 0x036F}, {0x0483, 0x0486}, {0x0488, 0x0489},
|
||||
{0x0591, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2},
|
||||
{0x05C4, 0x05C5}, {0x05C7, 0x05C7}, {0x0600, 0x0603},
|
||||
{0x0610, 0x0615}, {0x064B, 0x065E}, {0x0670, 0x0670},
|
||||
{0x06D6, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED},
|
||||
{0x070F, 0x070F}, {0x0711, 0x0711}, {0x0730, 0x074A},
|
||||
{0x07A6, 0x07B0}, {0x07EB, 0x07F3}, {0x0901, 0x0902},
|
||||
{0x093C, 0x093C}, {0x0941, 0x0948}, {0x094D, 0x094D},
|
||||
{0x0951, 0x0954}, {0x0962, 0x0963}, {0x0981, 0x0981},
|
||||
{0x09BC, 0x09BC}, {0x09C1, 0x09C4}, {0x09CD, 0x09CD},
|
||||
{0x09E2, 0x09E3}, {0x0A01, 0x0A02}, {0x0A3C, 0x0A3C},
|
||||
{0x0A41, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D},
|
||||
{0x0A70, 0x0A71}, {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC},
|
||||
{0x0AC1, 0x0AC5}, {0x0AC7, 0x0AC8}, {0x0ACD, 0x0ACD},
|
||||
{0x0AE2, 0x0AE3}, {0x0B01, 0x0B01}, {0x0B3C, 0x0B3C},
|
||||
{0x0B3F, 0x0B3F}, {0x0B41, 0x0B43}, {0x0B4D, 0x0B4D},
|
||||
{0x0B56, 0x0B56}, {0x0B82, 0x0B82}, {0x0BC0, 0x0BC0},
|
||||
{0x0BCD, 0x0BCD}, {0x0C3E, 0x0C40}, {0x0C46, 0x0C48},
|
||||
{0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, {0x0CBC, 0x0CBC},
|
||||
{0x0CBF, 0x0CBF}, {0x0CC6, 0x0CC6}, {0x0CCC, 0x0CCD},
|
||||
{0x0CE2, 0x0CE3}, {0x0D41, 0x0D43}, {0x0D4D, 0x0D4D},
|
||||
{0x0DCA, 0x0DCA}, {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6},
|
||||
{0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E},
|
||||
{0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC},
|
||||
{0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35},
|
||||
{0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F71, 0x0F7E},
|
||||
{0x0F80, 0x0F84}, {0x0F86, 0x0F87}, {0x0F90, 0x0F97},
|
||||
{0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102D, 0x1030},
|
||||
{0x1032, 0x1032}, {0x1036, 0x1037}, {0x1039, 0x1039},
|
||||
{0x1058, 0x1059}, {0x1160, 0x11FF}, {0x135F, 0x135F},
|
||||
{0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753},
|
||||
{0x1772, 0x1773}, {0x17B4, 0x17B5}, {0x17B7, 0x17BD},
|
||||
{0x17C6, 0x17C6}, {0x17C9, 0x17D3}, {0x17DD, 0x17DD},
|
||||
{0x180B, 0x180D}, {0x18A9, 0x18A9}, {0x1920, 0x1922},
|
||||
{0x1927, 0x1928}, {0x1932, 0x1932}, {0x1939, 0x193B},
|
||||
{0x1A17, 0x1A18}, {0x1B00, 0x1B03}, {0x1B34, 0x1B34},
|
||||
{0x1B36, 0x1B3A}, {0x1B3C, 0x1B3C}, {0x1B42, 0x1B42},
|
||||
{0x1B6B, 0x1B73}, {0x1DC0, 0x1DCA}, {0x1DFE, 0x1DFF},
|
||||
{0x200B, 0x200F}, {0x202A, 0x202E}, {0x2060, 0x2063},
|
||||
{0x206A, 0x206F}, {0x20D0, 0x20EF}, {0x302A, 0x302F},
|
||||
{0x3099, 0x309A}, {0xA806, 0xA806}, {0xA80B, 0xA80B},
|
||||
{0xA825, 0xA826}, {0xFB1E, 0xFB1E}, {0xFE00, 0xFE0F},
|
||||
{0xFE20, 0xFE23}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB},
|
||||
{0x10A01, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F},
|
||||
{0x10A38, 0x10A3A}, {0x10A3F, 0x10A3F}, {0x1D167, 0x1D169},
|
||||
{0x1D173, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD},
|
||||
{0x1D242, 0x1D244}, {0xE0001, 0xE0001}, {0xE0020, 0xE007F},
|
||||
{0xE0100, 0xE01EF}};
|
||||
|
||||
/* test for 8-bit control characters */
|
||||
if (ucs == 0)
|
||||
return 0;
|
||||
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
|
||||
return -1;
|
||||
|
||||
/* binary search in table of non-spacing characters */
|
||||
if (bisearch(ucs, combining, sizeof(combining) / sizeof(struct interval) - 1))
|
||||
return 0;
|
||||
|
||||
/* if we arrive here, ucs is not a combining or C0/C1 control character */
|
||||
|
||||
return 1 +
|
||||
(ucs >= 0x1100 &&
|
||||
(ucs <= 0x115f || /* Hangul Jamo init. consonants */
|
||||
ucs == 0x2329 || ucs == 0x232a ||
|
||||
(ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) || /* CJK ... Yi */
|
||||
(ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
|
||||
(ucs >= 0xf900 &&
|
||||
ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
|
||||
(ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
|
||||
(ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
|
||||
(ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
|
||||
(ucs >= 0xffe0 && ucs <= 0xffe6) ||
|
||||
(ucs >= 0x20000 && ucs <= 0x2fffd) ||
|
||||
(ucs >= 0x30000 && ucs <= 0x3fffd)));
|
||||
}
|
||||
|
||||
static inline int mk_wcswidth(const wchar_t *pwcs, size_t n) {
|
||||
int w, width = 0;
|
||||
|
||||
for (; *pwcs && n-- > 0; pwcs++)
|
||||
if ((w = mk_wcwidth(*pwcs)) < 0)
|
||||
return -1;
|
||||
else
|
||||
width += w;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following functions are the same as mk_wcwidth() and
|
||||
* mk_wcswidth(), except that spacing characters in the East Asian
|
||||
* Ambiguous (A) category as defined in Unicode Technical Report #11
|
||||
* have a column width of 2. This variant might be useful for users of
|
||||
* CJK legacy encodings who want to migrate to UCS without changing
|
||||
* the traditional terminal character-width behaviour. It is not
|
||||
* otherwise recommended for general use.
|
||||
*/
|
||||
static inline int mk_wcwidth_cjk(wchar_t ucs) {
|
||||
/* sorted list of non-overlapping intervals of East Asian Ambiguous
|
||||
* characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
|
||||
static const struct interval ambiguous[] = {
|
||||
{0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8},
|
||||
{0x00AA, 0x00AA}, {0x00AE, 0x00AE}, {0x00B0, 0x00B4},
|
||||
{0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6},
|
||||
{0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1},
|
||||
{0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED},
|
||||
{0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA},
|
||||
{0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101},
|
||||
{0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B},
|
||||
{0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133},
|
||||
{0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144},
|
||||
{0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153},
|
||||
{0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE},
|
||||
{0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4},
|
||||
{0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA},
|
||||
{0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261},
|
||||
{0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB},
|
||||
{0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB},
|
||||
{0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0391, 0x03A1},
|
||||
{0x03A3, 0x03A9}, {0x03B1, 0x03C1}, {0x03C3, 0x03C9},
|
||||
{0x0401, 0x0401}, {0x0410, 0x044F}, {0x0451, 0x0451},
|
||||
{0x2010, 0x2010}, {0x2013, 0x2016}, {0x2018, 0x2019},
|
||||
{0x201C, 0x201D}, {0x2020, 0x2022}, {0x2024, 0x2027},
|
||||
{0x2030, 0x2030}, {0x2032, 0x2033}, {0x2035, 0x2035},
|
||||
{0x203B, 0x203B}, {0x203E, 0x203E}, {0x2074, 0x2074},
|
||||
{0x207F, 0x207F}, {0x2081, 0x2084}, {0x20AC, 0x20AC},
|
||||
{0x2103, 0x2103}, {0x2105, 0x2105}, {0x2109, 0x2109},
|
||||
{0x2113, 0x2113}, {0x2116, 0x2116}, {0x2121, 0x2122},
|
||||
{0x2126, 0x2126}, {0x212B, 0x212B}, {0x2153, 0x2154},
|
||||
{0x215B, 0x215E}, {0x2160, 0x216B}, {0x2170, 0x2179},
|
||||
{0x2190, 0x2199}, {0x21B8, 0x21B9}, {0x21D2, 0x21D2},
|
||||
{0x21D4, 0x21D4}, {0x21E7, 0x21E7}, {0x2200, 0x2200},
|
||||
{0x2202, 0x2203}, {0x2207, 0x2208}, {0x220B, 0x220B},
|
||||
{0x220F, 0x220F}, {0x2211, 0x2211}, {0x2215, 0x2215},
|
||||
{0x221A, 0x221A}, {0x221D, 0x2220}, {0x2223, 0x2223},
|
||||
{0x2225, 0x2225}, {0x2227, 0x222C}, {0x222E, 0x222E},
|
||||
{0x2234, 0x2237}, {0x223C, 0x223D}, {0x2248, 0x2248},
|
||||
{0x224C, 0x224C}, {0x2252, 0x2252}, {0x2260, 0x2261},
|
||||
{0x2264, 0x2267}, {0x226A, 0x226B}, {0x226E, 0x226F},
|
||||
{0x2282, 0x2283}, {0x2286, 0x2287}, {0x2295, 0x2295},
|
||||
{0x2299, 0x2299}, {0x22A5, 0x22A5}, {0x22BF, 0x22BF},
|
||||
{0x2312, 0x2312}, {0x2460, 0x24E9}, {0x24EB, 0x254B},
|
||||
{0x2550, 0x2573}, {0x2580, 0x258F}, {0x2592, 0x2595},
|
||||
{0x25A0, 0x25A1}, {0x25A3, 0x25A9}, {0x25B2, 0x25B3},
|
||||
{0x25B6, 0x25B7}, {0x25BC, 0x25BD}, {0x25C0, 0x25C1},
|
||||
{0x25C6, 0x25C8}, {0x25CB, 0x25CB}, {0x25CE, 0x25D1},
|
||||
{0x25E2, 0x25E5}, {0x25EF, 0x25EF}, {0x2605, 0x2606},
|
||||
{0x2609, 0x2609}, {0x260E, 0x260F}, {0x2614, 0x2615},
|
||||
{0x261C, 0x261C}, {0x261E, 0x261E}, {0x2640, 0x2640},
|
||||
{0x2642, 0x2642}, {0x2660, 0x2661}, {0x2663, 0x2665},
|
||||
{0x2667, 0x266A}, {0x266C, 0x266D}, {0x266F, 0x266F},
|
||||
{0x273D, 0x273D}, {0x2776, 0x277F}, {0xE000, 0xF8FF},
|
||||
{0xFFFD, 0xFFFD}, {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}};
|
||||
|
||||
/* binary search in table of non-spacing characters */
|
||||
if (bisearch(ucs, ambiguous, sizeof(ambiguous) / sizeof(struct interval) - 1))
|
||||
return 2;
|
||||
|
||||
return mk_wcwidth(ucs);
|
||||
}
|
||||
|
||||
static inline int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) {
|
||||
int w, width = 0;
|
||||
|
||||
for (; *pwcs && n-- > 0; pwcs++)
|
||||
if ((w = mk_wcwidth_cjk(*pwcs)) < 0)
|
||||
return -1;
|
||||
else
|
||||
width += w;
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
// convert UTF-8 string to wstring
|
||||
#ifdef _MSC_VER
|
||||
static inline std::wstring utf8_decode(const std::string& s) {
|
||||
std::string curLocale = setlocale(LC_ALL, "");
|
||||
const char* _Source = s.c_str();
|
||||
size_t _Dsize = std::strlen(_Source) + 1;
|
||||
wchar_t* _Dest = new wchar_t[_Dsize];
|
||||
size_t _Osize;
|
||||
mbstowcs_s(&_Osize, _Dest, _Dsize, _Source, _Dsize);
|
||||
std::wstring result = _Dest;
|
||||
delete[] _Dest;
|
||||
setlocale(LC_ALL, curLocale.c_str());
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
static inline std::wstring utf8_decode(const std::string& s) {
|
||||
std::string curLocale = setlocale(LC_ALL, "");
|
||||
const char* _Source = s.c_str();
|
||||
size_t _Dsize = mbstowcs(NULL, _Source, 0) + 1;
|
||||
wchar_t* _Dest = new wchar_t[_Dsize];
|
||||
wmemset(_Dest, 0, _Dsize);
|
||||
mbstowcs(_Dest, _Source, _Dsize);
|
||||
std::wstring result = _Dest;
|
||||
delete[] _Dest;
|
||||
setlocale(LC_ALL, curLocale.c_str());
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace details
|
||||
|
||||
static inline int display_width(const std::string &input) {
|
||||
using namespace unicode::details;
|
||||
return mk_wcswidth(utf8_decode(input).c_str(), input.size());
|
||||
}
|
||||
|
||||
static inline int display_width(const std::wstring &input) {
|
||||
return details::mk_wcswidth(input.c_str(), input.size());
|
||||
}
|
||||
|
||||
} // namespace unicode
|
||||
|
||||
#endif
|
||||
122
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/dynamic_progress.hpp
Executable file
122
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/dynamic_progress.hpp
Executable file
@@ -0,0 +1,122 @@
|
||||
|
||||
#ifndef INDICATORS_DYNAMIC_PROGRESS
|
||||
#define INDICATORS_DYNAMIC_PROGRESS
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <indicators/color.hpp>
|
||||
#include <indicators/setting.hpp>
|
||||
#include <indicators/cursor_control.hpp>
|
||||
#include <indicators/cursor_movement.hpp>
|
||||
#include <indicators/details/stream_helper.hpp>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace indicators {
|
||||
|
||||
template <typename Indicator> class DynamicProgress {
|
||||
using Settings = std::tuple<option::HideBarWhenComplete>;
|
||||
|
||||
public:
|
||||
template <typename... Indicators> explicit DynamicProgress(Indicators &... bars) {
|
||||
bars_ = {bars...};
|
||||
for (auto &bar : bars_) {
|
||||
bar.get().multi_progress_mode_ = true;
|
||||
++total_count_;
|
||||
++incomplete_count_;
|
||||
}
|
||||
}
|
||||
|
||||
Indicator &operator[](size_t index) {
|
||||
print_progress();
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
return bars_[index].get();
|
||||
}
|
||||
|
||||
size_t push_back(Indicator &bar) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
bar.multi_progress_mode_ = true;
|
||||
bars_.push_back(bar);
|
||||
return bars_.size() - 1;
|
||||
}
|
||||
|
||||
template <typename T, details::ProgressBarOption id>
|
||||
void set_option(details::Setting<T, id> &&setting) {
|
||||
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
|
||||
std::declval<Settings>()))>::type>::value,
|
||||
"Setting has wrong type!");
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<id>() = std::move(setting).value;
|
||||
}
|
||||
|
||||
template <typename T, details::ProgressBarOption id>
|
||||
void set_option(const details::Setting<T, id> &setting) {
|
||||
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
|
||||
std::declval<Settings>()))>::type>::value,
|
||||
"Setting has wrong type!");
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<id>() = setting.value;
|
||||
}
|
||||
|
||||
private:
|
||||
Settings settings_;
|
||||
std::atomic<bool> started_{false};
|
||||
std::mutex mutex_;
|
||||
std::vector<std::reference_wrapper<Indicator>> bars_;
|
||||
std::atomic<size_t> total_count_{0};
|
||||
std::atomic<size_t> incomplete_count_{0};
|
||||
|
||||
template <details::ProgressBarOption id>
|
||||
auto get_value() -> decltype((details::get_value<id>(std::declval<Settings &>()).value)) {
|
||||
return details::get_value<id>(settings_).value;
|
||||
}
|
||||
|
||||
template <details::ProgressBarOption id>
|
||||
auto get_value() const
|
||||
-> decltype((details::get_value<id>(std::declval<const Settings &>()).value)) {
|
||||
return details::get_value<id>(settings_).value;
|
||||
}
|
||||
|
||||
public:
|
||||
void print_progress() {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
auto &hide_bar_when_complete = get_value<details::ProgressBarOption::hide_bar_when_complete>();
|
||||
if (hide_bar_when_complete) {
|
||||
// Hide completed bars
|
||||
if (started_) {
|
||||
for (size_t i = 0; i < incomplete_count_; ++i) {
|
||||
move_up(1);
|
||||
erase_line();
|
||||
std::cout << std::flush;
|
||||
}
|
||||
}
|
||||
incomplete_count_ = 0;
|
||||
for (auto &bar : bars_) {
|
||||
if (!bar.get().is_completed()) {
|
||||
bar.get().print_progress(true);
|
||||
std::cout << "\n";
|
||||
++incomplete_count_;
|
||||
}
|
||||
}
|
||||
if (!started_)
|
||||
started_ = true;
|
||||
} else {
|
||||
// Don't hide any bars
|
||||
if (started_)
|
||||
move_up(static_cast<int>(total_count_));
|
||||
for (auto &bar : bars_) {
|
||||
bar.get().print_progress(true);
|
||||
std::cout << "\n";
|
||||
}
|
||||
if (!started_)
|
||||
started_ = true;
|
||||
}
|
||||
total_count_ = bars_.size();
|
||||
std::cout << termcolor::reset;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace indicators
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
#ifndef INDICATORS_FONT_STYLE
|
||||
#define INDICATORS_FONT_STYLE
|
||||
|
||||
namespace indicators {
|
||||
enum class FontStyle { bold, dark, italic, underline, blink, reverse, concealed, crossed };
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,237 @@
|
||||
|
||||
#ifndef INDICATORS_INDETERMINATE_PROGRESS_BAR
|
||||
#define INDICATORS_INDETERMINATE_PROGRESS_BAR
|
||||
|
||||
#include <indicators/details/stream_helper.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <indicators/color.hpp>
|
||||
#include <indicators/setting.hpp>
|
||||
#include <indicators/terminal_size.hpp>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
namespace indicators {
|
||||
|
||||
class IndeterminateProgressBar {
|
||||
using Settings =
|
||||
std::tuple<option::BarWidth, option::PrefixText, option::PostfixText, option::Start,
|
||||
option::End, option::Fill, option::Lead, option::MaxPostfixTextLen,
|
||||
option::Completed, option::ForegroundColor, option::FontStyles, option::Stream>;
|
||||
|
||||
enum class Direction { forward, backward };
|
||||
|
||||
Direction direction_{Direction::forward};
|
||||
|
||||
public:
|
||||
template <typename... Args,
|
||||
typename std::enable_if<details::are_settings_from_tuple<
|
||||
Settings, typename std::decay<Args>::type...>::value,
|
||||
void *>::type = nullptr>
|
||||
explicit IndeterminateProgressBar(Args &&... args)
|
||||
: settings_(details::get<details::ProgressBarOption::bar_width>(option::BarWidth{100},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::prefix_text>(
|
||||
option::PrefixText{}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::postfix_text>(
|
||||
option::PostfixText{}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::start>(option::Start{"["},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::end>(option::End{"]"},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::fill>(option::Fill{"."},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::lead>(option::Lead{"<==>"},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::max_postfix_text_len>(
|
||||
option::MaxPostfixTextLen{0}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::completed>(option::Completed{false},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::foreground_color>(
|
||||
option::ForegroundColor{Color::unspecified}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::font_styles>(
|
||||
option::FontStyles{std::vector<FontStyle>{}}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::stream>(option::Stream{std::cout},
|
||||
std::forward<Args>(args)...)) {
|
||||
// starts with [<==>...........]
|
||||
// progress_ = 0
|
||||
|
||||
// ends with [...........<==>]
|
||||
// ^^^^^^^^^^^^^^^^^ bar_width
|
||||
// ^^^^^^^^^^^^ (bar_width - len(lead))
|
||||
// progress_ = bar_width - len(lead)
|
||||
progress_ = 0;
|
||||
max_progress_ = get_value<details::ProgressBarOption::bar_width>() -
|
||||
get_value<details::ProgressBarOption::lead>().size() +
|
||||
get_value<details::ProgressBarOption::start>().size() +
|
||||
get_value<details::ProgressBarOption::end>().size();
|
||||
}
|
||||
|
||||
template <typename T, details::ProgressBarOption id>
|
||||
void set_option(details::Setting<T, id> &&setting) {
|
||||
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
|
||||
std::declval<Settings>()))>::type>::value,
|
||||
"Setting has wrong type!");
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<id>() = std::move(setting).value;
|
||||
}
|
||||
|
||||
template <typename T, details::ProgressBarOption id>
|
||||
void set_option(const details::Setting<T, id> &setting) {
|
||||
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
|
||||
std::declval<Settings>()))>::type>::value,
|
||||
"Setting has wrong type!");
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<id>() = setting.value;
|
||||
}
|
||||
|
||||
void set_option(
|
||||
const details::Setting<std::string, details::ProgressBarOption::postfix_text> &setting) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<details::ProgressBarOption::postfix_text>() = setting.value;
|
||||
if (setting.value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
|
||||
get_value<details::ProgressBarOption::max_postfix_text_len>() = setting.value.length();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_option(details::Setting<std::string, details::ProgressBarOption::postfix_text> &&setting) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<details::ProgressBarOption::postfix_text>() = std::move(setting).value;
|
||||
auto &new_value = get_value<details::ProgressBarOption::postfix_text>();
|
||||
if (new_value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
|
||||
get_value<details::ProgressBarOption::max_postfix_text_len>() = new_value.length();
|
||||
}
|
||||
}
|
||||
|
||||
void tick() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
if (get_value<details::ProgressBarOption::completed>())
|
||||
return;
|
||||
|
||||
progress_ += (direction_ == Direction::forward) ? 1 : -1;
|
||||
if (direction_ == Direction::forward && progress_ == max_progress_) {
|
||||
// time to go back
|
||||
direction_ = Direction::backward;
|
||||
} else if (direction_ == Direction::backward && progress_ == 0) {
|
||||
direction_ = Direction::forward;
|
||||
}
|
||||
}
|
||||
print_progress();
|
||||
}
|
||||
|
||||
bool is_completed() { return get_value<details::ProgressBarOption::completed>(); }
|
||||
|
||||
void mark_as_completed() {
|
||||
get_value<details::ProgressBarOption::completed>() = true;
|
||||
print_progress();
|
||||
}
|
||||
|
||||
private:
|
||||
template <details::ProgressBarOption id>
|
||||
auto get_value() -> decltype((details::get_value<id>(std::declval<Settings &>()).value)) {
|
||||
return details::get_value<id>(settings_).value;
|
||||
}
|
||||
|
||||
template <details::ProgressBarOption id>
|
||||
auto get_value() const
|
||||
-> decltype((details::get_value<id>(std::declval<const Settings &>()).value)) {
|
||||
return details::get_value<id>(settings_).value;
|
||||
}
|
||||
|
||||
size_t progress_{0};
|
||||
size_t max_progress_;
|
||||
Settings settings_;
|
||||
std::chrono::nanoseconds elapsed_;
|
||||
std::mutex mutex_;
|
||||
|
||||
template <typename Indicator, size_t count> friend class MultiProgress;
|
||||
template <typename Indicator> friend class DynamicProgress;
|
||||
std::atomic<bool> multi_progress_mode_{false};
|
||||
|
||||
std::pair<std::string, size_t> get_prefix_text() {
|
||||
std::stringstream os;
|
||||
os << get_value<details::ProgressBarOption::prefix_text>();
|
||||
const auto result = os.str();
|
||||
const auto result_size = unicode::display_width(result);
|
||||
return {result, result_size};
|
||||
}
|
||||
|
||||
std::pair<std::string, size_t> get_postfix_text() {
|
||||
std::stringstream os;
|
||||
os << " " << get_value<details::ProgressBarOption::postfix_text>();
|
||||
|
||||
const auto result = os.str();
|
||||
const auto result_size = unicode::display_width(result);
|
||||
return {result, result_size};
|
||||
}
|
||||
|
||||
public:
|
||||
void print_progress(bool from_multi_progress = false) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
|
||||
auto &os = get_value<details::ProgressBarOption::stream>();
|
||||
|
||||
if (multi_progress_mode_ && !from_multi_progress) {
|
||||
return;
|
||||
}
|
||||
if (get_value<details::ProgressBarOption::foreground_color>() != Color::unspecified)
|
||||
details::set_stream_color(os, get_value<details::ProgressBarOption::foreground_color>());
|
||||
|
||||
for (auto &style : get_value<details::ProgressBarOption::font_styles>())
|
||||
details::set_font_style(os, style);
|
||||
|
||||
const auto prefix_pair = get_prefix_text();
|
||||
const auto prefix_text = prefix_pair.first;
|
||||
const auto prefix_length = prefix_pair.second;
|
||||
os << "\r" << prefix_text;
|
||||
|
||||
os << get_value<details::ProgressBarOption::start>();
|
||||
|
||||
details::IndeterminateProgressScaleWriter writer{
|
||||
os, get_value<details::ProgressBarOption::bar_width>(),
|
||||
get_value<details::ProgressBarOption::fill>(),
|
||||
get_value<details::ProgressBarOption::lead>()};
|
||||
writer.write(progress_);
|
||||
|
||||
os << get_value<details::ProgressBarOption::end>();
|
||||
|
||||
const auto postfix_pair = get_postfix_text();
|
||||
const auto postfix_text = postfix_pair.first;
|
||||
const auto postfix_length = postfix_pair.second;
|
||||
os << postfix_text;
|
||||
|
||||
// Get length of prefix text and postfix text
|
||||
const auto start_length = get_value<details::ProgressBarOption::start>().size();
|
||||
const auto bar_width = get_value<details::ProgressBarOption::bar_width>();
|
||||
const auto end_length = get_value<details::ProgressBarOption::end>().size();
|
||||
const auto terminal_width = terminal_size().second;
|
||||
// prefix + bar_width + postfix should be <= terminal_width
|
||||
const int remaining = terminal_width - (prefix_length + start_length + bar_width + end_length + postfix_length);
|
||||
if (remaining > 0) {
|
||||
os << std::string(remaining, ' ') << "\r";
|
||||
} else if (remaining < 0) {
|
||||
// Do nothing. Maybe in the future truncate postfix with ...
|
||||
}
|
||||
os.flush();
|
||||
|
||||
if (get_value<details::ProgressBarOption::completed>() &&
|
||||
!from_multi_progress) // Don't std::endl if calling from MultiProgress
|
||||
os << termcolor::reset << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace indicators
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
#ifndef INDICATORS_MULTI_PROGRESS
|
||||
#define INDICATORS_MULTI_PROGRESS
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include <indicators/color.hpp>
|
||||
#include <indicators/cursor_movement.hpp>
|
||||
#include <indicators/details/stream_helper.hpp>
|
||||
|
||||
namespace indicators {
|
||||
|
||||
template <typename Indicator, size_t count> class MultiProgress {
|
||||
public:
|
||||
template <typename... Indicators,
|
||||
typename = typename std::enable_if<(sizeof...(Indicators) == count)>::type>
|
||||
explicit MultiProgress(Indicators &... bars) {
|
||||
bars_ = {bars...};
|
||||
for (auto &bar : bars_) {
|
||||
bar.get().multi_progress_mode_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t index>
|
||||
typename std::enable_if<(index >= 0 && index < count), void>::type set_progress(size_t value) {
|
||||
if (!bars_[index].get().is_completed())
|
||||
bars_[index].get().set_progress(value);
|
||||
print_progress();
|
||||
}
|
||||
|
||||
template <size_t index>
|
||||
typename std::enable_if<(index >= 0 && index < count), void>::type set_progress(float value) {
|
||||
if (!bars_[index].get().is_completed())
|
||||
bars_[index].get().set_progress(value);
|
||||
print_progress();
|
||||
}
|
||||
|
||||
template <size_t index>
|
||||
typename std::enable_if<(index >= 0 && index < count), void>::type tick() {
|
||||
if (!bars_[index].get().is_completed())
|
||||
bars_[index].get().tick();
|
||||
print_progress();
|
||||
}
|
||||
|
||||
template <size_t index>
|
||||
typename std::enable_if<(index >= 0 && index < count), bool>::type is_completed() const {
|
||||
return bars_[index].get().is_completed();
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<bool> started_{false};
|
||||
std::mutex mutex_;
|
||||
std::vector<std::reference_wrapper<Indicator>> bars_;
|
||||
|
||||
bool _all_completed() {
|
||||
bool result{true};
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
result &= bars_[i].get().is_completed();
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
void print_progress() {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
if (started_)
|
||||
move_up(count);
|
||||
for (auto &bar : bars_) {
|
||||
bar.get().print_progress(true);
|
||||
std::cout << "\n";
|
||||
}
|
||||
std::cout << termcolor::reset;
|
||||
if (!started_)
|
||||
started_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace indicators
|
||||
|
||||
#endif
|
||||
360
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/progress_bar.hpp
Executable file
360
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/progress_bar.hpp
Executable file
@@ -0,0 +1,360 @@
|
||||
|
||||
#ifndef INDICATORS_PROGRESS_BAR
|
||||
#define INDICATORS_PROGRESS_BAR
|
||||
|
||||
#include <indicators/details/stream_helper.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <indicators/color.hpp>
|
||||
#include <indicators/setting.hpp>
|
||||
#include <indicators/terminal_size.hpp>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace indicators {
|
||||
|
||||
class ProgressBar {
|
||||
using Settings =
|
||||
std::tuple<option::BarWidth, option::PrefixText, option::PostfixText,
|
||||
option::Start, option::End, option::Fill, option::Lead,
|
||||
option::Remainder, option::MaxPostfixTextLen,
|
||||
option::Completed, option::ShowPercentage,
|
||||
option::ShowElapsedTime, option::ShowRemainingTime,
|
||||
option::SavedStartTime, option::ForegroundColor,
|
||||
option::FontStyles, option::MinProgress, option::MaxProgress,
|
||||
option::ProgressType, option::Stream>;
|
||||
|
||||
public:
|
||||
template <typename... Args,
|
||||
typename std::enable_if<
|
||||
details::are_settings_from_tuple<
|
||||
Settings, typename std::decay<Args>::type...>::value,
|
||||
void *>::type = nullptr>
|
||||
explicit ProgressBar(Args &&... args)
|
||||
: settings_(
|
||||
details::get<details::ProgressBarOption::bar_width>(
|
||||
option::BarWidth{100}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::prefix_text>(
|
||||
option::PrefixText{}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::postfix_text>(
|
||||
option::PostfixText{}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::start>(
|
||||
option::Start{"["}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::end>(
|
||||
option::End{"]"}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::fill>(
|
||||
option::Fill{"="}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::lead>(
|
||||
option::Lead{">"}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::remainder>(
|
||||
option::Remainder{" "}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::max_postfix_text_len>(
|
||||
option::MaxPostfixTextLen{0}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::completed>(
|
||||
option::Completed{false}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::show_percentage>(
|
||||
option::ShowPercentage{false}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::show_elapsed_time>(
|
||||
option::ShowElapsedTime{false}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::show_remaining_time>(
|
||||
option::ShowRemainingTime{false}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::saved_start_time>(
|
||||
option::SavedStartTime{false}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::foreground_color>(
|
||||
option::ForegroundColor{Color::unspecified},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::font_styles>(
|
||||
option::FontStyles{std::vector<FontStyle>{}},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::min_progress>(
|
||||
option::MinProgress{0}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::max_progress>(
|
||||
option::MaxProgress{100}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::progress_type>(
|
||||
option::ProgressType{ProgressType::incremental},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::stream>(
|
||||
option::Stream{std::cout}, std::forward<Args>(args)...)) {
|
||||
|
||||
// if progress is incremental, start from min_progress
|
||||
// else start from max_progress
|
||||
const auto type = get_value<details::ProgressBarOption::progress_type>();
|
||||
if (type == ProgressType::incremental)
|
||||
progress_ = get_value<details::ProgressBarOption::min_progress>();
|
||||
else
|
||||
progress_ = get_value<details::ProgressBarOption::max_progress>();
|
||||
}
|
||||
|
||||
template <typename T, details::ProgressBarOption id>
|
||||
void set_option(details::Setting<T, id> &&setting) {
|
||||
static_assert(
|
||||
!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
|
||||
std::declval<Settings>()))>::type>::value,
|
||||
"Setting has wrong type!");
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<id>() = std::move(setting).value;
|
||||
}
|
||||
|
||||
template <typename T, details::ProgressBarOption id>
|
||||
void set_option(const details::Setting<T, id> &setting) {
|
||||
static_assert(
|
||||
!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
|
||||
std::declval<Settings>()))>::type>::value,
|
||||
"Setting has wrong type!");
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<id>() = setting.value;
|
||||
}
|
||||
|
||||
void
|
||||
set_option(const details::Setting<
|
||||
std::string, details::ProgressBarOption::postfix_text> &setting) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<details::ProgressBarOption::postfix_text>() = setting.value;
|
||||
if (setting.value.length() >
|
||||
get_value<details::ProgressBarOption::max_postfix_text_len>()) {
|
||||
get_value<details::ProgressBarOption::max_postfix_text_len>() =
|
||||
setting.value.length();
|
||||
}
|
||||
}
|
||||
|
||||
void set_option(
|
||||
details::Setting<std::string, details::ProgressBarOption::postfix_text>
|
||||
&&setting) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<details::ProgressBarOption::postfix_text>() =
|
||||
std::move(setting).value;
|
||||
auto &new_value = get_value<details::ProgressBarOption::postfix_text>();
|
||||
if (new_value.length() >
|
||||
get_value<details::ProgressBarOption::max_postfix_text_len>()) {
|
||||
get_value<details::ProgressBarOption::max_postfix_text_len>() =
|
||||
new_value.length();
|
||||
}
|
||||
}
|
||||
|
||||
void set_progress(size_t new_progress) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
progress_ = new_progress;
|
||||
}
|
||||
|
||||
save_start_time();
|
||||
print_progress();
|
||||
}
|
||||
|
||||
void tick() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
const auto type = get_value<details::ProgressBarOption::progress_type>();
|
||||
if (type == ProgressType::incremental)
|
||||
progress_ += 1;
|
||||
else
|
||||
progress_ -= 1;
|
||||
}
|
||||
save_start_time();
|
||||
print_progress();
|
||||
}
|
||||
|
||||
size_t current() {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
return (std::min)(
|
||||
progress_,
|
||||
size_t(get_value<details::ProgressBarOption::max_progress>()));
|
||||
}
|
||||
|
||||
bool is_completed() const {
|
||||
return get_value<details::ProgressBarOption::completed>();
|
||||
}
|
||||
|
||||
void mark_as_completed() {
|
||||
get_value<details::ProgressBarOption::completed>() = true;
|
||||
print_progress();
|
||||
}
|
||||
|
||||
private:
|
||||
template <details::ProgressBarOption id>
|
||||
auto get_value()
|
||||
-> decltype((details::get_value<id>(std::declval<Settings &>()).value)) {
|
||||
return details::get_value<id>(settings_).value;
|
||||
}
|
||||
|
||||
template <details::ProgressBarOption id>
|
||||
auto get_value() const -> decltype(
|
||||
(details::get_value<id>(std::declval<const Settings &>()).value)) {
|
||||
return details::get_value<id>(settings_).value;
|
||||
}
|
||||
|
||||
size_t progress_{0};
|
||||
Settings settings_;
|
||||
std::chrono::nanoseconds elapsed_;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> start_time_point_;
|
||||
std::mutex mutex_;
|
||||
|
||||
template <typename Indicator, size_t count> friend class MultiProgress;
|
||||
template <typename Indicator> friend class DynamicProgress;
|
||||
std::atomic<bool> multi_progress_mode_{false};
|
||||
|
||||
void save_start_time() {
|
||||
auto &show_elapsed_time =
|
||||
get_value<details::ProgressBarOption::show_elapsed_time>();
|
||||
auto &saved_start_time =
|
||||
get_value<details::ProgressBarOption::saved_start_time>();
|
||||
auto &show_remaining_time =
|
||||
get_value<details::ProgressBarOption::show_remaining_time>();
|
||||
if ((show_elapsed_time || show_remaining_time) && !saved_start_time) {
|
||||
start_time_point_ = std::chrono::high_resolution_clock::now();
|
||||
saved_start_time = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::string, size_t> get_prefix_text() {
|
||||
std::stringstream os;
|
||||
os << get_value<details::ProgressBarOption::prefix_text>();
|
||||
const auto result = os.str();
|
||||
const auto result_size = unicode::display_width(result);
|
||||
return {result, result_size};
|
||||
}
|
||||
|
||||
std::pair<std::string, size_t> get_postfix_text() {
|
||||
std::stringstream os;
|
||||
const auto max_progress =
|
||||
get_value<details::ProgressBarOption::max_progress>();
|
||||
|
||||
if (get_value<details::ProgressBarOption::show_percentage>()) {
|
||||
os << " "
|
||||
<< (std::min)(static_cast<size_t>(static_cast<float>(progress_) /
|
||||
max_progress * 100),
|
||||
size_t(100))
|
||||
<< "%";
|
||||
}
|
||||
|
||||
auto &saved_start_time =
|
||||
get_value<details::ProgressBarOption::saved_start_time>();
|
||||
|
||||
if (get_value<details::ProgressBarOption::show_elapsed_time>()) {
|
||||
os << " [";
|
||||
if (saved_start_time)
|
||||
details::write_duration(os, elapsed_);
|
||||
else
|
||||
os << "00:00s";
|
||||
}
|
||||
|
||||
if (get_value<details::ProgressBarOption::show_remaining_time>()) {
|
||||
if (get_value<details::ProgressBarOption::show_elapsed_time>())
|
||||
os << "<";
|
||||
else
|
||||
os << " [";
|
||||
|
||||
if (saved_start_time) {
|
||||
auto eta = std::chrono::nanoseconds(
|
||||
progress_ > 0
|
||||
? static_cast<long long>(std::ceil(float(elapsed_.count()) *
|
||||
max_progress / progress_))
|
||||
: 0);
|
||||
auto remaining = eta > elapsed_ ? (eta - elapsed_) : (elapsed_ - eta);
|
||||
details::write_duration(os, remaining);
|
||||
} else {
|
||||
os << "00:00s";
|
||||
}
|
||||
|
||||
os << "]";
|
||||
} else {
|
||||
if (get_value<details::ProgressBarOption::show_elapsed_time>())
|
||||
os << "]";
|
||||
}
|
||||
|
||||
os << " " << get_value<details::ProgressBarOption::postfix_text>();
|
||||
|
||||
const auto result = os.str();
|
||||
const auto result_size = unicode::display_width(result);
|
||||
return {result, result_size};
|
||||
}
|
||||
|
||||
public:
|
||||
void print_progress(bool from_multi_progress = false) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
|
||||
auto &os = get_value<details::ProgressBarOption::stream>();
|
||||
|
||||
const auto type = get_value<details::ProgressBarOption::progress_type>();
|
||||
const auto min_progress =
|
||||
get_value<details::ProgressBarOption::min_progress>();
|
||||
const auto max_progress =
|
||||
get_value<details::ProgressBarOption::max_progress>();
|
||||
if (multi_progress_mode_ && !from_multi_progress) {
|
||||
if ((type == ProgressType::incremental && progress_ >= max_progress) ||
|
||||
(type == ProgressType::decremental && progress_ <= min_progress)) {
|
||||
get_value<details::ProgressBarOption::completed>() = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
if (!get_value<details::ProgressBarOption::completed>())
|
||||
elapsed_ = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
now - start_time_point_);
|
||||
|
||||
if (get_value<details::ProgressBarOption::foreground_color>() !=
|
||||
Color::unspecified)
|
||||
details::set_stream_color(
|
||||
os, get_value<details::ProgressBarOption::foreground_color>());
|
||||
|
||||
for (auto &style : get_value<details::ProgressBarOption::font_styles>())
|
||||
details::set_font_style(os, style);
|
||||
|
||||
const auto prefix_pair = get_prefix_text();
|
||||
const auto prefix_text = prefix_pair.first;
|
||||
const auto prefix_length = prefix_pair.second;
|
||||
os << "\r" << prefix_text;
|
||||
|
||||
os << get_value<details::ProgressBarOption::start>();
|
||||
|
||||
details::ProgressScaleWriter writer{
|
||||
os, get_value<details::ProgressBarOption::bar_width>(),
|
||||
get_value<details::ProgressBarOption::fill>(),
|
||||
get_value<details::ProgressBarOption::lead>(),
|
||||
get_value<details::ProgressBarOption::remainder>()};
|
||||
writer.write(double(progress_) / double(max_progress) * 100.0f);
|
||||
|
||||
os << get_value<details::ProgressBarOption::end>();
|
||||
|
||||
const auto postfix_pair = get_postfix_text();
|
||||
const auto postfix_text = postfix_pair.first;
|
||||
const auto postfix_length = postfix_pair.second;
|
||||
os << postfix_text;
|
||||
|
||||
// Get length of prefix text and postfix text
|
||||
const auto start_length = get_value<details::ProgressBarOption::start>().size();
|
||||
const auto bar_width = get_value<details::ProgressBarOption::bar_width>();
|
||||
const auto end_length = get_value<details::ProgressBarOption::end>().size();
|
||||
const auto terminal_width = terminal_size().second;
|
||||
// prefix + bar_width + postfix should be <= terminal_width
|
||||
const int remaining = terminal_width - (prefix_length + start_length + bar_width + end_length + postfix_length);
|
||||
if (remaining > 0) {
|
||||
os << std::string(remaining, ' ') << "\r";
|
||||
} else if (remaining < 0) {
|
||||
// Do nothing. Maybe in the future truncate postfix with ...
|
||||
}
|
||||
os.flush();
|
||||
|
||||
if ((type == ProgressType::incremental && progress_ >= max_progress) ||
|
||||
(type == ProgressType::decremental && progress_ <= min_progress)) {
|
||||
get_value<details::ProgressBarOption::completed>() = true;
|
||||
}
|
||||
if (get_value<details::ProgressBarOption::completed>() &&
|
||||
!from_multi_progress) // Don't std::endl if calling from MultiProgress
|
||||
os << termcolor::reset << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace indicators
|
||||
|
||||
#endif
|
||||
227
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/progress_spinner.hpp
Executable file
227
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/progress_spinner.hpp
Executable file
@@ -0,0 +1,227 @@
|
||||
|
||||
#ifndef INDICATORS_PROGRESS_SPINNER
|
||||
#define INDICATORS_PROGRESS_SPINNER
|
||||
|
||||
#include <indicators/details/stream_helper.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <indicators/color.hpp>
|
||||
#include <indicators/setting.hpp>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace indicators {
|
||||
|
||||
class ProgressSpinner {
|
||||
using Settings =
|
||||
std::tuple<option::ForegroundColor, option::PrefixText, option::PostfixText,
|
||||
option::ShowPercentage, option::ShowElapsedTime, option::ShowRemainingTime,
|
||||
option::ShowSpinner, option::SavedStartTime, option::Completed,
|
||||
option::MaxPostfixTextLen, option::SpinnerStates, option::FontStyles,
|
||||
option::MaxProgress, option::Stream>;
|
||||
|
||||
public:
|
||||
template <typename... Args,
|
||||
typename std::enable_if<details::are_settings_from_tuple<
|
||||
Settings, typename std::decay<Args>::type...>::value,
|
||||
void *>::type = nullptr>
|
||||
explicit ProgressSpinner(Args &&... args)
|
||||
: settings_(
|
||||
details::get<details::ProgressBarOption::foreground_color>(
|
||||
option::ForegroundColor{Color::unspecified}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::prefix_text>(option::PrefixText{},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::postfix_text>(option::PostfixText{},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::show_percentage>(option::ShowPercentage{true},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::show_elapsed_time>(
|
||||
option::ShowElapsedTime{false}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::show_remaining_time>(
|
||||
option::ShowRemainingTime{false}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::spinner_show>(option::ShowSpinner{true},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::saved_start_time>(
|
||||
option::SavedStartTime{false}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::completed>(option::Completed{false},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::max_postfix_text_len>(
|
||||
option::MaxPostfixTextLen{0}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::spinner_states>(
|
||||
option::SpinnerStates{
|
||||
std::vector<std::string>{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::font_styles>(
|
||||
option::FontStyles{std::vector<FontStyle>{}}, std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::max_progress>(option::MaxProgress{100},
|
||||
std::forward<Args>(args)...),
|
||||
details::get<details::ProgressBarOption::stream>(option::Stream{std::cout},
|
||||
std::forward<Args>(args)...)) {}
|
||||
|
||||
template <typename T, details::ProgressBarOption id>
|
||||
void set_option(details::Setting<T, id> &&setting) {
|
||||
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
|
||||
std::declval<Settings>()))>::type>::value,
|
||||
"Setting has wrong type!");
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<id>() = std::move(setting).value;
|
||||
}
|
||||
|
||||
template <typename T, details::ProgressBarOption id>
|
||||
void set_option(const details::Setting<T, id> &setting) {
|
||||
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
|
||||
std::declval<Settings>()))>::type>::value,
|
||||
"Setting has wrong type!");
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<id>() = setting.value;
|
||||
}
|
||||
|
||||
void set_option(
|
||||
const details::Setting<std::string, details::ProgressBarOption::postfix_text> &setting) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<details::ProgressBarOption::postfix_text>() = setting.value;
|
||||
if (setting.value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
|
||||
get_value<details::ProgressBarOption::max_postfix_text_len>() = setting.value.length();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_option(details::Setting<std::string, details::ProgressBarOption::postfix_text> &&setting) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
get_value<details::ProgressBarOption::postfix_text>() = std::move(setting).value;
|
||||
auto &new_value = get_value<details::ProgressBarOption::postfix_text>();
|
||||
if (new_value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
|
||||
get_value<details::ProgressBarOption::max_postfix_text_len>() = new_value.length();
|
||||
}
|
||||
}
|
||||
|
||||
void set_progress(size_t value) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
progress_ = value;
|
||||
}
|
||||
save_start_time();
|
||||
print_progress();
|
||||
}
|
||||
|
||||
void tick() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
progress_ += 1;
|
||||
}
|
||||
save_start_time();
|
||||
print_progress();
|
||||
}
|
||||
|
||||
size_t current() {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
return (std::min)(progress_, size_t(get_value<details::ProgressBarOption::max_progress>()));
|
||||
}
|
||||
|
||||
bool is_completed() const { return get_value<details::ProgressBarOption::completed>(); }
|
||||
|
||||
void mark_as_completed() {
|
||||
get_value<details::ProgressBarOption::completed>() = true;
|
||||
print_progress();
|
||||
}
|
||||
|
||||
private:
|
||||
Settings settings_;
|
||||
size_t progress_{0};
|
||||
size_t index_{0};
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> start_time_point_;
|
||||
std::mutex mutex_;
|
||||
|
||||
template <details::ProgressBarOption id>
|
||||
auto get_value() -> decltype((details::get_value<id>(std::declval<Settings &>()).value)) {
|
||||
return details::get_value<id>(settings_).value;
|
||||
}
|
||||
|
||||
template <details::ProgressBarOption id>
|
||||
auto get_value() const
|
||||
-> decltype((details::get_value<id>(std::declval<const Settings &>()).value)) {
|
||||
return details::get_value<id>(settings_).value;
|
||||
}
|
||||
|
||||
void save_start_time() {
|
||||
auto &show_elapsed_time = get_value<details::ProgressBarOption::show_elapsed_time>();
|
||||
auto &show_remaining_time = get_value<details::ProgressBarOption::show_remaining_time>();
|
||||
auto &saved_start_time = get_value<details::ProgressBarOption::saved_start_time>();
|
||||
if ((show_elapsed_time || show_remaining_time) && !saved_start_time) {
|
||||
start_time_point_ = std::chrono::high_resolution_clock::now();
|
||||
saved_start_time = true;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void print_progress() {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
|
||||
auto &os = get_value<details::ProgressBarOption::stream>();
|
||||
|
||||
const auto max_progress = get_value<details::ProgressBarOption::max_progress>();
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(now - start_time_point_);
|
||||
|
||||
if (get_value<details::ProgressBarOption::foreground_color>() != Color::unspecified)
|
||||
details::set_stream_color(os, get_value<details::ProgressBarOption::foreground_color>());
|
||||
|
||||
for (auto &style : get_value<details::ProgressBarOption::font_styles>())
|
||||
details::set_font_style(os, style);
|
||||
|
||||
os << get_value<details::ProgressBarOption::prefix_text>();
|
||||
if (get_value<details::ProgressBarOption::spinner_show>())
|
||||
os << get_value<details::ProgressBarOption::spinner_states>()
|
||||
[index_ % get_value<details::ProgressBarOption::spinner_states>().size()];
|
||||
if (get_value<details::ProgressBarOption::show_percentage>()) {
|
||||
os << " " << std::size_t(progress_ / double(max_progress) * 100) << "%";
|
||||
}
|
||||
|
||||
if (get_value<details::ProgressBarOption::show_elapsed_time>()) {
|
||||
os << " [";
|
||||
details::write_duration(os, elapsed);
|
||||
}
|
||||
|
||||
if (get_value<details::ProgressBarOption::show_remaining_time>()) {
|
||||
if (get_value<details::ProgressBarOption::show_elapsed_time>())
|
||||
os << "<";
|
||||
else
|
||||
os << " [";
|
||||
auto eta = std::chrono::nanoseconds(
|
||||
progress_ > 0
|
||||
? static_cast<long long>(std::ceil(float(elapsed.count()) *
|
||||
max_progress / progress_))
|
||||
: 0);
|
||||
auto remaining = eta > elapsed ? (eta - elapsed) : (elapsed - eta);
|
||||
details::write_duration(os, remaining);
|
||||
os << "]";
|
||||
} else {
|
||||
if (get_value<details::ProgressBarOption::show_elapsed_time>())
|
||||
os << "]";
|
||||
}
|
||||
|
||||
if (get_value<details::ProgressBarOption::max_postfix_text_len>() == 0)
|
||||
get_value<details::ProgressBarOption::max_postfix_text_len>() = 10;
|
||||
os << " " << get_value<details::ProgressBarOption::postfix_text>()
|
||||
<< std::string(get_value<details::ProgressBarOption::max_postfix_text_len>(), ' ') << "\r";
|
||||
os.flush();
|
||||
index_ += 1;
|
||||
if (progress_ > max_progress) {
|
||||
get_value<details::ProgressBarOption::completed>() = true;
|
||||
}
|
||||
if (get_value<details::ProgressBarOption::completed>())
|
||||
os << termcolor::reset << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace indicators
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
#ifndef INDICATORS_PROGRESS_TYPE
|
||||
#define INDICATORS_PROGRESS_TYPE
|
||||
|
||||
namespace indicators {
|
||||
enum class ProgressType { incremental, decremental };
|
||||
}
|
||||
|
||||
#endif
|
||||
222
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/setting.hpp
Executable file
222
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/setting.hpp
Executable file
@@ -0,0 +1,222 @@
|
||||
|
||||
/*
|
||||
Activity Indicators for Modern C++
|
||||
https://github.com/p-ranav/indicators
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
SPDX-License-Identifier: MIT
|
||||
Copyright (c) 2019 Dawid Pilarski <dawid.pilarski@panicsoftware.com>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef INDICATORS_SETTING
|
||||
#define INDICATORS_SETTING
|
||||
|
||||
#include <cstddef>
|
||||
#include <indicators/color.hpp>
|
||||
#include <indicators/font_style.hpp>
|
||||
#include <indicators/progress_type.hpp>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace indicators {
|
||||
|
||||
namespace details {
|
||||
|
||||
template <bool condition> struct if_else;
|
||||
|
||||
template <> struct if_else<true> { using type = std::true_type; };
|
||||
|
||||
template <> struct if_else<false> { using type = std::false_type; };
|
||||
|
||||
template <bool condition, typename True, typename False> struct if_else_type;
|
||||
|
||||
template <typename True, typename False> struct if_else_type<true, True, False> {
|
||||
using type = True;
|
||||
};
|
||||
|
||||
template <typename True, typename False> struct if_else_type<false, True, False> {
|
||||
using type = False;
|
||||
};
|
||||
|
||||
template <typename... Ops> struct conjuction;
|
||||
|
||||
template <> struct conjuction<> : std::true_type {};
|
||||
|
||||
template <typename Op, typename... TailOps>
|
||||
struct conjuction<Op, TailOps...>
|
||||
: if_else_type<!Op::value, std::false_type, conjuction<TailOps...>>::type {};
|
||||
|
||||
template <typename... Ops> struct disjunction;
|
||||
|
||||
template <> struct disjunction<> : std::false_type {};
|
||||
|
||||
template <typename Op, typename... TailOps>
|
||||
struct disjunction<Op, TailOps...>
|
||||
: if_else_type<Op::value, std::true_type, disjunction<TailOps...>>::type {};
|
||||
|
||||
enum class ProgressBarOption {
|
||||
bar_width = 0,
|
||||
prefix_text,
|
||||
postfix_text,
|
||||
start,
|
||||
end,
|
||||
fill,
|
||||
lead,
|
||||
remainder,
|
||||
max_postfix_text_len,
|
||||
completed,
|
||||
show_percentage,
|
||||
show_elapsed_time,
|
||||
show_remaining_time,
|
||||
saved_start_time,
|
||||
foreground_color,
|
||||
spinner_show,
|
||||
spinner_states,
|
||||
font_styles,
|
||||
hide_bar_when_complete,
|
||||
min_progress,
|
||||
max_progress,
|
||||
progress_type,
|
||||
stream
|
||||
};
|
||||
|
||||
template <typename T, ProgressBarOption Id> struct Setting {
|
||||
template <typename... Args,
|
||||
typename = typename std::enable_if<std::is_constructible<T, Args...>::value>::type>
|
||||
explicit Setting(Args &&... args) : value(std::forward<Args>(args)...) {}
|
||||
Setting(const Setting &) = default;
|
||||
Setting(Setting &&) = default;
|
||||
|
||||
static constexpr auto id = Id;
|
||||
using type = T;
|
||||
|
||||
T value{};
|
||||
};
|
||||
|
||||
template <typename T> struct is_setting : std::false_type {};
|
||||
|
||||
template <ProgressBarOption Id, typename T> struct is_setting<Setting<T, Id>> : std::true_type {};
|
||||
|
||||
template <typename... Args>
|
||||
struct are_settings : if_else<conjuction<is_setting<Args>...>::value>::type {};
|
||||
|
||||
template <> struct are_settings<> : std::true_type {};
|
||||
|
||||
template <typename Setting, typename Tuple> struct is_setting_from_tuple;
|
||||
|
||||
template <typename Setting> struct is_setting_from_tuple<Setting, std::tuple<>> : std::true_type {};
|
||||
|
||||
template <typename Setting, typename... TupleTypes>
|
||||
struct is_setting_from_tuple<Setting, std::tuple<TupleTypes...>>
|
||||
: if_else<disjunction<std::is_same<Setting, TupleTypes>...>::value>::type {};
|
||||
|
||||
template <typename Tuple, typename... Settings>
|
||||
struct are_settings_from_tuple
|
||||
: if_else<conjuction<is_setting_from_tuple<Settings, Tuple>...>::value>::type {};
|
||||
|
||||
template <ProgressBarOption Id> struct always_true { static constexpr auto value = true; };
|
||||
|
||||
template <ProgressBarOption Id, typename Default> Default &&get_impl(Default &&def) {
|
||||
return std::forward<Default>(def);
|
||||
}
|
||||
|
||||
template <ProgressBarOption Id, typename Default, typename T, typename... Args>
|
||||
auto get_impl(Default && /*def*/, T &&first, Args &&... /*tail*/) ->
|
||||
typename std::enable_if<(std::decay<T>::type::id == Id),
|
||||
decltype(std::forward<T>(first))>::type {
|
||||
return std::forward<T>(first);
|
||||
}
|
||||
|
||||
template <ProgressBarOption Id, typename Default, typename T, typename... Args>
|
||||
auto get_impl(Default &&def, T && /*first*/, Args &&... tail) ->
|
||||
typename std::enable_if<(std::decay<T>::type::id != Id),
|
||||
decltype(get_impl<Id>(std::forward<Default>(def),
|
||||
std::forward<Args>(tail)...))>::type {
|
||||
return get_impl<Id>(std::forward<Default>(def), std::forward<Args>(tail)...);
|
||||
}
|
||||
|
||||
template <ProgressBarOption Id, typename Default, typename... Args,
|
||||
typename = typename std::enable_if<are_settings<Args...>::value, void>::type>
|
||||
auto get(Default &&def, Args &&... args)
|
||||
-> decltype(details::get_impl<Id>(std::forward<Default>(def), std::forward<Args>(args)...)) {
|
||||
return details::get_impl<Id>(std::forward<Default>(def), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <ProgressBarOption Id> using StringSetting = Setting<std::string, Id>;
|
||||
|
||||
template <ProgressBarOption Id> using IntegerSetting = Setting<std::size_t, Id>;
|
||||
|
||||
template <ProgressBarOption Id> using BooleanSetting = Setting<bool, Id>;
|
||||
|
||||
template <ProgressBarOption Id, typename Tuple, std::size_t counter = 0> struct option_idx;
|
||||
|
||||
template <ProgressBarOption Id, typename T, typename... Settings, std::size_t counter>
|
||||
struct option_idx<Id, std::tuple<T, Settings...>, counter>
|
||||
: if_else_type<(Id == T::id), std::integral_constant<std::size_t, counter>,
|
||||
option_idx<Id, std::tuple<Settings...>, counter + 1>>::type {};
|
||||
|
||||
template <ProgressBarOption Id, std::size_t counter> struct option_idx<Id, std::tuple<>, counter> {
|
||||
static_assert(always_true<(ProgressBarOption)Id>::value, "No such option was found");
|
||||
};
|
||||
|
||||
template <ProgressBarOption Id, typename Settings>
|
||||
auto get_value(Settings &&settings)
|
||||
-> decltype((std::get<option_idx<Id, typename std::decay<Settings>::type>::value>(
|
||||
std::declval<Settings &&>()))) {
|
||||
return std::get<option_idx<Id, typename std::decay<Settings>::type>::value>(
|
||||
std::forward<Settings>(settings));
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
namespace option {
|
||||
using BarWidth = details::IntegerSetting<details::ProgressBarOption::bar_width>;
|
||||
using PrefixText = details::StringSetting<details::ProgressBarOption::prefix_text>;
|
||||
using PostfixText = details::StringSetting<details::ProgressBarOption::postfix_text>;
|
||||
using Start = details::StringSetting<details::ProgressBarOption::start>;
|
||||
using End = details::StringSetting<details::ProgressBarOption::end>;
|
||||
using Fill = details::StringSetting<details::ProgressBarOption::fill>;
|
||||
using Lead = details::StringSetting<details::ProgressBarOption::lead>;
|
||||
using Remainder = details::StringSetting<details::ProgressBarOption::remainder>;
|
||||
using MaxPostfixTextLen = details::IntegerSetting<details::ProgressBarOption::max_postfix_text_len>;
|
||||
using Completed = details::BooleanSetting<details::ProgressBarOption::completed>;
|
||||
using ShowPercentage = details::BooleanSetting<details::ProgressBarOption::show_percentage>;
|
||||
using ShowElapsedTime = details::BooleanSetting<details::ProgressBarOption::show_elapsed_time>;
|
||||
using ShowRemainingTime = details::BooleanSetting<details::ProgressBarOption::show_remaining_time>;
|
||||
using SavedStartTime = details::BooleanSetting<details::ProgressBarOption::saved_start_time>;
|
||||
using ForegroundColor = details::Setting<Color, details::ProgressBarOption::foreground_color>;
|
||||
using ShowSpinner = details::BooleanSetting<details::ProgressBarOption::spinner_show>;
|
||||
using SpinnerStates =
|
||||
details::Setting<std::vector<std::string>, details::ProgressBarOption::spinner_states>;
|
||||
using HideBarWhenComplete =
|
||||
details::BooleanSetting<details::ProgressBarOption::hide_bar_when_complete>;
|
||||
using FontStyles =
|
||||
details::Setting<std::vector<FontStyle>, details::ProgressBarOption::font_styles>;
|
||||
using MinProgress = details::IntegerSetting<details::ProgressBarOption::min_progress>;
|
||||
using MaxProgress = details::IntegerSetting<details::ProgressBarOption::max_progress>;
|
||||
using ProgressType = details::Setting<ProgressType, details::ProgressBarOption::progress_type>;
|
||||
using Stream = details::Setting<std::ostream &, details::ProgressBarOption::stream>;
|
||||
} // namespace option
|
||||
} // namespace indicators
|
||||
|
||||
#endif
|
||||
916
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/termcolor.hpp
Executable file
916
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/termcolor.hpp
Executable file
@@ -0,0 +1,916 @@
|
||||
//!
|
||||
//! termcolor
|
||||
//! ~~~~~~~~~
|
||||
//!
|
||||
//! termcolor is a header-only c++ library for printing colored messages
|
||||
//! to the terminal. Written just for fun with a help of the Force.
|
||||
//!
|
||||
//! :copyright: (c) 2013 by Ihor Kalnytskyi
|
||||
//! :license: BSD, see LICENSE for details
|
||||
//!
|
||||
|
||||
#ifndef TERMCOLOR_HPP_
|
||||
#define TERMCOLOR_HPP_
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
|
||||
// Detect target's platform and set some macros in order to wrap platform
|
||||
// specific code this library depends on.
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
# define TERMCOLOR_TARGET_WINDOWS
|
||||
#elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
|
||||
# define TERMCOLOR_TARGET_POSIX
|
||||
#endif
|
||||
|
||||
// If implementation has not been explicitly set, try to choose one based on
|
||||
// target platform.
|
||||
#if !defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES) && !defined(TERMCOLOR_USE_WINDOWS_API) && !defined(TERMCOLOR_USE_NOOP)
|
||||
# if defined(TERMCOLOR_TARGET_POSIX)
|
||||
# define TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES
|
||||
# define TERMCOLOR_AUTODETECTED_IMPLEMENTATION
|
||||
# elif defined(TERMCOLOR_TARGET_WINDOWS)
|
||||
# define TERMCOLOR_USE_WINDOWS_API
|
||||
# define TERMCOLOR_AUTODETECTED_IMPLEMENTATION
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// These headers provide isatty()/fileno() functions, which are used for
|
||||
// testing whether a standard stream refers to the terminal.
|
||||
#if defined(TERMCOLOR_TARGET_POSIX)
|
||||
# include <unistd.h>
|
||||
#elif defined(TERMCOLOR_TARGET_WINDOWS)
|
||||
#if defined(_MSC_VER)
|
||||
#if !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#endif
|
||||
# include <io.h>
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace termcolor
|
||||
{
|
||||
// Forward declaration of the `_internal` namespace.
|
||||
// All comments are below.
|
||||
namespace _internal
|
||||
{
|
||||
inline int colorize_index();
|
||||
inline FILE* get_standard_stream(const std::ostream& stream);
|
||||
inline bool is_colorized(std::ostream& stream);
|
||||
inline bool is_atty(const std::ostream& stream);
|
||||
|
||||
#if defined(TERMCOLOR_TARGET_WINDOWS)
|
||||
inline void win_change_attributes(std::ostream& stream, int foreground, int background=-1);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& colorize(std::ostream& stream)
|
||||
{
|
||||
stream.iword(_internal::colorize_index()) = 1L;
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& nocolorize(std::ostream& stream)
|
||||
{
|
||||
stream.iword(_internal::colorize_index()) = 0L;
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& reset(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[00m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1, -1);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& bold(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[1m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& dark(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[2m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& italic(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[3m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& underline(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[4m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1, COMMON_LVB_UNDERSCORE);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& blink(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[5m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& reverse(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[7m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& concealed(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[8m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& crossed(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[9m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
template <uint8_t code> inline
|
||||
std::ostream& color(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
char command[12];
|
||||
std::snprintf(command, sizeof(command), "\033[38;5;%dm", code);
|
||||
stream << command;
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
template <uint8_t code> inline
|
||||
std::ostream& on_color(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
char command[12];
|
||||
std::snprintf(command, sizeof(command), "\033[48;5;%dm", code);
|
||||
stream << command;
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
template <uint8_t r, uint8_t g, uint8_t b> inline
|
||||
std::ostream& color(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
char command[20];
|
||||
std::snprintf(command, sizeof(command), "\033[38;2;%d;%d;%dm", r, g, b);
|
||||
stream << command;
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
template <uint8_t r, uint8_t g, uint8_t b> inline
|
||||
std::ostream& on_color(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
char command[20];
|
||||
std::snprintf(command, sizeof(command), "\033[48;2;%d;%d;%dm", r, g, b);
|
||||
stream << command;
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& grey(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[30m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
0 // grey (black)
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& red(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[31m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
FOREGROUND_RED
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& green(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[32m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
FOREGROUND_GREEN
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& yellow(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[33m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
FOREGROUND_GREEN | FOREGROUND_RED
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& blue(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[34m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
FOREGROUND_BLUE
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& magenta(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[35m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
FOREGROUND_BLUE | FOREGROUND_RED
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& cyan(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[36m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
FOREGROUND_BLUE | FOREGROUND_GREEN
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& white(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[37m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
std::ostream& bright_grey(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[90m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
0 | FOREGROUND_INTENSITY // grey (black)
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& bright_red(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[91m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
FOREGROUND_RED | FOREGROUND_INTENSITY
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& bright_green(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[92m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
FOREGROUND_GREEN | FOREGROUND_INTENSITY
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& bright_yellow(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[93m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& bright_blue(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[94m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
FOREGROUND_BLUE | FOREGROUND_INTENSITY
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& bright_magenta(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[95m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& bright_cyan(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[96m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& bright_white(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[97m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream,
|
||||
FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
std::ostream& on_grey(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[40m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
0 // grey (black)
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& on_red(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[41m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
BACKGROUND_RED
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& on_green(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[42m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
BACKGROUND_GREEN
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& on_yellow(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[43m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
BACKGROUND_GREEN | BACKGROUND_RED
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& on_blue(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[44m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
BACKGROUND_BLUE
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& on_magenta(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[45m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
BACKGROUND_BLUE | BACKGROUND_RED
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& on_cyan(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[46m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
BACKGROUND_GREEN | BACKGROUND_BLUE
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& on_white(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[47m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
std::ostream& on_bright_grey(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[100m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
0 | BACKGROUND_INTENSITY // grey (black)
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& on_bright_red(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[101m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
BACKGROUND_RED | BACKGROUND_INTENSITY
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& on_bright_green(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[102m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
BACKGROUND_GREEN | BACKGROUND_INTENSITY
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& on_bright_yellow(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[103m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& on_bright_blue(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[104m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
BACKGROUND_BLUE | BACKGROUND_INTENSITY
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& on_bright_magenta(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[105m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& on_bright_cyan(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[106m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
inline
|
||||
std::ostream& on_bright_white(std::ostream& stream)
|
||||
{
|
||||
if (_internal::is_colorized(stream))
|
||||
{
|
||||
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
|
||||
stream << "\033[107m";
|
||||
#elif defined(TERMCOLOR_USE_WINDOWS_API)
|
||||
_internal::win_change_attributes(stream, -1,
|
||||
BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//! Since C++ hasn't a way to hide something in the header from
|
||||
//! the outer access, I have to introduce this namespace which
|
||||
//! is used for internal purpose and should't be access from
|
||||
//! the user code.
|
||||
namespace _internal
|
||||
{
|
||||
// An index to be used to access a private storage of I/O streams. See
|
||||
// colorize / nocolorize I/O manipulators for details. Due to the fact
|
||||
// that static variables ain't shared between translation units, inline
|
||||
// function with local static variable is used to do the trick and share
|
||||
// the variable value between translation units.
|
||||
inline int colorize_index()
|
||||
{
|
||||
static int colorize_index = std::ios_base::xalloc();
|
||||
return colorize_index;
|
||||
}
|
||||
|
||||
//! Since C++ hasn't a true way to extract stream handler
|
||||
//! from the a given `std::ostream` object, I have to write
|
||||
//! this kind of hack.
|
||||
inline
|
||||
FILE* get_standard_stream(const std::ostream& stream)
|
||||
{
|
||||
if (&stream == &std::cout)
|
||||
return stdout;
|
||||
else if ((&stream == &std::cerr) || (&stream == &std::clog))
|
||||
return stderr;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Say whether a given stream should be colorized or not. It's always
|
||||
// true for ATTY streams and may be true for streams marked with
|
||||
// colorize flag.
|
||||
inline
|
||||
bool is_colorized(std::ostream& stream)
|
||||
{
|
||||
return is_atty(stream) || static_cast<bool>(stream.iword(colorize_index()));
|
||||
}
|
||||
|
||||
//! Test whether a given `std::ostream` object refers to
|
||||
//! a terminal.
|
||||
inline
|
||||
bool is_atty(const std::ostream& stream)
|
||||
{
|
||||
FILE* std_stream = get_standard_stream(stream);
|
||||
|
||||
// Unfortunately, fileno() ends with segmentation fault
|
||||
// if invalid file descriptor is passed. So we need to
|
||||
// handle this case gracefully and assume it's not a tty
|
||||
// if standard stream is not detected, and 0 is returned.
|
||||
if (!std_stream)
|
||||
return false;
|
||||
|
||||
#if defined(TERMCOLOR_TARGET_POSIX)
|
||||
return ::isatty(fileno(std_stream));
|
||||
#elif defined(TERMCOLOR_TARGET_WINDOWS)
|
||||
return ::_isatty(_fileno(std_stream));
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(TERMCOLOR_TARGET_WINDOWS)
|
||||
//! Change Windows Terminal colors attribute. If some
|
||||
//! parameter is `-1` then attribute won't changed.
|
||||
inline void win_change_attributes(std::ostream& stream, int foreground, int background)
|
||||
{
|
||||
// yeah, i know.. it's ugly, it's windows.
|
||||
static WORD defaultAttributes = 0;
|
||||
|
||||
// Windows doesn't have ANSI escape sequences and so we use special
|
||||
// API to change Terminal output color. That means we can't
|
||||
// manipulate colors by means of "std::stringstream" and hence
|
||||
// should do nothing in this case.
|
||||
if (!_internal::is_atty(stream))
|
||||
return;
|
||||
|
||||
// get terminal handle
|
||||
HANDLE hTerminal = INVALID_HANDLE_VALUE;
|
||||
if (&stream == &std::cout)
|
||||
hTerminal = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
else if (&stream == &std::cerr)
|
||||
hTerminal = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
// save default terminal attributes if it unsaved
|
||||
if (!defaultAttributes)
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
if (!GetConsoleScreenBufferInfo(hTerminal, &info))
|
||||
return;
|
||||
defaultAttributes = info.wAttributes;
|
||||
}
|
||||
|
||||
// restore all default settings
|
||||
if (foreground == -1 && background == -1)
|
||||
{
|
||||
SetConsoleTextAttribute(hTerminal, defaultAttributes);
|
||||
return;
|
||||
}
|
||||
|
||||
// get current settings
|
||||
CONSOLE_SCREEN_BUFFER_INFO info;
|
||||
if (!GetConsoleScreenBufferInfo(hTerminal, &info))
|
||||
return;
|
||||
|
||||
if (foreground != -1)
|
||||
{
|
||||
info.wAttributes &= ~(info.wAttributes & 0x0F);
|
||||
info.wAttributes |= static_cast<WORD>(foreground);
|
||||
}
|
||||
|
||||
if (background != -1)
|
||||
{
|
||||
info.wAttributes &= ~(info.wAttributes & 0xF0);
|
||||
info.wAttributes |= static_cast<WORD>(background);
|
||||
}
|
||||
|
||||
SetConsoleTextAttribute(hTerminal, info.wAttributes);
|
||||
}
|
||||
#endif // TERMCOLOR_TARGET_WINDOWS
|
||||
|
||||
} // namespace _internal
|
||||
|
||||
} // namespace termcolor
|
||||
|
||||
|
||||
#undef TERMCOLOR_TARGET_POSIX
|
||||
#undef TERMCOLOR_TARGET_WINDOWS
|
||||
|
||||
#if defined(TERMCOLOR_AUTODETECTED_IMPLEMENTATION)
|
||||
# undef TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES
|
||||
# undef TERMCOLOR_USE_WINDOWS_API
|
||||
#endif
|
||||
|
||||
#endif // TERMCOLOR_HPP_
|
||||
44
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/terminal_size.hpp
Executable file
44
cpp-package/inspireface/cpp/test/3rd/indicators/indicators/terminal_size.hpp
Executable file
@@ -0,0 +1,44 @@
|
||||
|
||||
#ifndef INDICATORS_TERMINAL_SIZE
|
||||
#define INDICATORS_TERMINAL_SIZE
|
||||
#include <utility>
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
|
||||
namespace indicators {
|
||||
|
||||
static inline std::pair<size_t, size_t> terminal_size() {
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
int cols, rows;
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
|
||||
cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
||||
rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
||||
return {static_cast<size_t>(rows), static_cast<size_t>(cols)};
|
||||
}
|
||||
|
||||
static inline size_t terminal_width() { return terminal_size().second; }
|
||||
|
||||
} // namespace indicators
|
||||
|
||||
#else
|
||||
|
||||
#include <sys/ioctl.h> //ioctl() and TIOCGWINSZ
|
||||
#include <unistd.h> // for STDOUT_FILENO
|
||||
|
||||
namespace indicators {
|
||||
|
||||
static inline std::pair<size_t, size_t> terminal_size() {
|
||||
struct winsize size{};
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &size);
|
||||
return {static_cast<size_t>(size.ws_row), static_cast<size_t>(size.ws_col)};
|
||||
}
|
||||
|
||||
static inline size_t terminal_width() { return terminal_size().second; }
|
||||
|
||||
} // namespace indicators
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
/************************************
|
||||
* file enc : ascii
|
||||
* author : wuyanyi09@gmail.com
|
||||
************************************/
|
||||
|
||||
#ifndef LIMONP_ARGV_FUNCTS_H
|
||||
#define LIMONP_ARGV_FUNCTS_H
|
||||
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include "StringUtil.hpp"
|
||||
|
||||
namespace limonp {
|
||||
|
||||
using namespace std;
|
||||
|
||||
class ArgvContext {
|
||||
public :
|
||||
ArgvContext(int argc, const char* const * argv) {
|
||||
for(int i = 0; i < argc; i++) {
|
||||
if(StartsWith(argv[i], "-")) {
|
||||
if(i + 1 < argc && !StartsWith(argv[i + 1], "-")) {
|
||||
mpss_[argv[i]] = argv[i+1];
|
||||
i++;
|
||||
} else {
|
||||
sset_.insert(argv[i]);
|
||||
}
|
||||
} else {
|
||||
args_.push_back(argv[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
~ArgvContext() {
|
||||
}
|
||||
|
||||
friend ostream& operator << (ostream& os, const ArgvContext& args);
|
||||
string operator [](size_t i) const {
|
||||
if(i < args_.size()) {
|
||||
return args_[i];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
string operator [](const string& key) const {
|
||||
map<string, string>::const_iterator it = mpss_.find(key);
|
||||
if(it != mpss_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool HasKey(const string& key) const {
|
||||
if(mpss_.find(key) != mpss_.end() || sset_.find(key) != sset_.end()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
vector<string> args_;
|
||||
map<string, string> mpss_;
|
||||
set<string> sset_;
|
||||
}; // class ArgvContext
|
||||
|
||||
inline ostream& operator << (ostream& os, const ArgvContext& args) {
|
||||
return os<<args.args_<<args.mpss_<<args.sset_;
|
||||
}
|
||||
|
||||
} // namespace limonp
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef LIMONP_BLOCKINGQUEUE_HPP
|
||||
#define LIMONP_BLOCKINGQUEUE_HPP
|
||||
|
||||
#include <queue>
|
||||
#include "Condition.hpp"
|
||||
|
||||
namespace limonp {
|
||||
template<class T>
|
||||
class BlockingQueue: NonCopyable {
|
||||
public:
|
||||
BlockingQueue()
|
||||
: mutex_(), notEmpty_(mutex_), queue_() {
|
||||
}
|
||||
|
||||
void Push(const T& x) {
|
||||
MutexLockGuard lock(mutex_);
|
||||
queue_.push(x);
|
||||
notEmpty_.Notify(); // Wait morphing saves us
|
||||
}
|
||||
|
||||
T Pop() {
|
||||
MutexLockGuard lock(mutex_);
|
||||
// always use a while-loop, due to spurious wakeup
|
||||
while (queue_.empty()) {
|
||||
notEmpty_.Wait();
|
||||
}
|
||||
assert(!queue_.empty());
|
||||
T front(queue_.front());
|
||||
queue_.pop();
|
||||
return front;
|
||||
}
|
||||
|
||||
size_t Size() const {
|
||||
MutexLockGuard lock(mutex_);
|
||||
return queue_.size();
|
||||
}
|
||||
bool Empty() const {
|
||||
return Size() == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable MutexLock mutex_;
|
||||
Condition notEmpty_;
|
||||
std::queue<T> queue_;
|
||||
}; // class BlockingQueue
|
||||
|
||||
} // namespace limonp
|
||||
|
||||
#endif // LIMONP_BLOCKINGQUEUE_HPP
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef LIMONP_BOUNDED_BLOCKING_QUEUE_HPP
|
||||
#define LIMONP_BOUNDED_BLOCKING_QUEUE_HPP
|
||||
|
||||
#include "BoundedQueue.hpp"
|
||||
|
||||
namespace limonp {
|
||||
|
||||
template<typename T>
|
||||
class BoundedBlockingQueue : NonCopyable {
|
||||
public:
|
||||
explicit BoundedBlockingQueue(size_t maxSize)
|
||||
: mutex_(),
|
||||
notEmpty_(mutex_),
|
||||
notFull_(mutex_),
|
||||
queue_(maxSize) {
|
||||
}
|
||||
|
||||
void Push(const T& x) {
|
||||
MutexLockGuard lock(mutex_);
|
||||
while (queue_.Full()) {
|
||||
notFull_.Wait();
|
||||
}
|
||||
assert(!queue_.Full());
|
||||
queue_.Push(x);
|
||||
notEmpty_.Notify();
|
||||
}
|
||||
|
||||
T Pop() {
|
||||
MutexLockGuard lock(mutex_);
|
||||
while (queue_.Empty()) {
|
||||
notEmpty_.Wait();
|
||||
}
|
||||
assert(!queue_.Empty());
|
||||
T res = queue_.Pop();
|
||||
notFull_.Notify();
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Empty() const {
|
||||
MutexLockGuard lock(mutex_);
|
||||
return queue_.Empty();
|
||||
}
|
||||
|
||||
bool Full() const {
|
||||
MutexLockGuard lock(mutex_);
|
||||
return queue_.Full();
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
MutexLockGuard lock(mutex_);
|
||||
return queue_.size();
|
||||
}
|
||||
|
||||
size_t capacity() const {
|
||||
return queue_.capacity();
|
||||
}
|
||||
|
||||
private:
|
||||
mutable MutexLock mutex_;
|
||||
Condition notEmpty_;
|
||||
Condition notFull_;
|
||||
BoundedQueue<T> queue_;
|
||||
}; // class BoundedBlockingQueue
|
||||
|
||||
} // namespace limonp
|
||||
|
||||
#endif // LIMONP_BOUNDED_BLOCKING_QUEUE_HPP
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef LIMONP_BOUNDED_QUEUE_HPP
|
||||
#define LIMONP_BOUNDED_QUEUE_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <cassert>
|
||||
|
||||
namespace limonp {
|
||||
using namespace std;
|
||||
template<class T>
|
||||
class BoundedQueue {
|
||||
public:
|
||||
explicit BoundedQueue(size_t capacity): capacity_(capacity), circular_buffer_(capacity) {
|
||||
head_ = 0;
|
||||
tail_ = 0;
|
||||
size_ = 0;
|
||||
assert(capacity_);
|
||||
}
|
||||
~BoundedQueue() {
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
head_ = 0;
|
||||
tail_ = 0;
|
||||
size_ = 0;
|
||||
}
|
||||
bool Empty() const {
|
||||
return !size_;
|
||||
}
|
||||
bool Full() const {
|
||||
return capacity_ == size_;
|
||||
}
|
||||
size_t Size() const {
|
||||
return size_;
|
||||
}
|
||||
size_t Capacity() const {
|
||||
return capacity_;
|
||||
}
|
||||
|
||||
void Push(const T& t) {
|
||||
assert(!Full());
|
||||
circular_buffer_[tail_] = t;
|
||||
tail_ = (tail_ + 1) % capacity_;
|
||||
size_ ++;
|
||||
}
|
||||
|
||||
T Pop() {
|
||||
assert(!Empty());
|
||||
size_t oldPos = head_;
|
||||
head_ = (head_ + 1) % capacity_;
|
||||
size_ --;
|
||||
return circular_buffer_[oldPos];
|
||||
}
|
||||
|
||||
private:
|
||||
size_t head_;
|
||||
size_t tail_;
|
||||
size_t size_;
|
||||
const size_t capacity_;
|
||||
vector<T> circular_buffer_;
|
||||
|
||||
}; // class BoundedQueue
|
||||
} // namespace limonp
|
||||
|
||||
#endif
|
||||
206
cpp-package/inspireface/cpp/test/3rd/limonp/limonp/Closure.hpp
Normal file
206
cpp-package/inspireface/cpp/test/3rd/limonp/limonp/Closure.hpp
Normal file
@@ -0,0 +1,206 @@
|
||||
#ifndef LIMONP_CLOSURE_HPP
|
||||
#define LIMONP_CLOSURE_HPP
|
||||
|
||||
namespace limonp {
|
||||
|
||||
class ClosureInterface {
|
||||
public:
|
||||
virtual ~ClosureInterface() {
|
||||
}
|
||||
virtual void Run() = 0;
|
||||
};
|
||||
|
||||
template <class Funct>
|
||||
class Closure0: public ClosureInterface {
|
||||
public:
|
||||
Closure0(Funct fun) {
|
||||
fun_ = fun;
|
||||
}
|
||||
virtual ~Closure0() {
|
||||
}
|
||||
virtual void Run() {
|
||||
(*fun_)();
|
||||
}
|
||||
private:
|
||||
Funct fun_;
|
||||
};
|
||||
|
||||
template <class Funct, class Arg1>
|
||||
class Closure1: public ClosureInterface {
|
||||
public:
|
||||
Closure1(Funct fun, Arg1 arg1) {
|
||||
fun_ = fun;
|
||||
arg1_ = arg1;
|
||||
}
|
||||
virtual ~Closure1() {
|
||||
}
|
||||
virtual void Run() {
|
||||
(*fun_)(arg1_);
|
||||
}
|
||||
private:
|
||||
Funct fun_;
|
||||
Arg1 arg1_;
|
||||
};
|
||||
|
||||
template <class Funct, class Arg1, class Arg2>
|
||||
class Closure2: public ClosureInterface {
|
||||
public:
|
||||
Closure2(Funct fun, Arg1 arg1, Arg2 arg2) {
|
||||
fun_ = fun;
|
||||
arg1_ = arg1;
|
||||
arg2_ = arg2;
|
||||
}
|
||||
virtual ~Closure2() {
|
||||
}
|
||||
virtual void Run() {
|
||||
(*fun_)(arg1_, arg2_);
|
||||
}
|
||||
private:
|
||||
Funct fun_;
|
||||
Arg1 arg1_;
|
||||
Arg2 arg2_;
|
||||
};
|
||||
|
||||
template <class Funct, class Arg1, class Arg2, class Arg3>
|
||||
class Closure3: public ClosureInterface {
|
||||
public:
|
||||
Closure3(Funct fun, Arg1 arg1, Arg2 arg2, Arg3 arg3) {
|
||||
fun_ = fun;
|
||||
arg1_ = arg1;
|
||||
arg2_ = arg2;
|
||||
arg3_ = arg3;
|
||||
}
|
||||
virtual ~Closure3() {
|
||||
}
|
||||
virtual void Run() {
|
||||
(*fun_)(arg1_, arg2_, arg3_);
|
||||
}
|
||||
private:
|
||||
Funct fun_;
|
||||
Arg1 arg1_;
|
||||
Arg2 arg2_;
|
||||
Arg3 arg3_;
|
||||
};
|
||||
|
||||
template <class Obj, class Funct>
|
||||
class ObjClosure0: public ClosureInterface {
|
||||
public:
|
||||
ObjClosure0(Obj* p, Funct fun) {
|
||||
p_ = p;
|
||||
fun_ = fun;
|
||||
}
|
||||
virtual ~ObjClosure0() {
|
||||
}
|
||||
virtual void Run() {
|
||||
(p_->*fun_)();
|
||||
}
|
||||
private:
|
||||
Obj* p_;
|
||||
Funct fun_;
|
||||
};
|
||||
|
||||
template <class Obj, class Funct, class Arg1>
|
||||
class ObjClosure1: public ClosureInterface {
|
||||
public:
|
||||
ObjClosure1(Obj* p, Funct fun, Arg1 arg1) {
|
||||
p_ = p;
|
||||
fun_ = fun;
|
||||
arg1_ = arg1;
|
||||
}
|
||||
virtual ~ObjClosure1() {
|
||||
}
|
||||
virtual void Run() {
|
||||
(p_->*fun_)(arg1_);
|
||||
}
|
||||
private:
|
||||
Obj* p_;
|
||||
Funct fun_;
|
||||
Arg1 arg1_;
|
||||
};
|
||||
|
||||
template <class Obj, class Funct, class Arg1, class Arg2>
|
||||
class ObjClosure2: public ClosureInterface {
|
||||
public:
|
||||
ObjClosure2(Obj* p, Funct fun, Arg1 arg1, Arg2 arg2) {
|
||||
p_ = p;
|
||||
fun_ = fun;
|
||||
arg1_ = arg1;
|
||||
arg2_ = arg2;
|
||||
}
|
||||
virtual ~ObjClosure2() {
|
||||
}
|
||||
virtual void Run() {
|
||||
(p_->*fun_)(arg1_, arg2_);
|
||||
}
|
||||
private:
|
||||
Obj* p_;
|
||||
Funct fun_;
|
||||
Arg1 arg1_;
|
||||
Arg2 arg2_;
|
||||
};
|
||||
template <class Obj, class Funct, class Arg1, class Arg2, class Arg3>
|
||||
class ObjClosure3: public ClosureInterface {
|
||||
public:
|
||||
ObjClosure3(Obj* p, Funct fun, Arg1 arg1, Arg2 arg2, Arg3 arg3) {
|
||||
p_ = p;
|
||||
fun_ = fun;
|
||||
arg1_ = arg1;
|
||||
arg2_ = arg2;
|
||||
arg3_ = arg3;
|
||||
}
|
||||
virtual ~ObjClosure3() {
|
||||
}
|
||||
virtual void Run() {
|
||||
(p_->*fun_)(arg1_, arg2_, arg3_);
|
||||
}
|
||||
private:
|
||||
Obj* p_;
|
||||
Funct fun_;
|
||||
Arg1 arg1_;
|
||||
Arg2 arg2_;
|
||||
Arg3 arg3_;
|
||||
};
|
||||
|
||||
template<class R>
|
||||
ClosureInterface* NewClosure(R (*fun)()) {
|
||||
return new Closure0<R (*)()>(fun);
|
||||
}
|
||||
|
||||
template<class R, class Arg1>
|
||||
ClosureInterface* NewClosure(R (*fun)(Arg1), Arg1 arg1) {
|
||||
return new Closure1<R (*)(Arg1), Arg1>(fun, arg1);
|
||||
}
|
||||
|
||||
template<class R, class Arg1, class Arg2>
|
||||
ClosureInterface* NewClosure(R (*fun)(Arg1, Arg2), Arg1 arg1, Arg2 arg2) {
|
||||
return new Closure2<R (*)(Arg1, Arg2), Arg1, Arg2>(fun, arg1, arg2);
|
||||
}
|
||||
|
||||
template<class R, class Arg1, class Arg2, class Arg3>
|
||||
ClosureInterface* NewClosure(R (*fun)(Arg1, Arg2, Arg3), Arg1 arg1, Arg2 arg2, Arg3 arg3) {
|
||||
return new Closure3<R (*)(Arg1, Arg2, Arg3), Arg1, Arg2, Arg3>(fun, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
template<class R, class Obj>
|
||||
ClosureInterface* NewClosure(Obj* obj, R (Obj::* fun)()) {
|
||||
return new ObjClosure0<Obj, R (Obj::* )()>(obj, fun);
|
||||
}
|
||||
|
||||
template<class R, class Obj, class Arg1>
|
||||
ClosureInterface* NewClosure(Obj* obj, R (Obj::* fun)(Arg1), Arg1 arg1) {
|
||||
return new ObjClosure1<Obj, R (Obj::* )(Arg1), Arg1>(obj, fun, arg1);
|
||||
}
|
||||
|
||||
template<class R, class Obj, class Arg1, class Arg2>
|
||||
ClosureInterface* NewClosure(Obj* obj, R (Obj::* fun)(Arg1, Arg2), Arg1 arg1, Arg2 arg2) {
|
||||
return new ObjClosure2<Obj, R (Obj::*)(Arg1, Arg2), Arg1, Arg2>(obj, fun, arg1, arg2);
|
||||
}
|
||||
|
||||
template<class R, class Obj, class Arg1, class Arg2, class Arg3>
|
||||
ClosureInterface* NewClosure(Obj* obj, R (Obj::* fun)(Arg1, Arg2, Arg3), Arg1 arg1, Arg2 arg2, Arg3 arg3) {
|
||||
return new ObjClosure3<Obj, R (Obj::*)(Arg1, Arg2, Arg3), Arg1, Arg2, Arg3>(obj, fun, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
} // namespace limonp
|
||||
|
||||
#endif // LIMONP_CLOSURE_HPP
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef LIMONP_COLOR_PRINT_HPP
|
||||
#define LIMONP_COLOR_PRINT_HPP
|
||||
|
||||
#include <string>
|
||||
#include <stdarg.h>
|
||||
|
||||
namespace limonp {
|
||||
|
||||
using std::string;
|
||||
|
||||
enum Color {
|
||||
BLACK = 30,
|
||||
RED,
|
||||
GREEN,
|
||||
YELLOW,
|
||||
BLUE,
|
||||
PURPLE
|
||||
}; // enum Color
|
||||
|
||||
static void ColorPrintln(enum Color color, const char * fmt, ...) {
|
||||
va_list ap;
|
||||
printf("\033[0;%dm", color);
|
||||
va_start(ap, fmt);
|
||||
vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
printf("\033[0m\n"); // if not \n , in some situation , the next lines will be set the same color unexpectedly
|
||||
}
|
||||
|
||||
} // namespace limonp
|
||||
|
||||
#endif // LIMONP_COLOR_PRINT_HPP
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef LIMONP_CONDITION_HPP
|
||||
#define LIMONP_CONDITION_HPP
|
||||
|
||||
#include "MutexLock.hpp"
|
||||
|
||||
namespace limonp {
|
||||
|
||||
class Condition : NonCopyable {
|
||||
public:
|
||||
explicit Condition(MutexLock& mutex)
|
||||
: mutex_(mutex) {
|
||||
XCHECK(!pthread_cond_init(&pcond_, NULL));
|
||||
}
|
||||
|
||||
~Condition() {
|
||||
XCHECK(!pthread_cond_destroy(&pcond_));
|
||||
}
|
||||
|
||||
void Wait() {
|
||||
XCHECK(!pthread_cond_wait(&pcond_, mutex_.GetPthreadMutex()));
|
||||
}
|
||||
|
||||
void Notify() {
|
||||
XCHECK(!pthread_cond_signal(&pcond_));
|
||||
}
|
||||
|
||||
void NotifyAll() {
|
||||
XCHECK(!pthread_cond_broadcast(&pcond_));
|
||||
}
|
||||
|
||||
private:
|
||||
MutexLock& mutex_;
|
||||
pthread_cond_t pcond_;
|
||||
}; // class Condition
|
||||
|
||||
} // namespace limonp
|
||||
|
||||
#endif // LIMONP_CONDITION_HPP
|
||||
103
cpp-package/inspireface/cpp/test/3rd/limonp/limonp/Config.hpp
Normal file
103
cpp-package/inspireface/cpp/test/3rd/limonp/limonp/Config.hpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/************************************
|
||||
* file enc : utf8
|
||||
* author : wuyanyi09@gmail.com
|
||||
************************************/
|
||||
#ifndef LIMONP_CONFIG_H
|
||||
#define LIMONP_CONFIG_H
|
||||
|
||||
#include <map>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <assert.h>
|
||||
#include "StringUtil.hpp"
|
||||
|
||||
namespace limonp {
|
||||
|
||||
using namespace std;
|
||||
|
||||
class Config {
|
||||
public:
|
||||
explicit Config(const string& filePath) {
|
||||
LoadFile(filePath);
|
||||
}
|
||||
|
||||
operator bool () {
|
||||
return !map_.empty();
|
||||
}
|
||||
|
||||
string Get(const string& key, const string& defaultvalue) const {
|
||||
map<string, string>::const_iterator it = map_.find(key);
|
||||
if(map_.end() != it) {
|
||||
return it->second;
|
||||
}
|
||||
return defaultvalue;
|
||||
}
|
||||
int Get(const string& key, int defaultvalue) const {
|
||||
string str = Get(key, "");
|
||||
if("" == str) {
|
||||
return defaultvalue;
|
||||
}
|
||||
return atoi(str.c_str());
|
||||
}
|
||||
const char* operator [] (const char* key) const {
|
||||
if(NULL == key) {
|
||||
return NULL;
|
||||
}
|
||||
map<string, string>::const_iterator it = map_.find(key);
|
||||
if(map_.end() != it) {
|
||||
return it->second.c_str();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string GetConfigInfo() const {
|
||||
string res;
|
||||
res << *this;
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
void LoadFile(const string& filePath) {
|
||||
ifstream ifs(filePath.c_str());
|
||||
assert(ifs);
|
||||
string line;
|
||||
vector<string> vecBuf;
|
||||
size_t lineno = 0;
|
||||
while(getline(ifs, line)) {
|
||||
lineno ++;
|
||||
Trim(line);
|
||||
if(line.empty() || StartsWith(line, "#")) {
|
||||
continue;
|
||||
}
|
||||
vecBuf.clear();
|
||||
Split(line, vecBuf, "=");
|
||||
if(2 != vecBuf.size()) {
|
||||
fprintf(stderr, "line[%s] illegal.\n", line.c_str());
|
||||
assert(false);
|
||||
continue;
|
||||
}
|
||||
string& key = vecBuf[0];
|
||||
string& value = vecBuf[1];
|
||||
Trim(key);
|
||||
Trim(value);
|
||||
if(!map_.insert(make_pair(key, value)).second) {
|
||||
fprintf(stderr, "key[%s] already exits.\n", key.c_str());
|
||||
assert(false);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ifs.close();
|
||||
}
|
||||
|
||||
friend ostream& operator << (ostream& os, const Config& config);
|
||||
|
||||
map<string, string> map_;
|
||||
}; // class Config
|
||||
|
||||
inline ostream& operator << (ostream& os, const Config& config) {
|
||||
return os << config.map_;
|
||||
}
|
||||
|
||||
} // namespace limonp
|
||||
|
||||
#endif // LIMONP_CONFIG_H
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifndef LIMONP_FILELOCK_HPP
|
||||
#define LIMONP_FILELOCK_HPP
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace limonp {
|
||||
|
||||
using std::string;
|
||||
|
||||
class FileLock {
|
||||
public:
|
||||
FileLock() : fd_(-1), ok_(true) {
|
||||
}
|
||||
~FileLock() {
|
||||
if(fd_ > 0) {
|
||||
Close();
|
||||
}
|
||||
}
|
||||
void Open(const string& fname) {
|
||||
assert(fd_ == -1);
|
||||
fd_ = open(fname.c_str(), O_RDWR | O_CREAT, 0644);
|
||||
if(fd_ < 0) {
|
||||
ok_ = false;
|
||||
err_ = strerror(errno);
|
||||
}
|
||||
}
|
||||
void Close() {
|
||||
::close(fd_);
|
||||
}
|
||||
void Lock() {
|
||||
if(LockOrUnlock(fd_, true) < 0) {
|
||||
ok_ = false;
|
||||
err_ = strerror(errno);
|
||||
}
|
||||
}
|
||||
void UnLock() {
|
||||
if(LockOrUnlock(fd_, false) < 0) {
|
||||
ok_ = false;
|
||||
err_ = strerror(errno);
|
||||
}
|
||||
}
|
||||
bool Ok() const {
|
||||
return ok_;
|
||||
}
|
||||
string Error() const {
|
||||
return err_;
|
||||
}
|
||||
private:
|
||||
static int LockOrUnlock(int fd, bool lock) {
|
||||
errno = 0;
|
||||
struct flock f;
|
||||
memset(&f, 0, sizeof(f));
|
||||
f.l_type = (lock ? F_WRLCK : F_UNLCK);
|
||||
f.l_whence = SEEK_SET;
|
||||
f.l_start = 0;
|
||||
f.l_len = 0; // Lock/unlock entire file
|
||||
return fcntl(fd, F_SETLK, &f);
|
||||
}
|
||||
|
||||
int fd_;
|
||||
bool ok_;
|
||||
string err_;
|
||||
}; // class FileLock
|
||||
|
||||
}// namespace limonp
|
||||
|
||||
#endif // LIMONP_FILELOCK_HPP
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef LIMONP_FORCE_PUBLIC_H
|
||||
#define LIMONP_FORCE_PUBLIC_H
|
||||
|
||||
#define private public
|
||||
#define protected public
|
||||
|
||||
#endif // LIMONP_FORCE_PUBLIC_H
|
||||
@@ -0,0 +1,139 @@
|
||||
#ifndef LIMONP_LOCAL_VECTOR_HPP
|
||||
#define LIMONP_LOCAL_VECTOR_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace limonp {
|
||||
using namespace std;
|
||||
/*
|
||||
* LocalVector<T> : T must be primitive type (char , int, size_t), if T is struct or class, LocalVector<T> may be dangerous..
|
||||
* LocalVector<T> is simple and not well-tested.
|
||||
*/
|
||||
const size_t LOCAL_VECTOR_BUFFER_SIZE = 16;
|
||||
template <class T>
|
||||
class LocalVector {
|
||||
public:
|
||||
typedef const T* const_iterator ;
|
||||
typedef T value_type;
|
||||
typedef size_t size_type;
|
||||
private:
|
||||
T buffer_[LOCAL_VECTOR_BUFFER_SIZE];
|
||||
T * ptr_;
|
||||
size_t size_;
|
||||
size_t capacity_;
|
||||
public:
|
||||
LocalVector() {
|
||||
init_();
|
||||
};
|
||||
LocalVector(const LocalVector<T>& vec) {
|
||||
init_();
|
||||
*this = vec;
|
||||
}
|
||||
LocalVector(const_iterator begin, const_iterator end) { // TODO: make it faster
|
||||
init_();
|
||||
while(begin != end) {
|
||||
push_back(*begin++);
|
||||
}
|
||||
}
|
||||
LocalVector(size_t size, const T& t) { // TODO: make it faster
|
||||
init_();
|
||||
while(size--) {
|
||||
push_back(t);
|
||||
}
|
||||
}
|
||||
~LocalVector() {
|
||||
if(ptr_ != buffer_) {
|
||||
free(ptr_);
|
||||
}
|
||||
};
|
||||
public:
|
||||
LocalVector<T>& operator = (const LocalVector<T>& vec) {
|
||||
clear();
|
||||
size_ = vec.size();
|
||||
capacity_ = vec.capacity();
|
||||
if(vec.buffer_ == vec.ptr_) {
|
||||
memcpy(buffer_, vec.buffer_, sizeof(T) * size_);
|
||||
ptr_ = buffer_;
|
||||
} else {
|
||||
ptr_ = (T*) malloc(vec.capacity() * sizeof(T));
|
||||
assert(ptr_);
|
||||
memcpy(ptr_, vec.ptr_, vec.size() * sizeof(T));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
void init_() {
|
||||
ptr_ = buffer_;
|
||||
size_ = 0;
|
||||
capacity_ = LOCAL_VECTOR_BUFFER_SIZE;
|
||||
}
|
||||
public:
|
||||
T& operator [] (size_t i) {
|
||||
return ptr_[i];
|
||||
}
|
||||
const T& operator [] (size_t i) const {
|
||||
return ptr_[i];
|
||||
}
|
||||
void push_back(const T& t) {
|
||||
if(size_ == capacity_) {
|
||||
assert(capacity_);
|
||||
reserve(capacity_ * 2);
|
||||
}
|
||||
ptr_[size_ ++ ] = t;
|
||||
}
|
||||
void reserve(size_t size) {
|
||||
if(size <= capacity_) {
|
||||
return;
|
||||
}
|
||||
T * next = (T*)malloc(sizeof(T) * size);
|
||||
assert(next);
|
||||
T * old = ptr_;
|
||||
ptr_ = next;
|
||||
memcpy(ptr_, old, sizeof(T) * capacity_);
|
||||
capacity_ = size;
|
||||
if(old != buffer_) {
|
||||
free(old);
|
||||
}
|
||||
}
|
||||
bool empty() const {
|
||||
return 0 == size();
|
||||
}
|
||||
size_t size() const {
|
||||
return size_;
|
||||
}
|
||||
size_t capacity() const {
|
||||
return capacity_;
|
||||
}
|
||||
const_iterator begin() const {
|
||||
return ptr_;
|
||||
}
|
||||
const_iterator end() const {
|
||||
return ptr_ + size_;
|
||||
}
|
||||
void clear() {
|
||||
if(ptr_ != buffer_) {
|
||||
free(ptr_);
|
||||
}
|
||||
init_();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
ostream & operator << (ostream& os, const LocalVector<T>& vec) {
|
||||
if(vec.empty()) {
|
||||
return os << "[]";
|
||||
}
|
||||
os<<"[\""<<vec[0];
|
||||
for(size_t i = 1; i < vec.size(); i++) {
|
||||
os<<"\", \""<<vec[i];
|
||||
}
|
||||
os<<"\"]";
|
||||
return os;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,76 @@
|
||||
#ifndef LIMONP_LOGGING_HPP
|
||||
#define LIMONP_LOGGING_HPP
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
||||
#ifdef XLOG
|
||||
#error "XLOG has been defined already"
|
||||
#endif // XLOG
|
||||
#ifdef XCHECK
|
||||
#error "XCHECK has been defined already"
|
||||
#endif // XCHECK
|
||||
|
||||
#define XLOG(level) limonp::Logger(limonp::LL_##level, __FILE__, __LINE__).Stream()
|
||||
#define XCHECK(exp) if(!(exp)) XLOG(FATAL) << "exp: ["#exp << "] false. "
|
||||
|
||||
namespace limonp {
|
||||
|
||||
enum {
|
||||
LL_DEBUG = 0,
|
||||
LL_INFO = 1,
|
||||
LL_WARNING = 2,
|
||||
LL_ERROR = 3,
|
||||
LL_FATAL = 4,
|
||||
}; // enum
|
||||
|
||||
static const char * LOG_LEVEL_ARRAY[] = {"DEBUG","INFO","WARN","ERROR","FATAL"};
|
||||
static const char * LOG_TIME_FORMAT = "%Y-%m-%d %H:%M:%S";
|
||||
|
||||
class Logger {
|
||||
public:
|
||||
Logger(size_t level, const char* filename, int lineno)
|
||||
: level_(level) {
|
||||
#ifdef LOGGING_LEVEL
|
||||
if (level_ < LOGGING_LEVEL) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
assert(level_ <= sizeof(LOG_LEVEL_ARRAY)/sizeof(*LOG_LEVEL_ARRAY));
|
||||
char buf[32];
|
||||
time_t now;
|
||||
time(&now);
|
||||
strftime(buf, sizeof(buf), LOG_TIME_FORMAT, localtime(&now));
|
||||
stream_ << buf
|
||||
<< " " << filename
|
||||
<< ":" << lineno
|
||||
<< " " << LOG_LEVEL_ARRAY[level_]
|
||||
<< " ";
|
||||
}
|
||||
~Logger() {
|
||||
#ifdef LOGGING_LEVEL
|
||||
if (level_ < LOGGING_LEVEL) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
std::cerr << stream_.str() << std::endl;
|
||||
if (level_ == LL_FATAL) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& Stream() {
|
||||
return stream_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::ostringstream stream_;
|
||||
size_t level_;
|
||||
}; // class Logger
|
||||
|
||||
} // namespace limonp
|
||||
|
||||
#endif // LIMONP_LOGGING_HPP
|
||||
411
cpp-package/inspireface/cpp/test/3rd/limonp/limonp/Md5.hpp
Normal file
411
cpp-package/inspireface/cpp/test/3rd/limonp/limonp/Md5.hpp
Normal file
@@ -0,0 +1,411 @@
|
||||
#ifndef __MD5_H__
|
||||
#define __MD5_H__
|
||||
|
||||
// Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
||||
// rights reserved.
|
||||
|
||||
// License to copy and use this software is granted provided that it
|
||||
// is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
||||
// Algorithm" in all material mentioning or referencing this software
|
||||
// or this function.
|
||||
//
|
||||
// License is also granted to make and use derivative works provided
|
||||
// that such works are identified as "derived from the RSA Data
|
||||
// Security, Inc. MD5 Message-Digest Algorithm" in all material
|
||||
// mentioning or referencing the derived work.
|
||||
//
|
||||
// RSA Data Security, Inc. makes no representations concerning either
|
||||
// the merchantability of this software or the suitability of this
|
||||
// software for any particular purpose. It is provided "as is"
|
||||
// without express or implied warranty of any kind.
|
||||
//
|
||||
// These notices must be retained in any copies of any part of this
|
||||
// documentation and/or software.
|
||||
|
||||
|
||||
|
||||
// The original md5 implementation avoids external libraries.
|
||||
// This version has dependency on stdio.h for file input and
|
||||
// string.h for memcpy.
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
namespace limonp {
|
||||
|
||||
//#pragma region MD5 defines
|
||||
// Constants for MD5Transform routine.
|
||||
#define S11 7
|
||||
#define S12 12
|
||||
#define S13 17
|
||||
#define S14 22
|
||||
#define S21 5
|
||||
#define S22 9
|
||||
#define S23 14
|
||||
#define S24 20
|
||||
#define S31 4
|
||||
#define S32 11
|
||||
#define S33 16
|
||||
#define S34 23
|
||||
#define S41 6
|
||||
#define S42 10
|
||||
#define S43 15
|
||||
#define S44 21
|
||||
|
||||
|
||||
// F, G, H and I are basic MD5 functions.
|
||||
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
|
||||
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
|
||||
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define I(x, y, z) ((y) ^ ((x) | (~z)))
|
||||
|
||||
// ROTATE_LEFT rotates x left n bits.
|
||||
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
|
||||
|
||||
// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
|
||||
// Rotation is separate from addition to prevent recomputation.
|
||||
#define FF(a, b, c, d, x, s, ac) { \
|
||||
(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||
(a) = ROTATE_LEFT ((a), (s)); \
|
||||
(a) += (b); \
|
||||
}
|
||||
#define GG(a, b, c, d, x, s, ac) { \
|
||||
(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||
(a) = ROTATE_LEFT ((a), (s)); \
|
||||
(a) += (b); \
|
||||
}
|
||||
#define HH(a, b, c, d, x, s, ac) { \
|
||||
(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||
(a) = ROTATE_LEFT ((a), (s)); \
|
||||
(a) += (b); \
|
||||
}
|
||||
#define II(a, b, c, d, x, s, ac) { \
|
||||
(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||
(a) = ROTATE_LEFT ((a), (s)); \
|
||||
(a) += (b); \
|
||||
}
|
||||
//#pragma endregion
|
||||
|
||||
|
||||
typedef unsigned char BYTE ;
|
||||
|
||||
// POINTER defines a generic pointer type
|
||||
typedef unsigned char *POINTER;
|
||||
|
||||
// UINT2 defines a two byte word
|
||||
typedef unsigned short int UINT2;
|
||||
|
||||
// UINT4 defines a four byte word
|
||||
typedef unsigned int UINT4;
|
||||
|
||||
static unsigned char PADDING[64] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
// convenient object that wraps
|
||||
// the C-functions for use in C++ only
|
||||
class MD5 {
|
||||
private:
|
||||
struct __context_t {
|
||||
UINT4 state[4]; /* state (ABCD) */
|
||||
UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
|
||||
unsigned char buffer[64]; /* input buffer */
|
||||
} context ;
|
||||
|
||||
//#pragma region static helper functions
|
||||
// The core of the MD5 algorithm is here.
|
||||
// MD5 basic transformation. Transforms state based on block.
|
||||
static void MD5Transform( UINT4 state[4], unsigned char block[64] ) {
|
||||
UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
|
||||
|
||||
Decode (x, block, 64);
|
||||
|
||||
/* Round 1 */
|
||||
FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
|
||||
FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
|
||||
FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
|
||||
FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
|
||||
FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
|
||||
FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
|
||||
FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
|
||||
FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
|
||||
FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
|
||||
FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
|
||||
FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
|
||||
FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
|
||||
FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
|
||||
FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
|
||||
FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
|
||||
FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
|
||||
|
||||
/* Round 2 */
|
||||
GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
|
||||
GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
|
||||
GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
|
||||
GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
|
||||
GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
|
||||
GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
|
||||
GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
|
||||
GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
|
||||
GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
|
||||
GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
|
||||
GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
|
||||
GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
|
||||
GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
|
||||
GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
|
||||
GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
|
||||
GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
|
||||
|
||||
/* Round 3 */
|
||||
HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
|
||||
HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
|
||||
HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
|
||||
HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
|
||||
HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
|
||||
HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
|
||||
HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
|
||||
HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
|
||||
HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
|
||||
HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
|
||||
HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
|
||||
HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
|
||||
HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
|
||||
HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
|
||||
HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
|
||||
HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
|
||||
|
||||
/* Round 4 */
|
||||
II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
|
||||
II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
|
||||
II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
|
||||
II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
|
||||
II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
|
||||
II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
|
||||
II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
|
||||
II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
|
||||
II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
|
||||
II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
|
||||
II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
|
||||
II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
|
||||
II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
|
||||
II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
|
||||
II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
|
||||
II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
|
||||
// Zeroize sensitive information.
|
||||
memset((POINTER)x, 0, sizeof (x));
|
||||
}
|
||||
|
||||
// Encodes input (UINT4) into output (unsigned char). Assumes len is
|
||||
// a multiple of 4.
|
||||
static void Encode( unsigned char *output, UINT4 *input, unsigned int len ) {
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0, j = 0; j < len; i++, j += 4) {
|
||||
output[j] = (unsigned char)(input[i] & 0xff);
|
||||
output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
|
||||
output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
|
||||
output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
// Decodes input (unsigned char) into output (UINT4). Assumes len is
|
||||
// a multiple of 4.
|
||||
static void Decode( UINT4 *output, unsigned char *input, unsigned int len ) {
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0, j = 0; j < len; i++, j += 4)
|
||||
output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
|
||||
(((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
|
||||
}
|
||||
//#pragma endregion
|
||||
|
||||
|
||||
public:
|
||||
// MAIN FUNCTIONS
|
||||
MD5() {
|
||||
Init() ;
|
||||
}
|
||||
|
||||
// MD5 initialization. Begins an MD5 operation, writing a new context.
|
||||
void Init() {
|
||||
context.count[0] = context.count[1] = 0;
|
||||
|
||||
// Load magic initialization constants.
|
||||
context.state[0] = 0x67452301;
|
||||
context.state[1] = 0xefcdab89;
|
||||
context.state[2] = 0x98badcfe;
|
||||
context.state[3] = 0x10325476;
|
||||
}
|
||||
|
||||
// MD5 block update operation. Continues an MD5 message-digest
|
||||
// operation, processing another message block, and updating the
|
||||
// context.
|
||||
void Update(
|
||||
unsigned char *input, // input block
|
||||
unsigned int inputLen ) { // length of input block
|
||||
unsigned int i, index, partLen;
|
||||
|
||||
// Compute number of bytes mod 64
|
||||
index = (unsigned int)((context.count[0] >> 3) & 0x3F);
|
||||
|
||||
// Update number of bits
|
||||
if ((context.count[0] += ((UINT4)inputLen << 3))
|
||||
< ((UINT4)inputLen << 3))
|
||||
context.count[1]++;
|
||||
context.count[1] += ((UINT4)inputLen >> 29);
|
||||
|
||||
partLen = 64 - index;
|
||||
|
||||
// Transform as many times as possible.
|
||||
if (inputLen >= partLen) {
|
||||
memcpy((POINTER)&context.buffer[index], (POINTER)input, partLen);
|
||||
MD5Transform (context.state, context.buffer);
|
||||
|
||||
for (i = partLen; i + 63 < inputLen; i += 64)
|
||||
MD5Transform (context.state, &input[i]);
|
||||
|
||||
index = 0;
|
||||
} else
|
||||
i = 0;
|
||||
|
||||
/* Buffer remaining input */
|
||||
memcpy((POINTER)&context.buffer[index], (POINTER)&input[i], inputLen-i);
|
||||
}
|
||||
|
||||
// MD5 finalization. Ends an MD5 message-digest operation, writing the
|
||||
// the message digest and zeroizing the context.
|
||||
// Writes to digestRaw
|
||||
void Final() {
|
||||
unsigned char bits[8];
|
||||
unsigned int index, padLen;
|
||||
|
||||
// Save number of bits
|
||||
Encode( bits, context.count, 8 );
|
||||
|
||||
// Pad out to 56 mod 64.
|
||||
index = (unsigned int)((context.count[0] >> 3) & 0x3f);
|
||||
padLen = (index < 56) ? (56 - index) : (120 - index);
|
||||
Update( PADDING, padLen );
|
||||
|
||||
// Append length (before padding)
|
||||
Update( bits, 8 );
|
||||
|
||||
// Store state in digest
|
||||
Encode( digestRaw, context.state, 16);
|
||||
|
||||
// Zeroize sensitive information.
|
||||
memset((POINTER)&context, 0, sizeof (context));
|
||||
|
||||
writeToString() ;
|
||||
}
|
||||
|
||||
/// Buffer must be 32+1 (nul) = 33 chars long at least
|
||||
void writeToString() {
|
||||
int pos ;
|
||||
|
||||
for( pos = 0 ; pos < 16 ; pos++ )
|
||||
sprintf( digestChars+(pos*2), "%02x", digestRaw[pos] ) ;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
// an MD5 digest is a 16-byte number (32 hex digits)
|
||||
BYTE digestRaw[ 16 ] ;
|
||||
|
||||
// This version of the digest is actually
|
||||
// a "printf'd" version of the digest.
|
||||
char digestChars[ 33 ] ;
|
||||
|
||||
/// Load a file from disk and digest it
|
||||
// Digests a file and returns the result.
|
||||
const char* digestFile( const char *filename ) {
|
||||
if (NULL == filename || strcmp(filename, "") == 0)
|
||||
return NULL;
|
||||
|
||||
Init() ;
|
||||
|
||||
FILE *file;
|
||||
|
||||
unsigned char buffer[1024] ;
|
||||
|
||||
if((file = fopen (filename, "rb")) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
int len;
|
||||
while( (len = fread( buffer, 1, 1024, file )) )
|
||||
Update( buffer, len ) ;
|
||||
Final();
|
||||
|
||||
fclose( file );
|
||||
|
||||
return digestChars ;
|
||||
}
|
||||
|
||||
/// Digests a byte-array already in memory
|
||||
const char* digestMemory( BYTE *memchunk, int len ) {
|
||||
if (NULL == memchunk)
|
||||
return NULL;
|
||||
|
||||
Init() ;
|
||||
Update( memchunk, len ) ;
|
||||
Final() ;
|
||||
|
||||
return digestChars ;
|
||||
}
|
||||
|
||||
// Digests a string and prints the result.
|
||||
const char* digestString(const char *string ) {
|
||||
if (string == NULL)
|
||||
return NULL;
|
||||
|
||||
Init() ;
|
||||
Update( (unsigned char*)string, strlen(string) ) ;
|
||||
Final() ;
|
||||
|
||||
return digestChars ;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool md5String(const char* str, std::string& res) {
|
||||
if (NULL == str) {
|
||||
res = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
MD5 md5;
|
||||
const char *pRes = md5.digestString(str);
|
||||
if (NULL == pRes) {
|
||||
res = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
res = pRes;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool md5File(const char* filepath, std::string& res) {
|
||||
if (NULL == filepath || strcmp(filepath, "") == 0) {
|
||||
res = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
MD5 md5;
|
||||
const char *pRes = md5.digestFile(filepath);
|
||||
|
||||
if (NULL == pRes) {
|
||||
res = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
res = pRes;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef LIMONP_MUTEX_LOCK_HPP
|
||||
#define LIMONP_MUTEX_LOCK_HPP
|
||||
|
||||
#include <pthread.h>
|
||||
#include "NonCopyable.hpp"
|
||||
#include "Logging.hpp"
|
||||
|
||||
namespace limonp {
|
||||
|
||||
class MutexLock: NonCopyable {
|
||||
public:
|
||||
MutexLock() {
|
||||
XCHECK(!pthread_mutex_init(&mutex_, NULL));
|
||||
}
|
||||
~MutexLock() {
|
||||
XCHECK(!pthread_mutex_destroy(&mutex_));
|
||||
}
|
||||
pthread_mutex_t* GetPthreadMutex() {
|
||||
return &mutex_;
|
||||
}
|
||||
|
||||
private:
|
||||
void Lock() {
|
||||
XCHECK(!pthread_mutex_lock(&mutex_));
|
||||
}
|
||||
void Unlock() {
|
||||
XCHECK(!pthread_mutex_unlock(&mutex_));
|
||||
}
|
||||
friend class MutexLockGuard;
|
||||
|
||||
pthread_mutex_t mutex_;
|
||||
}; // class MutexLock
|
||||
|
||||
class MutexLockGuard: NonCopyable {
|
||||
public:
|
||||
explicit MutexLockGuard(MutexLock & mutex)
|
||||
: mutex_(mutex) {
|
||||
mutex_.Lock();
|
||||
}
|
||||
~MutexLockGuard() {
|
||||
mutex_.Unlock();
|
||||
}
|
||||
private:
|
||||
MutexLock & mutex_;
|
||||
}; // class MutexLockGuard
|
||||
|
||||
#define MutexLockGuard(x) XCHECK(false);
|
||||
|
||||
} // namespace limonp
|
||||
|
||||
#endif // LIMONP_MUTEX_LOCK_HPP
|
||||
@@ -0,0 +1,21 @@
|
||||
/************************************
|
||||
************************************/
|
||||
#ifndef LIMONP_NONCOPYABLE_H
|
||||
#define LIMONP_NONCOPYABLE_H
|
||||
|
||||
namespace limonp {
|
||||
|
||||
class NonCopyable {
|
||||
protected:
|
||||
NonCopyable() {
|
||||
}
|
||||
~NonCopyable() {
|
||||
}
|
||||
private:
|
||||
NonCopyable(const NonCopyable& );
|
||||
const NonCopyable& operator=(const NonCopyable& );
|
||||
}; // class NonCopyable
|
||||
|
||||
} // namespace limonp
|
||||
|
||||
#endif // LIMONP_NONCOPYABLE_H
|
||||
@@ -0,0 +1,157 @@
|
||||
#ifndef LIMONP_STD_EXTEMSION_HPP
|
||||
#define LIMONP_STD_EXTEMSION_HPP
|
||||
|
||||
#include <map>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#elif(__cplusplus >= 201103L)
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#elif defined _MSC_VER
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#else
|
||||
#include <tr1/unordered_map>
|
||||
#include <tr1/unordered_set>
|
||||
namespace std {
|
||||
using std::tr1::unordered_map;
|
||||
using std::tr1::unordered_set;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
namespace std {
|
||||
|
||||
template<typename T>
|
||||
ostream& operator << (ostream& os, const vector<T>& v) {
|
||||
if(v.empty()) {
|
||||
return os << "[]";
|
||||
}
|
||||
os<<"["<<v[0];
|
||||
for(size_t i = 1; i < v.size(); i++) {
|
||||
os<<", "<<v[i];
|
||||
}
|
||||
os<<"]";
|
||||
return os;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline ostream& operator << (ostream& os, const vector<string>& v) {
|
||||
if(v.empty()) {
|
||||
return os << "[]";
|
||||
}
|
||||
os<<"[\""<<v[0];
|
||||
for(size_t i = 1; i < v.size(); i++) {
|
||||
os<<"\", \""<<v[i];
|
||||
}
|
||||
os<<"\"]";
|
||||
return os;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ostream& operator << (ostream& os, const deque<T>& dq) {
|
||||
if(dq.empty()) {
|
||||
return os << "[]";
|
||||
}
|
||||
os<<"[\""<<dq[0];
|
||||
for(size_t i = 1; i < dq.size(); i++) {
|
||||
os<<"\", \""<<dq[i];
|
||||
}
|
||||
os<<"\"]";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
template<class T1, class T2>
|
||||
ostream& operator << (ostream& os, const pair<T1, T2>& pr) {
|
||||
os << pr.first << ":" << pr.second ;
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
template<class T>
|
||||
string& operator << (string& str, const T& obj) {
|
||||
stringstream ss;
|
||||
ss << obj; // call ostream& operator << (ostream& os,
|
||||
return str = ss.str();
|
||||
}
|
||||
|
||||
template<class T1, class T2>
|
||||
ostream& operator << (ostream& os, const map<T1, T2>& mp) {
|
||||
if(mp.empty()) {
|
||||
os<<"{}";
|
||||
return os;
|
||||
}
|
||||
os<<'{';
|
||||
typename map<T1, T2>::const_iterator it = mp.begin();
|
||||
os<<*it;
|
||||
it++;
|
||||
while(it != mp.end()) {
|
||||
os<<", "<<*it;
|
||||
it++;
|
||||
}
|
||||
os<<'}';
|
||||
return os;
|
||||
}
|
||||
template<class T1, class T2>
|
||||
ostream& operator << (ostream& os, const std::unordered_map<T1, T2>& mp) {
|
||||
if(mp.empty()) {
|
||||
return os << "{}";
|
||||
}
|
||||
os<<'{';
|
||||
typename std::unordered_map<T1, T2>::const_iterator it = mp.begin();
|
||||
os<<*it;
|
||||
it++;
|
||||
while(it != mp.end()) {
|
||||
os<<", "<<*it++;
|
||||
}
|
||||
return os<<'}';
|
||||
}
|
||||
|
||||
template<class T>
|
||||
ostream& operator << (ostream& os, const set<T>& st) {
|
||||
if(st.empty()) {
|
||||
os << "{}";
|
||||
return os;
|
||||
}
|
||||
os<<'{';
|
||||
typename set<T>::const_iterator it = st.begin();
|
||||
os<<*it;
|
||||
it++;
|
||||
while(it != st.end()) {
|
||||
os<<", "<<*it;
|
||||
it++;
|
||||
}
|
||||
os<<'}';
|
||||
return os;
|
||||
}
|
||||
|
||||
template<class KeyType, class ContainType>
|
||||
bool IsIn(const ContainType& contain, const KeyType& key) {
|
||||
return contain.end() != contain.find(key);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
basic_string<T> & operator << (basic_string<T> & s, ifstream & ifs) {
|
||||
return s.assign((istreambuf_iterator<T>(ifs)), istreambuf_iterator<T>());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
ofstream & operator << (ofstream & ofs, const basic_string<T>& s) {
|
||||
ostreambuf_iterator<T> itr (ofs);
|
||||
copy(s.begin(), s.end(), itr);
|
||||
return ofs;
|
||||
}
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,365 @@
|
||||
/************************************
|
||||
* file enc : ascii
|
||||
* author : wuyanyi09@gmail.com
|
||||
************************************/
|
||||
#ifndef LIMONP_STR_FUNCTS_H
|
||||
#define LIMONP_STR_FUNCTS_H
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <memory.h>
|
||||
#include <functional>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
#include <sys/types.h>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include "StdExtension.hpp"
|
||||
|
||||
namespace limonp {
|
||||
using namespace std;
|
||||
inline string StringFormat(const char* fmt, ...) {
|
||||
int size = 256;
|
||||
std::string str;
|
||||
va_list ap;
|
||||
while (1) {
|
||||
str.resize(size);
|
||||
va_start(ap, fmt);
|
||||
int n = vsnprintf((char *)str.c_str(), size, fmt, ap);
|
||||
va_end(ap);
|
||||
if (n > -1 && n < size) {
|
||||
str.resize(n);
|
||||
return str;
|
||||
}
|
||||
if (n > -1)
|
||||
size = n + 1;
|
||||
else
|
||||
size *= 2;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Join(T begin, T end, string& res, const string& connector) {
|
||||
if(begin == end) {
|
||||
return;
|
||||
}
|
||||
stringstream ss;
|
||||
ss<<*begin;
|
||||
begin++;
|
||||
while(begin != end) {
|
||||
ss << connector << *begin;
|
||||
begin ++;
|
||||
}
|
||||
res = ss.str();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
string Join(T begin, T end, const string& connector) {
|
||||
string res;
|
||||
Join(begin ,end, res, connector);
|
||||
return res;
|
||||
}
|
||||
|
||||
inline string& Upper(string& str) {
|
||||
transform(str.begin(), str.end(), str.begin(), (int (*)(int))toupper);
|
||||
return str;
|
||||
}
|
||||
|
||||
inline string& Lower(string& str) {
|
||||
transform(str.begin(), str.end(), str.begin(), (int (*)(int))tolower);
|
||||
return str;
|
||||
}
|
||||
|
||||
inline bool IsSpace(unsigned c) {
|
||||
// when passing large int as the argument of isspace, it core dump, so here need a type cast.
|
||||
return c > 0xff ? false : std::isspace(c & 0xff) != 0;
|
||||
}
|
||||
|
||||
inline std::string& LTrim(std::string &s) {
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<unsigned, bool>(IsSpace))));
|
||||
return s;
|
||||
}
|
||||
|
||||
inline std::string& RTrim(std::string &s) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<unsigned, bool>(IsSpace))).base(), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
inline std::string& Trim(std::string &s) {
|
||||
return LTrim(RTrim(s));
|
||||
}
|
||||
|
||||
inline std::string& LTrim(std::string & s, char x) {
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::bind2nd(std::equal_to<char>(), x))));
|
||||
return s;
|
||||
}
|
||||
|
||||
inline std::string& RTrim(std::string & s, char x) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::bind2nd(std::equal_to<char>(), x))).base(), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
inline std::string& Trim(std::string &s, char x) {
|
||||
return LTrim(RTrim(s, x), x);
|
||||
}
|
||||
|
||||
inline void Split(const string& src, vector<string>& res, const string& pattern, size_t maxsplit = string::npos) {
|
||||
res.clear();
|
||||
size_t Start = 0;
|
||||
size_t end = 0;
|
||||
string sub;
|
||||
while(Start < src.size()) {
|
||||
end = src.find_first_of(pattern, Start);
|
||||
if(string::npos == end || res.size() >= maxsplit) {
|
||||
sub = src.substr(Start);
|
||||
res.push_back(sub);
|
||||
return;
|
||||
}
|
||||
sub = src.substr(Start, end - Start);
|
||||
res.push_back(sub);
|
||||
Start = end + 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
inline vector<string> Split(const string& src, const string& pattern, size_t maxsplit = string::npos) {
|
||||
vector<string> res;
|
||||
Split(src, res, pattern, maxsplit);
|
||||
return res;
|
||||
}
|
||||
|
||||
inline bool StartsWith(const string& str, const string& prefix) {
|
||||
if(prefix.length() > str.length()) {
|
||||
return false;
|
||||
}
|
||||
return 0 == str.compare(0, prefix.length(), prefix);
|
||||
}
|
||||
|
||||
inline bool EndsWith(const string& str, const string& suffix) {
|
||||
if(suffix.length() > str.length()) {
|
||||
return false;
|
||||
}
|
||||
return 0 == str.compare(str.length() - suffix.length(), suffix.length(), suffix);
|
||||
}
|
||||
|
||||
inline bool IsInStr(const string& str, char ch) {
|
||||
return str.find(ch) != string::npos;
|
||||
}
|
||||
|
||||
inline uint16_t TwocharToUint16(char high, char low) {
|
||||
return (((uint16_t(high) & 0x00ff ) << 8) | (uint16_t(low) & 0x00ff));
|
||||
}
|
||||
|
||||
template <class Uint16Container>
|
||||
bool Utf8ToUnicode(const char * const str, size_t len, Uint16Container& vec) {
|
||||
if(!str) {
|
||||
return false;
|
||||
}
|
||||
char ch1, ch2;
|
||||
uint16_t tmp;
|
||||
vec.clear();
|
||||
for(size_t i = 0; i < len;) {
|
||||
if(!(str[i] & 0x80)) { // 0xxxxxxx
|
||||
vec.push_back(str[i]);
|
||||
i++;
|
||||
} else if ((uint8_t)str[i] <= 0xdf && i + 1 < len) { // 110xxxxxx
|
||||
ch1 = (str[i] >> 2) & 0x07;
|
||||
ch2 = (str[i+1] & 0x3f) | ((str[i] & 0x03) << 6 );
|
||||
tmp = (((uint16_t(ch1) & 0x00ff ) << 8) | (uint16_t(ch2) & 0x00ff));
|
||||
vec.push_back(tmp);
|
||||
i += 2;
|
||||
} else if((uint8_t)str[i] <= 0xef && i + 2 < len) {
|
||||
ch1 = ((uint8_t)str[i] << 4) | ((str[i+1] >> 2) & 0x0f );
|
||||
ch2 = (((uint8_t)str[i+1]<<6) & 0xc0) | (str[i+2] & 0x3f);
|
||||
tmp = (((uint16_t(ch1) & 0x00ff ) << 8) | (uint16_t(ch2) & 0x00ff));
|
||||
vec.push_back(tmp);
|
||||
i += 3;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Uint16Container>
|
||||
bool Utf8ToUnicode(const string& str, Uint16Container& vec) {
|
||||
return Utf8ToUnicode(str.c_str(), str.size(), vec);
|
||||
}
|
||||
|
||||
template <class Uint32Container>
|
||||
bool Utf8ToUnicode32(const string& str, Uint32Container& vec) {
|
||||
uint32_t tmp;
|
||||
vec.clear();
|
||||
for(size_t i = 0; i < str.size();) {
|
||||
if(!(str[i] & 0x80)) { // 0xxxxxxx
|
||||
// 7bit, total 7bit
|
||||
tmp = (uint8_t)(str[i]) & 0x7f;
|
||||
i++;
|
||||
} else if ((uint8_t)str[i] <= 0xdf && i + 1 < str.size()) { // 110xxxxxx
|
||||
// 5bit, total 5bit
|
||||
tmp = (uint8_t)(str[i]) & 0x1f;
|
||||
|
||||
// 6bit, total 11bit
|
||||
tmp <<= 6;
|
||||
tmp |= (uint8_t)(str[i+1]) & 0x3f;
|
||||
i += 2;
|
||||
} else if((uint8_t)str[i] <= 0xef && i + 2 < str.size()) { // 1110xxxxxx
|
||||
// 4bit, total 4bit
|
||||
tmp = (uint8_t)(str[i]) & 0x0f;
|
||||
|
||||
// 6bit, total 10bit
|
||||
tmp <<= 6;
|
||||
tmp |= (uint8_t)(str[i+1]) & 0x3f;
|
||||
|
||||
// 6bit, total 16bit
|
||||
tmp <<= 6;
|
||||
tmp |= (uint8_t)(str[i+2]) & 0x3f;
|
||||
|
||||
i += 3;
|
||||
} else if((uint8_t)str[i] <= 0xf7 && i + 3 < str.size()) { // 11110xxxx
|
||||
// 3bit, total 3bit
|
||||
tmp = (uint8_t)(str[i]) & 0x07;
|
||||
|
||||
// 6bit, total 9bit
|
||||
tmp <<= 6;
|
||||
tmp |= (uint8_t)(str[i+1]) & 0x3f;
|
||||
|
||||
// 6bit, total 15bit
|
||||
tmp <<= 6;
|
||||
tmp |= (uint8_t)(str[i+2]) & 0x3f;
|
||||
|
||||
// 6bit, total 21bit
|
||||
tmp <<= 6;
|
||||
tmp |= (uint8_t)(str[i+3]) & 0x3f;
|
||||
|
||||
i += 4;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
vec.push_back(tmp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Uint32ContainerConIter>
|
||||
void Unicode32ToUtf8(Uint32ContainerConIter begin, Uint32ContainerConIter end, string& res) {
|
||||
res.clear();
|
||||
uint32_t ui;
|
||||
while(begin != end) {
|
||||
ui = *begin;
|
||||
if(ui <= 0x7f) {
|
||||
res += char(ui);
|
||||
} else if(ui <= 0x7ff) {
|
||||
res += char(((ui >> 6) & 0x1f) | 0xc0);
|
||||
res += char((ui & 0x3f) | 0x80);
|
||||
} else if(ui <= 0xffff) {
|
||||
res += char(((ui >> 12) & 0x0f) | 0xe0);
|
||||
res += char(((ui >> 6) & 0x3f) | 0x80);
|
||||
res += char((ui & 0x3f) | 0x80);
|
||||
} else {
|
||||
res += char(((ui >> 18) & 0x03) | 0xf0);
|
||||
res += char(((ui >> 12) & 0x3f) | 0x80);
|
||||
res += char(((ui >> 6) & 0x3f) | 0x80);
|
||||
res += char((ui & 0x3f) | 0x80);
|
||||
}
|
||||
begin ++;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Uint16ContainerConIter>
|
||||
void UnicodeToUtf8(Uint16ContainerConIter begin, Uint16ContainerConIter end, string& res) {
|
||||
res.clear();
|
||||
uint16_t ui;
|
||||
while(begin != end) {
|
||||
ui = *begin;
|
||||
if(ui <= 0x7f) {
|
||||
res += char(ui);
|
||||
} else if(ui <= 0x7ff) {
|
||||
res += char(((ui>>6) & 0x1f) | 0xc0);
|
||||
res += char((ui & 0x3f) | 0x80);
|
||||
} else {
|
||||
res += char(((ui >> 12) & 0x0f )| 0xe0);
|
||||
res += char(((ui>>6) & 0x3f )| 0x80 );
|
||||
res += char((ui & 0x3f) | 0x80);
|
||||
}
|
||||
begin ++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <class Uint16Container>
|
||||
bool GBKTrans(const char* const str, size_t len, Uint16Container& vec) {
|
||||
vec.clear();
|
||||
if(!str) {
|
||||
return true;
|
||||
}
|
||||
size_t i = 0;
|
||||
while(i < len) {
|
||||
if(0 == (str[i] & 0x80)) {
|
||||
vec.push_back(uint16_t(str[i]));
|
||||
i++;
|
||||
} else {
|
||||
if(i + 1 < len) { //&& (str[i+1] & 0x80))
|
||||
uint16_t tmp = (((uint16_t(str[i]) & 0x00ff ) << 8) | (uint16_t(str[i+1]) & 0x00ff));
|
||||
vec.push_back(tmp);
|
||||
i += 2;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Uint16Container>
|
||||
bool GBKTrans(const string& str, Uint16Container& vec) {
|
||||
return GBKTrans(str.c_str(), str.size(), vec);
|
||||
}
|
||||
|
||||
template <class Uint16ContainerConIter>
|
||||
void GBKTrans(Uint16ContainerConIter begin, Uint16ContainerConIter end, string& res) {
|
||||
res.clear();
|
||||
//pair<char, char> pa;
|
||||
char first, second;
|
||||
while(begin != end) {
|
||||
//pa = uint16ToChar2(*begin);
|
||||
first = ((*begin)>>8) & 0x00ff;
|
||||
second = (*begin) & 0x00ff;
|
||||
if(first & 0x80) {
|
||||
res += first;
|
||||
res += second;
|
||||
} else {
|
||||
res += second;
|
||||
}
|
||||
begin++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* format example: "%Y-%m-%d %H:%M:%S"
|
||||
*/
|
||||
inline void GetTime(const string& format, string& timeStr) {
|
||||
time_t timeNow;
|
||||
time(&timeNow);
|
||||
timeStr.resize(64);
|
||||
size_t len = strftime((char*)timeStr.c_str(), timeStr.size(), format.c_str(), localtime(&timeNow));
|
||||
timeStr.resize(len);
|
||||
}
|
||||
|
||||
inline string PathJoin(const string& path1, const string& path2) {
|
||||
if(EndsWith(path1, "/")) {
|
||||
return path1 + path2;
|
||||
}
|
||||
return path1 + "/" + path2;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef LIMONP_THREAD_HPP
|
||||
#define LIMONP_THREAD_HPP
|
||||
|
||||
#include "Logging.hpp"
|
||||
#include "NonCopyable.hpp"
|
||||
|
||||
namespace limonp {
|
||||
|
||||
class IThread: NonCopyable {
|
||||
public:
|
||||
IThread(): isStarted(false), isJoined(false) {
|
||||
}
|
||||
virtual ~IThread() {
|
||||
if(isStarted && !isJoined) {
|
||||
XCHECK(!pthread_detach(thread_));
|
||||
}
|
||||
};
|
||||
|
||||
virtual void Run() = 0;
|
||||
void Start() {
|
||||
XCHECK(!isStarted);
|
||||
XCHECK(!pthread_create(&thread_, NULL, Worker, this));
|
||||
isStarted = true;
|
||||
}
|
||||
void Join() {
|
||||
XCHECK(!isJoined);
|
||||
XCHECK(!pthread_join(thread_, NULL));
|
||||
isJoined = true;
|
||||
}
|
||||
private:
|
||||
static void * Worker(void * data) {
|
||||
IThread * ptr = (IThread* ) data;
|
||||
ptr->Run();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_t thread_;
|
||||
bool isStarted;
|
||||
bool isJoined;
|
||||
}; // class IThread
|
||||
|
||||
} // namespace limonp
|
||||
|
||||
#endif // LIMONP_THREAD_HPP
|
||||
@@ -0,0 +1,86 @@
|
||||
#ifndef LIMONP_THREAD_POOL_HPP
|
||||
#define LIMONP_THREAD_POOL_HPP
|
||||
|
||||
#include "Thread.hpp"
|
||||
#include "BlockingQueue.hpp"
|
||||
#include "BoundedBlockingQueue.hpp"
|
||||
#include "Closure.hpp"
|
||||
|
||||
namespace limonp {
|
||||
|
||||
using namespace std;
|
||||
|
||||
//class ThreadPool;
|
||||
class ThreadPool: NonCopyable {
|
||||
public:
|
||||
class Worker: public IThread {
|
||||
public:
|
||||
Worker(ThreadPool* pool): ptThreadPool_(pool) {
|
||||
assert(ptThreadPool_);
|
||||
}
|
||||
virtual ~Worker() {
|
||||
}
|
||||
|
||||
virtual void Run() {
|
||||
while (true) {
|
||||
ClosureInterface* closure = ptThreadPool_->queue_.Pop();
|
||||
if (closure == NULL) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
closure->Run();
|
||||
} catch(std::exception& e) {
|
||||
XLOG(ERROR) << e.what();
|
||||
} catch(...) {
|
||||
XLOG(ERROR) << " unknown exception.";
|
||||
}
|
||||
delete closure;
|
||||
}
|
||||
}
|
||||
private:
|
||||
ThreadPool * ptThreadPool_;
|
||||
}; // class Worker
|
||||
|
||||
ThreadPool(size_t thread_num)
|
||||
: threads_(thread_num),
|
||||
queue_(thread_num) {
|
||||
assert(thread_num);
|
||||
for(size_t i = 0; i < threads_.size(); i ++) {
|
||||
threads_[i] = new Worker(this);
|
||||
}
|
||||
}
|
||||
~ThreadPool() {
|
||||
Stop();
|
||||
}
|
||||
|
||||
void Start() {
|
||||
for(size_t i = 0; i < threads_.size(); i++) {
|
||||
threads_[i]->Start();
|
||||
}
|
||||
}
|
||||
void Stop() {
|
||||
for(size_t i = 0; i < threads_.size(); i ++) {
|
||||
queue_.Push(NULL);
|
||||
}
|
||||
for(size_t i = 0; i < threads_.size(); i ++) {
|
||||
threads_[i]->Join();
|
||||
delete threads_[i];
|
||||
}
|
||||
threads_.clear();
|
||||
}
|
||||
|
||||
void Add(ClosureInterface* task) {
|
||||
assert(task);
|
||||
queue_.Push(task);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Worker;
|
||||
|
||||
vector<IThread*> threads_;
|
||||
BoundedBlockingQueue<ClosureInterface*> queue_;
|
||||
}; // class ThreadPool
|
||||
|
||||
} // namespace limonp
|
||||
|
||||
#endif // LIMONP_THREAD_POOL_HPP
|
||||
99
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/async.h
Normal file
99
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/async.h
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Async logging using global thread pool
|
||||
// All loggers created here share same global thread pool.
|
||||
// Each log message is pushed to a queue along with a shared pointer to the
|
||||
// logger.
|
||||
// If a logger deleted while having pending messages in the queue, it's actual
|
||||
// destruction will defer
|
||||
// until all its messages are processed by the thread pool.
|
||||
// This is because each message in the queue holds a shared_ptr to the
|
||||
// originating logger.
|
||||
|
||||
#include <spdlog/async_logger.h>
|
||||
#include <spdlog/details/registry.h>
|
||||
#include <spdlog/details/thread_pool.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
namespace details {
|
||||
static const size_t default_async_q_size = 8192;
|
||||
}
|
||||
|
||||
// async logger factory - creates async loggers backed with thread pool.
|
||||
// if a global thread pool doesn't already exist, create it with default queue
|
||||
// size of 8192 items and single thread.
|
||||
template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
|
||||
struct async_factory_impl
|
||||
{
|
||||
template<typename Sink, typename... SinkArgs>
|
||||
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args)
|
||||
{
|
||||
auto ®istry_inst = details::registry::instance();
|
||||
|
||||
// create global thread pool if not already exists..
|
||||
|
||||
auto &mutex = registry_inst.tp_mutex();
|
||||
std::lock_guard<std::recursive_mutex> tp_lock(mutex);
|
||||
auto tp = registry_inst.get_tp();
|
||||
if (tp == nullptr)
|
||||
{
|
||||
tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1U);
|
||||
registry_inst.set_tp(tp);
|
||||
}
|
||||
|
||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
||||
auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy);
|
||||
registry_inst.initialize_logger(new_logger);
|
||||
return new_logger;
|
||||
}
|
||||
};
|
||||
|
||||
using async_factory = async_factory_impl<async_overflow_policy::block>;
|
||||
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
|
||||
|
||||
template<typename Sink, typename... SinkArgs>
|
||||
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args)
|
||||
{
|
||||
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||
}
|
||||
|
||||
template<typename Sink, typename... SinkArgs>
|
||||
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args)
|
||||
{
|
||||
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||
}
|
||||
|
||||
// set global thread pool.
|
||||
inline void init_thread_pool(
|
||||
size_t q_size, size_t thread_count, std::function<void()> on_thread_start, std::function<void()> on_thread_stop)
|
||||
{
|
||||
auto tp = std::make_shared<details::thread_pool>(q_size, thread_count, on_thread_start, on_thread_stop);
|
||||
details::registry::instance().set_tp(std::move(tp));
|
||||
}
|
||||
|
||||
inline void init_thread_pool(size_t q_size, size_t thread_count, std::function<void()> on_thread_start)
|
||||
{
|
||||
init_thread_pool(q_size, thread_count, on_thread_start, [] {});
|
||||
}
|
||||
|
||||
inline void init_thread_pool(size_t q_size, size_t thread_count)
|
||||
{
|
||||
init_thread_pool(
|
||||
q_size, thread_count, [] {}, [] {});
|
||||
}
|
||||
|
||||
// get the global thread pool.
|
||||
inline std::shared_ptr<spdlog::details::thread_pool> thread_pool()
|
||||
{
|
||||
return details::registry::instance().get_tp();
|
||||
}
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
# include <spdlog/async_logger.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/sinks/sink.h>
|
||||
#include <spdlog/details/thread_pool.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
SPDLOG_INLINE spdlog::async_logger::async_logger(
|
||||
std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
||||
: async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy)
|
||||
{}
|
||||
|
||||
SPDLOG_INLINE spdlog::async_logger::async_logger(
|
||||
std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
||||
: async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy)
|
||||
{}
|
||||
|
||||
// send the log message to the thread pool
|
||||
SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg)
|
||||
{
|
||||
if (auto pool_ptr = thread_pool_.lock())
|
||||
{
|
||||
pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw_spdlog_ex("async log: thread pool doesn't exist anymore");
|
||||
}
|
||||
}
|
||||
|
||||
// send flush request to the thread pool
|
||||
SPDLOG_INLINE void spdlog::async_logger::flush_()
|
||||
{
|
||||
if (auto pool_ptr = thread_pool_.lock())
|
||||
{
|
||||
pool_ptr->post_flush(shared_from_this(), overflow_policy_);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw_spdlog_ex("async flush: thread pool doesn't exist anymore");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// backend functions - called from the thread pool to do the actual job
|
||||
//
|
||||
SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg)
|
||||
{
|
||||
for (auto &sink : sinks_)
|
||||
{
|
||||
if (sink->should_log(msg.level))
|
||||
{
|
||||
SPDLOG_TRY
|
||||
{
|
||||
sink->log(msg);
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH(msg.source)
|
||||
}
|
||||
}
|
||||
|
||||
if (should_flush_(msg))
|
||||
{
|
||||
backend_flush_();
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void spdlog::async_logger::backend_flush_()
|
||||
{
|
||||
for (auto &sink : sinks_)
|
||||
{
|
||||
SPDLOG_TRY
|
||||
{
|
||||
sink->flush();
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH(source_loc())
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name)
|
||||
{
|
||||
auto cloned = std::make_shared<spdlog::async_logger>(*this);
|
||||
cloned->name_ = std::move(new_name);
|
||||
return cloned;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// Fast asynchronous logger.
|
||||
// Uses pre allocated queue.
|
||||
// Creates a single back thread to pop messages from the queue and log them.
|
||||
//
|
||||
// Upon each log write the logger:
|
||||
// 1. Checks if its log level is enough to log the message
|
||||
// 2. Push a new copy of the message to a queue (or block the caller until
|
||||
// space is available in the queue)
|
||||
// Upon destruction, logs all remaining messages in the queue before
|
||||
// destructing..
|
||||
|
||||
#include <spdlog/logger.h>
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
// Async overflow policy - block by default.
|
||||
enum class async_overflow_policy
|
||||
{
|
||||
block, // Block until message can be enqueued
|
||||
overrun_oldest // Discard oldest message in the queue if full when trying to
|
||||
// add new item.
|
||||
};
|
||||
|
||||
namespace details {
|
||||
class thread_pool;
|
||||
}
|
||||
|
||||
class SPDLOG_API async_logger final : public std::enable_shared_from_this<async_logger>, public logger
|
||||
{
|
||||
friend class details::thread_pool;
|
||||
|
||||
public:
|
||||
template<typename It>
|
||||
async_logger(std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp,
|
||||
async_overflow_policy overflow_policy = async_overflow_policy::block)
|
||||
: logger(std::move(logger_name), begin, end)
|
||||
, thread_pool_(std::move(tp))
|
||||
, overflow_policy_(overflow_policy)
|
||||
{}
|
||||
|
||||
async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp,
|
||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
||||
|
||||
async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp,
|
||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
||||
|
||||
std::shared_ptr<logger> clone(std::string new_name) override;
|
||||
|
||||
protected:
|
||||
void sink_it_(const details::log_msg &msg) override;
|
||||
void flush_() override;
|
||||
void backend_sink_it_(const details::log_msg &incoming_log_msg);
|
||||
void backend_flush_();
|
||||
|
||||
private:
|
||||
std::weak_ptr<details::thread_pool> thread_pool_;
|
||||
async_overflow_policy overflow_policy_;
|
||||
};
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
# include "async_logger-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
#include <spdlog/cfg/helpers.h>
|
||||
#include <spdlog/details/registry.h>
|
||||
|
||||
//
|
||||
// Init log levels using each argv entry that starts with "SPDLOG_LEVEL="
|
||||
//
|
||||
// set all loggers to debug level:
|
||||
// example.exe "SPDLOG_LEVEL=debug"
|
||||
|
||||
// set logger1 to trace level
|
||||
// example.exe "SPDLOG_LEVEL=logger1=trace"
|
||||
|
||||
// turn off all logging except for logger1 and logger2:
|
||||
// example.exe "SPDLOG_LEVEL=off,logger1=debug,logger2=info"
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
|
||||
// search for SPDLOG_LEVEL= in the args and use it to init the levels
|
||||
inline void load_argv_levels(int argc, const char **argv)
|
||||
{
|
||||
const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
|
||||
for (int i = 1; i < argc; i++)
|
||||
{
|
||||
std::string arg = argv[i];
|
||||
if (arg.find(spdlog_level_prefix) == 0)
|
||||
{
|
||||
auto levels_string = arg.substr(spdlog_level_prefix.size());
|
||||
helpers::load_levels(levels_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void load_argv_levels(int argc, char **argv)
|
||||
{
|
||||
load_argv_levels(argc, const_cast<const char **>(argv));
|
||||
}
|
||||
|
||||
} // namespace cfg
|
||||
} // namespace spdlog
|
||||
38
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/cfg/env.h
Normal file
38
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/cfg/env.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
#include <spdlog/cfg/helpers.h>
|
||||
#include <spdlog/details/registry.h>
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
//
|
||||
// Init levels and patterns from env variables SPDLOG_LEVEL
|
||||
// Inspired from Rust's "env_logger" crate (https://crates.io/crates/env_logger).
|
||||
// Note - fallback to "info" level on unrecognized levels
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// set global level to debug:
|
||||
// export SPDLOG_LEVEL=debug
|
||||
//
|
||||
// turn off all logging except for logger1:
|
||||
// export SPDLOG_LEVEL="*=off,logger1=debug"
|
||||
//
|
||||
|
||||
// turn off all logging except for logger1 and logger2:
|
||||
// export SPDLOG_LEVEL="off,logger1=debug,logger2=info"
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
inline void load_env_levels()
|
||||
{
|
||||
auto env_val = details::os::getenv("SPDLOG_LEVEL");
|
||||
if (!env_val.empty())
|
||||
{
|
||||
helpers::load_levels(env_val);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cfg
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
# include <spdlog/cfg/helpers.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/details/registry.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <sstream>
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
namespace helpers {
|
||||
|
||||
// inplace convert to lowercase
|
||||
inline std::string &to_lower_(std::string &str)
|
||||
{
|
||||
std::transform(
|
||||
str.begin(), str.end(), str.begin(), [](char ch) { return static_cast<char>((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch); });
|
||||
return str;
|
||||
}
|
||||
|
||||
// inplace trim spaces
|
||||
inline std::string &trim_(std::string &str)
|
||||
{
|
||||
const char *spaces = " \n\r\t";
|
||||
str.erase(str.find_last_not_of(spaces) + 1);
|
||||
str.erase(0, str.find_first_not_of(spaces));
|
||||
return str;
|
||||
}
|
||||
|
||||
// return (name,value) trimmed pair from given "name=value" string.
|
||||
// return empty string on missing parts
|
||||
// "key=val" => ("key", "val")
|
||||
// " key = val " => ("key", "val")
|
||||
// "key=" => ("key", "")
|
||||
// "val" => ("", "val")
|
||||
|
||||
inline std::pair<std::string, std::string> extract_kv_(char sep, const std::string &str)
|
||||
{
|
||||
auto n = str.find(sep);
|
||||
std::string k, v;
|
||||
if (n == std::string::npos)
|
||||
{
|
||||
v = str;
|
||||
}
|
||||
else
|
||||
{
|
||||
k = str.substr(0, n);
|
||||
v = str.substr(n + 1);
|
||||
}
|
||||
return std::make_pair(trim_(k), trim_(v));
|
||||
}
|
||||
|
||||
// return vector of key/value pairs from sequence of "K1=V1,K2=V2,.."
|
||||
// "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
|
||||
inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str)
|
||||
{
|
||||
std::string token;
|
||||
std::istringstream token_stream(str);
|
||||
std::unordered_map<std::string, std::string> rv{};
|
||||
while (std::getline(token_stream, token, ','))
|
||||
{
|
||||
if (token.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto kv = extract_kv_('=', token);
|
||||
rv[kv.first] = kv.second;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void load_levels(const std::string &input)
|
||||
{
|
||||
if (input.empty() || input.size() > 512)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto key_vals = extract_key_vals_(input);
|
||||
std::unordered_map<std::string, level::level_enum> levels;
|
||||
level::level_enum global_level = level::info;
|
||||
bool global_level_found = false;
|
||||
|
||||
for (auto &name_level : key_vals)
|
||||
{
|
||||
auto &logger_name = name_level.first;
|
||||
auto level_name = to_lower_(name_level.second);
|
||||
auto level = level::from_str(level_name);
|
||||
// ignore unrecognized level names
|
||||
if (level == level::off && level_name != "off")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (logger_name.empty()) // no logger name indicate global level
|
||||
{
|
||||
global_level_found = true;
|
||||
global_level = level;
|
||||
}
|
||||
else
|
||||
{
|
||||
levels[logger_name] = level;
|
||||
}
|
||||
}
|
||||
|
||||
details::registry::instance().set_levels(std::move(levels), global_level_found ? &global_level : nullptr);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace cfg
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
namespace helpers {
|
||||
//
|
||||
// Init levels from given string
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// set global level to debug: "debug"
|
||||
// turn off all logging except for logger1: "off,logger1=debug"
|
||||
// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
|
||||
//
|
||||
SPDLOG_API void load_levels(const std::string &txt);
|
||||
} // namespace helpers
|
||||
|
||||
} // namespace cfg
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
# include "helpers-inl.h"
|
||||
#endif // SPDLOG_HEADER_ONLY
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
# include <spdlog/common.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
namespace spdlog {
|
||||
namespace level {
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
constexpr
|
||||
#endif
|
||||
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
|
||||
|
||||
static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
|
||||
|
||||
SPDLOG_INLINE const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
||||
{
|
||||
return level_string_views[l];
|
||||
}
|
||||
|
||||
SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
||||
{
|
||||
return short_level_names[l];
|
||||
}
|
||||
|
||||
SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
|
||||
{
|
||||
auto it = std::find(std::begin(level_string_views), std::end(level_string_views), name);
|
||||
if (it != std::end(level_string_views))
|
||||
return static_cast<level::level_enum>(it - std::begin(level_string_views));
|
||||
|
||||
// check also for "warn" and "err" before giving up..
|
||||
if (name == "warn")
|
||||
{
|
||||
return level::warn;
|
||||
}
|
||||
if (name == "err")
|
||||
{
|
||||
return level::err;
|
||||
}
|
||||
return level::off;
|
||||
}
|
||||
} // namespace level
|
||||
|
||||
SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg)
|
||||
: msg_(std::move(msg))
|
||||
{}
|
||||
|
||||
SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno)
|
||||
{
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
msg_ = std::system_error(std::error_code(last_errno, std::generic_category()), msg).what();
|
||||
#else
|
||||
memory_buf_t outbuf;
|
||||
fmt::format_system_error(outbuf, last_errno, msg.c_str());
|
||||
msg_ = fmt::to_string(outbuf);
|
||||
#endif
|
||||
}
|
||||
|
||||
SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT
|
||||
{
|
||||
return msg_.c_str();
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno)
|
||||
{
|
||||
SPDLOG_THROW(spdlog_ex(msg, last_errno));
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void throw_spdlog_ex(std::string msg)
|
||||
{
|
||||
SPDLOG_THROW(spdlog_ex(std::move(msg)));
|
||||
}
|
||||
|
||||
} // namespace spdlog
|
||||
361
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/common.h
Normal file
361
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/common.h
Normal file
@@ -0,0 +1,361 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/tweakme.h>
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
# include <string_view>
|
||||
#endif
|
||||
|
||||
#ifdef SPDLOG_COMPILED_LIB
|
||||
# undef SPDLOG_HEADER_ONLY
|
||||
# if defined(SPDLOG_SHARED_LIB)
|
||||
# if defined(_WIN32)
|
||||
# ifdef spdlog_EXPORTS
|
||||
# define SPDLOG_API __declspec(dllexport)
|
||||
# else // !spdlog_EXPORTS
|
||||
# define SPDLOG_API __declspec(dllimport)
|
||||
# endif
|
||||
# else // !defined(_WIN32)
|
||||
# define SPDLOG_API __attribute__((visibility("default")))
|
||||
# endif
|
||||
# else // !defined(SPDLOG_SHARED_LIB)
|
||||
# define SPDLOG_API
|
||||
# endif
|
||||
# define SPDLOG_INLINE
|
||||
#else // !defined(SPDLOG_COMPILED_LIB)
|
||||
# define SPDLOG_API
|
||||
# define SPDLOG_HEADER_ONLY
|
||||
# define SPDLOG_INLINE inline
|
||||
#endif // #ifdef SPDLOG_COMPILED_LIB
|
||||
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
|
||||
#if !defined(SPDLOG_USE_STD_FORMAT) && FMT_VERSION >= 80000 // backward compatibility with fmt versions older than 8
|
||||
# define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string)
|
||||
# define SPDLOG_FMT_STRING(format_string) FMT_STRING(format_string)
|
||||
# if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
||||
# include <spdlog/fmt/xchar.h>
|
||||
# endif
|
||||
#else
|
||||
# define SPDLOG_FMT_RUNTIME(format_string) format_string
|
||||
# define SPDLOG_FMT_STRING(format_string) format_string
|
||||
#endif
|
||||
|
||||
// visual studio up to 2013 does not support noexcept nor constexpr
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
# define SPDLOG_NOEXCEPT _NOEXCEPT
|
||||
# define SPDLOG_CONSTEXPR
|
||||
# define SPDLOG_CONSTEXPR_FUNC
|
||||
#else
|
||||
# define SPDLOG_NOEXCEPT noexcept
|
||||
# define SPDLOG_CONSTEXPR constexpr
|
||||
# if __cplusplus >= 201402L
|
||||
# define SPDLOG_CONSTEXPR_FUNC constexpr
|
||||
# else
|
||||
# define SPDLOG_CONSTEXPR_FUNC
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define SPDLOG_DEPRECATED __attribute__((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
# define SPDLOG_DEPRECATED __declspec(deprecated)
|
||||
#else
|
||||
# define SPDLOG_DEPRECATED
|
||||
#endif
|
||||
|
||||
// disable thread local on msvc 2013
|
||||
#ifndef SPDLOG_NO_TLS
|
||||
# if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
|
||||
# define SPDLOG_NO_TLS 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef SPDLOG_FUNCTION
|
||||
# define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__)
|
||||
#endif
|
||||
|
||||
#ifdef SPDLOG_NO_EXCEPTIONS
|
||||
# define SPDLOG_TRY
|
||||
# define SPDLOG_THROW(ex) \
|
||||
do \
|
||||
{ \
|
||||
printf("spdlog fatal error: %s\n", ex.what()); \
|
||||
std::abort(); \
|
||||
} while (0)
|
||||
# define SPDLOG_CATCH_STD
|
||||
#else
|
||||
# define SPDLOG_TRY try
|
||||
# define SPDLOG_THROW(ex) throw(ex)
|
||||
# define SPDLOG_CATCH_STD \
|
||||
catch (const std::exception &) {}
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
class formatter;
|
||||
|
||||
namespace sinks {
|
||||
class sink;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
using filename_t = std::wstring;
|
||||
// allow macro expansion to occur in SPDLOG_FILENAME_T
|
||||
# define SPDLOG_FILENAME_T_INNER(s) L##s
|
||||
# define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s)
|
||||
#else
|
||||
using filename_t = std::string;
|
||||
# define SPDLOG_FILENAME_T(s) s
|
||||
#endif
|
||||
|
||||
using log_clock = std::chrono::system_clock;
|
||||
using sink_ptr = std::shared_ptr<sinks::sink>;
|
||||
using sinks_init_list = std::initializer_list<sink_ptr>;
|
||||
using err_handler = std::function<void(const std::string &err_msg)>;
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
namespace fmt_lib = std;
|
||||
|
||||
using string_view_t = std::string_view;
|
||||
using memory_buf_t = std::string;
|
||||
|
||||
template<typename... Args>
|
||||
using format_string_t = std::string_view;
|
||||
|
||||
template<class T, class Char = char>
|
||||
struct is_convertible_to_basic_format_string : std::integral_constant<bool, std::is_convertible<T, std::basic_string_view<Char>>::value>
|
||||
{};
|
||||
|
||||
# if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
||||
using wstring_view_t = std::wstring_view;
|
||||
using wmemory_buf_t = std::wstring;
|
||||
|
||||
template<typename... Args>
|
||||
using wformat_string_t = std::wstring_view;
|
||||
# endif
|
||||
# define SPDLOG_BUF_TO_STRING(x) x
|
||||
#else // use fmt lib instead of std::format
|
||||
namespace fmt_lib = fmt;
|
||||
|
||||
using string_view_t = fmt::basic_string_view<char>;
|
||||
using memory_buf_t = fmt::basic_memory_buffer<char, 250>;
|
||||
|
||||
template<typename... Args>
|
||||
using format_string_t = fmt::format_string<Args...>;
|
||||
|
||||
template<class T>
|
||||
using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
|
||||
// clang doesn't like SFINAE disabled constructor in std::is_convertible<> so have to repeat the condition from basic_format_string here,
|
||||
// in addition, fmt::basic_runtime<Char> is only convertible to basic_format_string<Char> but not basic_string_view<Char>
|
||||
template<class T, class Char = char>
|
||||
struct is_convertible_to_basic_format_string
|
||||
: std::integral_constant<bool,
|
||||
std::is_convertible<T, fmt::basic_string_view<Char>>::value || std::is_same<remove_cvref_t<T>, fmt::basic_runtime<Char>>::value>
|
||||
{};
|
||||
|
||||
# if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
||||
using wstring_view_t = fmt::basic_string_view<wchar_t>;
|
||||
using wmemory_buf_t = fmt::basic_memory_buffer<wchar_t, 250>;
|
||||
|
||||
template<typename... Args>
|
||||
using wformat_string_t = fmt::wformat_string<Args...>;
|
||||
# endif
|
||||
# define SPDLOG_BUF_TO_STRING(x) fmt::to_string(x)
|
||||
#endif
|
||||
|
||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
# ifndef _WIN32
|
||||
# error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
|
||||
# endif // _WIN32
|
||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
|
||||
template<class T>
|
||||
struct is_convertible_to_any_format_string : std::integral_constant<bool, is_convertible_to_basic_format_string<T, char>::value ||
|
||||
is_convertible_to_basic_format_string<T, wchar_t>::value>
|
||||
{};
|
||||
|
||||
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
|
||||
using level_t = details::null_atomic_int;
|
||||
#else
|
||||
using level_t = std::atomic<int>;
|
||||
#endif
|
||||
|
||||
#define SPDLOG_LEVEL_TRACE 0
|
||||
#define SPDLOG_LEVEL_DEBUG 1
|
||||
#define SPDLOG_LEVEL_INFO 2
|
||||
#define SPDLOG_LEVEL_WARN 3
|
||||
#define SPDLOG_LEVEL_ERROR 4
|
||||
#define SPDLOG_LEVEL_CRITICAL 5
|
||||
#define SPDLOG_LEVEL_OFF 6
|
||||
|
||||
#if !defined(SPDLOG_ACTIVE_LEVEL)
|
||||
# define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
||||
#endif
|
||||
|
||||
// Log level enum
|
||||
namespace level {
|
||||
enum level_enum : int
|
||||
{
|
||||
trace = SPDLOG_LEVEL_TRACE,
|
||||
debug = SPDLOG_LEVEL_DEBUG,
|
||||
info = SPDLOG_LEVEL_INFO,
|
||||
warn = SPDLOG_LEVEL_WARN,
|
||||
err = SPDLOG_LEVEL_ERROR,
|
||||
critical = SPDLOG_LEVEL_CRITICAL,
|
||||
off = SPDLOG_LEVEL_OFF,
|
||||
n_levels
|
||||
};
|
||||
|
||||
#define SPDLOG_LEVEL_NAME_TRACE spdlog::string_view_t("trace", 5)
|
||||
#define SPDLOG_LEVEL_NAME_DEBUG spdlog::string_view_t("debug", 5)
|
||||
#define SPDLOG_LEVEL_NAME_INFO spdlog::string_view_t("info", 4)
|
||||
#define SPDLOG_LEVEL_NAME_WARNING spdlog::string_view_t("warning", 7)
|
||||
#define SPDLOG_LEVEL_NAME_ERROR spdlog::string_view_t("error", 5)
|
||||
#define SPDLOG_LEVEL_NAME_CRITICAL spdlog::string_view_t("critical", 8)
|
||||
#define SPDLOG_LEVEL_NAME_OFF spdlog::string_view_t("off", 3)
|
||||
|
||||
#if !defined(SPDLOG_LEVEL_NAMES)
|
||||
# define SPDLOG_LEVEL_NAMES \
|
||||
{ \
|
||||
SPDLOG_LEVEL_NAME_TRACE, SPDLOG_LEVEL_NAME_DEBUG, SPDLOG_LEVEL_NAME_INFO, SPDLOG_LEVEL_NAME_WARNING, SPDLOG_LEVEL_NAME_ERROR, \
|
||||
SPDLOG_LEVEL_NAME_CRITICAL, SPDLOG_LEVEL_NAME_OFF \
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(SPDLOG_SHORT_LEVEL_NAMES)
|
||||
|
||||
# define SPDLOG_SHORT_LEVEL_NAMES \
|
||||
{ \
|
||||
"T", "D", "I", "W", "E", "C", "O" \
|
||||
}
|
||||
#endif
|
||||
|
||||
SPDLOG_API const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
|
||||
|
||||
} // namespace level
|
||||
|
||||
//
|
||||
// Color mode used by sinks with color support.
|
||||
//
|
||||
enum class color_mode
|
||||
{
|
||||
always,
|
||||
automatic,
|
||||
never
|
||||
};
|
||||
|
||||
//
|
||||
// Pattern time - specific time getting to use for pattern_formatter.
|
||||
// local time by default
|
||||
//
|
||||
enum class pattern_time_type
|
||||
{
|
||||
local, // log localtime
|
||||
utc // log utc
|
||||
};
|
||||
|
||||
//
|
||||
// Log exception
|
||||
//
|
||||
class SPDLOG_API spdlog_ex : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit spdlog_ex(std::string msg);
|
||||
spdlog_ex(const std::string &msg, int last_errno);
|
||||
const char *what() const SPDLOG_NOEXCEPT override;
|
||||
|
||||
private:
|
||||
std::string msg_;
|
||||
};
|
||||
|
||||
[[noreturn]] SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
|
||||
[[noreturn]] SPDLOG_API void throw_spdlog_ex(std::string msg);
|
||||
|
||||
struct source_loc
|
||||
{
|
||||
SPDLOG_CONSTEXPR source_loc() = default;
|
||||
SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in)
|
||||
: filename{filename_in}
|
||||
, line{line_in}
|
||||
, funcname{funcname_in}
|
||||
{}
|
||||
|
||||
SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT
|
||||
{
|
||||
return line == 0;
|
||||
}
|
||||
const char *filename{nullptr};
|
||||
int line{0};
|
||||
const char *funcname{nullptr};
|
||||
};
|
||||
|
||||
struct file_event_handlers
|
||||
{
|
||||
file_event_handlers()
|
||||
: before_open(nullptr)
|
||||
, after_open(nullptr)
|
||||
, before_close(nullptr)
|
||||
, after_close(nullptr)
|
||||
{}
|
||||
|
||||
std::function<void(const filename_t &filename)> before_open;
|
||||
std::function<void(const filename_t &filename, std::FILE *file_stream)> after_open;
|
||||
std::function<void(const filename_t &filename, std::FILE *file_stream)> before_close;
|
||||
std::function<void(const filename_t &filename)> after_close;
|
||||
};
|
||||
|
||||
namespace details {
|
||||
|
||||
// make_unique support for pre c++14
|
||||
|
||||
#if __cplusplus >= 201402L // C++14 and beyond
|
||||
using std::enable_if_t;
|
||||
using std::make_unique;
|
||||
#else
|
||||
template<bool B, class T = void>
|
||||
using enable_if_t = typename std::enable_if<B, T>::type;
|
||||
|
||||
template<typename T, typename... Args>
|
||||
std::unique_ptr<T> make_unique(Args &&... args)
|
||||
{
|
||||
static_assert(!std::is_array<T>::value, "arrays not supported");
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
#endif
|
||||
|
||||
// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
|
||||
template<typename T, typename U, enable_if_t<!std::is_same<T, U>::value, int> = 0>
|
||||
constexpr T conditional_static_cast(U value)
|
||||
{
|
||||
return static_cast<T>(value);
|
||||
}
|
||||
|
||||
template<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>
|
||||
constexpr T conditional_static_cast(U value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
# include "common-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
# include <spdlog/details/backtracer.h>
|
||||
#endif
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
SPDLOG_INLINE backtracer::backtracer(const backtracer &other)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(other.mutex_);
|
||||
enabled_ = other.enabled();
|
||||
messages_ = other.messages_;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(other.mutex_);
|
||||
enabled_ = other.enabled();
|
||||
messages_ = std::move(other.messages_);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
enabled_ = other.enabled();
|
||||
messages_ = std::move(other.messages_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void backtracer::enable(size_t size)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
enabled_.store(true, std::memory_order_relaxed);
|
||||
messages_ = circular_q<log_msg_buffer>{size};
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void backtracer::disable()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
enabled_.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE bool backtracer::enabled() const
|
||||
{
|
||||
return enabled_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void backtracer::push_back(const log_msg &msg)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
messages_.push_back(log_msg_buffer{msg});
|
||||
}
|
||||
|
||||
// pop all items in the q and apply the given fun on each of them.
|
||||
SPDLOG_INLINE void backtracer::foreach_pop(std::function<void(const details::log_msg &)> fun)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
while (!messages_.empty())
|
||||
{
|
||||
auto &front_msg = messages_.front();
|
||||
fun(front_msg);
|
||||
messages_.pop_front();
|
||||
}
|
||||
}
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/details/log_msg_buffer.h>
|
||||
#include <spdlog/details/circular_q.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
|
||||
// Store log messages in circular buffer.
|
||||
// Useful for storing debug data in case of error/warning happens.
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class SPDLOG_API backtracer
|
||||
{
|
||||
mutable std::mutex mutex_;
|
||||
std::atomic<bool> enabled_{false};
|
||||
circular_q<log_msg_buffer> messages_;
|
||||
|
||||
public:
|
||||
backtracer() = default;
|
||||
backtracer(const backtracer &other);
|
||||
|
||||
backtracer(backtracer &&other) SPDLOG_NOEXCEPT;
|
||||
backtracer &operator=(backtracer other);
|
||||
|
||||
void enable(size_t size);
|
||||
void disable();
|
||||
bool enabled() const;
|
||||
void push_back(const log_msg &msg);
|
||||
|
||||
// pop all items in the q and apply the given fun on each of them.
|
||||
void foreach_pop(std::function<void(const details::log_msg &)> fun);
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
# include "backtracer-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,146 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
// circular q view of std::vector.
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
template<typename T>
|
||||
class circular_q
|
||||
{
|
||||
size_t max_items_ = 0;
|
||||
typename std::vector<T>::size_type head_ = 0;
|
||||
typename std::vector<T>::size_type tail_ = 0;
|
||||
size_t overrun_counter_ = 0;
|
||||
std::vector<T> v_;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
// empty ctor - create a disabled queue with no elements allocated at all
|
||||
circular_q() = default;
|
||||
|
||||
explicit circular_q(size_t max_items)
|
||||
: max_items_(max_items + 1) // one item is reserved as marker for full q
|
||||
, v_(max_items_)
|
||||
{}
|
||||
|
||||
circular_q(const circular_q &) = default;
|
||||
circular_q &operator=(const circular_q &) = default;
|
||||
|
||||
// move cannot be default,
|
||||
// since we need to reset head_, tail_, etc to zero in the moved object
|
||||
circular_q(circular_q &&other) SPDLOG_NOEXCEPT
|
||||
{
|
||||
copy_moveable(std::move(other));
|
||||
}
|
||||
|
||||
circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT
|
||||
{
|
||||
copy_moveable(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// push back, overrun (oldest) item if no room left
|
||||
void push_back(T &&item)
|
||||
{
|
||||
if (max_items_ > 0)
|
||||
{
|
||||
v_[tail_] = std::move(item);
|
||||
tail_ = (tail_ + 1) % max_items_;
|
||||
|
||||
if (tail_ == head_) // overrun last item if full
|
||||
{
|
||||
head_ = (head_ + 1) % max_items_;
|
||||
++overrun_counter_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return reference to the front item.
|
||||
// If there are no elements in the container, the behavior is undefined.
|
||||
const T &front() const
|
||||
{
|
||||
return v_[head_];
|
||||
}
|
||||
|
||||
T &front()
|
||||
{
|
||||
return v_[head_];
|
||||
}
|
||||
|
||||
// Return number of elements actually stored
|
||||
size_t size() const
|
||||
{
|
||||
if (tail_ >= head_)
|
||||
{
|
||||
return tail_ - head_;
|
||||
}
|
||||
else
|
||||
{
|
||||
return max_items_ - (head_ - tail_);
|
||||
}
|
||||
}
|
||||
|
||||
// Return const reference to item by index.
|
||||
// If index is out of range 0…size()-1, the behavior is undefined.
|
||||
const T &at(size_t i) const
|
||||
{
|
||||
assert(i < size());
|
||||
return v_[(head_ + i) % max_items_];
|
||||
}
|
||||
|
||||
// Pop item from front.
|
||||
// If there are no elements in the container, the behavior is undefined.
|
||||
void pop_front()
|
||||
{
|
||||
head_ = (head_ + 1) % max_items_;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return tail_ == head_;
|
||||
}
|
||||
|
||||
bool full() const
|
||||
{
|
||||
// head is ahead of the tail by 1
|
||||
if (max_items_ > 0)
|
||||
{
|
||||
return ((tail_ + 1) % max_items_) == head_;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t overrun_counter() const
|
||||
{
|
||||
return overrun_counter_;
|
||||
}
|
||||
|
||||
void reset_overrun_counter()
|
||||
{
|
||||
overrun_counter_ = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
// copy from other&& and reset it to disabled state
|
||||
void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT
|
||||
{
|
||||
max_items_ = other.max_items_;
|
||||
head_ = other.head_;
|
||||
tail_ = other.tail_;
|
||||
overrun_counter_ = other.overrun_counter_;
|
||||
v_ = std::move(other.v_);
|
||||
|
||||
// put &&other in disabled, but valid state
|
||||
other.max_items_ = 0;
|
||||
other.head_ = other.tail_ = 0;
|
||||
other.overrun_counter_ = 0;
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#include <mutex>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
struct console_mutex
|
||||
{
|
||||
using mutex_t = std::mutex;
|
||||
static mutex_t &mutex()
|
||||
{
|
||||
static mutex_t s_mutex;
|
||||
return s_mutex;
|
||||
}
|
||||
};
|
||||
|
||||
struct console_nullmutex
|
||||
{
|
||||
using mutex_t = null_mutex;
|
||||
static mutex_t &mutex()
|
||||
{
|
||||
static mutex_t s_mutex;
|
||||
return s_mutex;
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,172 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
# include <spdlog/details/file_helper.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/common.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
SPDLOG_INLINE file_helper::file_helper(const file_event_handlers &event_handlers)
|
||||
: event_handlers_(event_handlers)
|
||||
{}
|
||||
|
||||
SPDLOG_INLINE file_helper::~file_helper()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
|
||||
{
|
||||
close();
|
||||
filename_ = fname;
|
||||
|
||||
auto *mode = SPDLOG_FILENAME_T("ab");
|
||||
auto *trunc_mode = SPDLOG_FILENAME_T("wb");
|
||||
|
||||
if (event_handlers_.before_open)
|
||||
{
|
||||
event_handlers_.before_open(filename_);
|
||||
}
|
||||
for (int tries = 0; tries < open_tries_; ++tries)
|
||||
{
|
||||
// create containing folder if not exists already.
|
||||
os::create_dir(os::dir_name(fname));
|
||||
if (truncate)
|
||||
{
|
||||
// Truncate by opening-and-closing a tmp file in "wb" mode, always
|
||||
// opening the actual log-we-write-to in "ab" mode, since that
|
||||
// interacts more politely with eternal processes that might
|
||||
// rotate/truncate the file underneath us.
|
||||
std::FILE *tmp;
|
||||
if (os::fopen_s(&tmp, fname, trunc_mode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
std::fclose(tmp);
|
||||
}
|
||||
if (!os::fopen_s(&fd_, fname, mode))
|
||||
{
|
||||
if (event_handlers_.after_open)
|
||||
{
|
||||
event_handlers_.after_open(filename_, fd_);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
details::os::sleep_for_millis(open_interval_);
|
||||
}
|
||||
|
||||
throw_spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing", errno);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void file_helper::reopen(bool truncate)
|
||||
{
|
||||
if (filename_.empty())
|
||||
{
|
||||
throw_spdlog_ex("Failed re opening file - was not opened before");
|
||||
}
|
||||
this->open(filename_, truncate);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void file_helper::flush()
|
||||
{
|
||||
if (std::fflush(fd_) != 0)
|
||||
{
|
||||
throw_spdlog_ex("Failed flush to file " + os::filename_to_str(filename_), errno);
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void file_helper::close()
|
||||
{
|
||||
if (fd_ != nullptr)
|
||||
{
|
||||
if (event_handlers_.before_close)
|
||||
{
|
||||
event_handlers_.before_close(filename_, fd_);
|
||||
}
|
||||
|
||||
std::fclose(fd_);
|
||||
fd_ = nullptr;
|
||||
|
||||
if (event_handlers_.after_close)
|
||||
{
|
||||
event_handlers_.after_close(filename_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf)
|
||||
{
|
||||
size_t msg_size = buf.size();
|
||||
auto data = buf.data();
|
||||
if (std::fwrite(data, 1, msg_size, fd_) != msg_size)
|
||||
{
|
||||
throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno);
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE size_t file_helper::size() const
|
||||
{
|
||||
if (fd_ == nullptr)
|
||||
{
|
||||
throw_spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_));
|
||||
}
|
||||
return os::filesize(fd_);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE const filename_t &file_helper::filename() const
|
||||
{
|
||||
return filename_;
|
||||
}
|
||||
|
||||
//
|
||||
// return file path and its extension:
|
||||
//
|
||||
// "mylog.txt" => ("mylog", ".txt")
|
||||
// "mylog" => ("mylog", "")
|
||||
// "mylog." => ("mylog.", "")
|
||||
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
|
||||
//
|
||||
// the starting dot in filenames is ignored (hidden files):
|
||||
//
|
||||
// ".mylog" => (".mylog". "")
|
||||
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
||||
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
||||
SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension(const filename_t &fname)
|
||||
{
|
||||
auto ext_index = fname.rfind('.');
|
||||
|
||||
// no valid extension found - return whole path and empty string as
|
||||
// extension
|
||||
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
|
||||
{
|
||||
return std::make_tuple(fname, filename_t());
|
||||
}
|
||||
|
||||
// treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
|
||||
auto folder_index = fname.find_last_of(details::os::folder_seps_filename);
|
||||
if (folder_index != filename_t::npos && folder_index >= ext_index - 1)
|
||||
{
|
||||
return std::make_tuple(fname, filename_t());
|
||||
}
|
||||
|
||||
// finally - return a valid base and extension tuple
|
||||
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <tuple>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
// Helper class for file sinks.
|
||||
// When failing to open a file, retry several times(5) with a delay interval(10 ms).
|
||||
// Throw spdlog_ex exception on errors.
|
||||
|
||||
class SPDLOG_API file_helper
|
||||
{
|
||||
public:
|
||||
file_helper() = default;
|
||||
explicit file_helper(const file_event_handlers &event_handlers);
|
||||
|
||||
file_helper(const file_helper &) = delete;
|
||||
file_helper &operator=(const file_helper &) = delete;
|
||||
~file_helper();
|
||||
|
||||
void open(const filename_t &fname, bool truncate = false);
|
||||
void reopen(bool truncate);
|
||||
void flush();
|
||||
void close();
|
||||
void write(const memory_buf_t &buf);
|
||||
size_t size() const;
|
||||
const filename_t &filename() const;
|
||||
|
||||
//
|
||||
// return file path and its extension:
|
||||
//
|
||||
// "mylog.txt" => ("mylog", ".txt")
|
||||
// "mylog" => ("mylog", "")
|
||||
// "mylog." => ("mylog.", "")
|
||||
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
|
||||
//
|
||||
// the starting dot in filenames is ignored (hidden files):
|
||||
//
|
||||
// ".mylog" => (".mylog". "")
|
||||
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
||||
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
||||
static std::tuple<filename_t, filename_t> split_by_extension(const filename_t &fname);
|
||||
|
||||
private:
|
||||
const int open_tries_ = 5;
|
||||
const unsigned int open_interval_ = 10;
|
||||
std::FILE *fd_{nullptr};
|
||||
filename_t filename_;
|
||||
file_event_handlers event_handlers_;
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
# include "file_helper-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,169 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <type_traits>
|
||||
#include <iterator>
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include <spdlog/common.h>
|
||||
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
# include <charconv>
|
||||
# include <limits>
|
||||
#endif
|
||||
|
||||
// Some fmt helpers to efficiently format and pad ints and strings
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
namespace fmt_helper {
|
||||
|
||||
inline spdlog::string_view_t to_string_view(const memory_buf_t &buf) SPDLOG_NOEXCEPT
|
||||
{
|
||||
return spdlog::string_view_t{buf.data(), buf.size()};
|
||||
}
|
||||
|
||||
inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest)
|
||||
{
|
||||
auto *buf_ptr = view.data();
|
||||
dest.append(buf_ptr, buf_ptr + view.size());
|
||||
}
|
||||
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
template<typename T>
|
||||
inline void append_int(T n, memory_buf_t &dest)
|
||||
{
|
||||
// Buffer should be large enough to hold all digits (digits10 + 1) and a sign
|
||||
SPDLOG_CONSTEXPR const auto BUF_SIZE = std::numeric_limits<T>::digits10 + 2;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
auto [ptr, ec] = std::to_chars(buf, buf + BUF_SIZE, n, 10);
|
||||
if (ec == std::errc())
|
||||
{
|
||||
dest.append(buf, ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw_spdlog_ex("Failed to format int", static_cast<int>(ec));
|
||||
}
|
||||
}
|
||||
#else
|
||||
template<typename T>
|
||||
inline void append_int(T n, memory_buf_t &dest)
|
||||
{
|
||||
fmt::format_int i(n);
|
||||
dest.append(i.data(), i.data() + i.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
SPDLOG_CONSTEXPR_FUNC unsigned int count_digits_fallback(T n)
|
||||
{
|
||||
// taken from fmt: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/format.h#L899-L912
|
||||
unsigned int count = 1;
|
||||
for (;;)
|
||||
{
|
||||
// Integer division is slow so do it for a group of four digits instead
|
||||
// of for every digit. The idea comes from the talk by Alexandrescu
|
||||
// "Three Optimization Tips for C++". See speed-test for a comparison.
|
||||
if (n < 10)
|
||||
return count;
|
||||
if (n < 100)
|
||||
return count + 1;
|
||||
if (n < 1000)
|
||||
return count + 2;
|
||||
if (n < 10000)
|
||||
return count + 3;
|
||||
n /= 10000u;
|
||||
count += 4;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline unsigned int count_digits(T n)
|
||||
{
|
||||
using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
return count_digits_fallback(static_cast<count_type>(n));
|
||||
#else
|
||||
return static_cast<unsigned int>(fmt::
|
||||
// fmt 7.0.0 renamed the internal namespace to detail.
|
||||
// See: https://github.com/fmtlib/fmt/issues/1538
|
||||
# if FMT_VERSION < 70000
|
||||
internal
|
||||
# else
|
||||
detail
|
||||
# endif
|
||||
::count_digits(static_cast<count_type>(n)));
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void pad2(int n, memory_buf_t &dest)
|
||||
{
|
||||
if (n >= 0 && n < 100) // 0-99
|
||||
{
|
||||
dest.push_back(static_cast<char>('0' + n / 10));
|
||||
dest.push_back(static_cast<char>('0' + n % 10));
|
||||
}
|
||||
else // unlikely, but just in case, let fmt deal with it
|
||||
{
|
||||
fmt_lib::format_to(std::back_inserter(dest), SPDLOG_FMT_STRING("{:02}"), n);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
|
||||
{
|
||||
static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
|
||||
for (auto digits = count_digits(n); digits < width; digits++)
|
||||
{
|
||||
dest.push_back('0');
|
||||
}
|
||||
append_int(n, dest);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void pad3(T n, memory_buf_t &dest)
|
||||
{
|
||||
static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T");
|
||||
if (n < 1000)
|
||||
{
|
||||
dest.push_back(static_cast<char>(n / 100 + '0'));
|
||||
n = n % 100;
|
||||
dest.push_back(static_cast<char>((n / 10) + '0'));
|
||||
dest.push_back(static_cast<char>((n % 10) + '0'));
|
||||
}
|
||||
else
|
||||
{
|
||||
append_int(n, dest);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void pad6(T n, memory_buf_t &dest)
|
||||
{
|
||||
pad_uint(n, 6, dest);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void pad9(T n, memory_buf_t &dest)
|
||||
{
|
||||
pad_uint(n, 9, dest);
|
||||
}
|
||||
|
||||
// return fraction of a second of the given time_point.
|
||||
// e.g.
|
||||
// fraction<std::milliseconds>(tp) -> will return the millis part of the second
|
||||
template<typename ToDuration>
|
||||
inline ToDuration time_fraction(log_clock::time_point tp)
|
||||
{
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::seconds;
|
||||
auto duration = tp.time_since_epoch();
|
||||
auto secs = duration_cast<seconds>(duration);
|
||||
return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
|
||||
}
|
||||
|
||||
} // namespace fmt_helper
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
# include <spdlog/details/log_msg.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
SPDLOG_INLINE log_msg::log_msg(spdlog::log_clock::time_point log_time, spdlog::source_loc loc, string_view_t a_logger_name,
|
||||
spdlog::level::level_enum lvl, spdlog::string_view_t msg)
|
||||
: logger_name(a_logger_name)
|
||||
, level(lvl)
|
||||
, time(log_time)
|
||||
#ifndef SPDLOG_NO_THREAD_ID
|
||||
, thread_id(os::thread_id())
|
||||
#endif
|
||||
, source(loc)
|
||||
, payload(msg)
|
||||
{}
|
||||
|
||||
SPDLOG_INLINE log_msg::log_msg(
|
||||
spdlog::source_loc loc, string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
|
||||
: log_msg(os::now(), loc, a_logger_name, lvl, msg)
|
||||
{}
|
||||
|
||||
SPDLOG_INLINE log_msg::log_msg(string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
|
||||
: log_msg(os::now(), source_loc{}, a_logger_name, lvl, msg)
|
||||
{}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <string>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
struct SPDLOG_API log_msg
|
||||
{
|
||||
log_msg() = default;
|
||||
log_msg(log_clock::time_point log_time, source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
||||
log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
||||
log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
||||
log_msg(const log_msg &other) = default;
|
||||
log_msg &operator=(const log_msg &other) = default;
|
||||
|
||||
string_view_t logger_name;
|
||||
level::level_enum level{level::off};
|
||||
log_clock::time_point time;
|
||||
size_t thread_id{0};
|
||||
|
||||
// wrapping the formatted text with color (updated by pattern_formatter).
|
||||
mutable size_t color_range_start{0};
|
||||
mutable size_t color_range_end{0};
|
||||
|
||||
source_loc source;
|
||||
string_view_t payload;
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
# include "log_msg-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
# include <spdlog/details/log_msg_buffer.h>
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg)
|
||||
: log_msg{orig_msg}
|
||||
{
|
||||
buffer.append(logger_name.begin(), logger_name.end());
|
||||
buffer.append(payload.begin(), payload.end());
|
||||
update_string_views();
|
||||
}
|
||||
|
||||
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
|
||||
: log_msg{other}
|
||||
{
|
||||
buffer.append(logger_name.begin(), logger_name.end());
|
||||
buffer.append(payload.begin(), payload.end());
|
||||
update_string_views();
|
||||
}
|
||||
|
||||
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT : log_msg{other}, buffer{std::move(other.buffer)}
|
||||
{
|
||||
update_string_views();
|
||||
}
|
||||
|
||||
SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other)
|
||||
{
|
||||
log_msg::operator=(other);
|
||||
buffer.clear();
|
||||
buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size());
|
||||
update_string_views();
|
||||
return *this;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT
|
||||
{
|
||||
log_msg::operator=(other);
|
||||
buffer = std::move(other.buffer);
|
||||
update_string_views();
|
||||
return *this;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void log_msg_buffer::update_string_views()
|
||||
{
|
||||
logger_name = string_view_t{buffer.data(), logger_name.size()};
|
||||
payload = string_view_t{buffer.data() + logger_name.size(), payload.size()};
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/details/log_msg.h>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
// Extend log_msg with internal buffer to store its payload.
|
||||
// This is needed since log_msg holds string_views that points to stack data.
|
||||
|
||||
class SPDLOG_API log_msg_buffer : public log_msg
|
||||
{
|
||||
memory_buf_t buffer;
|
||||
void update_string_views();
|
||||
|
||||
public:
|
||||
log_msg_buffer() = default;
|
||||
explicit log_msg_buffer(const log_msg &orig_msg);
|
||||
log_msg_buffer(const log_msg_buffer &other);
|
||||
log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
|
||||
log_msg_buffer &operator=(const log_msg_buffer &other);
|
||||
log_msg_buffer &operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
# include "log_msg_buffer-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,132 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// multi producer-multi consumer blocking queue.
|
||||
// enqueue(..) - will block until room found to put the new message.
|
||||
// enqueue_nowait(..) - will return immediately with false if no room left in
|
||||
// the queue.
|
||||
// dequeue_for(..) - will block until the queue is not empty or timeout have
|
||||
// passed.
|
||||
|
||||
#include <spdlog/details/circular_q.h>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
template<typename T>
|
||||
class mpmc_blocking_queue
|
||||
{
|
||||
public:
|
||||
using item_type = T;
|
||||
explicit mpmc_blocking_queue(size_t max_items)
|
||||
: q_(max_items)
|
||||
{}
|
||||
|
||||
#ifndef __MINGW32__
|
||||
// try to enqueue and block if no room left
|
||||
void enqueue(T &&item)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
|
||||
q_.push_back(std::move(item));
|
||||
}
|
||||
push_cv_.notify_one();
|
||||
}
|
||||
|
||||
// enqueue immediately. overrun oldest message in the queue if no room left.
|
||||
void enqueue_nowait(T &&item)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
q_.push_back(std::move(item));
|
||||
}
|
||||
push_cv_.notify_one();
|
||||
}
|
||||
|
||||
// try to dequeue item. if no item found. wait up to timeout and try again
|
||||
// Return true, if succeeded dequeue item, false otherwise
|
||||
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
popped_item = std::move(q_.front());
|
||||
q_.pop_front();
|
||||
}
|
||||
pop_cv_.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
// apparently mingw deadlocks if the mutex is released before cv.notify_one(),
|
||||
// so release the mutex at the very end each function.
|
||||
|
||||
// try to enqueue and block if no room left
|
||||
void enqueue(T &&item)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
|
||||
q_.push_back(std::move(item));
|
||||
push_cv_.notify_one();
|
||||
}
|
||||
|
||||
// enqueue immediately. overrun oldest message in the queue if no room left.
|
||||
void enqueue_nowait(T &&item)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
q_.push_back(std::move(item));
|
||||
push_cv_.notify_one();
|
||||
}
|
||||
|
||||
// try to dequeue item. if no item found. wait up to timeout and try again
|
||||
// Return true, if succeeded dequeue item, false otherwise
|
||||
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
popped_item = std::move(q_.front());
|
||||
q_.pop_front();
|
||||
pop_cv_.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
size_t overrun_counter()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
return q_.overrun_counter();
|
||||
}
|
||||
|
||||
size_t size()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
return q_.size();
|
||||
}
|
||||
|
||||
void reset_overrun_counter()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
q_.reset_overrun_counter();
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex queue_mutex_;
|
||||
std::condition_variable push_cv_;
|
||||
std::condition_variable pop_cv_;
|
||||
spdlog::details::circular_q<T> q_;
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
// null, no cost dummy "mutex" and dummy "atomic" int
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
struct null_mutex
|
||||
{
|
||||
void lock() const {}
|
||||
void unlock() const {}
|
||||
};
|
||||
|
||||
struct null_atomic_int
|
||||
{
|
||||
int value;
|
||||
null_atomic_int() = default;
|
||||
|
||||
explicit null_atomic_int(int new_value)
|
||||
: value(new_value)
|
||||
{}
|
||||
|
||||
int load(std::memory_order = std::memory_order_relaxed) const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
void store(int new_value, std::memory_order = std::memory_order_relaxed)
|
||||
{
|
||||
value = new_value;
|
||||
}
|
||||
|
||||
int exchange(int new_value, std::memory_order = std::memory_order_relaxed)
|
||||
{
|
||||
std::swap(new_value, value);
|
||||
return new_value; // return value before the call
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,606 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
# include <spdlog/details/os.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/common.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <array>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
# include <io.h> // _get_osfhandle and _isatty support
|
||||
# include <process.h> // _get_pid support
|
||||
# include <spdlog/details/windows_include.h>
|
||||
|
||||
# ifdef __MINGW32__
|
||||
# include <share.h>
|
||||
# endif
|
||||
|
||||
# if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
|
||||
# include <limits>
|
||||
# endif
|
||||
|
||||
# include <direct.h> // for _mkdir/_wmkdir
|
||||
|
||||
#else // unix
|
||||
|
||||
# include <fcntl.h>
|
||||
# include <unistd.h>
|
||||
|
||||
# ifdef __linux__
|
||||
# include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
|
||||
|
||||
# elif defined(_AIX)
|
||||
# include <pthread.h> // for pthread_getthrds_np
|
||||
|
||||
# elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
# include <pthread_np.h> // for pthread_getthreadid_np
|
||||
|
||||
# elif defined(__NetBSD__)
|
||||
# include <lwp.h> // for _lwp_self
|
||||
|
||||
# elif defined(__sun)
|
||||
# include <thread.h> // for thr_self
|
||||
# endif
|
||||
|
||||
#endif // unix
|
||||
|
||||
#ifndef __has_feature // Clang - feature checking macros.
|
||||
# define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
namespace os {
|
||||
|
||||
SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
|
||||
{
|
||||
|
||||
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
|
||||
timespec ts;
|
||||
::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
|
||||
return std::chrono::time_point<log_clock, typename log_clock::duration>(
|
||||
std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
|
||||
|
||||
#else
|
||||
return log_clock::now();
|
||||
#endif
|
||||
}
|
||||
SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
std::tm tm;
|
||||
::localtime_s(&tm, &time_tt);
|
||||
#else
|
||||
std::tm tm;
|
||||
::localtime_r(&time_tt, &tm);
|
||||
#endif
|
||||
return tm;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT
|
||||
{
|
||||
std::time_t now_t = ::time(nullptr);
|
||||
return localtime(now_t);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
std::tm tm;
|
||||
::gmtime_s(&tm, &time_tt);
|
||||
#else
|
||||
std::tm tm;
|
||||
::gmtime_r(&time_tt, &tm);
|
||||
#endif
|
||||
return tm;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
|
||||
{
|
||||
std::time_t now_t = ::time(nullptr);
|
||||
return gmtime(now_t);
|
||||
}
|
||||
|
||||
// fopen_s on non windows for writing
|
||||
SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
# ifdef SPDLOG_WCHAR_FILENAMES
|
||||
*fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
||||
# else
|
||||
*fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
||||
# endif
|
||||
# if defined(SPDLOG_PREVENT_CHILD_FD)
|
||||
if (*fp != nullptr)
|
||||
{
|
||||
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
|
||||
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
|
||||
{
|
||||
::fclose(*fp);
|
||||
*fp = nullptr;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
#else // unix
|
||||
# if defined(SPDLOG_PREVENT_CHILD_FD)
|
||||
const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
|
||||
const int fd = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
|
||||
if (fd == -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
*fp = ::fdopen(fd, mode.c_str());
|
||||
if (*fp == nullptr)
|
||||
{
|
||||
::close(fd);
|
||||
}
|
||||
# else
|
||||
*fp = ::fopen((filename.c_str()), mode.c_str());
|
||||
# endif
|
||||
#endif
|
||||
|
||||
return *fp == nullptr;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT
|
||||
{
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
return ::_wremove(filename.c_str());
|
||||
#else
|
||||
return std::remove(filename.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT
|
||||
{
|
||||
return path_exists(filename) ? remove(filename) : 0;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
|
||||
{
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
return ::_wrename(filename1.c_str(), filename2.c_str());
|
||||
#else
|
||||
return std::rename(filename1.c_str(), filename2.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return true if path exists (file or directory)
|
||||
SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT
|
||||
{
|
||||
#ifdef _WIN32
|
||||
# ifdef SPDLOG_WCHAR_FILENAMES
|
||||
auto attribs = ::GetFileAttributesW(filename.c_str());
|
||||
# else
|
||||
auto attribs = ::GetFileAttributesA(filename.c_str());
|
||||
# endif
|
||||
return attribs != INVALID_FILE_ATTRIBUTES;
|
||||
#else // common linux/unix all have the stat system call
|
||||
struct stat buffer;
|
||||
return (::stat(filename.c_str(), &buffer) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// avoid warning about unreachable statement at the end of filesize()
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4702)
|
||||
#endif
|
||||
|
||||
// Return file size according to open FILE* object
|
||||
SPDLOG_INLINE size_t filesize(FILE *f)
|
||||
{
|
||||
if (f == nullptr)
|
||||
{
|
||||
throw_spdlog_ex("Failed getting file size. fd is null");
|
||||
}
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
int fd = ::_fileno(f);
|
||||
# if defined(_WIN64) // 64 bits
|
||||
__int64 ret = ::_filelengthi64(fd);
|
||||
if (ret >= 0)
|
||||
{
|
||||
return static_cast<size_t>(ret);
|
||||
}
|
||||
|
||||
# else // windows 32 bits
|
||||
long ret = ::_filelength(fd);
|
||||
if (ret >= 0)
|
||||
{
|
||||
return static_cast<size_t>(ret);
|
||||
}
|
||||
# endif
|
||||
|
||||
#else // unix
|
||||
// OpenBSD and AIX doesn't compile with :: before the fileno(..)
|
||||
# if defined(__OpenBSD__) || defined(_AIX)
|
||||
int fd = fileno(f);
|
||||
# else
|
||||
int fd = ::fileno(f);
|
||||
# endif
|
||||
// 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
|
||||
# if (defined(__linux__) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64))
|
||||
struct stat64 st;
|
||||
if (::fstat64(fd, &st) == 0)
|
||||
{
|
||||
return static_cast<size_t>(st.st_size);
|
||||
}
|
||||
# else // other unix or linux 32 bits or cygwin
|
||||
struct stat st;
|
||||
if (::fstat(fd, &st) == 0)
|
||||
{
|
||||
return static_cast<size_t>(st.st_size);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
throw_spdlog_ex("Failed getting file size from fd", errno);
|
||||
return 0; // will not be reached.
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
// Return utc offset in minutes or throw spdlog_ex on failure
|
||||
SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
# if _WIN32_WINNT < _WIN32_WINNT_WS08
|
||||
TIME_ZONE_INFORMATION tzinfo;
|
||||
auto rv = ::GetTimeZoneInformation(&tzinfo);
|
||||
# else
|
||||
DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
|
||||
auto rv = ::GetDynamicTimeZoneInformation(&tzinfo);
|
||||
# endif
|
||||
if (rv == TIME_ZONE_ID_INVALID)
|
||||
throw_spdlog_ex("Failed getting timezone info. ", errno);
|
||||
|
||||
int offset = -tzinfo.Bias;
|
||||
if (tm.tm_isdst)
|
||||
{
|
||||
offset -= tzinfo.DaylightBias;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset -= tzinfo.StandardBias;
|
||||
}
|
||||
return offset;
|
||||
#else
|
||||
|
||||
# if defined(sun) || defined(__sun) || defined(_AIX) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE))
|
||||
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
|
||||
struct helper
|
||||
{
|
||||
static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime())
|
||||
{
|
||||
int local_year = localtm.tm_year + (1900 - 1);
|
||||
int gmt_year = gmtm.tm_year + (1900 - 1);
|
||||
|
||||
long int days = (
|
||||
// difference in day of year
|
||||
localtm.tm_yday -
|
||||
gmtm.tm_yday
|
||||
|
||||
// + intervening leap days
|
||||
+ ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
|
||||
((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
|
||||
|
||||
// + difference in years * 365 */
|
||||
+ static_cast<long int>(local_year - gmt_year) * 365);
|
||||
|
||||
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
|
||||
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
|
||||
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
|
||||
|
||||
return secs;
|
||||
}
|
||||
};
|
||||
|
||||
auto offset_seconds = helper::calculate_gmt_offset(tm);
|
||||
# else
|
||||
auto offset_seconds = tm.tm_gmtoff;
|
||||
# endif
|
||||
|
||||
return static_cast<int>(offset_seconds / 60);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return current thread id as size_t
|
||||
// It exists because the std::this_thread::get_id() is much slower(especially
|
||||
// under VS 2013)
|
||||
SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return static_cast<size_t>(::GetCurrentThreadId());
|
||||
#elif defined(__linux__)
|
||||
# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
|
||||
# define SYS_gettid __NR_gettid
|
||||
# endif
|
||||
return static_cast<size_t>(::syscall(SYS_gettid));
|
||||
#elif defined(_AIX)
|
||||
struct __pthrdsinfo buf;
|
||||
int reg_size = 0;
|
||||
pthread_t pt = pthread_self();
|
||||
int retval = pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_TID, &buf, sizeof(buf), NULL, ®_size);
|
||||
int tid = (!retval) ? buf.__pi_tid : 0;
|
||||
return static_cast<size_t>(tid);
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
return static_cast<size_t>(::pthread_getthreadid_np());
|
||||
#elif defined(__NetBSD__)
|
||||
return static_cast<size_t>(::_lwp_self());
|
||||
#elif defined(__OpenBSD__)
|
||||
return static_cast<size_t>(::getthrid());
|
||||
#elif defined(__sun)
|
||||
return static_cast<size_t>(::thr_self());
|
||||
#elif __APPLE__
|
||||
uint64_t tid;
|
||||
pthread_threadid_np(nullptr, &tid);
|
||||
return static_cast<size_t>(tid);
|
||||
#else // Default to standard C++11 (other Unix)
|
||||
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return current thread id as size_t (from thread local storage)
|
||||
SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT
|
||||
{
|
||||
#if defined(SPDLOG_NO_TLS)
|
||||
return _thread_id();
|
||||
#else // cache thread id in tls
|
||||
static thread_local const size_t tid = _thread_id();
|
||||
return tid;
|
||||
#endif
|
||||
}
|
||||
|
||||
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
||||
// See https://github.com/gabime/spdlog/issues/609
|
||||
SPDLOG_INLINE void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
::Sleep(milliseconds);
|
||||
#else
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
||||
#endif
|
||||
}
|
||||
|
||||
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
|
||||
{
|
||||
memory_buf_t buf;
|
||||
wstr_to_utf8buf(filename, buf);
|
||||
return SPDLOG_BUF_TO_STRING(buf);
|
||||
}
|
||||
#else
|
||||
SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
#endif
|
||||
|
||||
SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
return conditional_static_cast<int>(::GetCurrentProcessId());
|
||||
#else
|
||||
return conditional_static_cast<int>(::getpid());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Determine if the terminal supports colors
|
||||
// Based on: https://github.com/agauniyal/rang/
|
||||
SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return true;
|
||||
#else
|
||||
|
||||
static const bool result = []() {
|
||||
const char *env_colorterm_p = std::getenv("COLORTERM");
|
||||
if (env_colorterm_p != nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr std::array<const char *, 16> terms = {{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux",
|
||||
"msys", "putty", "rxvt", "screen", "vt100", "xterm", "alacritty", "vt102"}};
|
||||
|
||||
const char *env_term_p = std::getenv("TERM");
|
||||
if (env_term_p == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::any_of(terms.begin(), terms.end(), [&](const char *term) { return std::strstr(env_term_p, term) != nullptr; });
|
||||
}();
|
||||
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Determine if the terminal attached
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
return ::_isatty(_fileno(file)) != 0;
|
||||
#else
|
||||
return ::isatty(fileno(file)) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||
SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
|
||||
{
|
||||
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 2 - 1)
|
||||
{
|
||||
throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
|
||||
}
|
||||
|
||||
int wstr_size = static_cast<int>(wstr.size());
|
||||
if (wstr_size == 0)
|
||||
{
|
||||
target.resize(0);
|
||||
return;
|
||||
}
|
||||
|
||||
int result_size = static_cast<int>(target.capacity());
|
||||
if ((wstr_size + 1) * 2 > result_size)
|
||||
{
|
||||
result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
if (result_size > 0)
|
||||
{
|
||||
target.resize(result_size);
|
||||
result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(), result_size, NULL, NULL);
|
||||
|
||||
if (result_size > 0)
|
||||
{
|
||||
target.resize(result_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw_spdlog_ex(fmt_lib::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target)
|
||||
{
|
||||
if (str.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) - 1)
|
||||
{
|
||||
throw_spdlog_ex("UTF-8 string is too big to be converted to UTF-16");
|
||||
}
|
||||
|
||||
int str_size = static_cast<int>(str.size());
|
||||
if (str_size == 0)
|
||||
{
|
||||
target.resize(0);
|
||||
return;
|
||||
}
|
||||
|
||||
int result_size = static_cast<int>(target.capacity());
|
||||
if (str_size + 1 > result_size)
|
||||
{
|
||||
result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0);
|
||||
}
|
||||
|
||||
if (result_size > 0)
|
||||
{
|
||||
target.resize(result_size);
|
||||
result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, target.data(), result_size);
|
||||
|
||||
if (result_size > 0)
|
||||
{
|
||||
target.resize(result_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw_spdlog_ex(fmt_lib::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError()));
|
||||
}
|
||||
#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||
|
||||
// return true on success
|
||||
static SPDLOG_INLINE bool mkdir_(const filename_t &path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
# ifdef SPDLOG_WCHAR_FILENAMES
|
||||
return ::_wmkdir(path.c_str()) == 0;
|
||||
# else
|
||||
return ::_mkdir(path.c_str()) == 0;
|
||||
# endif
|
||||
#else
|
||||
return ::mkdir(path.c_str(), mode_t(0755)) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// create the given directory - and all directories leading to it
|
||||
// return true on success or if the directory already exists
|
||||
SPDLOG_INLINE bool create_dir(const filename_t &path)
|
||||
{
|
||||
if (path_exists(path))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t search_offset = 0;
|
||||
do
|
||||
{
|
||||
auto token_pos = path.find_first_of(folder_seps_filename, search_offset);
|
||||
// treat the entire path as a folder if no folder separator not found
|
||||
if (token_pos == filename_t::npos)
|
||||
{
|
||||
token_pos = path.size();
|
||||
}
|
||||
|
||||
auto subdir = path.substr(0, token_pos);
|
||||
|
||||
if (!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir))
|
||||
{
|
||||
return false; // return error if failed creating dir
|
||||
}
|
||||
search_offset = token_pos + 1;
|
||||
} while (search_offset < path.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return directory name from given path or empty string
|
||||
// "abc/file" => "abc"
|
||||
// "abc/" => "abc"
|
||||
// "abc" => ""
|
||||
// "abc///" => "abc//"
|
||||
SPDLOG_INLINE filename_t dir_name(const filename_t &path)
|
||||
{
|
||||
auto pos = path.find_last_of(folder_seps_filename);
|
||||
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
|
||||
}
|
||||
|
||||
std::string SPDLOG_INLINE getenv(const char *field)
|
||||
{
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# if defined(__cplusplus_winrt)
|
||||
return std::string{}; // not supported under uwp
|
||||
# else
|
||||
size_t len = 0;
|
||||
char buf[128];
|
||||
bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
|
||||
return ok ? buf : std::string{};
|
||||
# endif
|
||||
#else // revert to getenv
|
||||
char *buf = ::getenv(field);
|
||||
return buf ? buf : std::string{};
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace os
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
118
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/details/os.h
Normal file
118
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/details/os.h
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <ctime> // std::time_t
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
namespace os {
|
||||
|
||||
SPDLOG_API spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
|
||||
|
||||
SPDLOG_API std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
|
||||
|
||||
SPDLOG_API std::tm localtime() SPDLOG_NOEXCEPT;
|
||||
|
||||
SPDLOG_API std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
|
||||
|
||||
SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
|
||||
|
||||
// eol definition
|
||||
#if !defined(SPDLOG_EOL)
|
||||
# ifdef _WIN32
|
||||
# define SPDLOG_EOL "\r\n"
|
||||
# else
|
||||
# define SPDLOG_EOL "\n"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
|
||||
|
||||
// folder separator
|
||||
#if !defined(SPDLOG_FOLDER_SEPS)
|
||||
# ifdef _WIN32
|
||||
# define SPDLOG_FOLDER_SEPS "\\/"
|
||||
# else
|
||||
# define SPDLOG_FOLDER_SEPS "/"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
SPDLOG_CONSTEXPR static const char folder_seps[] = SPDLOG_FOLDER_SEPS;
|
||||
SPDLOG_CONSTEXPR static const filename_t::value_type folder_seps_filename[] = SPDLOG_FILENAME_T(SPDLOG_FOLDER_SEPS);
|
||||
|
||||
// fopen_s on non windows for writing
|
||||
SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
|
||||
|
||||
// Remove filename. return 0 on success
|
||||
SPDLOG_API int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||
|
||||
// Remove file if exists. return 0 on success
|
||||
// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
|
||||
SPDLOG_API int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||
|
||||
SPDLOG_API int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
|
||||
|
||||
// Return if file exists.
|
||||
SPDLOG_API bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||
|
||||
// Return file size according to open FILE* object
|
||||
SPDLOG_API size_t filesize(FILE *f);
|
||||
|
||||
// Return utc offset in minutes or throw spdlog_ex on failure
|
||||
SPDLOG_API int utc_minutes_offset(const std::tm &tm = details::os::localtime());
|
||||
|
||||
// Return current thread id as size_t
|
||||
// It exists because the std::this_thread::get_id() is much slower(especially
|
||||
// under VS 2013)
|
||||
SPDLOG_API size_t _thread_id() SPDLOG_NOEXCEPT;
|
||||
|
||||
// Return current thread id as size_t (from thread local storage)
|
||||
SPDLOG_API size_t thread_id() SPDLOG_NOEXCEPT;
|
||||
|
||||
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
||||
// See https://github.com/gabime/spdlog/issues/609
|
||||
SPDLOG_API void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT;
|
||||
|
||||
SPDLOG_API std::string filename_to_str(const filename_t &filename);
|
||||
|
||||
SPDLOG_API int pid() SPDLOG_NOEXCEPT;
|
||||
|
||||
// Determine if the terminal supports colors
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
SPDLOG_API bool is_color_terminal() SPDLOG_NOEXCEPT;
|
||||
|
||||
// Determine if the terminal attached
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
SPDLOG_API bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
|
||||
|
||||
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||
SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
|
||||
|
||||
SPDLOG_API void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target);
|
||||
#endif
|
||||
|
||||
// Return directory name from given path or empty string
|
||||
// "abc/file" => "abc"
|
||||
// "abc/" => "abc"
|
||||
// "abc" => ""
|
||||
// "abc///" => "abc//"
|
||||
SPDLOG_API filename_t dir_name(const filename_t &path);
|
||||
|
||||
// Create a dir from the given path.
|
||||
// Return true if succeeded or if this dir already exists.
|
||||
SPDLOG_API bool create_dir(const filename_t &path);
|
||||
|
||||
// non thread safe, cross platform getenv/getenv_s
|
||||
// return empty string if field not found
|
||||
SPDLOG_API std::string getenv(const char *field);
|
||||
|
||||
} // namespace os
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
# include "os-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
# include <spdlog/details/periodic_worker.h>
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
// stop the worker thread and join it
|
||||
SPDLOG_INLINE periodic_worker::~periodic_worker()
|
||||
{
|
||||
if (worker_thread_.joinable())
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
active_ = false;
|
||||
}
|
||||
cv_.notify_one();
|
||||
worker_thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// periodic worker thread - periodically executes the given callback function.
|
||||
//
|
||||
// RAII over the owned thread:
|
||||
// creates the thread on construction.
|
||||
// stops and joins the thread on destruction (if the thread is executing a callback, wait for it to finish first).
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
class SPDLOG_API periodic_worker
|
||||
{
|
||||
public:
|
||||
template<typename Rep, typename Period>
|
||||
periodic_worker(const std::function<void()> &callback_fun, std::chrono::duration<Rep, Period> interval) {
|
||||
active_ = (interval > std::chrono::duration<Rep, Period>::zero());
|
||||
if (!active_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
worker_thread_ = std::thread([this, callback_fun, interval]() {
|
||||
for (;;)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(this->mutex_);
|
||||
if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; }))
|
||||
{
|
||||
return; // active_ == false, so exit this thread
|
||||
}
|
||||
callback_fun();
|
||||
}
|
||||
});
|
||||
}
|
||||
periodic_worker(const periodic_worker &) = delete;
|
||||
periodic_worker &operator=(const periodic_worker &) = delete;
|
||||
// stop the worker thread and join it
|
||||
~periodic_worker();
|
||||
|
||||
private:
|
||||
bool active_;
|
||||
std::thread worker_thread_;
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cv_;
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
# include "periodic_worker-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,306 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
# include <spdlog/details/registry.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/periodic_worker.h>
|
||||
#include <spdlog/logger.h>
|
||||
#include <spdlog/pattern_formatter.h>
|
||||
|
||||
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||
// support for the default stdout color logger
|
||||
# ifdef _WIN32
|
||||
# include <spdlog/sinks/wincolor_sink.h>
|
||||
# else
|
||||
# include <spdlog/sinks/ansicolor_sink.h>
|
||||
# endif
|
||||
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
SPDLOG_INLINE registry::registry()
|
||||
: formatter_(new pattern_formatter())
|
||||
{
|
||||
|
||||
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||
// create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows).
|
||||
# ifdef _WIN32
|
||||
auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
|
||||
# else
|
||||
auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
|
||||
# endif
|
||||
|
||||
const char *default_logger_name = "";
|
||||
default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
|
||||
loggers_[default_logger_name] = default_logger_;
|
||||
|
||||
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||
}
|
||||
|
||||
SPDLOG_INLINE registry::~registry() = default;
|
||||
|
||||
SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
register_logger_(std::move(new_logger));
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
new_logger->set_formatter(formatter_->clone());
|
||||
|
||||
if (err_handler_)
|
||||
{
|
||||
new_logger->set_error_handler(err_handler_);
|
||||
}
|
||||
|
||||
// set new level according to previously configured level or default level
|
||||
auto it = log_levels_.find(new_logger->name());
|
||||
auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
|
||||
new_logger->set_level(new_level);
|
||||
|
||||
new_logger->flush_on(flush_level_);
|
||||
|
||||
if (backtrace_n_messages_ > 0)
|
||||
{
|
||||
new_logger->enable_backtrace(backtrace_n_messages_);
|
||||
}
|
||||
|
||||
if (automatic_registration_)
|
||||
{
|
||||
register_logger_(std::move(new_logger));
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_name)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
auto found = loggers_.find(logger_name);
|
||||
return found == loggers_.end() ? nullptr : found->second;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::shared_ptr<logger> registry::default_logger()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
return default_logger_;
|
||||
}
|
||||
|
||||
// Return raw ptr to the default logger.
|
||||
// To be used directly by the spdlog default api (e.g. spdlog::info)
|
||||
// This make the default API faster, but cannot be used concurrently with set_default_logger().
|
||||
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
||||
SPDLOG_INLINE logger *registry::get_default_raw()
|
||||
{
|
||||
return default_logger_.get();
|
||||
}
|
||||
|
||||
// set default logger.
|
||||
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
||||
SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
// remove previous default logger from the map
|
||||
if (default_logger_ != nullptr)
|
||||
{
|
||||
loggers_.erase(default_logger_->name());
|
||||
}
|
||||
if (new_default_logger != nullptr)
|
||||
{
|
||||
loggers_[new_default_logger->name()] = new_default_logger;
|
||||
}
|
||||
default_logger_ = std::move(new_default_logger);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::set_tp(std::shared_ptr<thread_pool> tp)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||
tp_ = std::move(tp);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::shared_ptr<thread_pool> registry::get_tp()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||
return tp_;
|
||||
}
|
||||
|
||||
// Set global formatter. Each sink in each logger will get a clone of this object
|
||||
SPDLOG_INLINE void registry::set_formatter(std::unique_ptr<formatter> formatter)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
formatter_ = std::move(formatter);
|
||||
for (auto &l : loggers_)
|
||||
{
|
||||
l.second->set_formatter(formatter_->clone());
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
backtrace_n_messages_ = n_messages;
|
||||
|
||||
for (auto &l : loggers_)
|
||||
{
|
||||
l.second->enable_backtrace(n_messages);
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::disable_backtrace()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
backtrace_n_messages_ = 0;
|
||||
for (auto &l : loggers_)
|
||||
{
|
||||
l.second->disable_backtrace();
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
for (auto &l : loggers_)
|
||||
{
|
||||
l.second->set_level(log_level);
|
||||
}
|
||||
global_log_level_ = log_level;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
for (auto &l : loggers_)
|
||||
{
|
||||
l.second->flush_on(log_level);
|
||||
}
|
||||
flush_level_ = log_level;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::set_error_handler(err_handler handler)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
for (auto &l : loggers_)
|
||||
{
|
||||
l.second->set_error_handler(handler);
|
||||
}
|
||||
err_handler_ = std::move(handler);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
for (auto &l : loggers_)
|
||||
{
|
||||
fun(l.second);
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::flush_all()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
for (auto &l : loggers_)
|
||||
{
|
||||
l.second->flush();
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::drop(const std::string &logger_name)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
loggers_.erase(logger_name);
|
||||
if (default_logger_ && default_logger_->name() == logger_name)
|
||||
{
|
||||
default_logger_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::drop_all()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
loggers_.clear();
|
||||
default_logger_.reset();
|
||||
}
|
||||
|
||||
// clean all resources and threads started by the registry
|
||||
SPDLOG_INLINE void registry::shutdown()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
||||
periodic_flusher_.reset();
|
||||
}
|
||||
|
||||
drop_all();
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||
tp_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::recursive_mutex ®istry::tp_mutex()
|
||||
{
|
||||
return tp_mutex_;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registration)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
automatic_registration_ = automatic_registration;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::set_levels(log_levels levels, level::level_enum *global_level)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
log_levels_ = std::move(levels);
|
||||
auto global_level_requested = global_level != nullptr;
|
||||
global_log_level_ = global_level_requested ? *global_level : global_log_level_;
|
||||
|
||||
for (auto &logger : loggers_)
|
||||
{
|
||||
auto logger_entry = log_levels_.find(logger.first);
|
||||
if (logger_entry != log_levels_.end())
|
||||
{
|
||||
logger.second->set_level(logger_entry->second);
|
||||
}
|
||||
else if (global_level_requested)
|
||||
{
|
||||
logger.second->set_level(*global_level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE registry ®istry::instance()
|
||||
{
|
||||
static registry s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name)
|
||||
{
|
||||
if (loggers_.find(logger_name) != loggers_.end())
|
||||
{
|
||||
throw_spdlog_ex("logger with name '" + logger_name + "' already exists");
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::register_logger_(std::shared_ptr<logger> new_logger)
|
||||
{
|
||||
auto logger_name = new_logger->name();
|
||||
throw_if_exists_(logger_name);
|
||||
loggers_[logger_name] = std::move(new_logger);
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,121 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// Loggers registry of unique name->logger pointer
|
||||
// An attempt to create a logger with an already existing name will result with spdlog_ex exception.
|
||||
// If user requests a non existing logger, nullptr will be returned
|
||||
// This class is thread safe
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/periodic_worker.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
|
||||
namespace spdlog {
|
||||
class logger;
|
||||
|
||||
namespace details {
|
||||
class thread_pool;
|
||||
|
||||
class SPDLOG_API registry
|
||||
{
|
||||
public:
|
||||
using log_levels = std::unordered_map<std::string, level::level_enum>;
|
||||
registry(const registry &) = delete;
|
||||
registry &operator=(const registry &) = delete;
|
||||
|
||||
void register_logger(std::shared_ptr<logger> new_logger);
|
||||
void initialize_logger(std::shared_ptr<logger> new_logger);
|
||||
std::shared_ptr<logger> get(const std::string &logger_name);
|
||||
std::shared_ptr<logger> default_logger();
|
||||
|
||||
// Return raw ptr to the default logger.
|
||||
// To be used directly by the spdlog default api (e.g. spdlog::info)
|
||||
// This make the default API faster, but cannot be used concurrently with set_default_logger().
|
||||
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
||||
logger *get_default_raw();
|
||||
|
||||
// set default logger.
|
||||
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
||||
void set_default_logger(std::shared_ptr<logger> new_default_logger);
|
||||
|
||||
void set_tp(std::shared_ptr<thread_pool> tp);
|
||||
|
||||
std::shared_ptr<thread_pool> get_tp();
|
||||
|
||||
// Set global formatter. Each sink in each logger will get a clone of this object
|
||||
void set_formatter(std::unique_ptr<formatter> formatter);
|
||||
|
||||
void enable_backtrace(size_t n_messages);
|
||||
|
||||
void disable_backtrace();
|
||||
|
||||
void set_level(level::level_enum log_level);
|
||||
|
||||
void flush_on(level::level_enum log_level);
|
||||
|
||||
template<typename Rep, typename Period>
|
||||
void flush_every(std::chrono::duration<Rep, Period> interval)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
||||
auto clbk = [this]() { this->flush_all(); };
|
||||
periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
|
||||
}
|
||||
|
||||
void set_error_handler(err_handler handler);
|
||||
|
||||
void apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun);
|
||||
|
||||
void flush_all();
|
||||
|
||||
void drop(const std::string &logger_name);
|
||||
|
||||
void drop_all();
|
||||
|
||||
// clean all resources and threads started by the registry
|
||||
void shutdown();
|
||||
|
||||
std::recursive_mutex &tp_mutex();
|
||||
|
||||
void set_automatic_registration(bool automatic_registration);
|
||||
|
||||
// set levels for all existing/future loggers. global_level can be null if should not set.
|
||||
void set_levels(log_levels levels, level::level_enum *global_level);
|
||||
|
||||
static registry &instance();
|
||||
|
||||
private:
|
||||
registry();
|
||||
~registry();
|
||||
|
||||
void throw_if_exists_(const std::string &logger_name);
|
||||
void register_logger_(std::shared_ptr<logger> new_logger);
|
||||
bool set_level_from_cfg_(logger *logger);
|
||||
std::mutex logger_map_mutex_, flusher_mutex_;
|
||||
std::recursive_mutex tp_mutex_;
|
||||
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
|
||||
log_levels log_levels_;
|
||||
std::unique_ptr<formatter> formatter_;
|
||||
spdlog::level::level_enum global_log_level_ = level::info;
|
||||
level::level_enum flush_level_ = level::off;
|
||||
err_handler err_handler_;
|
||||
std::shared_ptr<thread_pool> tp_;
|
||||
std::unique_ptr<periodic_worker> periodic_flusher_;
|
||||
std::shared_ptr<logger> default_logger_;
|
||||
bool automatic_registration_ = true;
|
||||
size_t backtrace_n_messages_ = 0;
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
# include "registry-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "registry.h"
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
// Default logger factory- creates synchronous loggers
|
||||
class logger;
|
||||
|
||||
struct synchronous_factory
|
||||
{
|
||||
template<typename Sink, typename... SinkArgs>
|
||||
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args)
|
||||
{
|
||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
||||
auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink));
|
||||
details::registry::instance().initialize_logger(new_logger);
|
||||
return new_logger;
|
||||
}
|
||||
};
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,160 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
// tcp client helper
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#pragma comment(lib, "Mswsock.lib")
|
||||
#pragma comment(lib, "AdvApi32.lib")
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class tcp_client
|
||||
{
|
||||
SOCKET socket_ = INVALID_SOCKET;
|
||||
|
||||
static void init_winsock_()
|
||||
{
|
||||
WSADATA wsaData;
|
||||
auto rv = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (rv != 0)
|
||||
{
|
||||
throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
static void throw_winsock_error_(const std::string &msg, int last_error)
|
||||
{
|
||||
char buf[512];
|
||||
::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL);
|
||||
|
||||
throw_spdlog_ex(fmt_lib::format("tcp_sink - {}: {}", msg, buf));
|
||||
}
|
||||
|
||||
public:
|
||||
tcp_client()
|
||||
{
|
||||
init_winsock_();
|
||||
}
|
||||
|
||||
~tcp_client()
|
||||
{
|
||||
close();
|
||||
::WSACleanup();
|
||||
}
|
||||
|
||||
bool is_connected() const
|
||||
{
|
||||
return socket_ != INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
::closesocket(socket_);
|
||||
socket_ = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
SOCKET fd() const
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
// try to connect or throw on failure
|
||||
void connect(const std::string &host, int port)
|
||||
{
|
||||
if (is_connected())
|
||||
{
|
||||
close();
|
||||
}
|
||||
struct addrinfo hints
|
||||
{};
|
||||
ZeroMemory(&hints, sizeof(hints));
|
||||
|
||||
hints.ai_family = AF_INET; // IPv4
|
||||
hints.ai_socktype = SOCK_STREAM; // TCP
|
||||
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
|
||||
hints.ai_protocol = 0;
|
||||
|
||||
auto port_str = std::to_string(port);
|
||||
struct addrinfo *addrinfo_result;
|
||||
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
||||
int last_error = 0;
|
||||
if (rv != 0)
|
||||
{
|
||||
last_error = ::WSAGetLastError();
|
||||
WSACleanup();
|
||||
throw_winsock_error_("getaddrinfo failed", last_error);
|
||||
}
|
||||
|
||||
// Try each address until we successfully connect(2).
|
||||
|
||||
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
|
||||
{
|
||||
socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (socket_ == INVALID_SOCKET)
|
||||
{
|
||||
last_error = ::WSAGetLastError();
|
||||
WSACleanup();
|
||||
continue;
|
||||
}
|
||||
if (::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
last_error = ::WSAGetLastError();
|
||||
close();
|
||||
}
|
||||
}
|
||||
::freeaddrinfo(addrinfo_result);
|
||||
if (socket_ == INVALID_SOCKET)
|
||||
{
|
||||
WSACleanup();
|
||||
throw_winsock_error_("connect failed", last_error);
|
||||
}
|
||||
|
||||
// set TCP_NODELAY
|
||||
int enable_flag = 1;
|
||||
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
|
||||
}
|
||||
|
||||
// Send exactly n_bytes of the given data.
|
||||
// On error close the connection and throw.
|
||||
void send(const char *data, size_t n_bytes)
|
||||
{
|
||||
size_t bytes_sent = 0;
|
||||
while (bytes_sent < n_bytes)
|
||||
{
|
||||
const int send_flags = 0;
|
||||
auto write_result = ::send(socket_, data + bytes_sent, (int)(n_bytes - bytes_sent), send_flags);
|
||||
if (write_result == SOCKET_ERROR)
|
||||
{
|
||||
int last_error = ::WSAGetLastError();
|
||||
close();
|
||||
throw_winsock_error_("send failed", last_error);
|
||||
}
|
||||
|
||||
if (write_result == 0) // (probably should not happen but in any case..)
|
||||
{
|
||||
break;
|
||||
}
|
||||
bytes_sent += static_cast<size_t>(write_result);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,145 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
# error include tcp_client-windows.h instead
|
||||
#endif
|
||||
|
||||
// tcp client helper
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class tcp_client
|
||||
{
|
||||
int socket_ = -1;
|
||||
|
||||
public:
|
||||
bool is_connected() const
|
||||
{
|
||||
return socket_ != -1;
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (is_connected())
|
||||
{
|
||||
::close(socket_);
|
||||
socket_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int fd() const
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
~tcp_client()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
// try to connect or throw on failure
|
||||
void connect(const std::string &host, int port)
|
||||
{
|
||||
close();
|
||||
struct addrinfo hints
|
||||
{};
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_INET; // IPv4
|
||||
hints.ai_socktype = SOCK_STREAM; // TCP
|
||||
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
|
||||
hints.ai_protocol = 0;
|
||||
|
||||
auto port_str = std::to_string(port);
|
||||
struct addrinfo *addrinfo_result;
|
||||
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
||||
if (rv != 0)
|
||||
{
|
||||
throw_spdlog_ex(fmt_lib::format("::getaddrinfo failed: {}", gai_strerror(rv)));
|
||||
}
|
||||
|
||||
// Try each address until we successfully connect(2).
|
||||
int last_errno = 0;
|
||||
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
|
||||
{
|
||||
#if defined(SOCK_CLOEXEC)
|
||||
const int flags = SOCK_CLOEXEC;
|
||||
#else
|
||||
const int flags = 0;
|
||||
#endif
|
||||
socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
|
||||
if (socket_ == -1)
|
||||
{
|
||||
last_errno = errno;
|
||||
continue;
|
||||
}
|
||||
rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
|
||||
if (rv == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
last_errno = errno;
|
||||
::close(socket_);
|
||||
socket_ = -1;
|
||||
}
|
||||
::freeaddrinfo(addrinfo_result);
|
||||
if (socket_ == -1)
|
||||
{
|
||||
throw_spdlog_ex("::connect failed", last_errno);
|
||||
}
|
||||
|
||||
// set TCP_NODELAY
|
||||
int enable_flag = 1;
|
||||
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
|
||||
|
||||
// prevent sigpipe on systems where MSG_NOSIGNAL is not available
|
||||
#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
|
||||
::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
|
||||
#endif
|
||||
|
||||
#if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
|
||||
# error "tcp_sink would raise SIGPIPE since niether SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
|
||||
#endif
|
||||
}
|
||||
|
||||
// Send exactly n_bytes of the given data.
|
||||
// On error close the connection and throw.
|
||||
void send(const char *data, size_t n_bytes)
|
||||
{
|
||||
size_t bytes_sent = 0;
|
||||
while (bytes_sent < n_bytes)
|
||||
{
|
||||
#if defined(MSG_NOSIGNAL)
|
||||
const int send_flags = MSG_NOSIGNAL;
|
||||
#else
|
||||
const int send_flags = 0;
|
||||
#endif
|
||||
auto write_result = ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
|
||||
if (write_result < 0)
|
||||
{
|
||||
close();
|
||||
throw_spdlog_ex("write(2) failed", errno);
|
||||
}
|
||||
|
||||
if (write_result == 0) // (probably should not happen but in any case..)
|
||||
{
|
||||
break;
|
||||
}
|
||||
bytes_sent += static_cast<size_t>(write_result);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,141 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
# include <spdlog/details/thread_pool.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
SPDLOG_INLINE thread_pool::thread_pool(
|
||||
size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start, std::function<void()> on_thread_stop)
|
||||
: q_(q_max_items)
|
||||
{
|
||||
if (threads_n == 0 || threads_n > 1000)
|
||||
{
|
||||
throw_spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid "
|
||||
"range is 1-1000)");
|
||||
}
|
||||
for (size_t i = 0; i < threads_n; i++)
|
||||
{
|
||||
threads_.emplace_back([this, on_thread_start, on_thread_stop] {
|
||||
on_thread_start();
|
||||
this->thread_pool::worker_loop_();
|
||||
on_thread_stop();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start)
|
||||
: thread_pool(q_max_items, threads_n, on_thread_start, [] {})
|
||||
{}
|
||||
|
||||
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n)
|
||||
: thread_pool(
|
||||
q_max_items, threads_n, [] {}, [] {})
|
||||
{}
|
||||
|
||||
// message all threads to terminate gracefully join them
|
||||
SPDLOG_INLINE thread_pool::~thread_pool()
|
||||
{
|
||||
SPDLOG_TRY
|
||||
{
|
||||
for (size_t i = 0; i < threads_.size(); i++)
|
||||
{
|
||||
post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
|
||||
}
|
||||
|
||||
for (auto &t : threads_)
|
||||
{
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
SPDLOG_CATCH_STD
|
||||
}
|
||||
|
||||
void SPDLOG_INLINE thread_pool::post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy)
|
||||
{
|
||||
async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
|
||||
post_async_msg_(std::move(async_m), overflow_policy);
|
||||
}
|
||||
|
||||
void SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy)
|
||||
{
|
||||
post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
|
||||
}
|
||||
|
||||
size_t SPDLOG_INLINE thread_pool::overrun_counter()
|
||||
{
|
||||
return q_.overrun_counter();
|
||||
}
|
||||
|
||||
void SPDLOG_INLINE thread_pool::reset_overrun_counter()
|
||||
{
|
||||
q_.reset_overrun_counter();
|
||||
}
|
||||
|
||||
size_t SPDLOG_INLINE thread_pool::queue_size()
|
||||
{
|
||||
return q_.size();
|
||||
}
|
||||
|
||||
void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
|
||||
{
|
||||
if (overflow_policy == async_overflow_policy::block)
|
||||
{
|
||||
q_.enqueue(std::move(new_msg));
|
||||
}
|
||||
else
|
||||
{
|
||||
q_.enqueue_nowait(std::move(new_msg));
|
||||
}
|
||||
}
|
||||
|
||||
void SPDLOG_INLINE thread_pool::worker_loop_()
|
||||
{
|
||||
while (process_next_msg_()) {}
|
||||
}
|
||||
|
||||
// process next message in the queue
|
||||
// return true if this thread should still be active (while no terminate msg
|
||||
// was received)
|
||||
bool SPDLOG_INLINE thread_pool::process_next_msg_()
|
||||
{
|
||||
async_msg incoming_async_msg;
|
||||
bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10));
|
||||
if (!dequeued)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (incoming_async_msg.msg_type)
|
||||
{
|
||||
case async_msg_type::log: {
|
||||
incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg);
|
||||
return true;
|
||||
}
|
||||
case async_msg_type::flush: {
|
||||
incoming_async_msg.worker_ptr->backend_flush_();
|
||||
return true;
|
||||
}
|
||||
|
||||
case async_msg_type::terminate: {
|
||||
return false;
|
||||
}
|
||||
|
||||
default: {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/details/log_msg_buffer.h>
|
||||
#include <spdlog/details/mpmc_blocking_q.h>
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace spdlog {
|
||||
class async_logger;
|
||||
|
||||
namespace details {
|
||||
|
||||
using async_logger_ptr = std::shared_ptr<spdlog::async_logger>;
|
||||
|
||||
enum class async_msg_type
|
||||
{
|
||||
log,
|
||||
flush,
|
||||
terminate
|
||||
};
|
||||
|
||||
// Async msg to move to/from the queue
|
||||
// Movable only. should never be copied
|
||||
struct async_msg : log_msg_buffer
|
||||
{
|
||||
async_msg_type msg_type{async_msg_type::log};
|
||||
async_logger_ptr worker_ptr;
|
||||
|
||||
async_msg() = default;
|
||||
~async_msg() = default;
|
||||
|
||||
// should only be moved in or out of the queue..
|
||||
async_msg(const async_msg &) = delete;
|
||||
|
||||
// support for vs2013 move
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||
async_msg(async_msg &&other)
|
||||
: log_msg_buffer(std::move(other))
|
||||
, msg_type(other.msg_type)
|
||||
, worker_ptr(std::move(other.worker_ptr))
|
||||
{}
|
||||
|
||||
async_msg &operator=(async_msg &&other)
|
||||
{
|
||||
*static_cast<log_msg_buffer *>(this) = std::move(other);
|
||||
msg_type = other.msg_type;
|
||||
worker_ptr = std::move(other.worker_ptr);
|
||||
return *this;
|
||||
}
|
||||
#else // (_MSC_VER) && _MSC_VER <= 1800
|
||||
async_msg(async_msg &&) = default;
|
||||
async_msg &operator=(async_msg &&) = default;
|
||||
#endif
|
||||
|
||||
// construct from log_msg with given type
|
||||
async_msg(async_logger_ptr &&worker, async_msg_type the_type, const details::log_msg &m)
|
||||
: log_msg_buffer{m}
|
||||
, msg_type{the_type}
|
||||
, worker_ptr{std::move(worker)}
|
||||
{}
|
||||
|
||||
async_msg(async_logger_ptr &&worker, async_msg_type the_type)
|
||||
: log_msg_buffer{}
|
||||
, msg_type{the_type}
|
||||
, worker_ptr{std::move(worker)}
|
||||
{}
|
||||
|
||||
explicit async_msg(async_msg_type the_type)
|
||||
: async_msg{nullptr, the_type}
|
||||
{}
|
||||
};
|
||||
|
||||
class SPDLOG_API thread_pool
|
||||
{
|
||||
public:
|
||||
using item_type = async_msg;
|
||||
using q_type = details::mpmc_blocking_queue<item_type>;
|
||||
|
||||
thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start, std::function<void()> on_thread_stop);
|
||||
thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start);
|
||||
thread_pool(size_t q_max_items, size_t threads_n);
|
||||
|
||||
// message all threads to terminate gracefully and join them
|
||||
~thread_pool();
|
||||
|
||||
thread_pool(const thread_pool &) = delete;
|
||||
thread_pool &operator=(thread_pool &&) = delete;
|
||||
|
||||
void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
|
||||
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
|
||||
size_t overrun_counter();
|
||||
void reset_overrun_counter();
|
||||
size_t queue_size();
|
||||
|
||||
private:
|
||||
q_type q_;
|
||||
|
||||
std::vector<std::thread> threads_;
|
||||
|
||||
void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy);
|
||||
void worker_loop_();
|
||||
|
||||
// process next message in the queue
|
||||
// return true if this thread should still be active (while no terminate msg
|
||||
// was received)
|
||||
bool process_next_msg_();
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
# include "thread_pool-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,111 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// Helper RAII over winsock udp client socket.
|
||||
// Will throw on construction if socket creation failed.
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/details/windows_include.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#pragma comment(lib, "Mswsock.lib")
|
||||
#pragma comment(lib, "AdvApi32.lib")
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class udp_client
|
||||
{
|
||||
static constexpr int TX_BUFFER_SIZE = 1024 * 10;
|
||||
SOCKET socket_ = INVALID_SOCKET;
|
||||
sockaddr_in addr_ = {0};
|
||||
|
||||
static void init_winsock_()
|
||||
{
|
||||
WSADATA wsaData;
|
||||
auto rv = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (rv != 0)
|
||||
{
|
||||
throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
static void throw_winsock_error_(const std::string &msg, int last_error)
|
||||
{
|
||||
char buf[512];
|
||||
::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL);
|
||||
|
||||
throw_spdlog_ex(fmt_lib::format("udp_sink - {}: {}", msg, buf));
|
||||
}
|
||||
|
||||
void cleanup_()
|
||||
{
|
||||
if (socket_ != INVALID_SOCKET)
|
||||
{
|
||||
::closesocket(socket_);
|
||||
}
|
||||
socket_ = INVALID_SOCKET;
|
||||
::WSACleanup();
|
||||
}
|
||||
|
||||
public:
|
||||
udp_client(const std::string &host, uint16_t port)
|
||||
{
|
||||
init_winsock_();
|
||||
|
||||
addr_.sin_family = PF_INET;
|
||||
addr_.sin_port = htons(port);
|
||||
addr_.sin_addr.s_addr = INADDR_ANY;
|
||||
if (InetPtonA(PF_INET, host.c_str(), &addr_.sin_addr.s_addr) != 1)
|
||||
{
|
||||
int last_error = ::WSAGetLastError();
|
||||
::WSACleanup();
|
||||
throw_winsock_error_("error: Invalid address!", last_error);
|
||||
}
|
||||
|
||||
socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (socket_ == INVALID_SOCKET)
|
||||
{
|
||||
int last_error = ::WSAGetLastError();
|
||||
::WSACleanup();
|
||||
throw_winsock_error_("error: Create Socket failed", last_error);
|
||||
}
|
||||
|
||||
int option_value = TX_BUFFER_SIZE;
|
||||
if (::setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<const char *>(&option_value), sizeof(option_value)) < 0)
|
||||
{
|
||||
int last_error = ::WSAGetLastError();
|
||||
cleanup_();
|
||||
throw_winsock_error_("error: setsockopt(SO_SNDBUF) Failed!", last_error);
|
||||
}
|
||||
}
|
||||
|
||||
~udp_client()
|
||||
{
|
||||
cleanup_();
|
||||
}
|
||||
|
||||
SOCKET fd() const
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
void send(const char *data, size_t n_bytes)
|
||||
{
|
||||
socklen_t tolen = sizeof(struct sockaddr);
|
||||
if (::sendto(socket_, data, static_cast<int>(n_bytes), 0, (struct sockaddr *)&addr_, tolen) == -1)
|
||||
{
|
||||
throw_spdlog_ex("sendto(2) failed", errno);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// Helper RAII over unix udp client socket.
|
||||
// Will throw on construction if the socket creation failed.
|
||||
|
||||
#ifdef _WIN32
|
||||
# error "include udp_client-windows.h instead"
|
||||
#endif
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/udp.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
class udp_client
|
||||
{
|
||||
static constexpr int TX_BUFFER_SIZE = 1024 * 10;
|
||||
int socket_ = -1;
|
||||
struct sockaddr_in sockAddr_;
|
||||
|
||||
void cleanup_()
|
||||
{
|
||||
if (socket_ != -1)
|
||||
{
|
||||
::close(socket_);
|
||||
socket_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
udp_client(const std::string &host, uint16_t port)
|
||||
{
|
||||
socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (socket_ < 0)
|
||||
{
|
||||
throw_spdlog_ex("error: Create Socket Failed!");
|
||||
}
|
||||
|
||||
int option_value = TX_BUFFER_SIZE;
|
||||
if (::setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<const char *>(&option_value), sizeof(option_value)) < 0)
|
||||
{
|
||||
cleanup_();
|
||||
throw_spdlog_ex("error: setsockopt(SO_SNDBUF) Failed!");
|
||||
}
|
||||
|
||||
sockAddr_.sin_family = AF_INET;
|
||||
sockAddr_.sin_port = htons(port);
|
||||
|
||||
if (::inet_aton(host.c_str(), &sockAddr_.sin_addr) == 0)
|
||||
{
|
||||
cleanup_();
|
||||
throw_spdlog_ex("error: Invalid address!");
|
||||
}
|
||||
|
||||
::memset(sockAddr_.sin_zero, 0x00, sizeof(sockAddr_.sin_zero));
|
||||
}
|
||||
|
||||
~udp_client()
|
||||
{
|
||||
cleanup_();
|
||||
}
|
||||
|
||||
int fd() const
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
// Send exactly n_bytes of the given data.
|
||||
// On error close the connection and throw.
|
||||
void send(const char *data, size_t n_bytes)
|
||||
{
|
||||
ssize_t toslen = 0;
|
||||
socklen_t tolen = sizeof(struct sockaddr);
|
||||
if ((toslen = ::sendto(socket_, data, n_bytes, 0, (struct sockaddr *)&sockAddr_, tolen)) == -1)
|
||||
{
|
||||
throw_spdlog_ex("sendto(2) failed", errno);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef NOMINMAX
|
||||
# define NOMINMAX // prevent windows redefining min/max
|
||||
#endif
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
@@ -0,0 +1,248 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cctype>
|
||||
#include <spdlog/common.h>
|
||||
|
||||
#if defined(__has_include)
|
||||
# if __has_include(<version>)
|
||||
# include <version>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if __cpp_lib_span >= 202002L
|
||||
# include <span>
|
||||
#endif
|
||||
|
||||
//
|
||||
// Support for logging binary data as hex
|
||||
// format flags, any combination of the following:
|
||||
// {:X} - print in uppercase.
|
||||
// {:s} - don't separate each byte with space.
|
||||
// {:p} - don't print the position on each line start.
|
||||
// {:n} - don't split the output to lines.
|
||||
// {:a} - show ASCII if :n is not set
|
||||
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// std::vector<char> v(200, 0x0b);
|
||||
// logger->info("Some buffer {}", spdlog::to_hex(v));
|
||||
// char buf[128];
|
||||
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
|
||||
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf), 16));
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
template<typename It>
|
||||
class dump_info
|
||||
{
|
||||
public:
|
||||
dump_info(It range_begin, It range_end, size_t size_per_line)
|
||||
: begin_(range_begin)
|
||||
, end_(range_end)
|
||||
, size_per_line_(size_per_line)
|
||||
{}
|
||||
|
||||
// do not use begin() and end() to avoid collision with fmt/ranges
|
||||
It get_begin() const
|
||||
{
|
||||
return begin_;
|
||||
}
|
||||
It get_end() const
|
||||
{
|
||||
return end_;
|
||||
}
|
||||
size_t size_per_line() const
|
||||
{
|
||||
return size_per_line_;
|
||||
}
|
||||
|
||||
private:
|
||||
It begin_, end_;
|
||||
size_t size_per_line_;
|
||||
};
|
||||
} // namespace details
|
||||
|
||||
// create a dump_info that wraps the given container
|
||||
template<typename Container>
|
||||
inline details::dump_info<typename Container::const_iterator> to_hex(const Container &container, size_t size_per_line = 32)
|
||||
{
|
||||
static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
|
||||
using Iter = typename Container::const_iterator;
|
||||
return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
|
||||
}
|
||||
|
||||
#if __cpp_lib_span >= 202002L
|
||||
|
||||
template<typename Value, size_t Extent>
|
||||
inline details::dump_info<typename std::span<Value, Extent>::iterator> to_hex(
|
||||
const std::span<Value, Extent> &container, size_t size_per_line = 32)
|
||||
{
|
||||
using Container = std::span<Value, Extent>;
|
||||
static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
|
||||
using Iter = typename Container::iterator;
|
||||
return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// create dump_info from ranges
|
||||
template<typename It>
|
||||
inline details::dump_info<It> to_hex(const It range_begin, const It range_end, size_t size_per_line = 32)
|
||||
{
|
||||
return details::dump_info<It>(range_begin, range_end, size_per_line);
|
||||
}
|
||||
|
||||
} // namespace spdlog
|
||||
|
||||
namespace
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
std
|
||||
#else
|
||||
fmt
|
||||
#endif
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
struct formatter<spdlog::details::dump_info<T>, char>
|
||||
{
|
||||
const char delimiter = ' ';
|
||||
bool put_newlines = true;
|
||||
bool put_delimiters = true;
|
||||
bool use_uppercase = false;
|
||||
bool put_positions = true; // position on start of each line
|
||||
bool show_ascii = false;
|
||||
|
||||
// parse the format string flags
|
||||
template<typename ParseContext>
|
||||
SPDLOG_CONSTEXPR_FUNC auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
||||
{
|
||||
auto it = ctx.begin();
|
||||
while (it != ctx.end() && *it != '}')
|
||||
{
|
||||
switch (*it)
|
||||
{
|
||||
case 'X':
|
||||
use_uppercase = true;
|
||||
break;
|
||||
case 's':
|
||||
put_delimiters = false;
|
||||
break;
|
||||
case 'p':
|
||||
put_positions = false;
|
||||
break;
|
||||
case 'n':
|
||||
put_newlines = false;
|
||||
show_ascii = false;
|
||||
break;
|
||||
case 'a':
|
||||
if (put_newlines)
|
||||
{
|
||||
show_ascii = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
// format the given bytes range as hex
|
||||
template<typename FormatContext, typename Container>
|
||||
auto format(const spdlog::details::dump_info<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
|
||||
{
|
||||
SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
|
||||
SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
|
||||
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
|
||||
|
||||
#if !defined(SPDLOG_USE_STD_FORMAT) && FMT_VERSION < 60000
|
||||
auto inserter = ctx.begin();
|
||||
#else
|
||||
auto inserter = ctx.out();
|
||||
#endif
|
||||
|
||||
int size_per_line = static_cast<int>(the_range.size_per_line());
|
||||
auto start_of_line = the_range.get_begin();
|
||||
for (auto i = the_range.get_begin(); i != the_range.get_end(); i++)
|
||||
{
|
||||
auto ch = static_cast<unsigned char>(*i);
|
||||
|
||||
if (put_newlines && (i == the_range.get_begin() || i - start_of_line >= size_per_line))
|
||||
{
|
||||
if (show_ascii && i != the_range.get_begin())
|
||||
{
|
||||
*inserter++ = delimiter;
|
||||
*inserter++ = delimiter;
|
||||
for (auto j = start_of_line; j < i; j++)
|
||||
{
|
||||
auto pc = static_cast<unsigned char>(*j);
|
||||
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
|
||||
}
|
||||
}
|
||||
|
||||
put_newline(inserter, static_cast<size_t>(i - the_range.get_begin()));
|
||||
|
||||
// put first byte without delimiter in front of it
|
||||
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
||||
*inserter++ = hex_chars[ch & 0x0f];
|
||||
start_of_line = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (put_delimiters)
|
||||
{
|
||||
*inserter++ = delimiter;
|
||||
}
|
||||
|
||||
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
||||
*inserter++ = hex_chars[ch & 0x0f];
|
||||
}
|
||||
if (show_ascii) // add ascii to last line
|
||||
{
|
||||
if (the_range.get_end() - the_range.get_begin() > size_per_line)
|
||||
{
|
||||
auto blank_num = size_per_line - (the_range.get_end() - start_of_line);
|
||||
while (blank_num-- > 0)
|
||||
{
|
||||
*inserter++ = delimiter;
|
||||
*inserter++ = delimiter;
|
||||
if (put_delimiters)
|
||||
{
|
||||
*inserter++ = delimiter;
|
||||
}
|
||||
}
|
||||
}
|
||||
*inserter++ = delimiter;
|
||||
*inserter++ = delimiter;
|
||||
for (auto j = start_of_line; j != the_range.get_end(); j++)
|
||||
{
|
||||
auto pc = static_cast<unsigned char>(*j);
|
||||
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
|
||||
}
|
||||
}
|
||||
return inserter;
|
||||
}
|
||||
|
||||
// put newline(and position header)
|
||||
template<typename It>
|
||||
void put_newline(It inserter, std::size_t pos)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
*inserter++ = '\r';
|
||||
#endif
|
||||
*inserter++ = '\n';
|
||||
|
||||
if (put_positions)
|
||||
{
|
||||
spdlog::fmt_lib::format_to(inserter, SPDLOG_FMT_STRING("{:04X}: "), pos);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
@@ -0,0 +1,234 @@
|
||||
// Formatting library for C++ - dynamic format arguments
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_ARGS_H_
|
||||
#define FMT_ARGS_H_
|
||||
|
||||
#include <functional> // std::reference_wrapper
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <vector>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T> struct is_reference_wrapper : std::false_type {};
|
||||
template <typename T>
|
||||
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
|
||||
|
||||
template <typename T> const T& unwrap(const T& v) { return v; }
|
||||
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
|
||||
return static_cast<const T&>(v);
|
||||
}
|
||||
|
||||
class dynamic_arg_list {
|
||||
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
|
||||
// templates it doesn't complain about inability to deduce single translation
|
||||
// unit for placing vtable. So storage_node_base is made a fake template.
|
||||
template <typename = void> struct node {
|
||||
virtual ~node() = default;
|
||||
std::unique_ptr<node<>> next;
|
||||
};
|
||||
|
||||
template <typename T> struct typed_node : node<> {
|
||||
T value;
|
||||
|
||||
template <typename Arg>
|
||||
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
|
||||
: value(arg.data(), arg.size()) {}
|
||||
};
|
||||
|
||||
std::unique_ptr<node<>> head_;
|
||||
|
||||
public:
|
||||
template <typename T, typename Arg> const T& push(const Arg& arg) {
|
||||
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
|
||||
auto& value = new_node->value;
|
||||
new_node->next = std::move(head_);
|
||||
head_ = std::move(new_node);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
\rst
|
||||
A dynamic version of `fmt::format_arg_store`.
|
||||
It's equipped with a storage to potentially temporary objects which lifetimes
|
||||
could be shorter than the format arguments object.
|
||||
|
||||
It can be implicitly converted into `~fmt::basic_format_args` for passing
|
||||
into type-erased formatting functions such as `~fmt::vformat`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename Context>
|
||||
class dynamic_format_arg_store
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||
// Workaround a GCC template argument substitution bug.
|
||||
: public basic_format_args<Context>
|
||||
#endif
|
||||
{
|
||||
private:
|
||||
using char_type = typename Context::char_type;
|
||||
|
||||
template <typename T> struct need_copy {
|
||||
static constexpr detail::type mapped_type =
|
||||
detail::mapped_type_constant<T, Context>::value;
|
||||
|
||||
enum {
|
||||
value = !(detail::is_reference_wrapper<T>::value ||
|
||||
std::is_same<T, basic_string_view<char_type>>::value ||
|
||||
std::is_same<T, detail::std_string_view<char_type>>::value ||
|
||||
(mapped_type != detail::type::cstring_type &&
|
||||
mapped_type != detail::type::string_type &&
|
||||
mapped_type != detail::type::custom_type))
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using stored_type = conditional_t<detail::is_string<T>::value &&
|
||||
!has_formatter<T, Context>::value &&
|
||||
!detail::is_reference_wrapper<T>::value,
|
||||
std::basic_string<char_type>, T>;
|
||||
|
||||
// Storage of basic_format_arg must be contiguous.
|
||||
std::vector<basic_format_arg<Context>> data_;
|
||||
std::vector<detail::named_arg_info<char_type>> named_info_;
|
||||
|
||||
// Storage of arguments not fitting into basic_format_arg must grow
|
||||
// without relocation because items in data_ refer to it.
|
||||
detail::dynamic_arg_list dynamic_args_;
|
||||
|
||||
friend class basic_format_args<Context>;
|
||||
|
||||
unsigned long long get_types() const {
|
||||
return detail::is_unpacked_bit | data_.size() |
|
||||
(named_info_.empty()
|
||||
? 0ULL
|
||||
: static_cast<unsigned long long>(detail::has_named_args_bit));
|
||||
}
|
||||
|
||||
const basic_format_arg<Context>* data() const {
|
||||
return named_info_.empty() ? data_.data() : data_.data() + 1;
|
||||
}
|
||||
|
||||
template <typename T> void emplace_arg(const T& arg) {
|
||||
data_.emplace_back(detail::make_arg<Context>(arg));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
|
||||
if (named_info_.empty()) {
|
||||
constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
|
||||
data_.insert(data_.begin(), {zero_ptr, 0});
|
||||
}
|
||||
data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
|
||||
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
|
||||
data->pop_back();
|
||||
};
|
||||
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
|
||||
guard{&data_, pop_one};
|
||||
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
|
||||
data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
|
||||
guard.release();
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr dynamic_format_arg_store() = default;
|
||||
|
||||
/**
|
||||
\rst
|
||||
Adds an argument into the dynamic store for later passing to a formatting
|
||||
function.
|
||||
|
||||
Note that custom types and string types (but not string views) are copied
|
||||
into the store dynamically allocating memory if necessary.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
store.push_back(42);
|
||||
store.push_back("abc");
|
||||
store.push_back(1.5f);
|
||||
std::string result = fmt::vformat("{} and {} and {}", store);
|
||||
\endrst
|
||||
*/
|
||||
template <typename T> void push_back(const T& arg) {
|
||||
if (detail::const_check(need_copy<T>::value))
|
||||
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
|
||||
else
|
||||
emplace_arg(detail::unwrap(arg));
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Adds a reference to the argument into the dynamic store for later passing to
|
||||
a formatting function.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
char band[] = "Rolling Stones";
|
||||
store.push_back(std::cref(band));
|
||||
band[9] = 'c'; // Changing str affects the output.
|
||||
std::string result = fmt::vformat("{}", store);
|
||||
// result == "Rolling Scones"
|
||||
\endrst
|
||||
*/
|
||||
template <typename T> void push_back(std::reference_wrapper<T> arg) {
|
||||
static_assert(
|
||||
need_copy<T>::value,
|
||||
"objects of built-in types and string views are always copied");
|
||||
emplace_arg(arg.get());
|
||||
}
|
||||
|
||||
/**
|
||||
Adds named argument into the dynamic store for later passing to a formatting
|
||||
function. ``std::reference_wrapper`` is supported to avoid copying of the
|
||||
argument. The name is always copied into the store.
|
||||
*/
|
||||
template <typename T>
|
||||
void push_back(const detail::named_arg<char_type, T>& arg) {
|
||||
const char_type* arg_name =
|
||||
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
|
||||
if (detail::const_check(need_copy<T>::value)) {
|
||||
emplace_arg(
|
||||
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
|
||||
} else {
|
||||
emplace_arg(fmt::arg(arg_name, arg.value));
|
||||
}
|
||||
}
|
||||
|
||||
/** Erase all elements from the store */
|
||||
void clear() {
|
||||
data_.clear();
|
||||
named_info_.clear();
|
||||
dynamic_args_ = detail::dynamic_arg_list();
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Reserves space to store at least *new_cap* arguments including
|
||||
*new_cap_named* named arguments.
|
||||
\endrst
|
||||
*/
|
||||
void reserve(size_t new_cap, size_t new_cap_named) {
|
||||
FMT_ASSERT(new_cap >= new_cap_named,
|
||||
"Set of arguments includes set of named arguments");
|
||||
data_.reserve(new_cap);
|
||||
named_info_.reserve(new_cap_named);
|
||||
}
|
||||
};
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_ARGS_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,638 @@
|
||||
// Formatting library for C++ - color support
|
||||
//
|
||||
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_COLOR_H_
|
||||
#define FMT_COLOR_H_
|
||||
|
||||
#include "format.h"
|
||||
|
||||
// __declspec(deprecated) is broken in some MSVC versions.
|
||||
#if FMT_MSC_VER
|
||||
# define FMT_DEPRECATED_NONMSVC
|
||||
#else
|
||||
# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
enum class color : uint32_t {
|
||||
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
||||
aqua = 0x00FFFF, // rgb(0,255,255)
|
||||
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
||||
azure = 0xF0FFFF, // rgb(240,255,255)
|
||||
beige = 0xF5F5DC, // rgb(245,245,220)
|
||||
bisque = 0xFFE4C4, // rgb(255,228,196)
|
||||
black = 0x000000, // rgb(0,0,0)
|
||||
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
||||
blue = 0x0000FF, // rgb(0,0,255)
|
||||
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
||||
brown = 0xA52A2A, // rgb(165,42,42)
|
||||
burly_wood = 0xDEB887, // rgb(222,184,135)
|
||||
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
||||
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
||||
chocolate = 0xD2691E, // rgb(210,105,30)
|
||||
coral = 0xFF7F50, // rgb(255,127,80)
|
||||
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
||||
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
||||
crimson = 0xDC143C, // rgb(220,20,60)
|
||||
cyan = 0x00FFFF, // rgb(0,255,255)
|
||||
dark_blue = 0x00008B, // rgb(0,0,139)
|
||||
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
||||
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
||||
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
||||
dark_green = 0x006400, // rgb(0,100,0)
|
||||
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
||||
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
||||
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
||||
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
||||
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
||||
dark_red = 0x8B0000, // rgb(139,0,0)
|
||||
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
||||
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
||||
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
||||
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
||||
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
||||
dark_violet = 0x9400D3, // rgb(148,0,211)
|
||||
deep_pink = 0xFF1493, // rgb(255,20,147)
|
||||
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
||||
dim_gray = 0x696969, // rgb(105,105,105)
|
||||
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
||||
fire_brick = 0xB22222, // rgb(178,34,34)
|
||||
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
||||
forest_green = 0x228B22, // rgb(34,139,34)
|
||||
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
||||
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
||||
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
||||
gold = 0xFFD700, // rgb(255,215,0)
|
||||
golden_rod = 0xDAA520, // rgb(218,165,32)
|
||||
gray = 0x808080, // rgb(128,128,128)
|
||||
green = 0x008000, // rgb(0,128,0)
|
||||
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
||||
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
||||
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
||||
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
||||
indigo = 0x4B0082, // rgb(75,0,130)
|
||||
ivory = 0xFFFFF0, // rgb(255,255,240)
|
||||
khaki = 0xF0E68C, // rgb(240,230,140)
|
||||
lavender = 0xE6E6FA, // rgb(230,230,250)
|
||||
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
||||
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
||||
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
||||
light_blue = 0xADD8E6, // rgb(173,216,230)
|
||||
light_coral = 0xF08080, // rgb(240,128,128)
|
||||
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
||||
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
||||
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
||||
light_green = 0x90EE90, // rgb(144,238,144)
|
||||
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
||||
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
||||
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
||||
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
||||
light_slate_gray = 0x778899, // rgb(119,136,153)
|
||||
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
||||
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
||||
lime = 0x00FF00, // rgb(0,255,0)
|
||||
lime_green = 0x32CD32, // rgb(50,205,50)
|
||||
linen = 0xFAF0E6, // rgb(250,240,230)
|
||||
magenta = 0xFF00FF, // rgb(255,0,255)
|
||||
maroon = 0x800000, // rgb(128,0,0)
|
||||
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
||||
medium_blue = 0x0000CD, // rgb(0,0,205)
|
||||
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
||||
medium_purple = 0x9370DB, // rgb(147,112,219)
|
||||
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
||||
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
||||
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
||||
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
||||
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
||||
midnight_blue = 0x191970, // rgb(25,25,112)
|
||||
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
||||
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
||||
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
||||
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
||||
navy = 0x000080, // rgb(0,0,128)
|
||||
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
||||
olive = 0x808000, // rgb(128,128,0)
|
||||
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
||||
orange = 0xFFA500, // rgb(255,165,0)
|
||||
orange_red = 0xFF4500, // rgb(255,69,0)
|
||||
orchid = 0xDA70D6, // rgb(218,112,214)
|
||||
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
||||
pale_green = 0x98FB98, // rgb(152,251,152)
|
||||
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
||||
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
||||
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
||||
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
||||
peru = 0xCD853F, // rgb(205,133,63)
|
||||
pink = 0xFFC0CB, // rgb(255,192,203)
|
||||
plum = 0xDDA0DD, // rgb(221,160,221)
|
||||
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
||||
purple = 0x800080, // rgb(128,0,128)
|
||||
rebecca_purple = 0x663399, // rgb(102,51,153)
|
||||
red = 0xFF0000, // rgb(255,0,0)
|
||||
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
||||
royal_blue = 0x4169E1, // rgb(65,105,225)
|
||||
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
||||
salmon = 0xFA8072, // rgb(250,128,114)
|
||||
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
||||
sea_green = 0x2E8B57, // rgb(46,139,87)
|
||||
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
||||
sienna = 0xA0522D, // rgb(160,82,45)
|
||||
silver = 0xC0C0C0, // rgb(192,192,192)
|
||||
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
||||
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
||||
slate_gray = 0x708090, // rgb(112,128,144)
|
||||
snow = 0xFFFAFA, // rgb(255,250,250)
|
||||
spring_green = 0x00FF7F, // rgb(0,255,127)
|
||||
steel_blue = 0x4682B4, // rgb(70,130,180)
|
||||
tan = 0xD2B48C, // rgb(210,180,140)
|
||||
teal = 0x008080, // rgb(0,128,128)
|
||||
thistle = 0xD8BFD8, // rgb(216,191,216)
|
||||
tomato = 0xFF6347, // rgb(255,99,71)
|
||||
turquoise = 0x40E0D0, // rgb(64,224,208)
|
||||
violet = 0xEE82EE, // rgb(238,130,238)
|
||||
wheat = 0xF5DEB3, // rgb(245,222,179)
|
||||
white = 0xFFFFFF, // rgb(255,255,255)
|
||||
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||
}; // enum class color
|
||||
|
||||
enum class terminal_color : uint8_t {
|
||||
black = 30,
|
||||
red,
|
||||
green,
|
||||
yellow,
|
||||
blue,
|
||||
magenta,
|
||||
cyan,
|
||||
white,
|
||||
bright_black = 90,
|
||||
bright_red,
|
||||
bright_green,
|
||||
bright_yellow,
|
||||
bright_blue,
|
||||
bright_magenta,
|
||||
bright_cyan,
|
||||
bright_white
|
||||
};
|
||||
|
||||
enum class emphasis : uint8_t {
|
||||
bold = 1,
|
||||
faint = 1 << 1,
|
||||
italic = 1 << 2,
|
||||
underline = 1 << 3,
|
||||
blink = 1 << 4,
|
||||
reverse = 1 << 5,
|
||||
conceal = 1 << 6,
|
||||
strikethrough = 1 << 7,
|
||||
};
|
||||
|
||||
// rgb is a struct for red, green and blue colors.
|
||||
// Using the name "rgb" makes some editors show the color in a tooltip.
|
||||
struct rgb {
|
||||
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
|
||||
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
||||
FMT_CONSTEXPR rgb(uint32_t hex)
|
||||
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
|
||||
FMT_CONSTEXPR rgb(color hex)
|
||||
: r((uint32_t(hex) >> 16) & 0xFF),
|
||||
g((uint32_t(hex) >> 8) & 0xFF),
|
||||
b(uint32_t(hex) & 0xFF) {}
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
// color is a struct of either a rgb color or a terminal color.
|
||||
struct color_type {
|
||||
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
|
||||
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
|
||||
value{} {
|
||||
value.rgb_color = static_cast<uint32_t>(rgb_color);
|
||||
}
|
||||
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
|
||||
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
|
||||
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
||||
}
|
||||
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
|
||||
value{} {
|
||||
value.term_color = static_cast<uint8_t>(term_color);
|
||||
}
|
||||
bool is_rgb;
|
||||
union color_union {
|
||||
uint8_t term_color;
|
||||
uint32_t rgb_color;
|
||||
} value;
|
||||
};
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
/** A text style consisting of foreground and background colors and emphasis. */
|
||||
class text_style {
|
||||
public:
|
||||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
||||
: set_foreground_color(),
|
||||
set_background_color(),
|
||||
ems(em) {}
|
||||
|
||||
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
|
||||
if (!set_foreground_color) {
|
||||
set_foreground_color = rhs.set_foreground_color;
|
||||
foreground_color = rhs.foreground_color;
|
||||
} else if (rhs.set_foreground_color) {
|
||||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||
FMT_THROW(format_error("can't OR a terminal color"));
|
||||
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
||||
}
|
||||
|
||||
if (!set_background_color) {
|
||||
set_background_color = rhs.set_background_color;
|
||||
background_color = rhs.background_color;
|
||||
} else if (rhs.set_background_color) {
|
||||
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||
FMT_THROW(format_error("can't OR a terminal color"));
|
||||
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
||||
}
|
||||
|
||||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
||||
static_cast<uint8_t>(rhs.ems));
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
|
||||
const text_style& rhs) {
|
||||
return lhs |= rhs;
|
||||
}
|
||||
|
||||
FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=(
|
||||
const text_style& rhs) {
|
||||
return and_assign(rhs);
|
||||
}
|
||||
|
||||
FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style
|
||||
operator&(text_style lhs, const text_style& rhs) {
|
||||
return lhs.and_assign(rhs);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
||||
return set_foreground_color;
|
||||
}
|
||||
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
|
||||
return set_background_color;
|
||||
}
|
||||
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
||||
return static_cast<uint8_t>(ems) != 0;
|
||||
}
|
||||
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT {
|
||||
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
|
||||
return foreground_color;
|
||||
}
|
||||
FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT {
|
||||
FMT_ASSERT(has_background(), "no background specified for this style");
|
||||
return background_color;
|
||||
}
|
||||
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
|
||||
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
|
||||
return ems;
|
||||
}
|
||||
|
||||
private:
|
||||
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||
detail::color_type text_color) FMT_NOEXCEPT
|
||||
: set_foreground_color(),
|
||||
set_background_color(),
|
||||
ems() {
|
||||
if (is_foreground) {
|
||||
foreground_color = text_color;
|
||||
set_foreground_color = true;
|
||||
} else {
|
||||
background_color = text_color;
|
||||
set_background_color = true;
|
||||
}
|
||||
}
|
||||
|
||||
// DEPRECATED!
|
||||
FMT_CONSTEXPR text_style& and_assign(const text_style& rhs) {
|
||||
if (!set_foreground_color) {
|
||||
set_foreground_color = rhs.set_foreground_color;
|
||||
foreground_color = rhs.foreground_color;
|
||||
} else if (rhs.set_foreground_color) {
|
||||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||
FMT_THROW(format_error("can't AND a terminal color"));
|
||||
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
|
||||
}
|
||||
|
||||
if (!set_background_color) {
|
||||
set_background_color = rhs.set_background_color;
|
||||
background_color = rhs.background_color;
|
||||
} else if (rhs.set_background_color) {
|
||||
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||
FMT_THROW(format_error("can't AND a terminal color"));
|
||||
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
|
||||
}
|
||||
|
||||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
|
||||
static_cast<uint8_t>(rhs.ems));
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
|
||||
FMT_NOEXCEPT;
|
||||
|
||||
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
|
||||
FMT_NOEXCEPT;
|
||||
|
||||
detail::color_type foreground_color;
|
||||
detail::color_type background_color;
|
||||
bool set_foreground_color;
|
||||
bool set_background_color;
|
||||
emphasis ems;
|
||||
};
|
||||
|
||||
/** Creates a text style from the foreground (text) color. */
|
||||
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
|
||||
return text_style(true, foreground);
|
||||
}
|
||||
|
||||
/** Creates a text style from the background color. */
|
||||
FMT_CONSTEXPR inline text_style bg(detail::color_type background) FMT_NOEXCEPT {
|
||||
return text_style(false, background);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR inline text_style operator|(emphasis lhs,
|
||||
emphasis rhs) FMT_NOEXCEPT {
|
||||
return text_style(lhs) | rhs;
|
||||
}
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
template <typename Char> struct ansi_color_escape {
|
||||
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
|
||||
const char* esc) FMT_NOEXCEPT {
|
||||
// If we have a terminal color, we need to output another escape code
|
||||
// sequence.
|
||||
if (!text_color.is_rgb) {
|
||||
bool is_background = esc == string_view("\x1b[48;2;");
|
||||
uint32_t value = text_color.value.term_color;
|
||||
// Background ASCII codes are the same as the foreground ones but with
|
||||
// 10 more.
|
||||
if (is_background) value += 10u;
|
||||
|
||||
size_t index = 0;
|
||||
buffer[index++] = static_cast<Char>('\x1b');
|
||||
buffer[index++] = static_cast<Char>('[');
|
||||
|
||||
if (value >= 100u) {
|
||||
buffer[index++] = static_cast<Char>('1');
|
||||
value %= 100u;
|
||||
}
|
||||
buffer[index++] = static_cast<Char>('0' + value / 10u);
|
||||
buffer[index++] = static_cast<Char>('0' + value % 10u);
|
||||
|
||||
buffer[index++] = static_cast<Char>('m');
|
||||
buffer[index++] = static_cast<Char>('\0');
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
buffer[i] = static_cast<Char>(esc[i]);
|
||||
}
|
||||
rgb color(text_color.value.rgb_color);
|
||||
to_esc(color.r, buffer + 7, ';');
|
||||
to_esc(color.g, buffer + 11, ';');
|
||||
to_esc(color.b, buffer + 15, 'm');
|
||||
buffer[19] = static_cast<Char>(0);
|
||||
}
|
||||
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
||||
uint8_t em_codes[num_emphases] = {};
|
||||
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
|
||||
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
|
||||
if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
|
||||
if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
|
||||
if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
|
||||
if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
|
||||
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
|
||||
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
|
||||
|
||||
size_t index = 0;
|
||||
for (size_t i = 0; i < num_emphases; ++i) {
|
||||
if (!em_codes[i]) continue;
|
||||
buffer[index++] = static_cast<Char>('\x1b');
|
||||
buffer[index++] = static_cast<Char>('[');
|
||||
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
|
||||
buffer[index++] = static_cast<Char>('m');
|
||||
}
|
||||
buffer[index++] = static_cast<Char>(0);
|
||||
}
|
||||
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
|
||||
|
||||
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
|
||||
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT {
|
||||
return buffer + std::char_traits<Char>::length(buffer);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t num_emphases = 8;
|
||||
Char buffer[7u + 3u * num_emphases + 1u];
|
||||
|
||||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
||||
char delimiter) FMT_NOEXCEPT {
|
||||
out[0] = static_cast<Char>('0' + c / 100);
|
||||
out[1] = static_cast<Char>('0' + c / 10 % 10);
|
||||
out[2] = static_cast<Char>('0' + c % 10);
|
||||
out[3] = static_cast<Char>(delimiter);
|
||||
}
|
||||
static FMT_CONSTEXPR bool has_emphasis(emphasis em,
|
||||
emphasis mask) FMT_NOEXCEPT {
|
||||
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
||||
detail::color_type foreground) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
||||
detail::color_type background) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(background, "\x1b[48;2;");
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(em);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
|
||||
std::fputs(chars, stream);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
|
||||
std::fputws(chars, stream);
|
||||
}
|
||||
|
||||
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
|
||||
fputs("\x1b[0m", stream);
|
||||
}
|
||||
|
||||
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
||||
fputs(L"\x1b[0m", stream);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
|
||||
auto reset_color = string_view("\x1b[0m");
|
||||
buffer.append(reset_color.begin(), reset_color.end());
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
void vformat_to(buffer<Char>& buf, const text_style& ts,
|
||||
basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
bool has_style = false;
|
||||
if (ts.has_emphasis()) {
|
||||
has_style = true;
|
||||
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
|
||||
buf.append(emphasis.begin(), emphasis.end());
|
||||
}
|
||||
if (ts.has_foreground()) {
|
||||
has_style = true;
|
||||
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
|
||||
buf.append(foreground.begin(), foreground.end());
|
||||
}
|
||||
if (ts.has_background()) {
|
||||
has_style = true;
|
||||
auto background = detail::make_background_color<Char>(ts.get_background());
|
||||
buf.append(background.begin(), background.end());
|
||||
}
|
||||
detail::vformat_to(buf, format_str, args, {});
|
||||
if (has_style) detail::reset_color<Char>(buf);
|
||||
}
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buf;
|
||||
detail::vformat_to(buf, ts, to_string_view(format), args);
|
||||
buf.push_back(Char(0));
|
||||
detail::fputs(buf.data(), f);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats a string and prints it to the specified file stream using ANSI
|
||||
escape sequences to specify text formatting.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
vprint(f, ts, format_str,
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||
specify text formatting.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
void print(const text_style& ts, const S& format_str, const Args&... args) {
|
||||
return print(stdout, ts, format_str, args...);
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> vformat(
|
||||
const text_style& ts, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buf;
|
||||
detail::vformat_to(buf, ts, to_string_view(format_str), args);
|
||||
return fmt::to_string(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats arguments and returns the result as a string using ANSI
|
||||
escape sequences to specify text formatting.
|
||||
|
||||
**Example**::
|
||||
|
||||
#include <fmt/color.h>
|
||||
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"The answer is {}", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
return fmt::vformat(ts, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
/**
|
||||
Formats a string with the given text_style and writes the output to ``out``.
|
||||
*/
|
||||
template <typename OutputIt, typename Char,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
||||
OutputIt vformat_to(
|
||||
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
auto&& buf = detail::get_buffer<Char>(out);
|
||||
detail::vformat_to(buf, ts, format_str, args);
|
||||
return detail::get_iterator(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats arguments with the given text_style, writes the result to the output
|
||||
iterator ``out`` and returns the iterator past the end of the output range.
|
||||
|
||||
**Example**::
|
||||
|
||||
std::vector<char> out;
|
||||
fmt::format_to(std::back_inserter(out),
|
||||
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
|
||||
detail::is_string<S>::value>
|
||||
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
|
||||
Args&&... args) ->
|
||||
typename std::enable_if<enable, OutputIt>::type {
|
||||
return vformat_to(out, ts, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_COLOR_H_
|
||||
@@ -0,0 +1,642 @@
|
||||
// Formatting library for C++ - experimental format string compilation
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_COMPILE_H_
|
||||
#define FMT_COMPILE_H_
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
|
||||
// An output iterator that counts the number of objects written to it and
|
||||
// discards them.
|
||||
class counting_iterator {
|
||||
private:
|
||||
size_t count_;
|
||||
|
||||
public:
|
||||
using iterator_category = std::output_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = void;
|
||||
using reference = void;
|
||||
using _Unchecked_type = counting_iterator; // Mark iterator as checked.
|
||||
|
||||
struct value_type {
|
||||
template <typename T> void operator=(const T&) {}
|
||||
};
|
||||
|
||||
counting_iterator() : count_(0) {}
|
||||
|
||||
size_t count() const { return count_; }
|
||||
|
||||
counting_iterator& operator++() {
|
||||
++count_;
|
||||
return *this;
|
||||
}
|
||||
counting_iterator operator++(int) {
|
||||
auto it = *this;
|
||||
++*this;
|
||||
return it;
|
||||
}
|
||||
|
||||
friend counting_iterator operator+(counting_iterator it, difference_type n) {
|
||||
it.count_ += static_cast<size_t>(n);
|
||||
return it;
|
||||
}
|
||||
|
||||
value_type operator*() const { return {}; }
|
||||
};
|
||||
|
||||
template <typename Char, typename InputIt>
|
||||
inline counting_iterator copy_str(InputIt begin, InputIt end,
|
||||
counting_iterator it) {
|
||||
return it + (end - begin);
|
||||
}
|
||||
|
||||
template <typename OutputIt> class truncating_iterator_base {
|
||||
protected:
|
||||
OutputIt out_;
|
||||
size_t limit_;
|
||||
size_t count_ = 0;
|
||||
|
||||
truncating_iterator_base() : out_(), limit_(0) {}
|
||||
|
||||
truncating_iterator_base(OutputIt out, size_t limit)
|
||||
: out_(out), limit_(limit) {}
|
||||
|
||||
public:
|
||||
using iterator_category = std::output_iterator_tag;
|
||||
using value_type = typename std::iterator_traits<OutputIt>::value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = void;
|
||||
using reference = void;
|
||||
using _Unchecked_type =
|
||||
truncating_iterator_base; // Mark iterator as checked.
|
||||
|
||||
OutputIt base() const { return out_; }
|
||||
size_t count() const { return count_; }
|
||||
};
|
||||
|
||||
// An output iterator that truncates the output and counts the number of objects
|
||||
// written to it.
|
||||
template <typename OutputIt,
|
||||
typename Enable = typename std::is_void<
|
||||
typename std::iterator_traits<OutputIt>::value_type>::type>
|
||||
class truncating_iterator;
|
||||
|
||||
template <typename OutputIt>
|
||||
class truncating_iterator<OutputIt, std::false_type>
|
||||
: public truncating_iterator_base<OutputIt> {
|
||||
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
|
||||
|
||||
public:
|
||||
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
|
||||
|
||||
truncating_iterator() = default;
|
||||
|
||||
truncating_iterator(OutputIt out, size_t limit)
|
||||
: truncating_iterator_base<OutputIt>(out, limit) {}
|
||||
|
||||
truncating_iterator& operator++() {
|
||||
if (this->count_++ < this->limit_) ++this->out_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
truncating_iterator operator++(int) {
|
||||
auto it = *this;
|
||||
++*this;
|
||||
return it;
|
||||
}
|
||||
|
||||
value_type& operator*() const {
|
||||
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename OutputIt>
|
||||
class truncating_iterator<OutputIt, std::true_type>
|
||||
: public truncating_iterator_base<OutputIt> {
|
||||
public:
|
||||
truncating_iterator() = default;
|
||||
|
||||
truncating_iterator(OutputIt out, size_t limit)
|
||||
: truncating_iterator_base<OutputIt>(out, limit) {}
|
||||
|
||||
template <typename T> truncating_iterator& operator=(T val) {
|
||||
if (this->count_++ < this->limit_) *this->out_++ = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
truncating_iterator& operator++() { return *this; }
|
||||
truncating_iterator& operator++(int) { return *this; }
|
||||
truncating_iterator& operator*() { return *this; }
|
||||
};
|
||||
|
||||
// A compile-time string which is compiled into fast formatting code.
|
||||
class compiled_string {};
|
||||
|
||||
template <typename S>
|
||||
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Converts a string literal *s* into a format string that will be parsed at
|
||||
compile time and converted into efficient formatting code. Requires C++17
|
||||
``constexpr if`` compiler support.
|
||||
|
||||
**Example**::
|
||||
|
||||
// Converts 42 into std::string using the most efficient method and no
|
||||
// runtime format string processing.
|
||||
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
|
||||
\endrst
|
||||
*/
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
# define FMT_COMPILE(s) \
|
||||
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
|
||||
#else
|
||||
# define FMT_COMPILE(s) FMT_STRING(s)
|
||||
#endif
|
||||
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
template <typename Char, size_t N,
|
||||
fmt::detail_exported::fixed_string<Char, N> Str>
|
||||
struct udl_compiled_string : compiled_string {
|
||||
using char_type = Char;
|
||||
constexpr operator basic_string_view<char_type>() const {
|
||||
return {Str.data, N - 1};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <typename T, typename... Tail>
|
||||
const T& first(const T& value, const Tail&...) {
|
||||
return value;
|
||||
}
|
||||
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
template <typename... Args> struct type_list {};
|
||||
|
||||
// Returns a reference to the argument at index N from [first, rest...].
|
||||
template <int N, typename T, typename... Args>
|
||||
constexpr const auto& get([[maybe_unused]] const T& first,
|
||||
[[maybe_unused]] const Args&... rest) {
|
||||
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
||||
if constexpr (N == 0)
|
||||
return first;
|
||||
else
|
||||
return detail::get<N - 1>(rest...);
|
||||
}
|
||||
|
||||
template <typename Char, typename... Args>
|
||||
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
|
||||
type_list<Args...>) {
|
||||
return get_arg_index_by_name<Args...>(name);
|
||||
}
|
||||
|
||||
template <int N, typename> struct get_type_impl;
|
||||
|
||||
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
||||
using type =
|
||||
remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
|
||||
};
|
||||
|
||||
template <int N, typename T>
|
||||
using get_type = typename get_type_impl<N, T>::type;
|
||||
|
||||
template <typename T> struct is_compiled_format : std::false_type {};
|
||||
|
||||
template <typename Char> struct text {
|
||||
basic_string_view<Char> data;
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
constexpr OutputIt format(OutputIt out, const Args&...) const {
|
||||
return write<Char>(out, data);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_compiled_format<text<Char>> : std::true_type {};
|
||||
|
||||
template <typename Char>
|
||||
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
||||
size_t size) {
|
||||
return {{&s[pos], size}};
|
||||
}
|
||||
|
||||
template <typename Char> struct code_unit {
|
||||
Char value;
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
constexpr OutputIt format(OutputIt out, const Args&...) const {
|
||||
return write<Char>(out, value);
|
||||
}
|
||||
};
|
||||
|
||||
// This ensures that the argument type is convertible to `const T&`.
|
||||
template <typename T, int N, typename... Args>
|
||||
constexpr const T& get_arg_checked(const Args&... args) {
|
||||
const auto& arg = detail::get<N>(args...);
|
||||
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
|
||||
return arg.value;
|
||||
} else {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
struct is_compiled_format<code_unit<Char>> : std::true_type {};
|
||||
|
||||
// A replacement field that refers to argument N.
|
||||
template <typename Char, typename T, int N> struct field {
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||
return write<Char>(out, get_arg_checked<T, N>(args...));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename T, int N>
|
||||
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
||||
|
||||
// A replacement field that refers to argument with name.
|
||||
template <typename Char> struct runtime_named_field {
|
||||
using char_type = Char;
|
||||
basic_string_view<Char> name;
|
||||
|
||||
template <typename OutputIt, typename T>
|
||||
constexpr static bool try_format_argument(
|
||||
OutputIt& out,
|
||||
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
|
||||
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
|
||||
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
|
||||
if (arg_name == arg.name) {
|
||||
out = write<Char>(out, arg.value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||
bool found = (try_format_argument(out, name, args) || ...);
|
||||
if (!found) {
|
||||
FMT_THROW(format_error("argument with specified name is not found"));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
|
||||
|
||||
// A replacement field that refers to argument N and has format specifiers.
|
||||
template <typename Char, typename T, int N> struct spec_field {
|
||||
using char_type = Char;
|
||||
formatter<T, Char> fmt;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
constexpr FMT_INLINE OutputIt format(OutputIt out,
|
||||
const Args&... args) const {
|
||||
const auto& vargs =
|
||||
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
|
||||
basic_format_context<OutputIt, Char> ctx(out, vargs);
|
||||
return fmt.format(get_arg_checked<T, N>(args...), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename T, int N>
|
||||
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
|
||||
|
||||
template <typename L, typename R> struct concat {
|
||||
L lhs;
|
||||
R rhs;
|
||||
using char_type = typename L::char_type;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||
out = lhs.format(out, args...);
|
||||
return rhs.format(out, args...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename L, typename R>
|
||||
struct is_compiled_format<concat<L, R>> : std::true_type {};
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr concat<L, R> make_concat(L lhs, R rhs) {
|
||||
return {lhs, rhs};
|
||||
}
|
||||
|
||||
struct unknown_format {};
|
||||
|
||||
template <typename Char>
|
||||
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
|
||||
for (size_t size = str.size(); pos != size; ++pos) {
|
||||
if (str[pos] == '{' || str[pos] == '}') break;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
template <typename Args, size_t POS, int ID, typename S>
|
||||
constexpr auto compile_format_string(S format_str);
|
||||
|
||||
template <typename Args, size_t POS, int ID, typename T, typename S>
|
||||
constexpr auto parse_tail(T head, S format_str) {
|
||||
if constexpr (POS !=
|
||||
basic_string_view<typename S::char_type>(format_str).size()) {
|
||||
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
|
||||
unknown_format>())
|
||||
return tail;
|
||||
else
|
||||
return make_concat(head, tail);
|
||||
} else {
|
||||
return head;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Char> struct parse_specs_result {
|
||||
formatter<T, Char> fmt;
|
||||
size_t end;
|
||||
int next_arg_id;
|
||||
};
|
||||
|
||||
constexpr int manual_indexing_id = -1;
|
||||
|
||||
template <typename T, typename Char>
|
||||
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
||||
size_t pos, int next_arg_id) {
|
||||
str.remove_prefix(pos);
|
||||
auto ctx = basic_format_parse_context<Char>(str, {}, next_arg_id);
|
||||
auto f = formatter<T, Char>();
|
||||
auto end = f.parse(ctx);
|
||||
return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1,
|
||||
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
|
||||
}
|
||||
|
||||
template <typename Char> struct arg_id_handler {
|
||||
arg_ref<Char> arg_id;
|
||||
|
||||
constexpr int operator()() {
|
||||
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
|
||||
return 0;
|
||||
}
|
||||
constexpr int operator()(int id) {
|
||||
arg_id = arg_ref<Char>(id);
|
||||
return 0;
|
||||
}
|
||||
constexpr int operator()(basic_string_view<Char> id) {
|
||||
arg_id = arg_ref<Char>(id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr void on_error(const char* message) {
|
||||
FMT_THROW(format_error(message));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char> struct parse_arg_id_result {
|
||||
arg_ref<Char> arg_id;
|
||||
const Char* arg_id_end;
|
||||
};
|
||||
|
||||
template <int ID, typename Char>
|
||||
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
|
||||
auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
|
||||
auto arg_id_end = parse_arg_id(begin, end, handler);
|
||||
return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
|
||||
}
|
||||
|
||||
template <typename T, typename Enable = void> struct field_type {
|
||||
using type = remove_cvref_t<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
|
||||
using type = remove_cvref_t<decltype(T::value)>;
|
||||
};
|
||||
|
||||
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
|
||||
typename S>
|
||||
constexpr auto parse_replacement_field_then_tail(S format_str) {
|
||||
using char_type = typename S::char_type;
|
||||
constexpr auto str = basic_string_view<char_type>(format_str);
|
||||
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
|
||||
if constexpr (c == '}') {
|
||||
return parse_tail<Args, END_POS + 1, NEXT_ID>(
|
||||
field<char_type, typename field_type<T>::type, ARG_INDEX>(),
|
||||
format_str);
|
||||
} else if constexpr (c == ':') {
|
||||
constexpr auto result = parse_specs<typename field_type<T>::type>(
|
||||
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
|
||||
return parse_tail<Args, result.end, result.next_arg_id>(
|
||||
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
|
||||
result.fmt},
|
||||
format_str);
|
||||
}
|
||||
}
|
||||
|
||||
// Compiles a non-empty format string and returns the compiled representation
|
||||
// or unknown_format() on unrecognized input.
|
||||
template <typename Args, size_t POS, int ID, typename S>
|
||||
constexpr auto compile_format_string(S format_str) {
|
||||
using char_type = typename S::char_type;
|
||||
constexpr auto str = basic_string_view<char_type>(format_str);
|
||||
if constexpr (str[POS] == '{') {
|
||||
if constexpr (POS + 1 == str.size())
|
||||
FMT_THROW(format_error("unmatched '{' in format string"));
|
||||
if constexpr (str[POS + 1] == '{') {
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
|
||||
static_assert(ID != manual_indexing_id,
|
||||
"cannot switch from manual to automatic argument indexing");
|
||||
constexpr auto next_id =
|
||||
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
|
||||
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
|
||||
POS + 1, ID, next_id>(
|
||||
format_str);
|
||||
} else {
|
||||
constexpr auto arg_id_result =
|
||||
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
|
||||
constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
|
||||
constexpr char_type c =
|
||||
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
|
||||
static_assert(c == '}' || c == ':', "missing '}' in format string");
|
||||
if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
|
||||
static_assert(
|
||||
ID == manual_indexing_id || ID == 0,
|
||||
"cannot switch from automatic to manual argument indexing");
|
||||
constexpr auto arg_index = arg_id_result.arg_id.val.index;
|
||||
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
|
||||
Args, arg_id_end_pos,
|
||||
arg_index, manual_indexing_id>(
|
||||
format_str);
|
||||
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
|
||||
constexpr auto arg_index =
|
||||
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
|
||||
if constexpr (arg_index != invalid_arg_index) {
|
||||
constexpr auto next_id =
|
||||
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
|
||||
return parse_replacement_field_then_tail<
|
||||
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
|
||||
arg_index, next_id>(format_str);
|
||||
} else {
|
||||
if constexpr (c == '}') {
|
||||
return parse_tail<Args, arg_id_end_pos + 1, ID>(
|
||||
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
|
||||
format_str);
|
||||
} else if constexpr (c == ':') {
|
||||
return unknown_format(); // no type info for specs parsing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if constexpr (str[POS] == '}') {
|
||||
if constexpr (POS + 1 == str.size())
|
||||
FMT_THROW(format_error("unmatched '}' in format string"));
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||
} else {
|
||||
constexpr auto end = parse_text(str, POS + 1);
|
||||
if constexpr (end - POS > 1) {
|
||||
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
||||
format_str);
|
||||
} else {
|
||||
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
|
||||
format_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args, typename S,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
constexpr auto compile(S format_str) {
|
||||
constexpr auto str = basic_string_view<typename S::char_type>(format_str);
|
||||
if constexpr (str.size() == 0) {
|
||||
return detail::make_text(str, 0, 0);
|
||||
} else {
|
||||
constexpr auto result =
|
||||
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
|
||||
format_str);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
} // namespace detail
|
||||
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
|
||||
template <typename CompiledFormat, typename... Args,
|
||||
typename Char = typename CompiledFormat::char_type,
|
||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
auto s = std::basic_string<Char>();
|
||||
cf.format(std::back_inserter(s), args...);
|
||||
return s;
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
return cf.format(out, args...);
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
||||
Args&&... args) {
|
||||
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||
constexpr auto str = basic_string_view<typename S::char_type>(S());
|
||||
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
|
||||
const auto& first = detail::first(args...);
|
||||
if constexpr (detail::is_named_arg<
|
||||
remove_cvref_t<decltype(first)>>::value) {
|
||||
return fmt::to_string(first.value);
|
||||
} else {
|
||||
return fmt::to_string(first);
|
||||
}
|
||||
}
|
||||
}
|
||||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||
detail::unknown_format>()) {
|
||||
return format(static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||
std::forward<Args>(args)...);
|
||||
} else {
|
||||
return format(compiled, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
|
||||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||
detail::unknown_format>()) {
|
||||
return format_to(out,
|
||||
static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||
std::forward<Args>(args)...);
|
||||
} else {
|
||||
return format_to(out, compiled, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||
const S& format_str, Args&&... args) {
|
||||
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str,
|
||||
std::forward<Args>(args)...);
|
||||
return {it.base(), it.count()};
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
size_t formatted_size(const S& format_str, const Args&... args) {
|
||||
return format_to(detail::counting_iterator(), format_str, args...).count();
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
void print(std::FILE* f, const S& format_str, const Args&... args) {
|
||||
memory_buffer buffer;
|
||||
format_to(std::back_inserter(buffer), format_str, args...);
|
||||
detail::print(f, {buffer.data(), buffer.size()});
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
void print(const S& format_str, const Args&... args) {
|
||||
print(stdout, format_str, args...);
|
||||
}
|
||||
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
inline namespace literals {
|
||||
template <detail_exported::fixed_string Str>
|
||||
constexpr detail::udl_compiled_string<
|
||||
remove_cvref_t<decltype(Str.data[0])>,
|
||||
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str>
|
||||
operator""_cf() {
|
||||
return {};
|
||||
}
|
||||
} // namespace literals
|
||||
#endif
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_COMPILE_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2012 - present, Victor Zverovich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--- Optional exception to the license ---
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into a machine-executable object form of such
|
||||
source code, you may redistribute such embedded portions in such object form
|
||||
without including the above copyright and permission notices.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
#include "xchar.h"
|
||||
#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead
|
||||
@@ -0,0 +1,527 @@
|
||||
// Formatting library for C++ - optional OS-specific functionality
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_OS_H_
|
||||
#define FMT_OS_H_
|
||||
|
||||
#include <cerrno>
|
||||
#include <clocale> // locale_t
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib> // strtod_l
|
||||
#include <system_error> // std::system_error
|
||||
|
||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||
#endif
|
||||
|
||||
#include "format.h"
|
||||
|
||||
#ifndef FMT_USE_FCNTL
|
||||
// UWP doesn't provide _pipe.
|
||||
# if FMT_HAS_INCLUDE("winapifamily.h")
|
||||
# include <winapifamily.h>
|
||||
# endif
|
||||
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||
defined(__linux__)) && \
|
||||
(!defined(WINAPI_FAMILY) || \
|
||||
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||
# include <fcntl.h> // for O_RDONLY
|
||||
# define FMT_USE_FCNTL 1
|
||||
# else
|
||||
# define FMT_USE_FCNTL 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef FMT_POSIX
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX(call) _##call
|
||||
# else
|
||||
# define FMT_POSIX(call) call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
||||
#ifdef FMT_SYSTEM
|
||||
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||
#else
|
||||
# define FMT_SYSTEM(call) ::call
|
||||
# ifdef _WIN32
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX_CALL(call) ::_##call
|
||||
# else
|
||||
# define FMT_POSIX_CALL(call) ::call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Retries the expression while it evaluates to error_result and errno
|
||||
// equals to EINTR.
|
||||
#ifndef _WIN32
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) \
|
||||
do { \
|
||||
(result) = (expression); \
|
||||
} while ((result) == (error_result) && errno == EINTR)
|
||||
#else
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
||||
#endif
|
||||
|
||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
/**
|
||||
\rst
|
||||
A reference to a null-terminated string. It can be constructed from a C
|
||||
string or ``std::string``.
|
||||
|
||||
You can use one of the following type aliases for common character types:
|
||||
|
||||
+---------------+-----------------------------+
|
||||
| Type | Definition |
|
||||
+===============+=============================+
|
||||
| cstring_view | basic_cstring_view<char> |
|
||||
+---------------+-----------------------------+
|
||||
| wcstring_view | basic_cstring_view<wchar_t> |
|
||||
+---------------+-----------------------------+
|
||||
|
||||
This class is most useful as a parameter type to allow passing
|
||||
different types of strings to a function, for example::
|
||||
|
||||
template <typename... Args>
|
||||
std::string format(cstring_view format_str, const Args & ... args);
|
||||
|
||||
format("{}", 42);
|
||||
format(std::string("{}"), 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename Char> class basic_cstring_view {
|
||||
private:
|
||||
const Char* data_;
|
||||
|
||||
public:
|
||||
/** Constructs a string reference object from a C string. */
|
||||
basic_cstring_view(const Char* s) : data_(s) {}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a string reference from an ``std::string`` object.
|
||||
\endrst
|
||||
*/
|
||||
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
|
||||
|
||||
/** Returns the pointer to a C string. */
|
||||
const Char* c_str() const { return data_; }
|
||||
};
|
||||
|
||||
using cstring_view = basic_cstring_view<char>;
|
||||
using wcstring_view = basic_cstring_view<wchar_t>;
|
||||
|
||||
template <typename Char> struct formatter<std::error_code, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
out = detail::write_bytes(out, ec.category().name(),
|
||||
basic_format_specs<Char>());
|
||||
out = detail::write<Char>(out, Char(':'));
|
||||
out = detail::write<Char>(out, ec.value());
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
FMT_API const std::error_category& system_category() FMT_NOEXCEPT;
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
// A converter from UTF-16 to UTF-8.
|
||||
// It is only provided for Windows since other systems support UTF-8 natively.
|
||||
class utf16_to_utf8 {
|
||||
private:
|
||||
memory_buffer buffer_;
|
||||
|
||||
public:
|
||||
utf16_to_utf8() {}
|
||||
FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
|
||||
operator string_view() const { return string_view(&buffer_[0], size()); }
|
||||
size_t size() const { return buffer_.size() - 1; }
|
||||
const char* c_str() const { return &buffer_[0]; }
|
||||
std::string str() const { return std::string(&buffer_[0], size()); }
|
||||
|
||||
// Performs conversion returning a system error code instead of
|
||||
// throwing exception on conversion error. This method may still throw
|
||||
// in case of memory allocation error.
|
||||
FMT_API int convert(basic_string_view<wchar_t> s);
|
||||
};
|
||||
|
||||
FMT_API void format_windows_error(buffer<char>& out, int error_code,
|
||||
const char* message) FMT_NOEXCEPT;
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
|
||||
format_args args);
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a :class:`std::system_error` object with the description
|
||||
of the form
|
||||
|
||||
.. parsed-literal::
|
||||
*<message>*: *<system-message>*
|
||||
|
||||
where *<message>* is the formatted message and *<system-message>* is the
|
||||
system message corresponding to the error code.
|
||||
*error_code* is a Windows error code as given by ``GetLastError``.
|
||||
If *error_code* is not a valid error code such as -1, the system message
|
||||
will look like "error -1".
|
||||
|
||||
**Example**::
|
||||
|
||||
// This throws a system_error with the description
|
||||
// cannot open file 'madeup': The system cannot find the file specified.
|
||||
// or similar (system message may vary).
|
||||
const char *filename = "madeup";
|
||||
LPOFSTRUCT of = LPOFSTRUCT();
|
||||
HFILE file = OpenFile(filename, &of, OF_READ);
|
||||
if (file == HFILE_ERROR) {
|
||||
throw fmt::windows_error(GetLastError(),
|
||||
"cannot open file '{}'", filename);
|
||||
}
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
std::system_error windows_error(int error_code, string_view message,
|
||||
const Args&... args) {
|
||||
return vwindows_error(error_code, message, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
// Reports a Windows error without throwing an exception.
|
||||
// Can be used to report errors from destructors.
|
||||
FMT_API void report_windows_error(int error_code,
|
||||
const char* message) FMT_NOEXCEPT;
|
||||
#else
|
||||
inline const std::error_category& system_category() FMT_NOEXCEPT {
|
||||
return std::system_category();
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
// std::system is not available on some platforms such as iOS (#2248).
|
||||
#ifdef __OSX__
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
void say(const S& format_str, Args&&... args) {
|
||||
std::system(format("say \"{}\"", format(format_str, args...)).c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
// A buffered file.
|
||||
class buffered_file {
|
||||
private:
|
||||
FILE* file_;
|
||||
|
||||
friend class file;
|
||||
|
||||
explicit buffered_file(FILE* f) : file_(f) {}
|
||||
|
||||
public:
|
||||
buffered_file(const buffered_file&) = delete;
|
||||
void operator=(const buffered_file&) = delete;
|
||||
|
||||
// Constructs a buffered_file object which doesn't represent any file.
|
||||
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~buffered_file() FMT_NOEXCEPT;
|
||||
|
||||
public:
|
||||
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
|
||||
other.file_ = nullptr;
|
||||
}
|
||||
|
||||
buffered_file& operator=(buffered_file&& other) {
|
||||
close();
|
||||
file_ = other.file_;
|
||||
other.file_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Opens a file.
|
||||
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the pointer to a FILE object representing this file.
|
||||
FILE* get() const FMT_NOEXCEPT { return file_; }
|
||||
|
||||
// We place parentheses around fileno to workaround a bug in some versions
|
||||
// of MinGW that define fileno as a macro.
|
||||
FMT_API int(fileno)() const;
|
||||
|
||||
void vprint(string_view format_str, format_args args) {
|
||||
fmt::vprint(file_, format_str, args);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void print(string_view format_str, const Args&... args) {
|
||||
vprint(format_str, fmt::make_format_args(args...));
|
||||
}
|
||||
};
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
// A file. Closed file is represented by a file object with descriptor -1.
|
||||
// Methods that are not declared with FMT_NOEXCEPT may throw
|
||||
// fmt::system_error in case of failure. Note that some errors such as
|
||||
// closing the file multiple times will cause a crash on Windows rather
|
||||
// than an exception. You can get standard behavior by overriding the
|
||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||
class file {
|
||||
private:
|
||||
int fd_; // File descriptor.
|
||||
|
||||
// Constructs a file object with a given descriptor.
|
||||
explicit file(int fd) : fd_(fd) {}
|
||||
|
||||
public:
|
||||
// Possible values for the oflag argument to the constructor.
|
||||
enum {
|
||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
|
||||
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
|
||||
APPEND = FMT_POSIX(O_APPEND), // Open in append mode.
|
||||
TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file.
|
||||
};
|
||||
|
||||
// Constructs a file object which doesn't represent any file.
|
||||
file() FMT_NOEXCEPT : fd_(-1) {}
|
||||
|
||||
// Opens a file and constructs a file object representing this file.
|
||||
FMT_API file(cstring_view path, int oflag);
|
||||
|
||||
public:
|
||||
file(const file&) = delete;
|
||||
void operator=(const file&) = delete;
|
||||
|
||||
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
|
||||
|
||||
// Move assignment is not noexcept because close may throw.
|
||||
file& operator=(file&& other) {
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~file() FMT_NOEXCEPT;
|
||||
|
||||
// Returns the file descriptor.
|
||||
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the file size. The size has signed type for consistency with
|
||||
// stat::st_size.
|
||||
FMT_API long long size() const;
|
||||
|
||||
// Attempts to read count bytes from the file into the specified buffer.
|
||||
FMT_API size_t read(void* buffer, size_t count);
|
||||
|
||||
// Attempts to write count bytes from the specified buffer to the file.
|
||||
FMT_API size_t write(const void* buffer, size_t count);
|
||||
|
||||
// Duplicates a file descriptor with the dup function and returns
|
||||
// the duplicate as a file object.
|
||||
FMT_API static file dup(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
FMT_API void dup2(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
FMT_API void dup2(int fd, std::error_code& ec) FMT_NOEXCEPT;
|
||||
|
||||
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||
// and writing respectively.
|
||||
FMT_API static void pipe(file& read_end, file& write_end);
|
||||
|
||||
// Creates a buffered_file object associated with this file and detaches
|
||||
// this file object from the file.
|
||||
FMT_API buffered_file fdopen(const char* mode);
|
||||
};
|
||||
|
||||
// Returns the memory page size.
|
||||
long getpagesize();
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
struct buffer_size {
|
||||
buffer_size() = default;
|
||||
size_t value = 0;
|
||||
buffer_size operator=(size_t val) const {
|
||||
auto bs = buffer_size();
|
||||
bs.value = val;
|
||||
return bs;
|
||||
}
|
||||
};
|
||||
|
||||
struct ostream_params {
|
||||
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
|
||||
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
|
||||
|
||||
ostream_params() {}
|
||||
|
||||
template <typename... T>
|
||||
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
|
||||
oflag = new_oflag;
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
ostream_params(T... params, detail::buffer_size bs)
|
||||
: ostream_params(params...) {
|
||||
this->buffer_size = bs.value;
|
||||
}
|
||||
|
||||
// Intel has a bug that results in failure to deduce a constructor
|
||||
// for empty parameter packs.
|
||||
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
|
||||
ostream_params(int new_oflag) : oflag(new_oflag) {}
|
||||
ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
|
||||
# endif
|
||||
};
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
// Added {} below to work around default constructor error known to
|
||||
// occur in Xcode versions 7.2.1 and 8.2.1.
|
||||
constexpr detail::buffer_size buffer_size{};
|
||||
|
||||
/** A fast output stream which is not thread-safe. */
|
||||
class FMT_API ostream final : private detail::buffer<char> {
|
||||
private:
|
||||
file file_;
|
||||
|
||||
void grow(size_t) override;
|
||||
|
||||
ostream(cstring_view path, const detail::ostream_params& params)
|
||||
: file_(path, params.oflag) {
|
||||
set(new char[params.buffer_size], params.buffer_size);
|
||||
}
|
||||
|
||||
public:
|
||||
ostream(ostream&& other)
|
||||
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
|
||||
file_(std::move(other.file_)) {
|
||||
other.clear();
|
||||
other.set(nullptr, 0);
|
||||
}
|
||||
~ostream() {
|
||||
flush();
|
||||
delete[] data();
|
||||
}
|
||||
|
||||
void flush() {
|
||||
if (size() == 0) return;
|
||||
file_.write(data(), size());
|
||||
clear();
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
friend ostream output_file(cstring_view path, T... params);
|
||||
|
||||
void close() {
|
||||
flush();
|
||||
file_.close();
|
||||
}
|
||||
|
||||
/**
|
||||
Formats ``args`` according to specifications in ``fmt`` and writes the
|
||||
output to the file.
|
||||
*/
|
||||
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
|
||||
vformat_to(detail::buffer_appender<char>(*this), fmt,
|
||||
fmt::make_format_args(args...));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Opens a file for writing. Supported parameters passed in *params*:
|
||||
|
||||
* ``<integer>``: Flags passed to `open
|
||||
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
|
||||
(``file::WRONLY | file::CREATE`` by default)
|
||||
* ``buffer_size=<integer>``: Output buffer size
|
||||
|
||||
**Example**::
|
||||
|
||||
auto out = fmt::output_file("guide.txt");
|
||||
out.print("Don't {}", "Panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
inline ostream output_file(cstring_view path, T... params) {
|
||||
return {path, detail::ostream_params(params...)};
|
||||
}
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
#ifdef FMT_LOCALE
|
||||
// A "C" numeric locale.
|
||||
class locale {
|
||||
private:
|
||||
# ifdef _WIN32
|
||||
using locale_t = _locale_t;
|
||||
|
||||
static void freelocale(locale_t loc) { _free_locale(loc); }
|
||||
|
||||
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
|
||||
return _strtod_l(nptr, endptr, loc);
|
||||
}
|
||||
# endif
|
||||
|
||||
locale_t locale_;
|
||||
|
||||
public:
|
||||
using type = locale_t;
|
||||
locale(const locale&) = delete;
|
||||
void operator=(const locale&) = delete;
|
||||
|
||||
locale() {
|
||||
# ifndef _WIN32
|
||||
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
|
||||
# else
|
||||
locale_ = _create_locale(LC_NUMERIC, "C");
|
||||
# endif
|
||||
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
|
||||
}
|
||||
~locale() { freelocale(locale_); }
|
||||
|
||||
type get() const { return locale_; }
|
||||
|
||||
// Converts string to floating-point number and advances str past the end
|
||||
// of the parsed input.
|
||||
FMT_DEPRECATED double strtod(const char*& str) const {
|
||||
char* end = nullptr;
|
||||
double result = strtod_l(str, &end, locale_);
|
||||
str = end;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
using Locale FMT_DEPRECATED_ALIAS = locale;
|
||||
#endif // FMT_LOCALE
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OS_H_
|
||||
@@ -0,0 +1,135 @@
|
||||
// Formatting library for C++ - std::ostream support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_OSTREAM_H_
|
||||
#define FMT_OSTREAM_H_
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
template <typename OutputIt, typename Char> class basic_printf_context;
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Checks if T has a user-defined operator<<.
|
||||
template <typename T, typename Char, typename Enable = void>
|
||||
class is_streamable {
|
||||
private:
|
||||
template <typename U>
|
||||
static auto test(int)
|
||||
-> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
|
||||
<< std::declval<U>()) != 0>;
|
||||
|
||||
template <typename> static auto test(...) -> std::false_type;
|
||||
|
||||
using result = decltype(test<T>(0));
|
||||
|
||||
public:
|
||||
is_streamable() = default;
|
||||
|
||||
static const bool value = result::value;
|
||||
};
|
||||
|
||||
// Formatting of built-in types and arrays is intentionally disabled because
|
||||
// it's handled by standard (non-ostream) formatters.
|
||||
template <typename T, typename Char>
|
||||
struct is_streamable<
|
||||
T, Char,
|
||||
enable_if_t<
|
||||
std::is_arithmetic<T>::value || std::is_array<T>::value ||
|
||||
std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
|
||||
std::is_same<T, std::basic_string<Char>>::value ||
|
||||
std::is_same<T, std_string_view<Char>>::value ||
|
||||
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
|
||||
: std::false_type {};
|
||||
|
||||
// Write the content of buf to os.
|
||||
// It is a separate function rather than a part of vprint to simplify testing.
|
||||
template <typename Char>
|
||||
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||
const Char* buf_data = buf.data();
|
||||
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
|
||||
unsigned_streamsize size = buf.size();
|
||||
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
|
||||
do {
|
||||
unsigned_streamsize n = size <= max_size ? size : max_size;
|
||||
os.write(buf_data, static_cast<std::streamsize>(n));
|
||||
buf_data += n;
|
||||
size -= n;
|
||||
} while (size != 0);
|
||||
}
|
||||
|
||||
template <typename Char, typename T>
|
||||
void format_value(buffer<Char>& buf, const T& value,
|
||||
locale_ref loc = locale_ref()) {
|
||||
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
|
||||
auto&& output = std::basic_ostream<Char>(&format_buf);
|
||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||
if (loc) output.imbue(loc.get<std::locale>());
|
||||
#endif
|
||||
output << value;
|
||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||
buf.try_resize(buf.size());
|
||||
}
|
||||
|
||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||
template <typename T, typename Char>
|
||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||
: private formatter<basic_string_view<Char>, Char> {
|
||||
using formatter<basic_string_view<Char>, Char>::parse;
|
||||
|
||||
template <typename OutputIt>
|
||||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
|
||||
-> OutputIt {
|
||||
auto buffer = basic_memory_buffer<Char>();
|
||||
format_value(buffer, value, ctx.locale());
|
||||
return formatter<basic_string_view<Char>, Char>::format(
|
||||
{buffer.data(), buffer.size()}, ctx);
|
||||
}
|
||||
|
||||
// DEPRECATED!
|
||||
template <typename OutputIt>
|
||||
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
|
||||
-> OutputIt {
|
||||
auto buffer = basic_memory_buffer<Char>();
|
||||
format_value(buffer, value, ctx.locale());
|
||||
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
FMT_MODULE_EXPORT
|
||||
template <typename Char>
|
||||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
auto buffer = basic_memory_buffer<Char>();
|
||||
detail::vformat_to(buffer, format_str, args);
|
||||
detail::write_buffer(os, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to the stream *os*.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(cerr, "Don't {}!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
FMT_MODULE_EXPORT
|
||||
template <typename S, typename... Args,
|
||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
||||
vprint(os, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OSTREAM_H_
|
||||
@@ -0,0 +1,657 @@
|
||||
// Formatting library for C++ - legacy printf implementation
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_PRINTF_H_
|
||||
#define FMT_PRINTF_H_
|
||||
|
||||
#include <algorithm> // std::max
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <ostream>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
template <typename T> struct printf_formatter { printf_formatter() = delete; };
|
||||
|
||||
template <typename Char>
|
||||
class basic_printf_parse_context : public basic_format_parse_context<Char> {
|
||||
using basic_format_parse_context<Char>::basic_format_parse_context;
|
||||
};
|
||||
|
||||
template <typename OutputIt, typename Char> class basic_printf_context {
|
||||
private:
|
||||
OutputIt out_;
|
||||
basic_format_args<basic_printf_context> args_;
|
||||
|
||||
public:
|
||||
using char_type = Char;
|
||||
using format_arg = basic_format_arg<basic_printf_context>;
|
||||
using parse_context_type = basic_printf_parse_context<Char>;
|
||||
template <typename T> using formatter_type = printf_formatter<T>;
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a ``printf_context`` object. References to the arguments are
|
||||
stored in the context object so make sure they have appropriate lifetimes.
|
||||
\endrst
|
||||
*/
|
||||
basic_printf_context(OutputIt out,
|
||||
basic_format_args<basic_printf_context> args)
|
||||
: out_(out), args_(args) {}
|
||||
|
||||
OutputIt out() { return out_; }
|
||||
void advance_to(OutputIt it) { out_ = it; }
|
||||
|
||||
detail::locale_ref locale() { return {}; }
|
||||
|
||||
format_arg arg(int id) const { return args_.get(id); }
|
||||
|
||||
FMT_CONSTEXPR void on_error(const char* message) {
|
||||
detail::error_handler().on_error(message);
|
||||
}
|
||||
};
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||
// signed and unsigned integers.
|
||||
template <bool IsSigned> struct int_checker {
|
||||
template <typename T> static bool fits_in_int(T value) {
|
||||
unsigned max = max_value<int>();
|
||||
return value <= max;
|
||||
}
|
||||
static bool fits_in_int(bool) { return true; }
|
||||
};
|
||||
|
||||
template <> struct int_checker<true> {
|
||||
template <typename T> static bool fits_in_int(T value) {
|
||||
return value >= (std::numeric_limits<int>::min)() &&
|
||||
value <= max_value<int>();
|
||||
}
|
||||
static bool fits_in_int(int) { return true; }
|
||||
};
|
||||
|
||||
class printf_precision_handler {
|
||||
public:
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
int operator()(T value) {
|
||||
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
||||
FMT_THROW(format_error("number is too big"));
|
||||
return (std::max)(static_cast<int>(value), 0);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
int operator()(T) {
|
||||
FMT_THROW(format_error("precision is not integer"));
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// An argument visitor that returns true iff arg is a zero integer.
|
||||
class is_zero_int {
|
||||
public:
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
bool operator()(T value) {
|
||||
return value == 0;
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
bool operator()(T) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
|
||||
|
||||
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
|
||||
|
||||
template <typename T, typename Context> class arg_converter {
|
||||
private:
|
||||
using char_type = typename Context::char_type;
|
||||
|
||||
basic_format_arg<Context>& arg_;
|
||||
char_type type_;
|
||||
|
||||
public:
|
||||
arg_converter(basic_format_arg<Context>& arg, char_type type)
|
||||
: arg_(arg), type_(type) {}
|
||||
|
||||
void operator()(bool value) {
|
||||
if (type_ != 's') operator()<bool>(value);
|
||||
}
|
||||
|
||||
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
|
||||
void operator()(U value) {
|
||||
bool is_signed = type_ == 'd' || type_ == 'i';
|
||||
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
|
||||
if (const_check(sizeof(target_type) <= sizeof(int))) {
|
||||
// Extra casts are used to silence warnings.
|
||||
if (is_signed) {
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<int>(static_cast<target_type>(value)));
|
||||
} else {
|
||||
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<unsigned>(static_cast<unsigned_type>(value)));
|
||||
}
|
||||
} else {
|
||||
if (is_signed) {
|
||||
// glibc's printf doesn't sign extend arguments of smaller types:
|
||||
// std::printf("%lld", -42); // prints "4294967254"
|
||||
// but we don't have to do the same because it's a UB.
|
||||
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
|
||||
} else {
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<typename make_unsigned_or_bool<U>::type>(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
|
||||
void operator()(U) {} // No conversion needed for non-integral types.
|
||||
};
|
||||
|
||||
// Converts an integer argument to T for printf, if T is an integral type.
|
||||
// If T is void, the argument is converted to corresponding signed or unsigned
|
||||
// type depending on the type specifier: 'd' and 'i' - signed, other -
|
||||
// unsigned).
|
||||
template <typename T, typename Context, typename Char>
|
||||
void convert_arg(basic_format_arg<Context>& arg, Char type) {
|
||||
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
|
||||
}
|
||||
|
||||
// Converts an integer argument to char for printf.
|
||||
template <typename Context> class char_converter {
|
||||
private:
|
||||
basic_format_arg<Context>& arg_;
|
||||
|
||||
public:
|
||||
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
void operator()(T value) {
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<typename Context::char_type>(value));
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
void operator()(T) {} // No conversion needed for non-integral types.
|
||||
};
|
||||
|
||||
// An argument visitor that return a pointer to a C string if argument is a
|
||||
// string or null otherwise.
|
||||
template <typename Char> struct get_cstring {
|
||||
template <typename T> const Char* operator()(T) { return nullptr; }
|
||||
const Char* operator()(const Char* s) { return s; }
|
||||
};
|
||||
|
||||
// Checks if an argument is a valid printf width specifier and sets
|
||||
// left alignment if it is negative.
|
||||
template <typename Char> class printf_width_handler {
|
||||
private:
|
||||
using format_specs = basic_format_specs<Char>;
|
||||
|
||||
format_specs& specs_;
|
||||
|
||||
public:
|
||||
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
unsigned operator()(T value) {
|
||||
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
|
||||
if (detail::is_negative(value)) {
|
||||
specs_.align = align::left;
|
||||
width = 0 - width;
|
||||
}
|
||||
unsigned int_max = max_value<int>();
|
||||
if (width > int_max) FMT_THROW(format_error("number is too big"));
|
||||
return static_cast<unsigned>(width);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
unsigned operator()(T) {
|
||||
FMT_THROW(format_error("width is not integer"));
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// The ``printf`` argument formatter.
|
||||
template <typename OutputIt, typename Char>
|
||||
class printf_arg_formatter : public arg_formatter<Char> {
|
||||
private:
|
||||
using base = arg_formatter<Char>;
|
||||
using context_type = basic_printf_context<OutputIt, Char>;
|
||||
using format_specs = basic_format_specs<Char>;
|
||||
|
||||
context_type& context_;
|
||||
|
||||
OutputIt write_null_pointer(bool is_string = false) {
|
||||
auto s = this->specs;
|
||||
s.type = presentation_type::none;
|
||||
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
|
||||
}
|
||||
|
||||
public:
|
||||
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
|
||||
: base{iter, s, locale_ref()}, context_(ctx) {}
|
||||
|
||||
OutputIt operator()(monostate value) { return base::operator()(value); }
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
|
||||
OutputIt operator()(T value) {
|
||||
// MSVC2013 fails to compile separate overloads for bool and Char so use
|
||||
// std::is_same instead.
|
||||
if (std::is_same<T, Char>::value) {
|
||||
format_specs fmt_specs = this->specs;
|
||||
if (fmt_specs.type != presentation_type::none &&
|
||||
fmt_specs.type != presentation_type::chr) {
|
||||
return (*this)(static_cast<int>(value));
|
||||
}
|
||||
fmt_specs.sign = sign::none;
|
||||
fmt_specs.alt = false;
|
||||
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
|
||||
// align::numeric needs to be overwritten here since the '0' flag is
|
||||
// ignored for non-numeric types
|
||||
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
||||
fmt_specs.align = align::right;
|
||||
return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
|
||||
}
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
OutputIt operator()(T value) {
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
/** Formats a null-terminated C string. */
|
||||
OutputIt operator()(const char* value) {
|
||||
if (value) return base::operator()(value);
|
||||
return write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||
}
|
||||
|
||||
/** Formats a null-terminated wide C string. */
|
||||
OutputIt operator()(const wchar_t* value) {
|
||||
if (value) return base::operator()(value);
|
||||
return write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||
}
|
||||
|
||||
OutputIt operator()(basic_string_view<Char> value) {
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
/** Formats a pointer. */
|
||||
OutputIt operator()(const void* value) {
|
||||
return value ? base::operator()(value) : write_null_pointer();
|
||||
}
|
||||
|
||||
/** Formats an argument of a custom (user-defined) type. */
|
||||
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||
auto parse_ctx =
|
||||
basic_printf_parse_context<Char>(basic_string_view<Char>());
|
||||
handle.format(parse_ctx, context_);
|
||||
return this->out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
|
||||
const Char* end) {
|
||||
for (; it != end; ++it) {
|
||||
switch (*it) {
|
||||
case '-':
|
||||
specs.align = align::left;
|
||||
break;
|
||||
case '+':
|
||||
specs.sign = sign::plus;
|
||||
break;
|
||||
case '0':
|
||||
specs.fill[0] = '0';
|
||||
break;
|
||||
case ' ':
|
||||
if (specs.sign != sign::plus) {
|
||||
specs.sign = sign::space;
|
||||
}
|
||||
break;
|
||||
case '#':
|
||||
specs.alt = true;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char, typename GetArg>
|
||||
int parse_header(const Char*& it, const Char* end,
|
||||
basic_format_specs<Char>& specs, GetArg get_arg) {
|
||||
int arg_index = -1;
|
||||
Char c = *it;
|
||||
if (c >= '0' && c <= '9') {
|
||||
// Parse an argument index (if followed by '$') or a width possibly
|
||||
// preceded with '0' flag(s).
|
||||
int value = parse_nonnegative_int(it, end, -1);
|
||||
if (it != end && *it == '$') { // value is an argument index
|
||||
++it;
|
||||
arg_index = value != -1 ? value : max_value<int>();
|
||||
} else {
|
||||
if (c == '0') specs.fill[0] = '0';
|
||||
if (value != 0) {
|
||||
// Nonzero value means that we parsed width and don't need to
|
||||
// parse it or flags again, so return now.
|
||||
if (value == -1) FMT_THROW(format_error("number is too big"));
|
||||
specs.width = value;
|
||||
return arg_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
parse_flags(specs, it, end);
|
||||
// Parse width.
|
||||
if (it != end) {
|
||||
if (*it >= '0' && *it <= '9') {
|
||||
specs.width = parse_nonnegative_int(it, end, -1);
|
||||
if (specs.width == -1) FMT_THROW(format_error("number is too big"));
|
||||
} else if (*it == '*') {
|
||||
++it;
|
||||
specs.width = static_cast<int>(visit_format_arg(
|
||||
detail::printf_width_handler<Char>(specs), get_arg(-1)));
|
||||
}
|
||||
}
|
||||
return arg_index;
|
||||
}
|
||||
|
||||
template <typename Char, typename Context>
|
||||
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||
basic_format_args<Context> args) {
|
||||
using OutputIt = buffer_appender<Char>;
|
||||
auto out = OutputIt(buf);
|
||||
auto context = basic_printf_context<OutputIt, Char>(out, args);
|
||||
auto parse_ctx = basic_printf_parse_context<Char>(format);
|
||||
|
||||
// Returns the argument with specified index or, if arg_index is -1, the next
|
||||
// argument.
|
||||
auto get_arg = [&](int arg_index) {
|
||||
if (arg_index < 0)
|
||||
arg_index = parse_ctx.next_arg_id();
|
||||
else
|
||||
parse_ctx.check_arg_id(--arg_index);
|
||||
return detail::get_arg(context, arg_index);
|
||||
};
|
||||
|
||||
const Char* start = parse_ctx.begin();
|
||||
const Char* end = parse_ctx.end();
|
||||
auto it = start;
|
||||
while (it != end) {
|
||||
if (!detail::find<false, Char>(it, end, '%', it)) {
|
||||
it = end; // detail::find leaves it == nullptr if it doesn't find '%'
|
||||
break;
|
||||
}
|
||||
Char c = *it++;
|
||||
if (it != end && *it == c) {
|
||||
out = detail::write(
|
||||
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
|
||||
start = ++it;
|
||||
continue;
|
||||
}
|
||||
out = detail::write(out, basic_string_view<Char>(
|
||||
start, detail::to_unsigned(it - 1 - start)));
|
||||
|
||||
basic_format_specs<Char> specs;
|
||||
specs.align = align::right;
|
||||
|
||||
// Parse argument index, flags and width.
|
||||
int arg_index = parse_header(it, end, specs, get_arg);
|
||||
if (arg_index == 0) parse_ctx.on_error("argument not found");
|
||||
|
||||
// Parse precision.
|
||||
if (it != end && *it == '.') {
|
||||
++it;
|
||||
c = it != end ? *it : 0;
|
||||
if ('0' <= c && c <= '9') {
|
||||
specs.precision = parse_nonnegative_int(it, end, 0);
|
||||
} else if (c == '*') {
|
||||
++it;
|
||||
specs.precision = static_cast<int>(
|
||||
visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
|
||||
} else {
|
||||
specs.precision = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto arg = get_arg(arg_index);
|
||||
// For d, i, o, u, x, and X conversion specifiers, if a precision is
|
||||
// specified, the '0' flag is ignored
|
||||
if (specs.precision >= 0 && arg.is_integral())
|
||||
specs.fill[0] =
|
||||
' '; // Ignore '0' flag for non-numeric types or if '-' present.
|
||||
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
|
||||
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
|
||||
auto str_end = str + specs.precision;
|
||||
auto nul = std::find(str, str_end, Char());
|
||||
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
|
||||
basic_string_view<Char>(
|
||||
str, detail::to_unsigned(nul != str_end ? nul - str
|
||||
: specs.precision)));
|
||||
}
|
||||
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
|
||||
specs.alt = false;
|
||||
if (specs.fill[0] == '0') {
|
||||
if (arg.is_arithmetic() && specs.align != align::left)
|
||||
specs.align = align::numeric;
|
||||
else
|
||||
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
|
||||
// flag is also present.
|
||||
}
|
||||
|
||||
// Parse length and convert the argument to the required type.
|
||||
c = it != end ? *it++ : 0;
|
||||
Char t = it != end ? *it : 0;
|
||||
using detail::convert_arg;
|
||||
switch (c) {
|
||||
case 'h':
|
||||
if (t == 'h') {
|
||||
++it;
|
||||
t = it != end ? *it : 0;
|
||||
convert_arg<signed char>(arg, t);
|
||||
} else {
|
||||
convert_arg<short>(arg, t);
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if (t == 'l') {
|
||||
++it;
|
||||
t = it != end ? *it : 0;
|
||||
convert_arg<long long>(arg, t);
|
||||
} else {
|
||||
convert_arg<long>(arg, t);
|
||||
}
|
||||
break;
|
||||
case 'j':
|
||||
convert_arg<intmax_t>(arg, t);
|
||||
break;
|
||||
case 'z':
|
||||
convert_arg<size_t>(arg, t);
|
||||
break;
|
||||
case 't':
|
||||
convert_arg<std::ptrdiff_t>(arg, t);
|
||||
break;
|
||||
case 'L':
|
||||
// printf produces garbage when 'L' is omitted for long double, no
|
||||
// need to do the same.
|
||||
break;
|
||||
default:
|
||||
--it;
|
||||
convert_arg<void>(arg, c);
|
||||
}
|
||||
|
||||
// Parse type.
|
||||
if (it == end) FMT_THROW(format_error("invalid format string"));
|
||||
char type = static_cast<char>(*it++);
|
||||
if (arg.is_integral()) {
|
||||
// Normalize type.
|
||||
switch (type) {
|
||||
case 'i':
|
||||
case 'u':
|
||||
type = 'd';
|
||||
break;
|
||||
case 'c':
|
||||
visit_format_arg(
|
||||
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
|
||||
arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
specs.type = parse_presentation_type(type);
|
||||
if (specs.type == presentation_type::none)
|
||||
parse_ctx.on_error("invalid type specifier");
|
||||
|
||||
start = it;
|
||||
|
||||
// Format argument.
|
||||
out = visit_format_arg(
|
||||
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
|
||||
}
|
||||
detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
|
||||
}
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
template <typename Char>
|
||||
using basic_printf_context_t =
|
||||
basic_printf_context<detail::buffer_appender<Char>, Char>;
|
||||
|
||||
using printf_context = basic_printf_context_t<char>;
|
||||
using wprintf_context = basic_printf_context_t<wchar_t>;
|
||||
|
||||
using printf_args = basic_format_args<printf_context>;
|
||||
using wprintf_args = basic_format_args<wprintf_context>;
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||
arguments and can be implicitly converted to `~fmt::printf_args`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
inline auto make_printf_args(const T&... args)
|
||||
-> format_arg_store<printf_context, T...> {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||
arguments and can be implicitly converted to `~fmt::wprintf_args`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
inline auto make_wprintf_args(const T&... args)
|
||||
-> format_arg_store<wprintf_context, T...> {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline auto vsprintf(
|
||||
const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> std::basic_string<Char> {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
vprintf(buffer, to_string_view(fmt), args);
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats arguments and returns the result as a string.
|
||||
|
||||
**Example**::
|
||||
|
||||
std::string message = fmt::sprintf("The answer is %d", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... T,
|
||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
|
||||
using context = basic_printf_context_t<Char>;
|
||||
return vsprintf(to_string_view(fmt), fmt::make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline auto vfprintf(
|
||||
std::FILE* f, const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> int {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
vprintf(buffer, to_string_view(fmt), args);
|
||||
size_t size = buffer.size();
|
||||
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
||||
? -1
|
||||
: static_cast<int>(size);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to the file *f*.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... T, typename Char = char_t<S>>
|
||||
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
|
||||
using context = basic_printf_context_t<Char>;
|
||||
return vfprintf(f, to_string_view(fmt),
|
||||
fmt::make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline auto vprintf(
|
||||
const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> int {
|
||||
return vfprintf(stdout, to_string_view(fmt), args);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to ``stdout``.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
inline auto printf(const S& fmt, const T&... args) -> int {
|
||||
return vprintf(
|
||||
to_string_view(fmt),
|
||||
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
FMT_DEPRECATED auto vfprintf(
|
||||
std::basic_ostream<Char>& os, const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> int {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
vprintf(buffer, to_string_view(fmt), args);
|
||||
os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
|
||||
return static_cast<int>(buffer.size());
|
||||
}
|
||||
template <typename S, typename... T, typename Char = char_t<S>>
|
||||
FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt,
|
||||
const T&... args) -> int {
|
||||
return vfprintf(os, to_string_view(fmt),
|
||||
fmt::make_format_args<basic_printf_context_t<Char>>(args...));
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_PRINTF_H_
|
||||
@@ -0,0 +1,793 @@
|
||||
// Formatting library for C++ - experimental range support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
//
|
||||
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
||||
// All Rights Reserved
|
||||
// {fmt} support for ranges, containers and types tuple interface.
|
||||
|
||||
#ifndef FMT_RANGES_H_
|
||||
#define FMT_RANGES_H_
|
||||
|
||||
#include <initializer_list>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename RangeT, typename OutputIterator>
|
||||
OutputIterator copy(const RangeT& range, OutputIterator out) {
|
||||
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
||||
*out++ = *it;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
OutputIterator copy(const char* str, OutputIterator out) {
|
||||
while (*str) *out++ = *str++;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
OutputIterator copy(char ch, OutputIterator out) {
|
||||
*out++ = ch;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIterator>
|
||||
OutputIterator copy(wchar_t ch, OutputIterator out) {
|
||||
*out++ = ch;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Returns true if T has a std::string-like interface, like std::string_view.
|
||||
template <typename T> class is_std_string_like {
|
||||
template <typename U>
|
||||
static auto check(U* p)
|
||||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
is_string<T>::value ||
|
||||
std::is_convertible<T, std_string_view<char>>::value ||
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
|
||||
|
||||
template <typename T> class is_map {
|
||||
template <typename U> static auto check(U*) -> typename U::mapped_type;
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
#ifdef FMT_FORMAT_MAP_AS_LIST
|
||||
static FMT_CONSTEXPR_DECL const bool value = false;
|
||||
#else
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename T> class is_set {
|
||||
template <typename U> static auto check(U*) -> typename U::key_type;
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
#ifdef FMT_FORMAT_SET_AS_LIST
|
||||
static FMT_CONSTEXPR_DECL const bool value = false;
|
||||
#else
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename... Ts> struct conditional_helper {};
|
||||
|
||||
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
||||
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
||||
|
||||
# define FMT_DECLTYPE_RETURN(val) \
|
||||
->decltype(val) { return val; } \
|
||||
static_assert( \
|
||||
true, "") // This makes it so that a semicolon is required after the
|
||||
// macro, which helps clang-format handle the formatting.
|
||||
|
||||
// C array overload
|
||||
template <typename T, std::size_t N>
|
||||
auto range_begin(const T (&arr)[N]) -> const T* {
|
||||
return arr;
|
||||
}
|
||||
template <typename T, std::size_t N>
|
||||
auto range_end(const T (&arr)[N]) -> const T* {
|
||||
return arr + N;
|
||||
}
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct has_member_fn_begin_end_t : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end())>>
|
||||
: std::true_type {};
|
||||
|
||||
// Member function overload
|
||||
template <typename T>
|
||||
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
|
||||
template <typename T>
|
||||
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
|
||||
|
||||
// ADL overload. Only participates in overload resolution if member functions
|
||||
// are not found.
|
||||
template <typename T>
|
||||
auto range_begin(T&& rng)
|
||||
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
|
||||
decltype(begin(static_cast<T&&>(rng)))> {
|
||||
return begin(static_cast<T&&>(rng));
|
||||
}
|
||||
template <typename T>
|
||||
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
|
||||
decltype(end(static_cast<T&&>(rng)))> {
|
||||
return end(static_cast<T&&>(rng));
|
||||
}
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct has_const_begin_end : std::false_type {};
|
||||
template <typename T, typename Enable = void>
|
||||
struct has_mutable_begin_end : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_const_begin_end<
|
||||
T,
|
||||
void_t<
|
||||
decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
|
||||
decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_mutable_begin_end<
|
||||
T, void_t<decltype(detail::range_begin(std::declval<T>())),
|
||||
decltype(detail::range_end(std::declval<T>())),
|
||||
enable_if_t<std::is_copy_constructible<T>::value>>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_range_<T, void>
|
||||
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
|
||||
has_mutable_begin_end<T>::value)> {};
|
||||
# undef FMT_DECLTYPE_RETURN
|
||||
#endif
|
||||
|
||||
// tuple_size and tuple_element check.
|
||||
template <typename T> class is_tuple_like_ {
|
||||
template <typename U>
|
||||
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
// Check for integer_sequence
|
||||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
||||
template <typename T, T... N>
|
||||
using integer_sequence = std::integer_sequence<T, N...>;
|
||||
template <size_t... N> using index_sequence = std::index_sequence<N...>;
|
||||
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
|
||||
#else
|
||||
template <typename T, T... N> struct integer_sequence {
|
||||
using value_type = T;
|
||||
|
||||
static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
|
||||
};
|
||||
|
||||
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
|
||||
|
||||
template <typename T, size_t N, T... Ns>
|
||||
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
|
||||
template <typename T, T... Ns>
|
||||
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
|
||||
|
||||
template <size_t N>
|
||||
using make_index_sequence = make_integer_sequence<size_t, N>;
|
||||
#endif
|
||||
|
||||
template <class Tuple, class F, size_t... Is>
|
||||
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
|
||||
using std::get;
|
||||
// using free function get<I>(T) now.
|
||||
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
|
||||
(void)_; // blocks warnings
|
||||
}
|
||||
|
||||
template <class T>
|
||||
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
|
||||
T const&) {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
||||
const auto indexes = get_indexes(tup);
|
||||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
using value_type =
|
||||
remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>;
|
||||
|
||||
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
|
||||
*out++ = ',';
|
||||
*out++ = ' ';
|
||||
return out;
|
||||
}
|
||||
|
||||
struct singleton {
|
||||
unsigned char upper;
|
||||
unsigned char lower_count;
|
||||
};
|
||||
|
||||
inline auto is_printable(uint16_t x, const singleton* singletons,
|
||||
size_t singletons_size,
|
||||
const unsigned char* singleton_lowers,
|
||||
const unsigned char* normal, size_t normal_size)
|
||||
-> bool {
|
||||
auto upper = x >> 8;
|
||||
auto lower_start = 0;
|
||||
for (size_t i = 0; i < singletons_size; ++i) {
|
||||
auto s = singletons[i];
|
||||
auto lower_end = lower_start + s.lower_count;
|
||||
if (upper < s.upper) break;
|
||||
if (upper == s.upper) {
|
||||
for (auto j = lower_start; j < lower_end; ++j) {
|
||||
if (singleton_lowers[j] == (x & 0xff)) return false;
|
||||
}
|
||||
}
|
||||
lower_start = lower_end;
|
||||
}
|
||||
|
||||
auto xsigned = static_cast<int>(x);
|
||||
auto current = true;
|
||||
for (size_t i = 0; i < normal_size; ++i) {
|
||||
auto v = static_cast<int>(normal[i]);
|
||||
auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v;
|
||||
xsigned -= len;
|
||||
if (xsigned < 0) break;
|
||||
current = !current;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
// Returns true iff the code point cp is printable.
|
||||
// This code is generated by support/printable.py.
|
||||
inline auto is_printable(uint32_t cp) -> bool {
|
||||
static constexpr singleton singletons0[] = {
|
||||
{0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8},
|
||||
{0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13},
|
||||
{0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5},
|
||||
{0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22},
|
||||
{0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3},
|
||||
{0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8},
|
||||
{0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9},
|
||||
};
|
||||
static constexpr unsigned char singletons0_lower[] = {
|
||||
0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90,
|
||||
0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f,
|
||||
0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
|
||||
0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04,
|
||||
0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d,
|
||||
0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
|
||||
0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
|
||||
0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d,
|
||||
0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
|
||||
0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d,
|
||||
0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5,
|
||||
0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
|
||||
0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49,
|
||||
0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7,
|
||||
0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
|
||||
0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e,
|
||||
0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16,
|
||||
0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
|
||||
0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f,
|
||||
0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf,
|
||||
0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
|
||||
0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27,
|
||||
0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91,
|
||||
0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
|
||||
0xfe, 0xff,
|
||||
};
|
||||
static constexpr singleton singletons1[] = {
|
||||
{0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2},
|
||||
{0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5},
|
||||
{0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5},
|
||||
{0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2},
|
||||
{0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5},
|
||||
{0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2},
|
||||
{0xfa, 2}, {0xfb, 1},
|
||||
};
|
||||
static constexpr unsigned char singletons1_lower[] = {
|
||||
0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07,
|
||||
0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36,
|
||||
0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
|
||||
0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
|
||||
0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b,
|
||||
0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
|
||||
0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66,
|
||||
0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27,
|
||||
0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
|
||||
0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7,
|
||||
0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6,
|
||||
0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
|
||||
0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66,
|
||||
0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0,
|
||||
0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
|
||||
};
|
||||
static constexpr unsigned char normal0[] = {
|
||||
0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04,
|
||||
0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0,
|
||||
0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
|
||||
0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03,
|
||||
0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03,
|
||||
0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
|
||||
0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15,
|
||||
0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f,
|
||||
0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
|
||||
0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07,
|
||||
0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06,
|
||||
0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
|
||||
0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac,
|
||||
0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c,
|
||||
0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
|
||||
0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c,
|
||||
0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b,
|
||||
0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
|
||||
0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03,
|
||||
0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80,
|
||||
0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
|
||||
0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c,
|
||||
0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17,
|
||||
0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
|
||||
0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80,
|
||||
0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d,
|
||||
};
|
||||
static constexpr unsigned char normal1[] = {
|
||||
0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f,
|
||||
0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e,
|
||||
0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
|
||||
0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09,
|
||||
0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16,
|
||||
0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
|
||||
0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36,
|
||||
0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33,
|
||||
0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
|
||||
0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e,
|
||||
0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41,
|
||||
0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
|
||||
0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22,
|
||||
0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04,
|
||||
0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
|
||||
0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03,
|
||||
0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81,
|
||||
0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
|
||||
0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1,
|
||||
0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a,
|
||||
0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
|
||||
0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09,
|
||||
0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89,
|
||||
0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
|
||||
0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09,
|
||||
0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50,
|
||||
0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
|
||||
0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83,
|
||||
0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05,
|
||||
0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
|
||||
0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80,
|
||||
0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07,
|
||||
0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
|
||||
0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07,
|
||||
0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06,
|
||||
};
|
||||
auto lower = static_cast<uint16_t>(cp);
|
||||
if (cp < 0x10000) {
|
||||
return is_printable(lower, singletons0,
|
||||
sizeof(singletons0) / sizeof(*singletons0),
|
||||
singletons0_lower, normal0, sizeof(normal0));
|
||||
}
|
||||
if (cp < 0x20000) {
|
||||
return is_printable(lower, singletons1,
|
||||
sizeof(singletons1) / sizeof(*singletons1),
|
||||
singletons1_lower, normal1, sizeof(normal1));
|
||||
}
|
||||
if (0x2a6de <= cp && cp < 0x2a700) return false;
|
||||
if (0x2b735 <= cp && cp < 0x2b740) return false;
|
||||
if (0x2b81e <= cp && cp < 0x2b820) return false;
|
||||
if (0x2cea2 <= cp && cp < 0x2ceb0) return false;
|
||||
if (0x2ebe1 <= cp && cp < 0x2f800) return false;
|
||||
if (0x2fa1e <= cp && cp < 0x30000) return false;
|
||||
if (0x3134b <= cp && cp < 0xe0100) return false;
|
||||
if (0xe01f0 <= cp && cp < 0x110000) return false;
|
||||
return cp < 0x110000;
|
||||
}
|
||||
|
||||
inline auto needs_escape(uint32_t cp) -> bool {
|
||||
return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
|
||||
!is_printable(cp);
|
||||
}
|
||||
|
||||
template <typename Char> struct find_escape_result {
|
||||
const Char* begin;
|
||||
const Char* end;
|
||||
uint32_t cp;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
auto find_escape(const Char* begin, const Char* end)
|
||||
-> find_escape_result<Char> {
|
||||
for (; begin != end; ++begin) {
|
||||
auto cp = static_cast<typename std::make_unsigned<Char>::type>(*begin);
|
||||
if (sizeof(Char) == 1 && cp >= 0x80) continue;
|
||||
if (needs_escape(cp)) return {begin, begin + 1, cp};
|
||||
}
|
||||
return {begin, nullptr, 0};
|
||||
}
|
||||
|
||||
inline auto find_escape(const char* begin, const char* end)
|
||||
-> find_escape_result<char> {
|
||||
if (!is_utf8()) return find_escape<char>(begin, end);
|
||||
auto result = find_escape_result<char>{end, nullptr, 0};
|
||||
for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
|
||||
[&](uint32_t cp, string_view sv) {
|
||||
if (needs_escape(cp)) {
|
||||
result = {sv.begin(), sv.end(), cp};
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
|
||||
*out++ = '"';
|
||||
auto begin = str.begin(), end = str.end();
|
||||
do {
|
||||
auto escape = find_escape(begin, end);
|
||||
out = copy_str<Char>(begin, escape.begin, out);
|
||||
begin = escape.end;
|
||||
if (!begin) break;
|
||||
auto c = static_cast<Char>(escape.cp);
|
||||
switch (escape.cp) {
|
||||
case '\n':
|
||||
*out++ = '\\';
|
||||
c = 'n';
|
||||
break;
|
||||
case '\r':
|
||||
*out++ = '\\';
|
||||
c = 'r';
|
||||
break;
|
||||
case '\t':
|
||||
*out++ = '\\';
|
||||
c = 't';
|
||||
break;
|
||||
case '"':
|
||||
FMT_FALLTHROUGH;
|
||||
case '\\':
|
||||
*out++ = '\\';
|
||||
break;
|
||||
default:
|
||||
if (is_utf8()) {
|
||||
if (escape.cp < 0x100) {
|
||||
out = format_to(out, "\\x{:02x}", escape.cp);
|
||||
continue;
|
||||
}
|
||||
if (escape.cp < 0x10000) {
|
||||
out = format_to(out, "\\u{:04x}", escape.cp);
|
||||
continue;
|
||||
}
|
||||
if (escape.cp < 0x110000) {
|
||||
out = format_to(out, "\\U{:08x}", escape.cp);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (Char escape_char : basic_string_view<Char>(
|
||||
escape.begin, to_unsigned(escape.end - escape.begin))) {
|
||||
out = format_to(
|
||||
out, "\\x{:02x}",
|
||||
static_cast<typename std::make_unsigned<Char>::type>(escape_char));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
*out++ = c;
|
||||
} while (begin != end);
|
||||
*out++ = '"';
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
|
||||
inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
|
||||
auto sv = std_string_view<Char>(str);
|
||||
return write_range_entry<Char>(out, basic_string_view<Char>(sv));
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename Arg,
|
||||
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
|
||||
OutputIt write_range_entry(OutputIt out, const Arg v) {
|
||||
*out++ = '\'';
|
||||
*out++ = v;
|
||||
*out++ = '\'';
|
||||
return out;
|
||||
}
|
||||
|
||||
template <
|
||||
typename Char, typename OutputIt, typename Arg,
|
||||
FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
|
||||
!std::is_same<Arg, Char>::value)>
|
||||
OutputIt write_range_entry(OutputIt out, const Arg& v) {
|
||||
return write<Char>(out, v);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T> struct is_tuple_like {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
|
||||
};
|
||||
|
||||
template <typename TupleT, typename Char>
|
||||
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||
private:
|
||||
// C++11 generic lambda for format().
|
||||
template <typename FormatContext> struct format_each {
|
||||
template <typename T> void operator()(const T& v) {
|
||||
if (i > 0) out = detail::write_delimiter(out);
|
||||
out = detail::write_range_entry<Char>(out, v);
|
||||
++i;
|
||||
}
|
||||
int i;
|
||||
typename FormatContext::iterator& out;
|
||||
};
|
||||
|
||||
public:
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext = format_context>
|
||||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
*out++ = '(';
|
||||
detail::for_each(values, format_each<FormatContext>{0, out});
|
||||
*out++ = ')';
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char> struct is_range {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
|
||||
!detail::is_map<T>::value &&
|
||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<
|
||||
T, Char,
|
||||
enable_if_t<
|
||||
fmt::is_range<T, Char>::value
|
||||
// Workaround a bug in MSVC 2019 and earlier.
|
||||
#if !FMT_MSC_VER
|
||||
&& (is_formattable<detail::value_type<T>, Char>::value ||
|
||||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
|
||||
#endif
|
||||
>> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <
|
||||
typename FormatContext, typename U,
|
||||
FMT_ENABLE_IF(
|
||||
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
|
||||
const T, T>>::value)>
|
||||
auto format(U& range, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
#ifdef FMT_DEPRECATED_BRACED_RANGES
|
||||
Char prefix = '{';
|
||||
Char postfix = '}';
|
||||
#else
|
||||
Char prefix = detail::is_set<T>::value ? '{' : '[';
|
||||
Char postfix = detail::is_set<T>::value ? '}' : ']';
|
||||
#endif
|
||||
auto out = ctx.out();
|
||||
*out++ = prefix;
|
||||
int i = 0;
|
||||
auto it = std::begin(range);
|
||||
auto end = std::end(range);
|
||||
for (; it != end; ++it) {
|
||||
if (i > 0) out = detail::write_delimiter(out);
|
||||
out = detail::write_range_entry<Char>(out, *it);
|
||||
++i;
|
||||
}
|
||||
*out++ = postfix;
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<
|
||||
T, Char,
|
||||
enable_if_t<
|
||||
detail::is_map<T>::value
|
||||
// Workaround a bug in MSVC 2019 and earlier.
|
||||
#if !FMT_MSC_VER
|
||||
&& (is_formattable<detail::value_type<T>, Char>::value ||
|
||||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
|
||||
#endif
|
||||
>> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <
|
||||
typename FormatContext, typename U,
|
||||
FMT_ENABLE_IF(
|
||||
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
|
||||
const T, T>>::value)>
|
||||
auto format(U& map, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
*out++ = '{';
|
||||
int i = 0;
|
||||
for (const auto& item : map) {
|
||||
if (i > 0) out = detail::write_delimiter(out);
|
||||
out = detail::write_range_entry<Char>(out, item.first);
|
||||
*out++ = ':';
|
||||
*out++ = ' ';
|
||||
out = detail::write_range_entry<Char>(out, item.second);
|
||||
++i;
|
||||
}
|
||||
*out++ = '}';
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
||||
const std::tuple<T...>& tuple;
|
||||
basic_string_view<Char> sep;
|
||||
|
||||
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||
: tuple(t), sep{s} {}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T>
|
||||
using tuple_arg_join = tuple_join_view<Char, T...>;
|
||||
|
||||
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
|
||||
// support in tuple_join. It is disabled by default because of issues with
|
||||
// the dynamic width and precision.
|
||||
#ifndef FMT_TUPLE_JOIN_SPECIFIERS
|
||||
# define FMT_TUPLE_JOIN_SPECIFIERS 0
|
||||
#endif
|
||||
|
||||
template <typename Char, typename... T>
|
||||
struct formatter<tuple_join_view<Char, T...>, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const tuple_join_view<Char, T...>& value,
|
||||
FormatContext& ctx) const -> typename FormatContext::iterator {
|
||||
return do_format(value, ctx,
|
||||
std::integral_constant<size_t, sizeof...(T)>());
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||
std::integral_constant<size_t, 0>)
|
||||
-> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename ParseContext, size_t N>
|
||||
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||
std::integral_constant<size_t, N>)
|
||||
-> decltype(ctx.begin()) {
|
||||
auto end = ctx.begin();
|
||||
#if FMT_TUPLE_JOIN_SPECIFIERS
|
||||
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
|
||||
if (N > 1) {
|
||||
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
|
||||
if (end != end1)
|
||||
FMT_THROW(format_error("incompatible format specs for tuple elements"));
|
||||
}
|
||||
#endif
|
||||
return end;
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
|
||||
std::integral_constant<size_t, 0>) const ->
|
||||
typename FormatContext::iterator {
|
||||
return ctx.out();
|
||||
}
|
||||
|
||||
template <typename FormatContext, size_t N>
|
||||
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
||||
std::integral_constant<size_t, N>) const ->
|
||||
typename FormatContext::iterator {
|
||||
auto out = std::get<sizeof...(T) - N>(formatters_)
|
||||
.format(std::get<sizeof...(T) - N>(value.tuple), ctx);
|
||||
if (N > 1) {
|
||||
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||
ctx.advance_to(out);
|
||||
return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns an object that formats `tuple` with elements separated by `sep`.
|
||||
|
||||
**Example**::
|
||||
|
||||
std::tuple<int, char> t = {1, 'a'};
|
||||
fmt::print("{}", fmt::join(t, ", "));
|
||||
// Output: "1, a"
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
|
||||
-> tuple_join_view<char, T...> {
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
|
||||
basic_string_view<wchar_t> sep)
|
||||
-> tuple_join_view<wchar_t, T...> {
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns an object that formats `initializer_list` with elements separated by
|
||||
`sep`.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
|
||||
// Output: "1, 2, 3"
|
||||
\endrst
|
||||
*/
|
||||
template <typename T>
|
||||
auto join(std::initializer_list<T> list, string_view sep)
|
||||
-> join_view<const T*, const T*> {
|
||||
return join(std::begin(list), std::end(list), sep);
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_RANGES_H_
|
||||
@@ -0,0 +1,236 @@
|
||||
// Formatting library for C++ - optional wchar_t and exotic character support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_XCHAR_H_
|
||||
#define FMT_XCHAR_H_
|
||||
|
||||
#include <cwchar>
|
||||
#include <tuple>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
using wstring_view = basic_string_view<wchar_t>;
|
||||
using wformat_parse_context = basic_format_parse_context<wchar_t>;
|
||||
using wformat_context = buffer_context<wchar_t>;
|
||||
using wformat_args = basic_format_args<wformat_context>;
|
||||
using wmemory_buffer = basic_memory_buffer<wchar_t>;
|
||||
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||
// Workaround broken conversion on older gcc.
|
||||
template <typename... Args> using wformat_string = wstring_view;
|
||||
#else
|
||||
template <typename... Args>
|
||||
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
|
||||
#endif
|
||||
|
||||
template <> struct is_char<wchar_t> : std::true_type {};
|
||||
template <> struct is_char<detail::char8_type> : std::true_type {};
|
||||
template <> struct is_char<char16_t> : std::true_type {};
|
||||
template <> struct is_char<char32_t> : std::true_type {};
|
||||
|
||||
template <typename... Args>
|
||||
constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
|
||||
const Args&... args) {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
inline namespace literals {
|
||||
constexpr auto operator"" _format(const wchar_t* s, size_t n)
|
||||
-> detail::udl_formatter<wchar_t> {
|
||||
return {{s, n}};
|
||||
}
|
||||
|
||||
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
|
||||
return {s};
|
||||
}
|
||||
#endif
|
||||
} // namespace literals
|
||||
|
||||
template <typename It, typename Sentinel>
|
||||
auto join(It begin, Sentinel end, wstring_view sep)
|
||||
-> join_view<It, Sentinel, wchar_t> {
|
||||
return {begin, end, sep};
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
auto join(Range&& range, wstring_view sep)
|
||||
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
|
||||
wchar_t> {
|
||||
return join(std::begin(range), std::end(range), sep);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto join(std::initializer_list<T> list, wstring_view sep)
|
||||
-> join_view<const T*, const T*, wchar_t> {
|
||||
return join(std::begin(list), std::end(list), sep);
|
||||
}
|
||||
|
||||
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||
auto vformat(basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> std::basic_string<Char> {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
detail::vformat_to(buffer, format_str, args);
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
// Pass char_t as a default template parameter instead of using
|
||||
// std::basic_string<char_t<S>> to reduce the symbol size.
|
||||
template <typename S, typename... Args, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
|
||||
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||
return vformat(to_string_view(format_str), vargs);
|
||||
}
|
||||
|
||||
template <typename Locale, typename S, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto vformat(
|
||||
const Locale& loc, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> std::basic_string<Char> {
|
||||
return detail::vformat(loc, to_string_view(format_str), args);
|
||||
}
|
||||
|
||||
template <typename Locale, typename S, typename... Args,
|
||||
typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto format(const Locale& loc, const S& format_str, Args&&... args)
|
||||
-> std::basic_string<Char> {
|
||||
return detail::vformat(loc, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
auto vformat_to(OutputIt out, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> OutputIt {
|
||||
auto&& buf = detail::get_buffer<Char>(out);
|
||||
detail::vformat_to(buf, to_string_view(format_str), args);
|
||||
return detail::get_iterator(buf);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt {
|
||||
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
|
||||
return vformat_to(out, to_string_view(fmt), vargs);
|
||||
}
|
||||
|
||||
template <typename S, typename... Args, typename Char, size_t SIZE,
|
||||
typename Allocator, FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf,
|
||||
const S& format_str, Args&&... args) ->
|
||||
typename buffer_context<Char>::iterator {
|
||||
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||
detail::vformat_to(buf, to_string_view(format_str), vargs, {});
|
||||
return detail::buffer_appender<Char>(buf);
|
||||
}
|
||||
|
||||
template <typename Locale, typename S, typename OutputIt, typename... Args,
|
||||
typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_locale<Locale>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto vformat_to(
|
||||
OutputIt out, const Locale& loc, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
|
||||
auto&& buf = detail::get_buffer<Char>(out);
|
||||
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
|
||||
return detail::get_iterator(buf);
|
||||
}
|
||||
|
||||
template <
|
||||
typename OutputIt, typename Locale, typename S, typename... Args,
|
||||
typename Char = char_t<S>,
|
||||
bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
|
||||
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
|
||||
Args&&... args) ->
|
||||
typename std::enable_if<enable, OutputIt>::type {
|
||||
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||
return vformat_to(out, loc, to_string_view(format_str), vargs);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto vformat_to_n(
|
||||
OutputIt out, size_t n, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> format_to_n_result<OutputIt> {
|
||||
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
|
||||
n);
|
||||
detail::vformat_to(buf, format_str, args);
|
||||
return {buf.out(), buf.count()};
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
|
||||
const Args&... args) -> format_to_n_result<OutputIt> {
|
||||
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
|
||||
return vformat_to_n(out, n, to_string_view(fmt), vargs);
|
||||
}
|
||||
|
||||
template <typename S, typename... Args, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
|
||||
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
|
||||
detail::counting_buffer<Char> buf;
|
||||
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
|
||||
detail::vformat_to(buf, to_string_view(fmt), vargs);
|
||||
return buf.count();
|
||||
}
|
||||
|
||||
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
|
||||
wmemory_buffer buffer;
|
||||
detail::vformat_to(buffer, fmt, args);
|
||||
buffer.push_back(L'\0');
|
||||
if (std::fputws(buffer.data(), f) == -1)
|
||||
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
|
||||
}
|
||||
|
||||
inline void vprint(wstring_view fmt, wformat_args args) {
|
||||
vprint(stdout, fmt, args);
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
|
||||
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||
}
|
||||
|
||||
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
|
||||
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||
}
|
||||
|
||||
/**
|
||||
Converts *value* to ``std::wstring`` using the default format for type *T*.
|
||||
*/
|
||||
template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
|
||||
return format(FMT_STRING(L"{}"), value);
|
||||
}
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_XCHAR_H_
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright(c) 2016 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
//
|
||||
// include bundled or external copy of fmtlib's chrono support
|
||||
//
|
||||
|
||||
#if !defined(SPDLOG_USE_STD_FORMAT)
|
||||
# if !defined(SPDLOG_FMT_EXTERNAL)
|
||||
# ifdef SPDLOG_HEADER_ONLY
|
||||
# ifndef FMT_HEADER_ONLY
|
||||
# define FMT_HEADER_ONLY
|
||||
# endif
|
||||
# endif
|
||||
# include <spdlog/fmt/bundled/chrono.h>
|
||||
# else
|
||||
# include <fmt/chrono.h>
|
||||
# endif
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright(c) 2016 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
//
|
||||
// include bundled or external copy of fmtlib's compile-time support
|
||||
//
|
||||
|
||||
#if !defined(SPDLOG_USE_STD_FORMAT)
|
||||
# if !defined(SPDLOG_FMT_EXTERNAL)
|
||||
# ifdef SPDLOG_HEADER_ONLY
|
||||
# ifndef FMT_HEADER_ONLY
|
||||
# define FMT_HEADER_ONLY
|
||||
# endif
|
||||
# endif
|
||||
# include <spdlog/fmt/bundled/compile.h>
|
||||
# else
|
||||
# include <fmt/compile.h>
|
||||
# endif
|
||||
#endif
|
||||
29
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/fmt/fmt.h
Normal file
29
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/fmt/fmt.h
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Copyright(c) 2016-2018 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Include a bundled header-only copy of fmtlib or an external one.
|
||||
// By default spdlog include its own copy.
|
||||
//
|
||||
|
||||
#if defined(SPDLOG_USE_STD_FORMAT) // SPDLOG_USE_STD_FORMAT is defined - use std::format
|
||||
# include <format>
|
||||
#elif !defined(SPDLOG_FMT_EXTERNAL)
|
||||
# if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY)
|
||||
# define FMT_HEADER_ONLY
|
||||
# endif
|
||||
# ifndef FMT_USE_WINDOWS_H
|
||||
# define FMT_USE_WINDOWS_H 0
|
||||
# endif
|
||||
// enable the 'n' flag in for backward compatibility with fmt 6.x
|
||||
# define FMT_DEPRECATED_N_SPECIFIER
|
||||
# include <spdlog/fmt/bundled/core.h>
|
||||
# include <spdlog/fmt/bundled/format.h>
|
||||
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
|
||||
# include <fmt/core.h>
|
||||
# include <fmt/format.h>
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright(c) 2016 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
//
|
||||
// include bundled or external copy of fmtlib's ostream support
|
||||
//
|
||||
|
||||
#if !defined(SPDLOG_USE_STD_FORMAT)
|
||||
# if !defined(SPDLOG_FMT_EXTERNAL)
|
||||
# ifdef SPDLOG_HEADER_ONLY
|
||||
# ifndef FMT_HEADER_ONLY
|
||||
# define FMT_HEADER_ONLY
|
||||
# endif
|
||||
# endif
|
||||
# include <spdlog/fmt/bundled/ostream.h>
|
||||
# else
|
||||
# include <fmt/ostream.h>
|
||||
# endif
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright(c) 2016 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
//
|
||||
// include bundled or external copy of fmtlib's ranges support
|
||||
//
|
||||
|
||||
#if !defined(SPDLOG_USE_STD_FORMAT)
|
||||
# if !defined(SPDLOG_FMT_EXTERNAL)
|
||||
# ifdef SPDLOG_HEADER_ONLY
|
||||
# ifndef FMT_HEADER_ONLY
|
||||
# define FMT_HEADER_ONLY
|
||||
# endif
|
||||
# endif
|
||||
# include <spdlog/fmt/bundled/ranges.h>
|
||||
# else
|
||||
# include <fmt/ranges.h>
|
||||
# endif
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright(c) 2016 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
//
|
||||
// include bundled or external copy of fmtlib's xchar support
|
||||
//
|
||||
|
||||
#if !defined(SPDLOG_USE_STD_FORMAT)
|
||||
# if !defined(SPDLOG_FMT_EXTERNAL)
|
||||
# ifdef SPDLOG_HEADER_ONLY
|
||||
# ifndef FMT_HEADER_ONLY
|
||||
# define FMT_HEADER_ONLY
|
||||
# endif
|
||||
# endif
|
||||
# include <spdlog/fmt/bundled/xchar.h>
|
||||
# else
|
||||
# include <fmt/xchar.h>
|
||||
# endif
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include <spdlog/details/log_msg.h>
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
class formatter
|
||||
{
|
||||
public:
|
||||
virtual ~formatter() = default;
|
||||
virtual void format(const details::log_msg &msg, memory_buf_t &dest) = 0;
|
||||
virtual std::unique_ptr<formatter> clone() const = 0;
|
||||
};
|
||||
} // namespace spdlog
|
||||
18
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/fwd.h
Normal file
18
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/fwd.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace spdlog {
|
||||
class logger;
|
||||
class formatter;
|
||||
|
||||
namespace sinks {
|
||||
class sink;
|
||||
}
|
||||
|
||||
namespace level {
|
||||
enum level_enum : int;
|
||||
}
|
||||
|
||||
} // namespace spdlog
|
||||
257
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/logger-inl.h
Normal file
257
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/logger-inl.h
Normal file
@@ -0,0 +1,257 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
# include <spdlog/logger.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/sinks/sink.h>
|
||||
#include <spdlog/details/backtracer.h>
|
||||
#include <spdlog/pattern_formatter.h>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
// public methods
|
||||
SPDLOG_INLINE logger::logger(const logger &other)
|
||||
: name_(other.name_)
|
||||
, sinks_(other.sinks_)
|
||||
, level_(other.level_.load(std::memory_order_relaxed))
|
||||
, flush_level_(other.flush_level_.load(std::memory_order_relaxed))
|
||||
, custom_err_handler_(other.custom_err_handler_)
|
||||
, tracer_(other.tracer_)
|
||||
{}
|
||||
|
||||
SPDLOG_INLINE logger::logger(logger &&other) SPDLOG_NOEXCEPT : name_(std::move(other.name_)),
|
||||
sinks_(std::move(other.sinks_)),
|
||||
level_(other.level_.load(std::memory_order_relaxed)),
|
||||
flush_level_(other.flush_level_.load(std::memory_order_relaxed)),
|
||||
custom_err_handler_(std::move(other.custom_err_handler_)),
|
||||
tracer_(std::move(other.tracer_))
|
||||
|
||||
{}
|
||||
|
||||
SPDLOG_INLINE logger &logger::operator=(logger other) SPDLOG_NOEXCEPT
|
||||
{
|
||||
this->swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void logger::swap(spdlog::logger &other) SPDLOG_NOEXCEPT
|
||||
{
|
||||
name_.swap(other.name_);
|
||||
sinks_.swap(other.sinks_);
|
||||
|
||||
// swap level_
|
||||
auto other_level = other.level_.load();
|
||||
auto my_level = level_.exchange(other_level);
|
||||
other.level_.store(my_level);
|
||||
|
||||
// swap flush level_
|
||||
other_level = other.flush_level_.load();
|
||||
my_level = flush_level_.exchange(other_level);
|
||||
other.flush_level_.store(my_level);
|
||||
|
||||
custom_err_handler_.swap(other.custom_err_handler_);
|
||||
std::swap(tracer_, other.tracer_);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void swap(logger &a, logger &b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void logger::set_level(level::level_enum log_level)
|
||||
{
|
||||
level_.store(log_level);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE level::level_enum logger::level() const
|
||||
{
|
||||
return static_cast<level::level_enum>(level_.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
SPDLOG_INLINE const std::string &logger::name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
// set formatting for the sinks in this logger.
|
||||
// each sink will get a separate instance of the formatter object.
|
||||
SPDLOG_INLINE void logger::set_formatter(std::unique_ptr<formatter> f)
|
||||
{
|
||||
for (auto it = sinks_.begin(); it != sinks_.end(); ++it)
|
||||
{
|
||||
if (std::next(it) == sinks_.end())
|
||||
{
|
||||
// last element - we can be move it.
|
||||
(*it)->set_formatter(std::move(f));
|
||||
break; // to prevent clang-tidy warning
|
||||
}
|
||||
else
|
||||
{
|
||||
(*it)->set_formatter(f->clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void logger::set_pattern(std::string pattern, pattern_time_type time_type)
|
||||
{
|
||||
auto new_formatter = details::make_unique<pattern_formatter>(std::move(pattern), time_type);
|
||||
set_formatter(std::move(new_formatter));
|
||||
}
|
||||
|
||||
// create new backtrace sink and move to it all our child sinks
|
||||
SPDLOG_INLINE void logger::enable_backtrace(size_t n_messages)
|
||||
{
|
||||
tracer_.enable(n_messages);
|
||||
}
|
||||
|
||||
// restore orig sinks and level and delete the backtrace sink
|
||||
SPDLOG_INLINE void logger::disable_backtrace()
|
||||
{
|
||||
tracer_.disable();
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void logger::dump_backtrace()
|
||||
{
|
||||
dump_backtrace_();
|
||||
}
|
||||
|
||||
// flush functions
|
||||
SPDLOG_INLINE void logger::flush()
|
||||
{
|
||||
flush_();
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void logger::flush_on(level::level_enum log_level)
|
||||
{
|
||||
flush_level_.store(log_level);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE level::level_enum logger::flush_level() const
|
||||
{
|
||||
return static_cast<level::level_enum>(flush_level_.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
// sinks
|
||||
SPDLOG_INLINE const std::vector<sink_ptr> &logger::sinks() const
|
||||
{
|
||||
return sinks_;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::vector<sink_ptr> &logger::sinks()
|
||||
{
|
||||
return sinks_;
|
||||
}
|
||||
|
||||
// error handler
|
||||
SPDLOG_INLINE void logger::set_error_handler(err_handler handler)
|
||||
{
|
||||
custom_err_handler_ = std::move(handler);
|
||||
}
|
||||
|
||||
// create new logger with same sinks and configuration.
|
||||
SPDLOG_INLINE std::shared_ptr<logger> logger::clone(std::string logger_name)
|
||||
{
|
||||
auto cloned = std::make_shared<logger>(*this);
|
||||
cloned->name_ = std::move(logger_name);
|
||||
return cloned;
|
||||
}
|
||||
|
||||
// protected methods
|
||||
SPDLOG_INLINE void logger::log_it_(const spdlog::details::log_msg &log_msg, bool log_enabled, bool traceback_enabled)
|
||||
{
|
||||
if (log_enabled)
|
||||
{
|
||||
sink_it_(log_msg);
|
||||
}
|
||||
if (traceback_enabled)
|
||||
{
|
||||
tracer_.push_back(log_msg);
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void logger::sink_it_(const details::log_msg &msg)
|
||||
{
|
||||
for (auto &sink : sinks_)
|
||||
{
|
||||
if (sink->should_log(msg.level))
|
||||
{
|
||||
SPDLOG_TRY
|
||||
{
|
||||
sink->log(msg);
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH(msg.source)
|
||||
}
|
||||
}
|
||||
|
||||
if (should_flush_(msg))
|
||||
{
|
||||
flush_();
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void logger::flush_()
|
||||
{
|
||||
for (auto &sink : sinks_)
|
||||
{
|
||||
SPDLOG_TRY
|
||||
{
|
||||
sink->flush();
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH(source_loc())
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void logger::dump_backtrace_()
|
||||
{
|
||||
using details::log_msg;
|
||||
if (tracer_.enabled())
|
||||
{
|
||||
sink_it_(log_msg{name(), level::info, "****************** Backtrace Start ******************"});
|
||||
tracer_.foreach_pop([this](const log_msg &msg) { this->sink_it_(msg); });
|
||||
sink_it_(log_msg{name(), level::info, "****************** Backtrace End ********************"});
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE bool logger::should_flush_(const details::log_msg &msg)
|
||||
{
|
||||
auto flush_level = flush_level_.load(std::memory_order_relaxed);
|
||||
return (msg.level >= flush_level) && (msg.level != level::off);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void logger::err_handler_(const std::string &msg)
|
||||
{
|
||||
if (custom_err_handler_)
|
||||
{
|
||||
custom_err_handler_(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
using std::chrono::system_clock;
|
||||
static std::mutex mutex;
|
||||
static std::chrono::system_clock::time_point last_report_time;
|
||||
static size_t err_counter = 0;
|
||||
std::lock_guard<std::mutex> lk{mutex};
|
||||
auto now = system_clock::now();
|
||||
err_counter++;
|
||||
if (now - last_report_time < std::chrono::seconds(1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
last_report_time = now;
|
||||
auto tm_time = details::os::localtime(system_clock::to_time_t(now));
|
||||
char date_buf[64];
|
||||
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
|
||||
#if defined(USING_R) && defined(R_R_H) // if in R environment
|
||||
REprintf("[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
|
||||
#else
|
||||
std::fprintf(stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} // namespace spdlog
|
||||
454
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/logger.h
Normal file
454
cpp-package/inspireface/cpp/test/3rd/spdlog/spdlog/logger.h
Normal file
@@ -0,0 +1,454 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// Thread safe logger (except for set_error_handler())
|
||||
// Has name, log level, vector of std::shared sink pointers and formatter
|
||||
// Upon each log write the logger:
|
||||
// 1. Checks if its log level is enough to log the message and if yes:
|
||||
// 2. Call the underlying sinks to do the job.
|
||||
// 3. Each sink use its own private copy of a formatter to format the message
|
||||
// and send to its destination.
|
||||
//
|
||||
// The use of private formatter per sink provides the opportunity to cache some
|
||||
// formatted data, and support for different format per sink.
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/log_msg.h>
|
||||
#include <spdlog/details/backtracer.h>
|
||||
|
||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
# ifndef _WIN32
|
||||
# error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
|
||||
# endif
|
||||
# include <spdlog/details/os.h>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
#ifndef SPDLOG_NO_EXCEPTIONS
|
||||
# define SPDLOG_LOGGER_CATCH(location) \
|
||||
catch (const std::exception &ex) \
|
||||
{ \
|
||||
if (location.filename) \
|
||||
{ \
|
||||
err_handler_(fmt_lib::format(SPDLOG_FMT_STRING("{} [{}({})]"), ex.what(), location.filename, location.line)); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
err_handler_(ex.what()); \
|
||||
} \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
err_handler_("Rethrowing unknown exception in logger"); \
|
||||
throw; \
|
||||
}
|
||||
#else
|
||||
# define SPDLOG_LOGGER_CATCH(location)
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
class SPDLOG_API logger
|
||||
{
|
||||
public:
|
||||
// Empty logger
|
||||
explicit logger(std::string name)
|
||||
: name_(std::move(name))
|
||||
, sinks_()
|
||||
{}
|
||||
|
||||
// Logger with range on sinks
|
||||
template<typename It>
|
||||
logger(std::string name, It begin, It end)
|
||||
: name_(std::move(name))
|
||||
, sinks_(begin, end)
|
||||
{}
|
||||
|
||||
// Logger with single sink
|
||||
logger(std::string name, sink_ptr single_sink)
|
||||
: logger(std::move(name), {std::move(single_sink)})
|
||||
{}
|
||||
|
||||
// Logger with sinks init list
|
||||
logger(std::string name, sinks_init_list sinks)
|
||||
: logger(std::move(name), sinks.begin(), sinks.end())
|
||||
{}
|
||||
|
||||
virtual ~logger() = default;
|
||||
|
||||
logger(const logger &other);
|
||||
logger(logger &&other) SPDLOG_NOEXCEPT;
|
||||
logger &operator=(logger other) SPDLOG_NOEXCEPT;
|
||||
void swap(spdlog::logger &other) SPDLOG_NOEXCEPT;
|
||||
|
||||
template<typename... Args>
|
||||
void log(source_loc loc, level::level_enum lvl, format_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log_(loc, lvl, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void log(level::level_enum lvl, format_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void log(level::level_enum lvl, const T &msg)
|
||||
{
|
||||
log(source_loc{}, lvl, msg);
|
||||
}
|
||||
|
||||
// T cannot be statically converted to format string (including string_view/wstring_view)
|
||||
template<class T, typename std::enable_if<!is_convertible_to_any_format_string<const T &>::value, int>::type = 0>
|
||||
void log(source_loc loc, level::level_enum lvl, const T &msg)
|
||||
{
|
||||
log(loc, lvl, "{}", msg);
|
||||
}
|
||||
|
||||
void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, string_view_t msg)
|
||||
{
|
||||
bool log_enabled = should_log(lvl);
|
||||
bool traceback_enabled = tracer_.enabled();
|
||||
if (!log_enabled && !traceback_enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
details::log_msg log_msg(log_time, loc, name_, lvl, msg);
|
||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||
}
|
||||
|
||||
void log(source_loc loc, level::level_enum lvl, string_view_t msg)
|
||||
{
|
||||
bool log_enabled = should_log(lvl);
|
||||
bool traceback_enabled = tracer_.enabled();
|
||||
if (!log_enabled && !traceback_enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
details::log_msg log_msg(loc, name_, lvl, msg);
|
||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||
}
|
||||
|
||||
void log(level::level_enum lvl, string_view_t msg)
|
||||
{
|
||||
log(source_loc{}, lvl, msg);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void trace(format_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::trace, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void debug(format_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::debug, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void info(format_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::info, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void warn(format_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::warn, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void error(format_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::err, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void critical(format_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::critical, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
template<typename... Args>
|
||||
void log(source_loc loc, level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log_(loc, lvl, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void log(level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, wstring_view_t msg)
|
||||
{
|
||||
bool log_enabled = should_log(lvl);
|
||||
bool traceback_enabled = tracer_.enabled();
|
||||
if (!log_enabled && !traceback_enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
memory_buf_t buf;
|
||||
details::os::wstr_to_utf8buf(wstring_view_t(msg.data(), msg.size()), buf);
|
||||
details::log_msg log_msg(log_time, loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||
}
|
||||
|
||||
void log(source_loc loc, level::level_enum lvl, wstring_view_t msg)
|
||||
{
|
||||
bool log_enabled = should_log(lvl);
|
||||
bool traceback_enabled = tracer_.enabled();
|
||||
if (!log_enabled && !traceback_enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
memory_buf_t buf;
|
||||
details::os::wstr_to_utf8buf(wstring_view_t(msg.data(), msg.size()), buf);
|
||||
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||
}
|
||||
|
||||
void log(level::level_enum lvl, wstring_view_t msg)
|
||||
{
|
||||
log(source_loc{}, lvl, msg);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void trace(wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::trace, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void debug(wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::debug, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void info(wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::info, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void warn(wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::warn, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void error(wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::err, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void critical(wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::critical, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
void trace(const T &msg)
|
||||
{
|
||||
log(level::trace, msg);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void debug(const T &msg)
|
||||
{
|
||||
log(level::debug, msg);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void info(const T &msg)
|
||||
{
|
||||
log(level::info, msg);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void warn(const T &msg)
|
||||
{
|
||||
log(level::warn, msg);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void error(const T &msg)
|
||||
{
|
||||
log(level::err, msg);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void critical(const T &msg)
|
||||
{
|
||||
log(level::critical, msg);
|
||||
}
|
||||
|
||||
// return true logging is enabled for the given level.
|
||||
bool should_log(level::level_enum msg_level) const
|
||||
{
|
||||
return msg_level >= level_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// return true if backtrace logging is enabled.
|
||||
bool should_backtrace() const
|
||||
{
|
||||
return tracer_.enabled();
|
||||
}
|
||||
|
||||
void set_level(level::level_enum log_level);
|
||||
|
||||
level::level_enum level() const;
|
||||
|
||||
const std::string &name() const;
|
||||
|
||||
// set formatting for the sinks in this logger.
|
||||
// each sink will get a separate instance of the formatter object.
|
||||
void set_formatter(std::unique_ptr<formatter> f);
|
||||
|
||||
// set formatting for the sinks in this logger.
|
||||
// equivalent to
|
||||
// set_formatter(make_unique<pattern_formatter>(pattern, time_type))
|
||||
// Note: each sink will get a new instance of a formatter object, replacing the old one.
|
||||
void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
|
||||
|
||||
// backtrace support.
|
||||
// efficiently store all debug/trace messages in a circular buffer until needed for debugging.
|
||||
void enable_backtrace(size_t n_messages);
|
||||
void disable_backtrace();
|
||||
void dump_backtrace();
|
||||
|
||||
// flush functions
|
||||
void flush();
|
||||
void flush_on(level::level_enum log_level);
|
||||
level::level_enum flush_level() const;
|
||||
|
||||
// sinks
|
||||
const std::vector<sink_ptr> &sinks() const;
|
||||
|
||||
std::vector<sink_ptr> &sinks();
|
||||
|
||||
// error handler
|
||||
void set_error_handler(err_handler);
|
||||
|
||||
// create new logger with same sinks and configuration.
|
||||
virtual std::shared_ptr<logger> clone(std::string logger_name);
|
||||
|
||||
protected:
|
||||
std::string name_;
|
||||
std::vector<sink_ptr> sinks_;
|
||||
spdlog::level_t level_{level::info};
|
||||
spdlog::level_t flush_level_{level::off};
|
||||
err_handler custom_err_handler_{nullptr};
|
||||
details::backtracer tracer_;
|
||||
|
||||
// common implementation for after templated public api has been resolved
|
||||
template<typename... Args>
|
||||
void log_(source_loc loc, level::level_enum lvl, string_view_t fmt, Args &&... args)
|
||||
{
|
||||
bool log_enabled = should_log(lvl);
|
||||
bool traceback_enabled = tracer_.enabled();
|
||||
if (!log_enabled && !traceback_enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SPDLOG_TRY
|
||||
{
|
||||
memory_buf_t buf;
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
fmt_lib::vformat_to(std::back_inserter(buf), fmt, fmt_lib::make_format_args(std::forward<Args>(args)...));
|
||||
#else
|
||||
// seems that fmt::detail::vformat_to(buf, ...) is ~20ns faster than fmt::vformat_to(std::back_inserter(buf),..)
|
||||
fmt::detail::vformat_to(buf, fmt, fmt::make_format_args(std::forward<Args>(args)...));
|
||||
#endif
|
||||
|
||||
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH(loc)
|
||||
}
|
||||
|
||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
template<typename... Args>
|
||||
void log_(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args &&... args)
|
||||
{
|
||||
bool log_enabled = should_log(lvl);
|
||||
bool traceback_enabled = tracer_.enabled();
|
||||
if (!log_enabled && !traceback_enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SPDLOG_TRY
|
||||
{
|
||||
// format to wmemory_buffer and convert to utf8
|
||||
wmemory_buf_t wbuf;
|
||||
# ifdef SPDLOG_USE_STD_FORMAT
|
||||
fmt_lib::vformat_to(
|
||||
std::back_inserter(wbuf), fmt, fmt_lib::make_format_args<fmt_lib::wformat_context>(std::forward<Args>(args)...));
|
||||
# else
|
||||
fmt::detail::vformat_to(wbuf, fmt, fmt::make_format_args<fmt::wformat_context>(std::forward<Args>(args)...));
|
||||
# endif
|
||||
|
||||
memory_buf_t buf;
|
||||
details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);
|
||||
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH(loc)
|
||||
}
|
||||
|
||||
// T can be statically converted to wstring_view, and no formatting needed.
|
||||
template<class T, typename std::enable_if<std::is_convertible<const T &, spdlog::wstring_view_t>::value, int>::type = 0>
|
||||
void log_(source_loc loc, level::level_enum lvl, const T &msg)
|
||||
{
|
||||
bool log_enabled = should_log(lvl);
|
||||
bool traceback_enabled = tracer_.enabled();
|
||||
if (!log_enabled && !traceback_enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SPDLOG_TRY
|
||||
{
|
||||
memory_buf_t buf;
|
||||
details::os::wstr_to_utf8buf(msg, buf);
|
||||
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH(loc)
|
||||
}
|
||||
|
||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
|
||||
// log the given message (if the given log level is high enough),
|
||||
// and save backtrace (if backtrace is enabled).
|
||||
void log_it_(const details::log_msg &log_msg, bool log_enabled, bool traceback_enabled);
|
||||
virtual void sink_it_(const details::log_msg &msg);
|
||||
virtual void flush_();
|
||||
void dump_backtrace_();
|
||||
bool should_flush_(const details::log_msg &msg);
|
||||
|
||||
// handle errors during logging.
|
||||
// default handler prints the error to stderr at max rate of 1 message/sec.
|
||||
void err_handler_(const std::string &msg);
|
||||
};
|
||||
|
||||
void swap(logger &a, logger &b);
|
||||
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
# include "logger-inl.h"
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,128 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/log_msg.h>
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/formatter.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
// padding information.
|
||||
struct padding_info
|
||||
{
|
||||
enum class pad_side
|
||||
{
|
||||
left,
|
||||
right,
|
||||
center
|
||||
};
|
||||
|
||||
padding_info() = default;
|
||||
padding_info(size_t width, padding_info::pad_side side, bool truncate)
|
||||
: width_(width)
|
||||
, side_(side)
|
||||
, truncate_(truncate)
|
||||
, enabled_(true)
|
||||
{}
|
||||
|
||||
bool enabled() const
|
||||
{
|
||||
return enabled_;
|
||||
}
|
||||
size_t width_ = 0;
|
||||
pad_side side_ = pad_side::left;
|
||||
bool truncate_ = false;
|
||||
bool enabled_ = false;
|
||||
};
|
||||
|
||||
class SPDLOG_API flag_formatter
|
||||
{
|
||||
public:
|
||||
explicit flag_formatter(padding_info padinfo)
|
||||
: padinfo_(padinfo)
|
||||
{}
|
||||
flag_formatter() = default;
|
||||
virtual ~flag_formatter() = default;
|
||||
virtual void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) = 0;
|
||||
|
||||
protected:
|
||||
padding_info padinfo_;
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
class SPDLOG_API custom_flag_formatter : public details::flag_formatter
|
||||
{
|
||||
public:
|
||||
virtual std::unique_ptr<custom_flag_formatter> clone() const = 0;
|
||||
|
||||
void set_padding_info(const details::padding_info &padding)
|
||||
{
|
||||
flag_formatter::padinfo_ = padding;
|
||||
}
|
||||
};
|
||||
|
||||
class SPDLOG_API pattern_formatter final : public formatter
|
||||
{
|
||||
public:
|
||||
using custom_flags = std::unordered_map<char, std::unique_ptr<custom_flag_formatter>>;
|
||||
|
||||
explicit pattern_formatter(std::string pattern, pattern_time_type time_type = pattern_time_type::local,
|
||||
std::string eol = spdlog::details::os::default_eol, custom_flags custom_user_flags = custom_flags());
|
||||
|
||||
// use default pattern is not given
|
||||
explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol);
|
||||
|
||||
pattern_formatter(const pattern_formatter &other) = delete;
|
||||
pattern_formatter &operator=(const pattern_formatter &other) = delete;
|
||||
|
||||
std::unique_ptr<formatter> clone() const override;
|
||||
void format(const details::log_msg &msg, memory_buf_t &dest) override;
|
||||
|
||||
template<typename T, typename... Args>
|
||||
pattern_formatter &add_flag(char flag, Args &&... args)
|
||||
{
|
||||
custom_handlers_[flag] = details::make_unique<T>(std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
void set_pattern(std::string pattern);
|
||||
void need_localtime(bool need = true);
|
||||
|
||||
private:
|
||||
std::string pattern_;
|
||||
std::string eol_;
|
||||
pattern_time_type pattern_time_type_;
|
||||
bool need_localtime_;
|
||||
std::tm cached_tm_;
|
||||
std::chrono::seconds last_log_secs_;
|
||||
std::vector<std::unique_ptr<details::flag_formatter>> formatters_;
|
||||
custom_flags custom_handlers_;
|
||||
|
||||
std::tm get_time_(const details::log_msg &msg);
|
||||
template<typename Padder>
|
||||
void handle_flag_(char flag, details::padding_info padding);
|
||||
|
||||
// Extract given pad spec (e.g. %8X)
|
||||
// Advance the given it pass the end of the padding spec found (if any)
|
||||
// Return padding.
|
||||
static details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end);
|
||||
|
||||
void compile_pattern_(const std::string &pattern);
|
||||
};
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
# include "pattern_formatter-inl.h"
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user