Header optimizations (#1684)

Shouldn't break anything. I hope.
This commit is contained in:
Ivan 2016-04-27 01:27:24 +03:00
parent da7472fe81
commit aafcf44581
89 changed files with 2370 additions and 2348 deletions

253
Utilities/BitSet.h Normal file
View file

@ -0,0 +1,253 @@
#pragma once
#include "types.h"
// Small bitset for enum class types with available values [0, BitSize).
// T must be either enum type or convertible to (registered with via simple_t<T>).
// Internal representation is single value of type T.
template<typename T, std::size_t BitSize = sizeof(T) * CHAR_BIT>
struct bitset_t
{
using type = simple_t<T>;
using under = std::underlying_type_t<type>;
static constexpr auto bitsize = BitSize;
bitset_t() = default;
constexpr bitset_t(type _enum_const)
: m_value(static_cast<type>(shift(_enum_const)))
{
}
constexpr bitset_t(under raw_value, const std::nothrow_t&)
: m_value(static_cast<T>(raw_value))
{
}
// Get underlying value
constexpr under _value() const
{
return static_cast<under>(m_value);
}
explicit constexpr operator bool() const
{
return _value() ? true : false;
}
bitset_t& operator +=(bitset_t rhs)
{
return *this = { _value() | rhs._value(), std::nothrow };
}
bitset_t& operator -=(bitset_t rhs)
{
return *this = { _value() & ~rhs._value(), std::nothrow };
}
bitset_t& operator &=(bitset_t rhs)
{
return *this = { _value() & rhs._value(), std::nothrow };
}
bitset_t& operator ^=(bitset_t rhs)
{
return *this = { _value() ^ rhs._value(), std::nothrow };
}
friend constexpr bitset_t operator +(bitset_t lhs, bitset_t rhs)
{
return{ lhs._value() | rhs._value(), std::nothrow };
}
friend constexpr bitset_t operator -(bitset_t lhs, bitset_t rhs)
{
return{ lhs._value() & ~rhs._value(), std::nothrow };
}
friend constexpr bitset_t operator &(bitset_t lhs, bitset_t rhs)
{
return{ lhs._value() & rhs._value(), std::nothrow };
}
friend constexpr bitset_t operator ^(bitset_t lhs, bitset_t rhs)
{
return{ lhs._value() ^ rhs._value(), std::nothrow };
}
bool test(bitset_t rhs) const
{
const under v = _value();
const under s = rhs._value();
return (v & s) != 0;
}
bool test_and_set(bitset_t rhs)
{
const under v = _value();
const under s = rhs._value();
*this = { v | s, std::nothrow };
return (v & s) != 0;
}
bool test_and_reset(bitset_t rhs)
{
const under v = _value();
const under s = rhs._value();
*this = { v & ~s, std::nothrow };
return (v & s) != 0;
}
bool test_and_complement(bitset_t rhs)
{
const under v = _value();
const under s = rhs._value();
*this = { v ^ s, std::nothrow };
return (v & s) != 0;
}
private:
static constexpr under shift(const T& value)
{
return static_cast<under>(value) < BitSize ? static_cast<under>(1) << static_cast<under>(value) : throw value;
}
T m_value;
};
template<typename T, typename RT = T>
constexpr RT make_bitset()
{
return RT{};
}
// Fold enum constants into bitset_t<> (must be implemented with constexpr initializer_list constructor instead)
template<typename T = void, typename Arg, typename... Args, typename RT = std::conditional_t<std::is_void<T>::value, bitset_t<Arg>, T>>
constexpr RT make_bitset(Arg&& _enum_const, Args&&... args)
{
return RT{ std::forward<Arg>(_enum_const) } + make_bitset<RT>(std::forward<Args>(args)...);
}
template<typename T, typename CT>
struct atomic_add<bitset_t<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
{
using under = typename bitset_t<T>::under;
static inline bitset_t<T> op1(bitset_t<T>& left, bitset_t<T> right)
{
return{ atomic_storage<under>::fetch_or(reinterpret_cast<under&>(left), right._value()), std::nothrow };
}
static constexpr auto fetch_op = &op1;
static inline bitset_t<T> op2(bitset_t<T>& left, bitset_t<T> right)
{
return{ atomic_storage<under>::or_fetch(reinterpret_cast<under&>(left), right._value()), std::nothrow };
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename T, typename CT>
struct atomic_sub<bitset_t<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
{
using under = typename bitset_t<T>::under;
static inline bitset_t<T> op1(bitset_t<T>& left, bitset_t<T> right)
{
return{ atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), ~right._value()), std::nothrow };
}
static constexpr auto fetch_op = &op1;
static inline bitset_t<T> op2(bitset_t<T>& left, bitset_t<T> right)
{
return{ atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), ~right._value()), std::nothrow };
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename T, typename CT>
struct atomic_and<bitset_t<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
{
using under = typename bitset_t<T>::under;
static inline bitset_t<T> op1(bitset_t<T>& left, bitset_t<T> right)
{
return{ atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), right._value()), std::nothrow };
}
static constexpr auto fetch_op = &op1;
static inline bitset_t<T> op2(bitset_t<T>& left, bitset_t<T> right)
{
return{ atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), right._value()), std::nothrow };
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename T, typename CT>
struct atomic_xor<bitset_t<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
{
using under = typename bitset_t<T>::under;
static inline bitset_t<T> op1(bitset_t<T>& left, bitset_t<T> right)
{
return{ atomic_storage<under>::fetch_xor(reinterpret_cast<under&>(left), right._value()), std::nothrow };
}
static constexpr auto fetch_op = &op1;
static inline bitset_t<T> op2(bitset_t<T>& left, bitset_t<T> right)
{
return{ atomic_storage<under>::xor_fetch(reinterpret_cast<under&>(left), right._value()), std::nothrow };
}
static constexpr auto op_fetch = &op2;
static constexpr auto atomic_op = &op2;
};
template<typename T>
struct atomic_test_and_set<bitset_t<T>, T, std::enable_if_t<std::is_enum<T>::value>>
{
using under = typename bitset_t<T>::under;
static inline bool _op(bitset_t<T>& left, const T& value)
{
return atomic_storage<under>::bts(reinterpret_cast<under&>(left), static_cast<uint>(value));
}
static constexpr auto atomic_op = &_op;
};
template<typename T>
struct atomic_test_and_reset<bitset_t<T>, T, std::enable_if_t<std::is_enum<T>::value>>
{
using under = typename bitset_t<T>::under;
static inline bool _op(bitset_t<T>& left, const T& value)
{
return atomic_storage<under>::btr(reinterpret_cast<under&>(left), static_cast<uint>(value));
}
static constexpr auto atomic_op = &_op;
};
template<typename T>
struct atomic_test_and_complement<bitset_t<T>, T, std::enable_if_t<std::is_enum<T>::value>>
{
using under = typename bitset_t<T>::under;
static inline bool _op(bitset_t<T>& left, const T& value)
{
return atomic_storage<under>::btc(reinterpret_cast<under&>(left), static_cast<uint>(value));
}
static constexpr auto atomic_op = &_op;
};

View file

@ -296,11 +296,11 @@ namespace cfg
std::string to_string() const override
{
for (const auto& pair : bijective<T, const char*>::map)
for (std::size_t i = 0; i < sizeof(bijective<T, const char*>::map) / sizeof(bijective_pair<T, const char*>); i++)
{
if (pair.first == m_value)
if (bijective<T, const char*>::map[i].v1 == m_value)
{
return pair.second;
return bijective<T, const char*>::map[i].v2;
}
}
@ -309,11 +309,11 @@ namespace cfg
bool from_string(const std::string& value) override
{
for (const auto& pair : bijective<T, const char*>::map)
for (std::size_t i = 0; i < sizeof(bijective<T, const char*>::map) / sizeof(bijective_pair<T, const char*>); i++)
{
if (pair.second == value)
if (bijective<T, const char*>::map[i].v2 == value)
{
m_value = pair.first;
m_value = bijective<T, const char*>::map[i].v1;
return true;
}
}
@ -325,9 +325,9 @@ namespace cfg
{
std::vector<std::string> result;
for (const auto& pair : bijective<T, const char*>::map)
for (std::size_t i = 0; i < sizeof(bijective<T, const char*>::map) / sizeof(bijective_pair<T, const char*>); i++)
{
result.emplace_back(pair.second);
result.emplace_back(bijective<T, const char*>::map[i].v2);
}
return result;

View file

@ -5,6 +5,7 @@
#include <unordered_map>
#include <algorithm>
#include <cerrno>
#ifdef _WIN32
@ -90,6 +91,8 @@ static time_t to_time(const FILETIME& ft)
namespace fs
{
thread_local uint error = 0;
class device_manager final
{
mutable shared_mutex m_mutex;
@ -213,8 +216,8 @@ bool fs::stat(const std::string& path, stat_t& info)
// TODO: convert Win32 error code to errno
switch (DWORD error = GetLastError())
{
case ERROR_FILE_NOT_FOUND: errno = ENOENT; break;
case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
case ERROR_FILE_NOT_FOUND: fs::error = ENOENT; break;
case ERROR_PATH_NOT_FOUND: fs::error = ENOENT; break;
default: throw fmt::exception("Unknown Win32 error: %u (%s)." HERE, error, path);
}
@ -231,6 +234,7 @@ bool fs::stat(const std::string& path, stat_t& info)
struct ::stat file_info;
if (::stat(path.c_str(), &file_info) != 0)
{
fs::error = errno;
return false;
}
@ -259,8 +263,8 @@ bool fs::exists(const std::string& path)
// TODO: convert Win32 error code to errno
switch (DWORD error = GetLastError())
{
case ERROR_FILE_NOT_FOUND: errno = ENOENT; break;
case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
case ERROR_FILE_NOT_FOUND: fs::error = ENOENT; break;
case ERROR_PATH_NOT_FOUND: fs::error = ENOENT; break;
default: throw fmt::exception("Unknown Win32 error: %u (%s)." HERE, error, path);
}
@ -270,7 +274,13 @@ bool fs::exists(const std::string& path)
return true;
#else
struct ::stat file_info;
return !::stat(path.c_str(), &file_info);
if (::stat(path.c_str(), &file_info) != 0)
{
fs::error = errno;
return false;
}
return true;
#endif
}
@ -286,7 +296,7 @@ bool fs::is_file(const std::string& path)
if (info.is_directory)
{
errno = EEXIST;
fs::error = EEXIST;
return false;
}
@ -300,8 +310,8 @@ bool fs::is_file(const std::string& path)
// TODO: convert Win32 error code to errno
switch (DWORD error = GetLastError())
{
case ERROR_FILE_NOT_FOUND: errno = ENOENT; break;
case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
case ERROR_FILE_NOT_FOUND: fs::error = ENOENT; break;
case ERROR_PATH_NOT_FOUND: fs::error = ENOENT; break;
default: throw fmt::exception("Unknown Win32 error: %u (%s)." HERE, error, path);
}
@ -311,6 +321,7 @@ bool fs::is_file(const std::string& path)
struct ::stat file_info;
if (::stat(path.c_str(), &file_info) != 0)
{
fs::error = errno;
return false;
}
#endif
@ -322,7 +333,7 @@ bool fs::is_file(const std::string& path)
if (S_ISDIR(file_info.st_mode))
#endif
{
errno = EEXIST;
fs::error = EEXIST;
return false;
}
@ -341,7 +352,7 @@ bool fs::is_dir(const std::string& path)
if (info.is_directory == false)
{
errno = EEXIST;
fs::error = EEXIST;
return false;
}
@ -355,8 +366,8 @@ bool fs::is_dir(const std::string& path)
// TODO: convert Win32 error code to errno
switch (DWORD error = GetLastError())
{
case ERROR_FILE_NOT_FOUND: errno = ENOENT; break;
case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
case ERROR_FILE_NOT_FOUND: fs::error = ENOENT; break;
case ERROR_PATH_NOT_FOUND: fs::error = ENOENT; break;
default: throw fmt::exception("Unknown Win32 error: %u (%s)." HERE, error, path);
}
@ -366,6 +377,7 @@ bool fs::is_dir(const std::string& path)
struct ::stat file_info;
if (::stat(path.c_str(), &file_info) != 0)
{
fs::error = errno;
return false;
}
#endif
@ -376,7 +388,7 @@ bool fs::is_dir(const std::string& path)
if (!S_ISDIR(file_info.st_mode))
#endif
{
errno = EEXIST;
fs::error = EEXIST;
return false;
}
@ -396,8 +408,8 @@ bool fs::create_dir(const std::string& path)
// TODO: convert Win32 error code to errno
switch (DWORD error = GetLastError())
{
case ERROR_ALREADY_EXISTS: errno = EEXIST; break;
case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
case ERROR_ALREADY_EXISTS: fs::error = EEXIST; break;
case ERROR_PATH_NOT_FOUND: fs::error = ENOENT; break;
default: throw fmt::exception("Unknown Win32 error: %u (%s)." HERE, error, path);
}
@ -406,7 +418,13 @@ bool fs::create_dir(const std::string& path)
return true;
#else
return !::mkdir(path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
if (::mkdir(path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0)
{
fs::error = errno;
return false;
}
return true;
#endif
}
@ -435,7 +453,7 @@ bool fs::remove_dir(const std::string& path)
// TODO: convert Win32 error code to errno
switch (DWORD error = GetLastError())
{
case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
case ERROR_PATH_NOT_FOUND: fs::error = ENOENT; break;
default: throw fmt::exception("Unknown Win32 error: %u (%s)." HERE, error, path);
}
@ -444,7 +462,13 @@ bool fs::remove_dir(const std::string& path)
return true;
#else
return !::rmdir(path.c_str());
if (::rmdir(path.c_str()) != 0)
{
fs::error = errno;
return false;
}
return true;
#endif
}
@ -468,7 +492,7 @@ bool fs::rename(const std::string& from, const std::string& to)
// TODO: convert Win32 error code to errno
switch (DWORD error = GetLastError())
{
case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
case ERROR_PATH_NOT_FOUND: fs::error = ENOENT; break;
default: throw fmt::exception("Unknown Win32 error: %u.\nFrom: %s\nTo: %s" HERE, error, from, to);
}
@ -477,7 +501,13 @@ bool fs::rename(const std::string& from, const std::string& to)
return true;
#else
return !::rename(from.c_str(), to.c_str());
if (::rename(from.c_str(), to.c_str()) != 0)
{
fs::error = errno;
return false;
}
return true;
#endif
}
@ -496,7 +526,7 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
// TODO: convert Win32 error code to errno
switch (DWORD error = GetLastError())
{
case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
case ERROR_PATH_NOT_FOUND: fs::error = ENOENT; break;
default: throw fmt::exception("Unknown Win32 error: %u.\nFrom: %s\nTo: %s" HERE, error, from, to);
}
@ -510,6 +540,7 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
const int input = ::open(from.c_str(), O_RDONLY);
if (input == -1)
{
fs::error = errno;
return false;
}
@ -519,7 +550,7 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
const int err = errno;
::close(input);
errno = err;
fs::error = err;
return false;
}
@ -538,7 +569,7 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
::close(input);
::close(output);
errno = err;
fs::error = err;
return false;
}
@ -561,8 +592,8 @@ bool fs::remove_file(const std::string& path)
// TODO: convert Win32 error code to errno
switch (DWORD error = GetLastError())
{
case ERROR_FILE_NOT_FOUND: errno = ENOENT; break;
case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
case ERROR_FILE_NOT_FOUND: fs::error = ENOENT; break;
case ERROR_PATH_NOT_FOUND: fs::error = ENOENT; break;
default: throw fmt::exception("Unknown Win32 error: %u (%s)." HERE, error, path);
}
@ -571,7 +602,13 @@ bool fs::remove_file(const std::string& path)
return true;
#else
return !::unlink(path.c_str());
if (::unlink(path.c_str()) != 0)
{
fs::error = errno;
return false;
}
return true;
#endif
}
@ -590,8 +627,8 @@ bool fs::truncate_file(const std::string& path, u64 length)
// TODO: convert Win32 error code to errno
switch (DWORD error = GetLastError())
{
case ERROR_FILE_NOT_FOUND: errno = ENOENT; break;
case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
case ERROR_FILE_NOT_FOUND: fs::error = ENOENT; break;
case ERROR_PATH_NOT_FOUND: fs::error = ENOENT; break;
default: throw fmt::exception("Unknown Win32 error: %u (%s)." HERE, error, path);
}
@ -607,7 +644,7 @@ bool fs::truncate_file(const std::string& path, u64 length)
// TODO: convert Win32 error code to errno
switch (DWORD error = GetLastError())
{
case ERROR_NEGATIVE_SEEK: errno = EINVAL; break;
case ERROR_NEGATIVE_SEEK: fs::error = EINVAL; break;
default: throw fmt::exception("Unknown Win32 error: %u (length=0x%llx)." HERE, error, length);
}
@ -618,7 +655,13 @@ bool fs::truncate_file(const std::string& path, u64 length)
CloseHandle(handle);
return true;
#else
return !::truncate(path.c_str(), length);
if (::truncate(path.c_str(), length) != 0)
{
fs::error = errno;
return false;
}
return true;
#endif
}
@ -629,10 +672,10 @@ void fs::file::xnull() const
void fs::file::xfail() const
{
throw fmt::exception("Unexpected fs::file error %d", errno);
throw fmt::exception("Unexpected fs::file error %u", fs::error);
}
bool fs::file::open(const std::string& path, mset<open_mode> mode)
bool fs::file::open(const std::string& path, bitset_t<open_mode> mode)
{
if (auto device = get_virtual_device(path))
{
@ -661,7 +704,7 @@ bool fs::file::open(const std::string& path, mset<open_mode> mode)
{
if (mode & fs::excl)
{
errno = EINVAL;
fs::error = EINVAL;
return false;
}
@ -675,9 +718,9 @@ bool fs::file::open(const std::string& path, mset<open_mode> mode)
// TODO: convert Win32 error code to errno
switch (DWORD error = GetLastError())
{
case ERROR_FILE_NOT_FOUND: errno = ENOENT; break;
case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
case ERROR_FILE_EXISTS: errno = EEXIST; break;
case ERROR_FILE_NOT_FOUND: fs::error = ENOENT; break;
case ERROR_PATH_NOT_FOUND: fs::error = ENOENT; break;
case ERROR_FILE_EXISTS: fs::error = EEXIST; break;
default: throw fmt::exception("Unknown Win32 error: %u (%s)." HERE, error, path);
}
@ -746,7 +789,7 @@ bool fs::file::open(const std::string& path, mset<open_mode> mode)
// TODO: convert Win32 error code to errno
switch (DWORD error = GetLastError())
{
case ERROR_NEGATIVE_SEEK: errno = EINVAL; break;
case ERROR_NEGATIVE_SEEK: fs::error = EINVAL; break;
default: throw fmt::exception("Unknown Win32 error: %u." HERE, error);
}
@ -871,6 +914,7 @@ bool fs::file::open(const std::string& path, mset<open_mode> mode)
if (fd == -1)
{
// TODO: errno
fs::error = errno;
return false;
}
@ -1086,8 +1130,8 @@ bool fs::dir::open(const std::string& path)
// TODO: convert Win32 error code to errno
switch (DWORD error = GetLastError())
{
case ERROR_FILE_NOT_FOUND: errno = ENOENT; break;
case ERROR_PATH_NOT_FOUND: errno = ENOENT; break;
case ERROR_FILE_NOT_FOUND: fs::error = ENOENT; break;
case ERROR_PATH_NOT_FOUND: fs::error = ENOENT; break;
default: throw fmt::exception("Unknown Win32 error: %u." HERE, error);
}
@ -1162,6 +1206,7 @@ bool fs::dir::open(const std::string& path)
if (!ptr)
{
// TODO: errno
fs::error = errno;
return false;
}

View file

@ -6,9 +6,13 @@
#include <type_traits>
#include "types.h"
#include "BitSet.h"
namespace fs
{
// Error code returned
extern thread_local uint error;
// File open mode flags
enum struct open_mode : u32
{
@ -20,14 +24,14 @@ namespace fs
excl,
};
constexpr mset<open_mode> read = open_mode::read; // Enable reading
constexpr mset<open_mode> write = open_mode::write; // Enable writing
constexpr mset<open_mode> append = open_mode::append; // Always append to the end of the file
constexpr mset<open_mode> create = open_mode::create; // Create file if it doesn't exist
constexpr mset<open_mode> trunc = open_mode::trunc; // Clear opened file if it's not empty
constexpr mset<open_mode> excl = open_mode::excl; // Failure if the file already exists (used with `create`)
constexpr bitset_t<open_mode> read = open_mode::read; // Enable reading
constexpr bitset_t<open_mode> write = open_mode::write; // Enable writing
constexpr bitset_t<open_mode> append = open_mode::append; // Always append to the end of the file
constexpr bitset_t<open_mode> create = open_mode::create; // Create file if it doesn't exist
constexpr bitset_t<open_mode> trunc = open_mode::trunc; // Clear opened file if it's not empty
constexpr bitset_t<open_mode> excl = open_mode::excl; // Failure if the file already exists (used with `create`)
constexpr mset<open_mode> rewrite = write + create + trunc;
constexpr bitset_t<open_mode> rewrite = write + create + trunc;
// File seek mode
enum class seek_mode : u32
@ -92,7 +96,7 @@ namespace fs
virtual bool remove(const std::string& path) = 0;
virtual bool trunc(const std::string& path, u64 length) = 0;
virtual std::unique_ptr<file_base> open(const std::string& path, mset<open_mode> mode) = 0;
virtual std::unique_ptr<file_base> open(const std::string& path, bitset_t<open_mode> mode) = 0;
virtual std::unique_ptr<dir_base> open_dir(const std::string& path) = 0;
};
@ -150,13 +154,13 @@ namespace fs
file() = default;
// Open file with specified mode
explicit file(const std::string& path, mset<open_mode> mode = ::fs::read)
explicit file(const std::string& path, bitset_t<open_mode> mode = ::fs::read)
{
open(path, mode);
}
// Open file with specified mode
bool open(const std::string& path, mset<open_mode> mode = ::fs::read);
bool open(const std::string& path, bitset_t<open_mode> mode = ::fs::read);
// Open memory for read
explicit file(const void* ptr, std::size_t size);

View file

@ -1,8 +1,53 @@
#include "Log.h"
#include "File.h"
#include "StrFmt.h"
#include <cstdarg>
#include <string>
// Thread-specific log prefix provider
thread_local std::string(*g_tls_log_prefix)() = nullptr;
namespace _log
{
struct listener
{
listener() = default;
virtual ~listener() = default;
virtual void log(const channel& ch, level sev, const std::string& text) = 0;
};
class file_writer
{
// Could be memory-mapped file
fs::file m_file;
public:
file_writer(const std::string& name);
virtual ~file_writer() = default;
// Append raw data
void log(const std::string& text);
// Get current file size (may be used by secondary readers)
std::size_t size() const;
};
struct file_listener : public file_writer, public listener
{
file_listener(const std::string& name)
: file_writer(name)
, listener()
{
}
// Encode level, current thread name, channel name and write log message
virtual void log(const channel& ch, level sev, const std::string& text) override;
};
static file_listener& get_logger()
{
// Use magic static
@ -10,8 +55,6 @@ namespace _log
return logger;
}
file_writer g_tty_file("TTY.log");
channel GENERAL(nullptr, level::notice);
channel LOADER("LDR", level::notice);
channel MEMORY("MEM", level::notice);
@ -20,15 +63,13 @@ namespace _log
channel PPU("PPU", level::notice);
channel SPU("SPU", level::notice);
channel ARMv7("ARMv7");
thread_local std::string(*g_tls_make_prefix)(const channel&, level, const std::string&) = nullptr;
}
void _log::channel::broadcast(const _log::channel& ch, _log::level sev, const char* fmt...)
{
va_list args;
va_start(args, fmt);
get_logger().log(ch, sev, fmt::_vformat(fmt, args));
get_logger().log(ch, sev, fmt::unsafe_vformat(fmt, args));
va_end(args);
}
@ -40,7 +81,7 @@ _log::file_writer::file_writer(const std::string& name)
{
if (!m_file.open(fs::get_config_dir() + name, fs::rewrite + fs::append))
{
throw fmt::exception("Can't create log file %s (error %d)", name, errno);
throw fmt::exception("Can't create log file %s (error %d)", name, fs::error);
}
}
catch (...)
@ -78,10 +119,10 @@ void _log::file_listener::log(const _log::channel& ch, _log::level sev, const st
// TODO: print time?
if (auto func = g_tls_make_prefix)
if (auto prefix = g_tls_log_prefix)
{
msg += '{';
msg += func(ch, sev, text);
msg += prefix();
msg += "} ";
}

View file

@ -2,8 +2,6 @@
#include "types.h"
#include "Atomic.h"
#include "File.h"
#include "StrFmt.h"
namespace _log
{
@ -19,10 +17,6 @@ namespace _log
trace, // lowest level (usually disabled)
};
struct channel;
struct listener;
// Log channel
struct channel
{
// Channel prefix (added to every log message)
@ -33,23 +27,20 @@ namespace _log
// Constant initialization: name and initial log level
constexpr channel(const char* name, level enabled = level::trace)
: name{ name }
, enabled{ enabled }
: name(name)
, enabled(enabled)
{
}
// Log without formatting
void log(level sev, const std::string& text) const
{
if (sev <= enabled)
broadcast(*this, sev, "%s", text.c_str());
}
// Log with formatting
// Formatting function
template<typename... Args>
void format(level sev, const char* fmt, const Args&... args) const
{
#ifdef _MSC_VER
if (sev <= enabled)
#else
if (__builtin_expect(sev <= enabled, 0))
#endif
broadcast(*this, sev, fmt, ::unveil<Args>::get(args)...);
}
@ -75,49 +66,7 @@ namespace _log
static void broadcast(const channel& ch, level sev, const char* fmt...);
};
// Log listener (destination)
struct listener
{
listener() = default;
virtual ~listener() = default;
virtual void log(const channel& ch, level sev, const std::string& text) = 0;
};
class file_writer
{
// Could be memory-mapped file
fs::file m_file;
public:
file_writer(const std::string& name);
virtual ~file_writer() = default;
// Append raw data
void log(const std::string& text);
// Get current file size (may be used by secondary readers)
std::size_t size() const;
};
struct file_listener : public file_writer, public listener
{
file_listener(const std::string& name)
: file_writer(name)
, listener()
{
}
// Encode level, current thread name, channel name and write log message
virtual void log(const channel& ch, level sev, const std::string& text) override;
};
// Global variable for TTY.log
extern file_writer g_tty_file;
// Small set of predefined channels:
/* Small set of predefined channels */
extern channel GENERAL;
extern channel LOADER;
@ -127,14 +76,12 @@ namespace _log
extern channel PPU;
extern channel SPU;
extern channel ARMv7;
extern thread_local std::string(*g_tls_make_prefix)(const channel&, level, const std::string&);
}
template<>
struct bijective<_log::level, const char*>
{
static constexpr std::pair<_log::level, const char*> map[]
static constexpr bijective_pair<_log::level, const char*> map[]
{
{ _log::level::always, "Nothing" },
{ _log::level::fatal, "Fatal" },

View file

@ -75,5 +75,6 @@ constexpr std::uint32_t size32(const T(&)[Size])
#define Expects ASSERT
#define Ensures ASSERT
#define DECLARE(static_member) decltype(static_member) static_member
#define DECLARE(...) decltype(__VA_ARGS__) __VA_ARGS__
#define STR_CASE(value) case value: return #value

View file

@ -88,114 +88,6 @@ inline std::uint64_t cntlz64(std::uint64_t arg)
#endif
}
template<typename T>
struct add_flags_result_t
{
T result;
bool carry;
//bool overflow;
bool zero;
bool sign;
add_flags_result_t() = default;
// Straighforward ADD with flags
add_flags_result_t(T a, T b)
: result(a + b)
, carry(result < a)
//, overflow((result ^ ~(a ^ b)) >> (sizeof(T) * 8 - 1) != 0)
, zero(result == 0)
, sign(result >> (sizeof(T) * 8 - 1) != 0)
{
}
// Straighforward ADC with flags
add_flags_result_t(T a, T b, bool c)
: add_flags_result_t(a, b)
{
add_flags_result_t r(result, c);
result = r.result;
carry |= r.carry;
//overflow |= r.overflow;
zero = r.zero;
sign = r.sign;
}
};
inline add_flags_result_t<std::uint32_t> add32_flags(std::uint32_t a, std::uint32_t b)
{
//add_flags_result_t<std::uint32_t> r;
//r.carry = _addcarry_u32(0, a, b, &r.result) != 0;
//r.zero = r.result == 0;
//r.sign = r.result >> 31;
//return r;
return{ a, b };
}
inline add_flags_result_t<std::uint32_t> add32_flags(std::uint32_t a, std::uint32_t b, bool c)
{
return{ a, b, c };
}
inline add_flags_result_t<std::uint64_t> add64_flags(std::uint64_t a, std::uint64_t b)
{
return{ a, b };
}
inline add_flags_result_t<std::uint64_t> add64_flags(std::uint64_t a, std::uint64_t b, bool c)
{
return{ a, b, c };
}
// Compare 16 packed unsigned bytes (greater than)
inline __m128i sse_cmpgt_epu8(__m128i A, __m128i B)
{
// (A xor 0x80) > (B xor 0x80)
const auto sign = _mm_set1_epi32(0x80808080);
return _mm_cmpgt_epi8(_mm_xor_si128(A, sign), _mm_xor_si128(B, sign));
}
inline __m128i sse_cmpgt_epu16(__m128i A, __m128i B)
{
const auto sign = _mm_set1_epi32(0x80008000);
return _mm_cmpgt_epi16(_mm_xor_si128(A, sign), _mm_xor_si128(B, sign));
}
inline __m128i sse_cmpgt_epu32(__m128i A, __m128i B)
{
const auto sign = _mm_set1_epi32(0x80000000);
return _mm_cmpgt_epi32(_mm_xor_si128(A, sign), _mm_xor_si128(B, sign));
}
inline __m128 sse_exp2_ps(__m128 A)
{
const auto x0 = _mm_max_ps(_mm_min_ps(A, _mm_set1_ps(127.4999961f)), _mm_set1_ps(-127.4999961f));
const auto x1 = _mm_add_ps(x0, _mm_set1_ps(0.5f));
const auto x2 = _mm_sub_epi32(_mm_cvtps_epi32(x1), _mm_and_si128(_mm_castps_si128(_mm_cmpnlt_ps(_mm_setzero_ps(), x1)), _mm_set1_epi32(1)));
const auto x3 = _mm_sub_ps(x0, _mm_cvtepi32_ps(x2));
const auto x4 = _mm_mul_ps(x3, x3);
const auto x5 = _mm_mul_ps(x3, _mm_add_ps(_mm_mul_ps(_mm_add_ps(_mm_mul_ps(x4, _mm_set1_ps(0.023093347705f)), _mm_set1_ps(20.20206567f)), x4), _mm_set1_ps(1513.906801f)));
const auto x6 = _mm_mul_ps(x5, _mm_rcp_ps(_mm_sub_ps(_mm_add_ps(_mm_mul_ps(_mm_set1_ps(233.1842117f), x4), _mm_set1_ps(4368.211667f)), x5)));
return _mm_mul_ps(_mm_add_ps(_mm_add_ps(x6, x6), _mm_set1_ps(1.0f)), _mm_castsi128_ps(_mm_slli_epi32(_mm_add_epi32(x2, _mm_set1_epi32(127)), 23)));
}
inline __m128 sse_log2_ps(__m128 A)
{
const auto _1 = _mm_set1_ps(1.0f);
const auto _c = _mm_set1_ps(1.442695040f);
const auto x0 = _mm_max_ps(A, _mm_castsi128_ps(_mm_set1_epi32(0x00800000)));
const auto x1 = _mm_or_ps(_mm_and_ps(x0, _mm_castsi128_ps(_mm_set1_epi32(0x807fffff))), _1);
const auto x2 = _mm_rcp_ps(_mm_add_ps(x1, _1));
const auto x3 = _mm_mul_ps(_mm_sub_ps(x1, _1), x2);
const auto x4 = _mm_add_ps(x3, x3);
const auto x5 = _mm_mul_ps(x4, x4);
const auto x6 = _mm_add_ps(_mm_mul_ps(_mm_add_ps(_mm_mul_ps(_mm_set1_ps(-0.7895802789f), x5), _mm_set1_ps(16.38666457f)), x5), _mm_set1_ps(-64.1409953f));
const auto x7 = _mm_rcp_ps(_mm_add_ps(_mm_mul_ps(_mm_add_ps(_mm_mul_ps(_mm_set1_ps(-35.67227983f), x5), _mm_set1_ps(312.0937664f)), x5), _mm_set1_ps(-769.6919436f)));
const auto x8 = _mm_cvtepi32_ps(_mm_sub_epi32(_mm_srli_epi32(_mm_castps_si128(x0), 23), _mm_set1_epi32(127)));
return _mm_add_ps(_mm_mul_ps(_mm_mul_ps(_mm_mul_ps(_mm_mul_ps(x5, x6), x7), x4), _c), _mm_add_ps(_mm_mul_ps(x4, _c), x8));
}
// Helper function, used by ""_u16, ""_u32, ""_u64
constexpr std::uint8_t to_u8(char c)
{

View file

@ -1,120 +1,90 @@
#include "stdafx.h"
#include "Utilities/Semaphore.h"
bool semaphore_t::try_wait()
#include <mutex>
#include <condition_variable>
struct benaphore::internal
{
// check m_value without interlocked op
if (m_var.load().value == 0)
{
return false;
}
std::mutex mutex;
// try to decrement m_value atomically
const auto old = m_var.fetch_op([](sync_var_t& var)
{
if (var.value)
{
var.value--;
}
});
std::size_t acq_order{};
std::size_t rel_order{};
// recheck atomic result
if (old.value == 0)
{
return false;
}
std::condition_variable cond;
};
return true;
}
bool semaphore_t::try_post()
void benaphore::wait_hard()
{
// check m_value without interlocked op
if (m_var.load().value >= max_value)
{
return false;
}
// try to increment m_value atomically
const auto old = m_var.fetch_op([&](sync_var_t& var)
{
if (var.value < max_value)
{
var.value++;
}
});
// recheck atomic result
if (old.value >= max_value)
{
return false;
}
if (old.waiters)
{
// notify waiting thread
std::lock_guard<std::mutex> lock(m_mutex);
m_cv.notify_one();
}
return true;
}
void semaphore_t::wait()
{
if (m_var.atomic_op([](sync_var_t& var) -> bool
{
if (var.value)
{
var.value--;
return true;
}
else
{
//var.waiters++;
return false;
}
}))
initialize_once();
std::unique_lock<std::mutex> lock(m_data->mutex);
// Notify non-zero waiter queue size
if (m_value.exchange(-1) == 1)
{
// Return immediately (acquired)
m_value = 0;
return;
}
std::unique_lock<std::mutex> lock(m_mutex);
// Remember the order
const std::size_t order = ++m_data->acq_order;
m_var.atomic_op([](sync_var_t& var)
// Wait for the appropriate rel_order (TODO)
while (m_data->rel_order < order)
{
var.waiters++;
});
m_data->cond.wait(lock);
}
while (!m_var.atomic_op([](sync_var_t& var) -> bool
if (order == m_data->acq_order && m_data->acq_order == m_data->rel_order)
{
if (var.value)
{
var.value--;
var.waiters--;
return true;
}
else
{
return false;
}
}))
{
m_cv.wait(lock);
// Cleaup
m_data->acq_order = 0;
m_data->rel_order = 0;
m_value.compare_and_swap(-1, 0);
}
}
bool semaphore_t::post_and_wait()
void benaphore::post_hard()
{
// TODO: merge these functions? Probably has a race condition.
if (try_wait()) return false;
initialize_once();
try_post();
wait();
std::unique_lock<std::mutex> lock(m_data->mutex);
return true;
if (m_value.compare_and_swap(0, 1) != -1)
{
// Do nothing (released)
return;
}
if (m_data->acq_order == m_data->rel_order)
{
m_value = 1;
return;
}
// Awake one thread
m_data->rel_order += 1;
// Unlock and notify
lock.unlock();
m_data->cond.notify_one();
}
void benaphore::initialize_once()
{
if (UNLIKELY(!m_data))
{
auto ptr = new benaphore::internal;
if (!m_data.compare_and_swap_test(nullptr, ptr))
{
delete ptr;
}
}
}
benaphore::~benaphore()
{
delete m_data;
}

View file

@ -2,39 +2,47 @@
#include "types.h"
#include "Atomic.h"
#include "Platform.h"
class semaphore_t
// Binary semaphore
class benaphore
{
// semaphore mutex
std::mutex m_mutex;
struct internal;
// semaphore condition variable
std::condition_variable m_cv;
// Reserved value (-1) enforces *_hard() calls
atomic_t<u32> m_value{};
struct alignas(8) sync_var_t
{
u32 value; // current semaphore value
u32 waiters; // current amount of waiters
};
atomic_t<internal*> m_data{};
// current semaphore value
atomic_t<sync_var_t> m_var;
void wait_hard();
void post_hard();
public:
// max semaphore value
const u32 max_value;
constexpr benaphore() = default;
semaphore_t(u32 max_value = 1, u32 value = 0)
: m_var(sync_var_t{ value, 0 })
, max_value(max_value)
~benaphore();
// Initialize internal data
void initialize_once();
void wait()
{
if (UNLIKELY(!m_value.compare_and_swap_test(1, 0)))
{
wait_hard();
}
}
bool try_wait();
bool try_wait()
{
return LIKELY(m_value.compare_and_swap_test(1, 0));
}
bool try_post();
void wait();
bool post_and_wait();
void post()
{
if (UNLIKELY(!m_value.compare_and_swap_test(0, 1)))
{
post_hard();
}
}
};

View file

@ -15,24 +15,6 @@ struct shared_mutex::internal
std::condition_variable ocv; // For current exclusive owner
};
shared_mutex::~shared_mutex()
{
delete m_data;
}
void shared_mutex::initialize_once()
{
if (!m_data)
{
auto ptr = new shared_mutex::internal;
if (!m_data.compare_and_swap_test(nullptr, ptr))
{
delete ptr;
}
}
}
void shared_mutex::lock_shared_hard()
{
initialize_once();
@ -81,17 +63,18 @@ void shared_mutex::unlock_shared_notify()
{
initialize_once();
// Mutex is locked for reliable notification because m_ctrl has been changed outside
std::lock_guard<std::mutex> lock(m_data->mutex);
std::unique_lock<std::mutex> lock(m_data->mutex);
if ((m_ctrl & SM_READER_MASK) == 0 && m_data->wq_size)
{
// Notify exclusive owner
lock.unlock();
m_data->ocv.notify_one();
}
else if (m_data->rq_size)
{
// Notify other readers
lock.unlock();
m_data->rcv.notify_one();
}
}
@ -141,17 +124,36 @@ void shared_mutex::unlock_notify()
{
initialize_once();
// Mutex is locked for reliable notification because m_ctrl has been changed outside
std::lock_guard<std::mutex> lock(m_data->mutex);
std::unique_lock<std::mutex> lock(m_data->mutex);
if (m_data->wq_size)
{
// Notify next exclusive owner
lock.unlock();
m_data->wcv.notify_one();
}
else if (m_data->rq_size)
{
// Notify all readers
lock.unlock();
m_data->rcv.notify_all();
}
}
void shared_mutex::initialize_once()
{
if (UNLIKELY(!m_data))
{
auto ptr = new shared_mutex::internal;
if (!m_data.compare_and_swap_test(nullptr, ptr))
{
delete ptr;
}
}
}
shared_mutex::~shared_mutex()
{
delete m_data;
}

View file

@ -35,11 +35,11 @@ class shared_mutex final
public:
constexpr shared_mutex() = default;
~shared_mutex();
// Initialize internal data
void initialize_once();
~shared_mutex();
bool try_lock_shared()
{
auto ctrl = m_ctrl.load();

View file

@ -1,5 +1,6 @@
#include "StrFmt.h"
#include "BEType.h"
#include "StrUtil.h"
#include <cassert>
#include <array>
@ -15,70 +16,7 @@ std::string v128::to_xyzw() const
return fmt::format("x: %g y: %g z: %g w: %g", _f[3], _f[2], _f[1], _f[0]);
}
std::string fmt::to_hex(u64 value, u64 count)
{
if (count - 1 >= 16)
{
throw exception("fmt::to_hex(): invalid count: 0x%llx", count);
}
count = std::max<u64>(count, 16 - cntlz64(value) / 4);
char res[16] = {};
for (size_t i = count - 1; ~i; i--, value /= 16)
{
res[i] = "0123456789abcdef"[value % 16];
}
return std::string(res, count);
}
std::string fmt::to_udec(u64 value)
{
char res[20] = {};
size_t first = sizeof(res);
if (!value)
{
res[--first] = '0';
}
for (; value; value /= 10)
{
res[--first] = '0' + (value % 10);
}
return std::string(&res[first], sizeof(res) - first);
}
std::string fmt::to_sdec(s64 svalue)
{
const bool sign = svalue < 0;
u64 value = sign ? -svalue : svalue;
char res[20] = {};
size_t first = sizeof(res);
if (!value)
{
res[--first] = '0';
}
for (; value; value /= 10)
{
res[--first] = '0' + (value % 10);
}
if (sign)
{
res[--first] = '-';
}
return std::string(&res[first], sizeof(res) - first);
}
std::string fmt::_vformat(const char* fmt, va_list _args) noexcept
std::string fmt::unsafe_vformat(const char* fmt, va_list _args) noexcept
{
// Fixed stack buffer for the first attempt
std::array<char, 4096> fixed_buf;
@ -115,18 +53,18 @@ std::string fmt::_vformat(const char* fmt, va_list _args) noexcept
}
}
std::string fmt::_format(const char* fmt...) noexcept
std::string fmt::unsafe_format(const char* fmt...) noexcept
{
va_list args;
va_start(args, fmt);
auto result = fmt::_vformat(fmt, args);
auto result = unsafe_vformat(fmt, args);
va_end(args);
return result;
}
fmt::exception_base::exception_base(const char* fmt...)
: std::runtime_error((va_start(m_args, fmt), _vformat(fmt, m_args)))
: std::runtime_error((va_start(m_args, fmt), unsafe_vformat(fmt, m_args)))
{
va_end(m_args);
}
@ -196,73 +134,11 @@ std::string fmt::trim(const std::string& source, const std::string& values)
return source.substr(begin, source.find_last_not_of(values) + 1);
}
std::string fmt::escape(const std::string& source, std::initializer_list<char> more)
{
const std::pair<std::string, std::string> escape_list[] =
{
{ "\\", "\\\\" },
{ "\a", "\\a" },
{ "\b", "\\b" },
{ "\f", "\\f" },
{ "\n", "\\n" },
{ "\r", "\\r" },
{ "\t", "\\t" },
{ "\v", "\\v" },
};
std::string result = fmt::replace_all(source, escape_list);
for (char c = 0; c < 32; c++)
{
result = fmt::replace_all(result, std::string(1, c), fmt::format("\\x%02X", c));
}
for (char c : more)
{
result = fmt::replace_all(result, std::string(1, c), fmt::format("\\x%02X", c));
}
return result;
}
std::string fmt::unescape(const std::string& source)
std::string fmt::to_upper(const std::string& string)
{
std::string result;
for (auto it = source.begin(); it != source.end();)
{
const char bs = *it++;
if (bs == '\\' && it != source.end())
{
switch (const char code = *it++)
{
case 'a': result += '\a'; break;
case 'b': result += '\b'; break;
case 'f': result += '\f'; break;
case 'n': result += '\n'; break;
case 'r': result += '\r'; break;
case 't': result += '\t'; break;
case 'v': result += '\v'; break;
case 'x':
{
// Detect hexadecimal character code (TODO)
if (source.end() - it >= 2)
{
result += std::stoi(std::string{ *it++, *it++ }, 0, 16);
}
}
// Octal/unicode not supported
default: result += code;
}
}
else
{
result += bs;
}
}
result.resize(string.size());
std::transform(string.begin(), string.end(), result.begin(), ::toupper);
return result;
}

View file

@ -2,114 +2,21 @@
#include <cstdarg>
#include <string>
#include <vector>
#include <functional>
#include <exception>
#include "Platform.h"
#include "types.h"
// Copy null-terminated string from std::string to char array with truncation
template<std::size_t N>
force_inline void strcpy_trunc(char(&dst)[N], const std::string& src)
{
const std::size_t count = src.size() >= N ? N - 1 : src.size();
std::memcpy(dst, src.c_str(), count);
dst[count] = '\0';
}
// Copy null-terminated string from char array to another char array with truncation
template<std::size_t N, std::size_t N2>
force_inline void strcpy_trunc(char(&dst)[N], const char(&src)[N2])
{
const std::size_t count = N2 >= N ? N - 1 : N2;
std::memcpy(dst, src, count);
dst[count] = '\0';
}
// Formatting helper, type-specific preprocessing for improving safety and functionality
template<typename T, typename>
struct unveil
{
// TODO
static inline const T& get(const T& arg)
{
return arg;
}
};
template<>
struct unveil<std::string, void>
{
static inline const char* get(const std::string& arg)
{
return arg.c_str();
}
};
namespace fmt
{
std::string replace_first(const std::string& src, const std::string& from, const std::string& to);
std::string replace_all(const std::string &src, const std::string& from, const std::string& to);
std::string unsafe_format(const char* fmt...) noexcept;
std::string unsafe_vformat(const char*, va_list) noexcept;
template<size_t list_size>
std::string replace_all(std::string src, const std::pair<std::string, std::string>(&list)[list_size])
{
for (size_t pos = 0; pos < src.length(); ++pos)
{
for (size_t i = 0; i < list_size; ++i)
{
const size_t comp_length = list[i].first.length();
if (src.length() - pos < comp_length)
continue;
if (src.substr(pos, comp_length) == list[i].first)
{
src = (pos ? src.substr(0, pos) + list[i].second : list[i].second) + src.substr(pos + comp_length);
pos += list[i].second.length() - 1;
break;
}
}
}
return src;
}
template<size_t list_size>
std::string replace_all(std::string src, const std::pair<std::string, std::function<std::string()>>(&list)[list_size])
{
for (size_t pos = 0; pos < src.length(); ++pos)
{
for (size_t i = 0; i < list_size; ++i)
{
const size_t comp_length = list[i].first.length();
if (src.length() - pos < comp_length)
continue;
if (src.substr(pos, comp_length) == list[i].first)
{
src = (pos ? src.substr(0, pos) + list[i].second() : list[i].second()) + src.substr(pos + comp_length);
pos += list[i].second().length() - 1;
break;
}
}
}
return src;
}
std::string to_hex(u64 value, u64 count = 1);
std::string to_udec(u64 value);
std::string to_sdec(s64 value);
std::string _format(const char* fmt...) noexcept;
std::string _vformat(const char*, va_list) noexcept;
// Formatting function with special functionality (fmt::unveil)
// Formatting function
template<typename... Args>
force_inline std::string format(const char* fmt, const Args&... args) noexcept
inline std::string format(const char* fmt, const Args&... args) noexcept
{
return _format(fmt, ::unveil<Args>::get(args)...);
return unsafe_format(fmt, ::unveil<Args>::get(args)...);
}
// Helper class
@ -143,86 +50,4 @@ namespace fmt
if (static_cast<From>(result) != value) throw fmt::exception(format_str, value, args...);
return result;
}
std::vector<std::string> split(const std::string& source, std::initializer_list<std::string> separators, bool is_skip_empty = true);
std::string trim(const std::string& source, const std::string& values = " \t");
template<typename T>
std::string merge(const T& source, const std::string& separator)
{
if (!source.size())
{
return{};
}
std::string result;
auto it = source.begin();
auto end = source.end();
for (--end; it != end; ++it)
{
result += *it + separator;
}
return result + source.back();
}
template<typename T>
std::string merge(std::initializer_list<T> sources, const std::string& separator)
{
if (!sources.size())
{
return{};
}
std::string result;
bool first = true;
for (auto &v : sources)
{
if (first)
{
result = fmt::merge(v, separator);
first = false;
}
else
{
result += separator + fmt::merge(v, separator);
}
}
return result;
}
template<typename IT>
std::string to_lower(IT _begin, IT _end)
{
std::string result; result.resize(_end - _begin);
std::transform(_begin, _end, result.begin(), ::tolower);
return result;
}
template<typename T>
std::string to_lower(const T& string)
{
return to_lower(std::begin(string), std::end(string));
}
template<typename IT>
std::string to_upper(IT _begin, IT _end)
{
std::string result; result.resize(_end - _begin);
std::transform(_begin, _end, result.begin(), ::toupper);
return result;
}
template<typename T>
std::string to_upper(const T& string)
{
return to_upper(std::begin(string), std::end(string));
}
std::string escape(const std::string& source, std::initializer_list<char> more = {});
std::string unescape(const std::string& source);
bool match(const std::string &source, const std::string &mask);
}

