From dddfe85604bb2ed6c14dd73a77a067ec278403eb Mon Sep 17 00:00:00 2001 From: Frederic BORRY Date: Tue, 14 Jul 2026 19:22:25 +0200 Subject: [PATCH 1/2] Fix for https://github.com/ngscopeclient/xptools/issues/40 --- StringUtil.cpp | 27 ++++++++++++++++++++------- StringUtil.h | 4 ++-- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/StringUtil.cpp b/StringUtil.cpp index 0904ee3..e112495 100644 --- a/StringUtil.cpp +++ b/StringUtil.cpp @@ -37,8 +37,8 @@ #ifdef _WIN32 #include #else -#include -#include +#include +#include #endif /** @@ -62,9 +62,15 @@ std::string WstringToString(const std::wstring &wstr) return res; #else // Linux / macOS - std::wstring ws(wstr); - std::wstring_convert> conv; - return conv.to_bytes(ws); + std::size_t len = std::wcstombs(nullptr, wstr.c_str(), 0); + + if (len == static_cast(-1)) + return "Invalid wide-character sequence or invalid locale"; + + std::string result(len, '\0'); + std::wcstombs(result.data(), wstr.c_str(), len); + + return result; #endif // _WIN32 } @@ -89,7 +95,14 @@ std::wstring StringToWstring(const std::string &str) return res; #else // Linux / macOS - std::wstring_convert> conv; - return conv.from_bytes(str); + std::size_t len = std::mbstowcs(nullptr, str.c_str(), 0); + + if (len == static_cast(-1)) + return L"Invalid UTF-8 string or invalid locale"; + + std::wstring result(len, L'\0'); + std::mbstowcs(result.data(), str.c_str(), len); + + return result; #endif // _WIN32 } diff --git a/StringUtil.h b/StringUtil.h index 365d5d8..905bff7 100644 --- a/StringUtil.h +++ b/StringUtil.h @@ -33,8 +33,8 @@ @brief Declaration of global string utility functions */ -#ifndef TimeUtil_h -#define TimeUtil_h +#ifndef StringUtil_h +#define StringUtil_h #include From abb8354a0c4878f38cc4a7c3b5032c32a2143c3a Mon Sep 17 00:00:00 2001 From: Frederic BORRY Date: Tue, 21 Jul 2026 18:52:57 +0200 Subject: [PATCH 2/2] Fix to make the code thread safe and Windows compatible. --- StringUtil.cpp | 96 ++++++++++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 42 deletions(-) diff --git a/StringUtil.cpp b/StringUtil.cpp index e112495..3d3df5b 100644 --- a/StringUtil.cpp +++ b/StringUtil.cpp @@ -34,13 +34,37 @@ */ #include "StringUtil.h" -#ifdef _WIN32 -#include -#else +#include #include #include +#include + +#ifdef _WIN32 +#include #endif +static void EnsureUtf8LocaleForCurrentThread() +{ +#ifdef _WIN32 + static thread_local bool initialized = false; + + if (!initialized) + { + _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); + std::setlocale(LC_CTYPE, ".UTF-8"); + initialized = true; + } +#else + static bool initialized = false; + + if (!initialized) + { + std::setlocale(LC_CTYPE, ""); + initialized = true; + } +#endif +} + /** * @brief Convert a std::wstring into an std::string * @param wstr the wstring to convert @@ -48,30 +72,24 @@ */ std::string WstringToString(const std::wstring &wstr) { -#ifdef _WIN32 - std::string res; - if(!wstr.empty()) - { - int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0],(int)wstr.size(), NULL, 0, NULL, NULL); - if (size_needed) - { - res = std::string(size_needed, 0); - WideCharToMultiByte (CP_UTF8, 0, &wstr[0],(int)wstr.size(), &res[0], size_needed, NULL, NULL); - } - } - return res; -#else - // Linux / macOS - std::size_t len = std::wcstombs(nullptr, wstr.c_str(), 0); + EnsureUtf8LocaleForCurrentThread(); - if (len == static_cast(-1)) - return "Invalid wide-character sequence or invalid locale"; + std::mbstate_t state{}; + const wchar_t* src = wstr.c_str(); + std::size_t len = std::wcsrtombs(nullptr, &src, 0, &state); - std::string result(len, '\0'); - std::wcstombs(result.data(), wstr.c_str(), len); + if (len == static_cast(-1)) + return "Invalid wide-character string or invalid locale"; - return result; -#endif // _WIN32 + std::string result(len, '\0'); + state = std::mbstate_t{}; + src = wstr.c_str(); + std::size_t converted = std::wcsrtombs(result.data(), &src, result.size(), &state); + + if (converted == static_cast(-1)) + return "Wstring to string conversion failed"; + + return result; } /** @@ -81,28 +99,22 @@ std::string WstringToString(const std::wstring &wstr) */ std::wstring StringToWstring(const std::string &str) { -#ifdef _WIN32 - std::wstring res; - if( !str.empty()) - { - int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0],(int)str.size(), NULL, 0); - if (size_needed) - { - res = std::wstring(size_needed, 0); - MultiByteToWideChar(CP_UTF8, 0, &str[0],(int)str.size(), &res[0], size_needed); - } - } - return res; -#else - // Linux / macOS - std::size_t len = std::mbstowcs(nullptr, str.c_str(), 0); + EnsureUtf8LocaleForCurrentThread(); - if (len == static_cast(-1)) + std::mbstate_t state{}; + const char* src = str.c_str(); + std::size_t len = std::mbsrtowcs(nullptr, &src, 0, &state); + + if (len == static_cast(-1)) return L"Invalid UTF-8 string or invalid locale"; std::wstring result(len, L'\0'); - std::mbstowcs(result.data(), str.c_str(), len); + state = std::mbstate_t{}; + src = str.c_str(); + std::size_t converted = std::mbsrtowcs(result.data(), &src, result.size(), &state); + + if (converted == static_cast(-1)) + return L"String to wstring conversion failed"; return result; -#endif // _WIN32 }