// ********************************************************************** // // Copyright (c) 2003-2010 ZeroC, Inc. All rights reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** #ifndef ICE_ICONV_STRING_CONVERTER #define ICE_ICONV_STRING_CONVERTER #include #include #include #include #ifndef _WIN32 #include #endif #if (defined(__APPLE__) && _LIBICONV_VERSION < 0x010B) || (defined(__sun) && !defined(_XPG6)) // // See http://sourceware.org/bugzilla/show_bug.cgi?id=2962 // # define ICE_CONST_ICONV_INBUF 1 #endif // // On Windows, we need to be very careful with errno: if we use different C // runtime libraries for the main program and the libiconv DLL, we end up with // two different errnos ... a not-so-good work-around is to ignore errno // altogether, by defining ICE_NO_ERRNO // namespace Ice { // // Converts charT encoded with internalCode to and from UTF-8 byte sequences // // The implementation allocates a pair of iconv_t on each thread, to avoid // opening / closing iconv_t objects all the time. // // template class IconvStringConverter : public Ice::BasicStringConverter { public: #ifdef _WIN32 IconvStringConverter(const char*); #else IconvStringConverter(const char* = nl_langinfo(CODESET)); #endif virtual ~IconvStringConverter(); virtual Ice::Byte* toUTF8(const charT*, const charT*, Ice::UTF8Buffer&) const; virtual void fromUTF8(const Ice::Byte*, const Ice::Byte*, std::basic_string&) const; private: std::pair createDescriptors() const; std::pair getDescriptors() const; static void cleanupKey(void*); static void close(std::pair); #ifdef _WIN32 DWORD _key; #else mutable pthread_key_t _key; #endif const std::string _internalCode; }; // // Implementation // #ifdef __SUNPRO_CC extern "C" { typedef void (*IcePthreadKeyDestructor)(void*); } #endif template IconvStringConverter::IconvStringConverter(const char* internalCode) : _internalCode(internalCode) { // // Verify that iconv supports conversion to/from internalCode // try { close(createDescriptors()); } catch(const Ice::StringConversionException& sce) { throw Ice::InitializationException(__FILE__, __LINE__, sce.reason); } // // Create thread-specific key // #ifdef _WIN32 _key = TlsAlloc(); if(_key == TLS_OUT_OF_INDEXES) { throw IceUtil::ThreadSyscallException(__FILE__, __LINE__, GetLastError()); } #else #ifdef __SUNPRO_CC int rs = pthread_key_create(&_key, reinterpret_cast(&cleanupKey)); #else int rs = pthread_key_create(&_key, &cleanupKey); #endif if(rs != 0) { throw IceUtil::ThreadSyscallException(__FILE__, __LINE__, rs); } #endif } template IconvStringConverter::~IconvStringConverter() { #ifdef _WIN32 void* val = TlsGetValue(_key); if(val != 0) { cleanupKey(val); } if(TlsFree(_key) == 0) { assert(0); } #else void* val = pthread_getspecific(_key); if(val != 0) { cleanupKey(val); } if(pthread_key_delete(_key) != 0) { assert(0); } #endif } template std::pair IconvStringConverter::createDescriptors() const { std::pair cdp; const char* externalCode = "UTF-8"; cdp.first = iconv_open(_internalCode.c_str(), externalCode); if(cdp.first == iconv_t(-1)) { throw Ice::StringConversionException( __FILE__, __LINE__, std::string("iconv cannot convert from ") + externalCode + " to " + _internalCode); } cdp.second = iconv_open(externalCode, _internalCode.c_str()); if(cdp.second == iconv_t(-1)) { iconv_close(cdp.first); throw Ice::StringConversionException( __FILE__, __LINE__, std::string("iconv cannot convert from ") + _internalCode + " to " + externalCode); } return cdp; } template std::pair IconvStringConverter::getDescriptors() const { #ifdef _WIN32 void* val = TlsGetValue(_key); #else void* val = pthread_getspecific(_key); #endif if(val != 0) { return *static_cast*>(val); } else { std::pair cdp = createDescriptors(); #ifdef _WIN32 if(TlsSetValue(_key, new std::pair(cdp)) == 0) { throw IceUtil::ThreadSyscallException(__FILE__, __LINE__, GetLastError()); } #else int rs = pthread_setspecific(_key, new std::pair(cdp)); if(rs != 0) { throw IceUtil::ThreadSyscallException(__FILE__, __LINE__, rs); } #endif return cdp; } } template /*static*/ void IconvStringConverter::cleanupKey(void* val) { std::pair* cdp = static_cast*>(val); close(*cdp); delete cdp; } template /*static*/ void IconvStringConverter::close(std::pair cdp) { int rs = iconv_close(cdp.first); assert(rs == 0); rs = iconv_close(cdp.second); assert(rs == 0); } template Ice::Byte* IconvStringConverter::toUTF8(const charT* sourceStart, const charT* sourceEnd, Ice::UTF8Buffer& buf) const { iconv_t cd = getDescriptors().second; // // Reset cd // #ifdef NDEBUG iconv(cd, 0, 0, 0, 0); #else int rs = iconv(cd, 0, 0, 0, 0); assert(rs == 0); #endif #ifdef ICE_CONST_ICONV_INBUF const char* inbuf = reinterpret_cast(sourceStart); #else char* inbuf = reinterpret_cast(const_cast(sourceStart)); #endif size_t inbytesleft = (sourceEnd - sourceStart) * sizeof(charT); char* outbuf = 0; size_t count = 0; // // Loop while we need more buffer space // do { size_t howMany = std::max(inbytesleft, size_t(4)); outbuf = reinterpret_cast(buf.getMoreBytes(howMany, reinterpret_cast(outbuf))); count = iconv(cd, &inbuf, &inbytesleft, &outbuf, &howMany); #ifdef ICE_NO_ERRNO } while(count == size_t(-1)); #else } while(count == size_t(-1) && errno == E2BIG); #endif if(count == size_t(-1)) { std::string msg = "Unknown error"; #ifndef ICE_NO_ERRNO if(errno != 0) { msg = strerror(errno); } #endif throw Ice::StringConversionException(__FILE__, __LINE__, msg); } return reinterpret_cast(outbuf); } template void IconvStringConverter::fromUTF8(const Ice::Byte* sourceStart, const Ice::Byte* sourceEnd, std::basic_string& target) const { iconv_t cd = getDescriptors().first; // // Reset cd // #ifdef NDEBUG iconv(cd, 0, 0, 0, 0); #else int rs = iconv(cd, 0, 0, 0, 0); assert(rs == 0); #endif #ifdef ICE_CONST_ICONV_INBUF const char* inbuf = reinterpret_cast(sourceStart); #else char* inbuf = reinterpret_cast(const_cast(sourceStart)); #endif size_t inbytesleft = sourceEnd - sourceStart; // // Result buffer // char* buf = 0; size_t bufsize = 0; char* outbuf = 0; size_t outbytesleft = 0; size_t count = 0; // // Loop while we need more buffer space // do { size_t increment = std::max(inbytesleft * sizeof(wchar_t), size_t(8)); bufsize += increment; char* newbuf = static_cast(realloc(buf, bufsize)); if(newbuf == 0) { free(buf); throw Ice::StringConversionException( __FILE__, __LINE__, "Out of memory"); } outbuf = newbuf + (outbuf - buf); outbytesleft += increment; buf = newbuf; count = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft); #ifdef ICE_NO_ERRNO } while(count == size_t(-1)); #else } while(count == size_t(-1) && errno == E2BIG); #endif if(count == size_t(-1)) { std::string msg = "Unknown error"; #ifndef ICE_NO_ERRNO if(errno != 0) { msg = strerror(errno); } #endif free(buf); throw Ice::StringConversionException(__FILE__, __LINE__, msg); } size_t length = (bufsize - outbytesleft) / sizeof(charT); std::basic_string result(reinterpret_cast(buf), length); target.swap(result); free(buf); } } #endif