133
Utilities/StrUtil.h Normal file
View file

@ -0,0 +1,133 @@
#pragma once
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <functional>
// Copy null-terminated string from std::string to char array with truncation
template<std::size_t N>
inline void strcpy_trunc(char(&dst)[N], const std::string& src)
{
const std::size_t count = src.size() >= N ? N - 1 : src.size();
std::memcpy(dst, src.c_str(), count);
dst[count] = '\0';
}
// Copy null-terminated string from char array to another char array with truncation
template<std::size_t N, std::size_t N2>
inline void strcpy_trunc(char(&dst)[N], const char(&src)[N2])
{
const std::size_t count = N2 >= N ? N - 1 : N2;
std::memcpy(dst, src, count);
dst[count] = '\0';
}
namespace fmt
{
std::string replace_first(const std::string& src, const std::string& from, const std::string& to);
std::string replace_all(const std::string &src, const std::string& from, const std::string& to);
template<size_t list_size>
std::string replace_all(std::string src, const std::pair<std::string, std::string>(&list)[list_size])
{
for (size_t pos = 0; pos < src.length(); ++pos)
{
for (size_t i = 0; i < list_size; ++i)
{
const size_t comp_length = list[i].first.length();
if (src.length() - pos < comp_length)
continue;
if (src.substr(pos, comp_length) == list[i].first)
{
src = (pos ? src.substr(0, pos) + list[i].second : list[i].second) + src.substr(pos + comp_length);
pos += list[i].second.length() - 1;
break;
}
}
}
return src;
}
template<size_t list_size>
std::string replace_all(std::string src, const std::pair<std::string, std::function<std::string()>>(&list)[list_size])
{
for (size_t pos = 0; pos < src.length(); ++pos)
{
for (size_t i = 0; i < list_size; ++i)
{
const size_t comp_length = list[i].first.length();
if (src.length() - pos < comp_length)
continue;
if (src.substr(pos, comp_length) == list[i].first)
{
src = (pos ? src.substr(0, pos) + list[i].second() : list[i].second()) + src.substr(pos + comp_length);
pos += list[i].second().length() - 1;
break;
}
}
}
return src;
}
std::vector<std::string> split(const std::string& source, std::initializer_list<std::string> separators, bool is_skip_empty = true);
std::string trim(const std::string& source, const std::string& values = " \t");
template<typename T>
std::string merge(const T& source, const std::string& separator)
{
if (!source.size())
{
return{};
}
std::string result;
auto it = source.begin();
auto end = source.end();
for (--end; it != end; ++it)
{
result += *it + separator;
}
return result + source.back();
}
template<typename T>
std::string merge(std::initializer_list<T> sources, const std::string& separator)
{
if (!sources.size())
{
return{};
}
std::string result;
bool first = true;
for (auto &v : sources)
{
if (first)
{
result = fmt::merge(v, separator);
first = false;
}
else
{
result += separator + fmt::merge(v, separator);
}
}
return result;
}
std::string to_upper(const std::string& string);
bool match(const std::string &source, const std::string &mask);
}

