diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/common/detached_tasks.cpp | 41 | ||||
-rw-r--r-- | src/common/detached_tasks.h | 40 | ||||
-rw-r--r-- | src/common/file_util.h | 2 | ||||
-rw-r--r-- | src/common/logging/text_formatter.cpp | 2 | ||||
-rw-r--r-- | src/common/param_package.cpp | 18 | ||||
-rw-r--r-- | src/common/param_package.h | 2 | ||||
-rw-r--r-- | src/common/string_util.cpp | 134 | ||||
-rw-r--r-- | src/common/string_util.h | 21 | ||||
-rw-r--r-- | src/common/web_result.h | 24 |
10 files changed, 136 insertions, 153 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 6a3f1fe08..d0e506689 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -29,7 +29,7 @@ if ($ENV{CI}) if (BUILD_VERSION) # This leaves a trailing space on the last word, but we actually want that # because of how it's styled in the title bar. - set(BUILD_FULLNAME "${REPO_NAME} #${BUILD_VERSION} ") + set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ") else() set(BUILD_FULLNAME "") endif() @@ -41,6 +41,8 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU add_library(common STATIC alignment.h assert.h + detached_tasks.cpp + detached_tasks.h bit_field.h bit_set.h cityhash.cpp @@ -87,6 +89,7 @@ add_library(common STATIC timer.cpp timer.h vector_math.h + web_result.h ) if(ARCHITECTURE_x86_64) diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp new file mode 100644 index 000000000..a347d9e02 --- /dev/null +++ b/src/common/detached_tasks.cpp @@ -0,0 +1,41 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <thread> +#include "common/assert.h" +#include "common/detached_tasks.h" + +namespace Common { + +DetachedTasks* DetachedTasks::instance = nullptr; + +DetachedTasks::DetachedTasks() { + ASSERT(instance == nullptr); + instance = this; +} + +void DetachedTasks::WaitForAllTasks() { + std::unique_lock<std::mutex> lock(mutex); + cv.wait(lock, [this]() { return count == 0; }); +} + +DetachedTasks::~DetachedTasks() { + std::unique_lock<std::mutex> lock(mutex); + ASSERT(count == 0); + instance = nullptr; +} + +void DetachedTasks::AddTask(std::function<void()> task) { + std::unique_lock<std::mutex> lock(instance->mutex); + ++instance->count; + std::thread([task{std::move(task)}]() { + task(); + std::unique_lock<std::mutex> lock(instance->mutex); + --instance->count; + std::notify_all_at_thread_exit(instance->cv, std::move(lock)); + }) + .detach(); +} + +} // namespace Common diff --git a/src/common/detached_tasks.h b/src/common/detached_tasks.h new file mode 100644 index 000000000..5dd8fc27b --- /dev/null +++ b/src/common/detached_tasks.h @@ -0,0 +1,40 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <condition_variable> +#include <functional> + +namespace Common { + +/** + * A background manager which ensures that all detached task is finished before program exits. + * + * Some tasks, telemetry submission for example, prefer executing asynchronously and don't care + * about the result. These tasks are suitable for std::thread::detach(). However, this is unsafe if + * the task is launched just before the program exits (which is a common case for telemetry), so we + * need to block on these tasks on program exit. + * + * To make detached task safe, a single DetachedTasks object should be placed in the main(), and + * call WaitForAllTasks() after all program execution but before global/static variable destruction. + * Any potentially unsafe detached task should be executed via DetachedTasks::AddTask. + */ +class DetachedTasks { +public: + DetachedTasks(); + ~DetachedTasks(); + void WaitForAllTasks(); + + static void AddTask(std::function<void()> task); + +private: + static DetachedTasks* instance; + + std::condition_variable cv; + std::mutex mutex; + int count = 0; +}; + +} // namespace Common diff --git a/src/common/file_util.h b/src/common/file_util.h index 3d8fe6264..571503d2a 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -285,7 +285,7 @@ private: template <typename T> void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) { #ifdef _MSC_VER - fstream.open(Common::UTF8ToTStr(filename).c_str(), openmode); + fstream.open(Common::UTF8ToUTF16W(filename).c_str(), openmode); #else fstream.open(filename.c_str(), openmode); #endif diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp index 05437c137..6a0605c63 100644 --- a/src/common/logging/text_formatter.cpp +++ b/src/common/logging/text_formatter.cpp @@ -31,7 +31,7 @@ std::string FormatLogMessage(const Entry& entry) { } void PrintMessage(const Entry& entry) { - auto str = FormatLogMessage(entry) + '\n'; + const auto str = FormatLogMessage(entry).append(1, '\n'); fputs(str.c_str(), stderr); } diff --git a/src/common/param_package.cpp b/src/common/param_package.cpp index 9526ca0c6..b916b4866 100644 --- a/src/common/param_package.cpp +++ b/src/common/param_package.cpp @@ -20,7 +20,15 @@ constexpr char KEY_VALUE_SEPARATOR_ESCAPE[] = "$0"; constexpr char PARAM_SEPARATOR_ESCAPE[] = "$1"; constexpr char ESCAPE_CHARACTER_ESCAPE[] = "$2"; +/// A placeholder for empty param packages to avoid empty strings +/// (they may be recognized as "not set" by some frontend libraries like qt) +constexpr char EMPTY_PLACEHOLDER[] = "[empty]"; + ParamPackage::ParamPackage(const std::string& serialized) { + if (serialized == EMPTY_PLACEHOLDER) { + return; + } + std::vector<std::string> pairs; Common::SplitString(serialized, PARAM_SEPARATOR, pairs); @@ -46,7 +54,7 @@ ParamPackage::ParamPackage(std::initializer_list<DataType::value_type> list) : d std::string ParamPackage::Serialize() const { if (data.empty()) - return ""; + return EMPTY_PLACEHOLDER; std::string result; @@ -120,4 +128,12 @@ bool ParamPackage::Has(const std::string& key) const { return data.find(key) != data.end(); } +void ParamPackage::Erase(const std::string& key) { + data.erase(key); +} + +void ParamPackage::Clear() { + data.clear(); +} + } // namespace Common diff --git a/src/common/param_package.h b/src/common/param_package.h index 7842cd4ef..6a0a9b656 100644 --- a/src/common/param_package.h +++ b/src/common/param_package.h @@ -32,6 +32,8 @@ public: void Set(const std::string& key, int value); void Set(const std::string& key, float value); bool Has(const std::string& key) const; + void Erase(const std::string& key); + void Clear(); private: DataType data; diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index c9a5425a7..731d1db34 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp @@ -5,6 +5,7 @@ #include <algorithm> #include <cctype> #include <cerrno> +#include <codecvt> #include <cstdio> #include <cstdlib> #include <cstring> @@ -13,11 +14,7 @@ #include "common/string_util.h" #ifdef _WIN32 -#include <codecvt> #include <windows.h> -#include "common/common_funcs.h" -#else -#include <iconv.h> #endif namespace Common { @@ -195,11 +192,9 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st return result; } -#ifdef _WIN32 - std::string UTF16ToUTF8(const std::u16string& input) { -#if _MSC_VER >= 1900 - // Workaround for missing char16_t/char32_t instantiations in MSVC2015 +#ifdef _MSC_VER + // Workaround for missing char16_t/char32_t instantiations in MSVC2017 std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert; std::basic_string<__int16> tmp_buffer(input.cbegin(), input.cend()); return convert.to_bytes(tmp_buffer); @@ -210,8 +205,8 @@ std::string UTF16ToUTF8(const std::u16string& input) { } std::u16string UTF8ToUTF16(const std::string& input) { -#if _MSC_VER >= 1900 - // Workaround for missing char16_t/char32_t instantiations in MSVC2015 +#ifdef _MSC_VER + // Workaround for missing char16_t/char32_t instantiations in MSVC2017 std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert; auto tmp_buffer = convert.from_bytes(input); return std::u16string(tmp_buffer.cbegin(), tmp_buffer.cend()); @@ -221,6 +216,7 @@ std::u16string UTF8ToUTF16(const std::string& input) { #endif } +#ifdef _WIN32 static std::wstring CPToUTF16(u32 code_page, const std::string& input) { const auto size = MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0); @@ -261,124 +257,6 @@ std::wstring UTF8ToUTF16W(const std::string& input) { return CPToUTF16(CP_UTF8, input); } -std::string SHIFTJISToUTF8(const std::string& input) { - return UTF16ToUTF8(CPToUTF16(932, input)); -} - -std::string CP1252ToUTF8(const std::string& input) { - return UTF16ToUTF8(CPToUTF16(1252, input)); -} - -#else - -template <typename T> -static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input) { - iconv_t const conv_desc = iconv_open("UTF-8", fromcode); - if ((iconv_t)(-1) == conv_desc) { - LOG_ERROR(Common, "Iconv initialization failure [{}]: {}", fromcode, strerror(errno)); - iconv_close(conv_desc); - return {}; - } - - const std::size_t in_bytes = sizeof(T) * input.size(); - // Multiply by 4, which is the max number of bytes to encode a codepoint - const std::size_t out_buffer_size = 4 * in_bytes; - - std::string out_buffer(out_buffer_size, '\0'); - - auto src_buffer = &input[0]; - std::size_t src_bytes = in_bytes; - auto dst_buffer = &out_buffer[0]; - std::size_t dst_bytes = out_buffer.size(); - - while (0 != src_bytes) { - std::size_t const iconv_result = - iconv(conv_desc, (char**)(&src_buffer), &src_bytes, &dst_buffer, &dst_bytes); - - if (static_cast<std::size_t>(-1) == iconv_result) { - if (EILSEQ == errno || EINVAL == errno) { - // Try to skip the bad character - if (0 != src_bytes) { - --src_bytes; - ++src_buffer; - } - } else { - LOG_ERROR(Common, "iconv failure [{}]: {}", fromcode, strerror(errno)); - break; - } - } - } - - std::string result; - out_buffer.resize(out_buffer_size - dst_bytes); - out_buffer.swap(result); - - iconv_close(conv_desc); - - return result; -} - -std::u16string UTF8ToUTF16(const std::string& input) { - iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8"); - if ((iconv_t)(-1) == conv_desc) { - LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: {}", strerror(errno)); - iconv_close(conv_desc); - return {}; - } - - const std::size_t in_bytes = sizeof(char) * input.size(); - // Multiply by 4, which is the max number of bytes to encode a codepoint - const std::size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes; - - std::u16string out_buffer(out_buffer_size, char16_t{}); - - char* src_buffer = const_cast<char*>(&input[0]); - std::size_t src_bytes = in_bytes; - char* dst_buffer = (char*)(&out_buffer[0]); - std::size_t dst_bytes = out_buffer.size(); - - while (0 != src_bytes) { - std::size_t const iconv_result = - iconv(conv_desc, &src_buffer, &src_bytes, &dst_buffer, &dst_bytes); - - if (static_cast<std::size_t>(-1) == iconv_result) { - if (EILSEQ == errno || EINVAL == errno) { - // Try to skip the bad character - if (0 != src_bytes) { - --src_bytes; - ++src_buffer; - } - } else { - LOG_ERROR(Common, "iconv failure [UTF-8]: {}", strerror(errno)); - break; - } - } - } - - std::u16string result; - out_buffer.resize(out_buffer_size - dst_bytes); - out_buffer.swap(result); - - iconv_close(conv_desc); - - return result; -} - -std::string UTF16ToUTF8(const std::u16string& input) { - return CodeToUTF8("UTF-16LE", input); -} - -std::string CP1252ToUTF8(const std::string& input) { - // return CodeToUTF8("CP1252//TRANSLIT", input); - // return CodeToUTF8("CP1252//IGNORE", input); - return CodeToUTF8("CP1252", input); -} - -std::string SHIFTJISToUTF8(const std::string& input) { - // return CodeToUTF8("CP932", input); - return CodeToUTF8("SJIS", input); -} - #endif std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len) { diff --git a/src/common/string_util.h b/src/common/string_util.h index dcca6bc38..32bf6a19c 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h @@ -72,31 +72,10 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st std::string UTF16ToUTF8(const std::u16string& input); std::u16string UTF8ToUTF16(const std::string& input); -std::string CP1252ToUTF8(const std::string& str); -std::string SHIFTJISToUTF8(const std::string& str); - #ifdef _WIN32 std::string UTF16ToUTF8(const std::wstring& input); std::wstring UTF8ToUTF16W(const std::string& str); -#ifdef _UNICODE -inline std::string TStrToUTF8(const std::wstring& str) { - return UTF16ToUTF8(str); -} - -inline std::wstring UTF8ToTStr(const std::string& str) { - return UTF8ToUTF16W(str); -} -#else -inline std::string TStrToUTF8(const std::string& str) { - return str; -} - -inline std::string UTF8ToTStr(const std::string& str) { - return str; -} -#endif - #endif /** diff --git a/src/common/web_result.h b/src/common/web_result.h new file mode 100644 index 000000000..969926674 --- /dev/null +++ b/src/common/web_result.h @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> + +namespace Common { +struct WebResult { + enum class Code : u32 { + Success, + InvalidURL, + CredentialsMissing, + LibError, + HttpError, + WrongContent, + NoWebservice, + }; + Code result_code; + std::string result_string; + std::string returned_data; +}; +} // namespace Common |