mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-03 13:31:27 +12:00
parent
da7472fe81
commit
aafcf44581
89 changed files with 2370 additions and 2348 deletions
253
Utilities/BitSet.h
Normal file
253
Utilities/BitSet.h
Normal 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;
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 += "} ";
|
||||
}
|
||||
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
133
Utilities/StrUtil.h
Normal 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);
|
||||
}
|
|
@ -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
1037
Utilities/geometry.h
Normal file
File diff suppressed because it is too large
Load diff
1351
Utilities/types.h
1351
Utilities/types.h
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue