From f0a50cf4cc524cd60e4c211d44f5b07fee72859b Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Sat, 22 Dec 2018 17:29:19 +1300 Subject: [PATCH] osx: Implement Wide2UTF8 Copied from linux implementation using iconv API Added if guard before calling iconv_close() to fix > ./build/bin/renderdoccmd test renderdoccmd(77013,0x11660e5c0) malloc: *** error for object 0xffffffffffffffff: pointer being freed was not allocated renderdoccmd(77013,0x11660e5c0) malloc: *** set a breakpoint in malloc_error_break to debug Abort trap: 6 --- renderdoc/CMakeLists.txt | 1 + renderdoc/os/posix/apple/apple_stringio.cpp | 66 +++++++++++++++++++-- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/renderdoc/CMakeLists.txt b/renderdoc/CMakeLists.txt index c4195a4dd..a3a663f44 100644 --- a/renderdoc/CMakeLists.txt +++ b/renderdoc/CMakeLists.txt @@ -15,6 +15,7 @@ if(ANDROID) PRIVATE -landroid) elseif(APPLE) list(APPEND RDOC_LIBRARIES + PRIVATE -liconv PRIVATE -lm PRIVATE -ldl) diff --git a/renderdoc/os/posix/apple/apple_stringio.cpp b/renderdoc/os/posix/apple/apple_stringio.cpp index cb60c95ec..ea61ec1dc 100644 --- a/renderdoc/os/posix/apple/apple_stringio.cpp +++ b/renderdoc/os/posix/apple/apple_stringio.cpp @@ -23,12 +23,15 @@ ******************************************************************************/ #include +#include +#include #include #include #include #include #include #include +#include "common/threading.h" #include "os/os_specific.h" namespace Keyboard @@ -129,14 +132,67 @@ void GetLibraryFilename(string &selfName) namespace StringFormat { -string Wide2UTF8(const std::wstring &s) -{ - RDCFATAL("Converting wide strings to UTF-8 is not supported on Apple!"); - return ""; -} +// cache iconv_t descriptor to save on iconv_open/iconv_close each time +iconv_t iconvWide2UTF8 = (iconv_t)-1; + +// iconv is not thread safe when sharing an iconv_t descriptor +// I don't expect much contention but if it happens we could TryLock +// before creating a temporary iconv_t, or hold two iconv_ts, or something. +Threading::CriticalSection lockWide2UTF8; void Shutdown() { + SCOPED_LOCK(lockWide2UTF8); + if(iconvWide2UTF8 != (iconv_t)-1) + iconv_close(iconvWide2UTF8); + iconvWide2UTF8 = (iconv_t)-1; +} + +string Wide2UTF8(const std::wstring &s) +{ + // include room for null terminator, assuming unicode input (not ucs) + // utf-8 characters can be max 4 bytes. + size_t len = (s.length() + 1) * 4; + + vector charBuffer; + + if(charBuffer.size() < len) + charBuffer.resize(len); + + size_t ret; + + { + SCOPED_LOCK(lockWide2UTF8); + + if(iconvWide2UTF8 == (iconv_t)-1) + iconvWide2UTF8 = iconv_open("UTF-8", "WCHAR_T"); + + if(iconvWide2UTF8 == (iconv_t)-1) + { + RDCERR("Couldn't open iconv for WCHAR_T to UTF-8: %d", errno); + return ""; + } + + char *inbuf = (char *)s.c_str(); + size_t insize = (s.length() + 1) * sizeof(wchar_t); // include null terminator + char *outbuf = &charBuffer[0]; + size_t outsize = len; + + ret = iconv(iconvWide2UTF8, &inbuf, &insize, &outbuf, &outsize); + } + + if(ret == (size_t)-1) + { +#if ENABLED(RDOC_DEVEL) + RDCWARN("Failed to convert wstring"); +#endif + return ""; + } + + // convert to string from null-terminated string - utf-8 never contains + // 0 bytes before the null terminator, and this way we don't care if + // charBuffer is larger than the string + return string(&charBuffer[0]); } };