View file

@ -1294,11 +1294,20 @@ extern std::mutex& get_current_thread_mutex()
// TODO
extern atomic_t<u32> g_thread_count(0);
extern thread_local std::string(*g_tls_log_prefix)();
void thread_ctrl::initialize()
{
// Initialize TLS variable
g_tls_this_thread = this;
g_tls_log_prefix = []
{
return g_tls_this_thread->m_name;
};
++g_thread_count;
#if defined(_MSC_VER)
struct THREADNAME_INFO
@ -1328,13 +1337,6 @@ void thread_ctrl::initialize()
}
#endif
_log::g_tls_make_prefix = [](const auto&, auto, const auto&)
{
return g_tls_this_thread->m_name;
};
++g_thread_count;
}
void thread_ctrl::set_exception() noexcept
@ -1378,7 +1380,7 @@ thread_ctrl::~thread_ctrl()
void thread_ctrl::initialize_once() const
{
if (!m_data)
if (UNLIKELY(!m_data))
{
auto ptr = new thread_ctrl::internal;
@ -1391,10 +1393,10 @@ void thread_ctrl::initialize_once() const
void thread_ctrl::join()
{
if (m_thread.joinable())
if (LIKELY(m_thread.joinable()))
{
// Increase contention counter
if (m_joining++)
if (UNLIKELY(m_joining++))
{
// Hard way
initialize_once();
@ -1408,7 +1410,7 @@ void thread_ctrl::join()
m_thread.join();
// Notify others if necessary
if (m_joining > 1)
if (UNLIKELY(m_joining > 1))
{
initialize_once();
@ -1420,7 +1422,7 @@ void thread_ctrl::join()
}
}
if (m_data && m_data->exception)
if (UNLIKELY(m_data && m_data->exception))
{
std::rethrow_exception(m_data->exception);
}

1037
Utilities/geometry.h Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff