// ********************************************************************** // // Copyright (c) 2006 ZeroC, Inc. All rights reserved. // // ********************************************************************** #include #include #include #include // // 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: IconvStringConverter(const char* internalCode); virtual ~IconvStringConverter(); virtual Ice::Byte* toUTF8(const charT* sourceStart, const charT* sourceEnd, Ice::UTF8Buffer& buf) const; virtual void fromUTF8(const Ice::Byte* sourceStart, const Ice::Byte* sourceEnd, std::basic_string& target) const; private: std::pair createDescriptors() const; std::pair getDescriptors() const; static void cleanupKey(void*); static void close(std::pair); mutable pthread_key_t _key; const char* _internalCode; }; 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 // int rs = pthread_key_create(&_key, &cleanupKey); if(rs != 0) { throw IceUtil::ThreadSyscallException(__FILE__, __LINE__, rs); } } template IconvStringConverter::~IconvStringConverter() { void* val = pthread_getspecific(_key); if(val != 0) { cleanupKey(val); } int rs = pthread_key_delete(_key); assert(rs == 0); } template std::pair IconvStringConverter::createDescriptors() const { std::pair cdp; const char* externalCode = "UTF-8"; cdp.first = iconv_open(_internalCode, externalCode); if(cdp.first == iconv_t(-1)) { throw Ice::StringConversionException( __FILE__, __LINE__, std::string("iconv can convert from ") + externalCode + " to " + _internalCode); } cdp.second = iconv_open(externalCode, _internalCode); if(cdp.second == iconv_t(-1)) { iconv_close(cdp.first); throw Ice::StringConversionException( __FILE__, __LINE__, std::string("iconv can convert from ") + _internalCode + " to " + externalCode); } return cdp; } template std::pair IconvStringConverter::getDescriptors() const { void* val = pthread_getspecific(_key); if(val != 0) { return *static_cast*>(val); } else { std::pair cdp = createDescriptors(); int rs = pthread_setspecific(_key, new std::pair(cdp)); if(rs != 0) { throw IceUtil::ThreadSyscallException(__FILE__, __LINE__, rs); } 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 // int rs = iconv(cd, 0, 0, 0, 0); assert(rs == 0); char* inbuf = reinterpret_cast(const_cast(sourceStart)); size_t inbytesleft = (sourceEnd - sourceStart) * sizeof(charT); Ice::Byte* outbuf = 0; size_t count = 0; // // Loop while we need more buffer space // do { size_t howMany = std::max(inbytesleft, size_t(4)); outbuf = buf.getMoreBytes(howMany, outbuf); count = iconv(cd, &inbuf, &inbytesleft, reinterpret_cast(&outbuf), &howMany); } while(count == size_t(-1) && errno == E2BIG); if(count == size_t(-1)) { throw Ice::StringConversionException(__FILE__, __LINE__); } return 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 // int rs = iconv(cd, 0, 0, 0, 0); assert(rs == 0); char* inbuf = reinterpret_cast(const_cast(sourceStart)); 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); } while(count == size_t(-1) && errno == E2BIG); if(count == size_t(-1)) { free(buf); throw Ice::StringConversionException(__FILE__, __LINE__); } size_t length = (bufsize - outbytesleft) / sizeof(charT); std::basic_string result(reinterpret_cast(buf), length); target.swap(result); free(buf); };