Ice 3.7 C++11 API Reference
IconvStringConverter.h
Go to the documentation of this file.
1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 #ifndef ICE_ICONV_STRING_CONVERTER
6 #define ICE_ICONV_STRING_CONVERTER
7 
8 #include <Ice/Config.h>
9 
10 //
11 // For all platforms except Windows
12 //
13 #ifndef _WIN32
14 
15 #include <Ice/StringConverter.h>
16 #include <IceUtil/StringUtil.h>
18 #include <IceUtil/UndefSysMacros.h>
19 
20 #include <algorithm>
21 #include <iconv.h>
22 #include <langinfo.h>
23 
24 #if (defined(__APPLE__) && _LIBICONV_VERSION < 0x010B)
25 //
26 // See http://sourceware.org/bugzilla/show_bug.cgi?id=2962
27 //
28 # define ICE_CONST_ICONV_INBUF 1
29 #endif
30 
31 namespace Ice
32 {
33 
38 class ICE_API IconvInitializationException : public IceUtil::ExceptionHelper<IconvInitializationException>
39 {
40 public:
41 
48  IconvInitializationException(const char* file, int line, const std::string& reason);
49 
50 #ifndef ICE_CPP11_COMPILER
51  virtual ~IconvInitializationException() throw();
52 #endif
53 
58  virtual std::string ice_id() const;
59 
64  virtual void ice_print(std::ostream& str) const;
65 
66 #ifndef ICE_CPP11_MAPPING
67 
71  virtual IconvInitializationException* ice_clone() const;
72 #endif
73 
78  std::string reason() const;
79 
80 private:
81 
82  std::string _reason;
83 };
84 
85 }
86 
87 namespace IceInternal
88 {
89 
90 //
91 // Converts charT encoded with internalCode to and from UTF-8 byte sequences
92 //
93 // The implementation allocates a pair of iconv_t on each thread, to avoid
94 // opening / closing iconv_t objects all the time.
95 //
96 //
97 template<typename charT>
98 class IconvStringConverter : public IceUtil::BasicStringConverter<charT>
99 {
100 public:
101 
102  IconvStringConverter(const std::string&);
103 
104  virtual ~IconvStringConverter();
105 
106  virtual Ice::Byte* toUTF8(const charT*, const charT*, Ice::UTF8Buffer&) const;
107 
108  virtual void fromUTF8(const Ice::Byte*, const Ice::Byte*, std::basic_string<charT>&) const;
109 
110 private:
111 
112  std::pair<iconv_t, iconv_t> createDescriptors() const;
113  std::pair<iconv_t, iconv_t> getDescriptors() const;
114 
115  static void cleanupKey(void*);
116  static void close(std::pair<iconv_t, iconv_t>);
117 
118  mutable pthread_key_t _key;
119  const std::string _internalCode;
120 };
121 
122 //
123 // Implementation
124 //
125 
126 #ifdef __SUNPRO_CC
127 extern "C"
128 {
129  typedef void (*IcePthreadKeyDestructor)(void*);
130 }
131 #endif
132 
133 template<typename charT>
134 IconvStringConverter<charT>::IconvStringConverter(const std::string& internalCode) :
135  _internalCode(internalCode)
136 {
137  //
138  // Verify that iconv supports conversion to/from internalCode
139  //
140  try
141  {
142  close(createDescriptors());
143  }
144  catch(const Ice::IllegalConversionException& sce)
145  {
146  throw Ice::IconvInitializationException(__FILE__, __LINE__, sce.reason());
147  }
148 
149  //
150  // Create thread-specific key
151  //
152 #ifdef __SUNPRO_CC
153  int rs = pthread_key_create(&_key, reinterpret_cast<IcePthreadKeyDestructor>(&cleanupKey));
154 #else
155  int rs = pthread_key_create(&_key, &cleanupKey);
156 #endif
157 
158  if(rs != 0)
159  {
160  throw IceUtil::ThreadSyscallException(__FILE__, __LINE__, rs);
161  }
162 }
163 
164 template<typename charT>
165 IconvStringConverter<charT>::~IconvStringConverter()
166 {
167  void* val = pthread_getspecific(_key);
168  if(val != 0)
169  {
170  cleanupKey(val);
171  }
172  if(pthread_key_delete(_key) != 0)
173  {
174  assert(0);
175  }
176 }
177 
178 template<typename charT> std::pair<iconv_t, iconv_t>
179 IconvStringConverter<charT>::createDescriptors() const
180 {
181  std::pair<iconv_t, iconv_t> cdp;
182 
183  const char* externalCode = "UTF-8";
184 
185  cdp.first = iconv_open(_internalCode.c_str(), externalCode);
186  if(cdp.first == iconv_t(-1))
187  {
188  std::ostringstream os;
189  os << "iconv cannot convert from " << externalCode << " to " << _internalCode;
190  throw Ice::IllegalConversionException(__FILE__, __LINE__, os.str());
191  }
192 
193  cdp.second = iconv_open(externalCode, _internalCode.c_str());
194  if(cdp.second == iconv_t(-1))
195  {
196  iconv_close(cdp.first);
197  std::ostringstream os;
198  os << "iconv cannot convert from " << _internalCode << " to " << externalCode;
199  throw Ice::IllegalConversionException(__FILE__, __LINE__, os.str());
200  }
201  return cdp;
202 }
203 
204 template<typename charT> std::pair<iconv_t, iconv_t>
205 IconvStringConverter<charT>::getDescriptors() const
206 {
207  void* val = pthread_getspecific(_key);
208  if(val != 0)
209  {
210  return *static_cast<std::pair<iconv_t, iconv_t>*>(val);
211  }
212  else
213  {
214  std::pair<iconv_t, iconv_t> cdp = createDescriptors();
215  int rs = pthread_setspecific(_key, new std::pair<iconv_t, iconv_t>(cdp));
216  if(rs != 0)
217  {
218  throw IceUtil::ThreadSyscallException(__FILE__, __LINE__, rs);
219  }
220  return cdp;
221  }
222 }
223 
224 template<typename charT> /*static*/ void
225 IconvStringConverter<charT>::cleanupKey(void* val)
226 {
227  std::pair<iconv_t, iconv_t>* cdp = static_cast<std::pair<iconv_t, iconv_t>*>(val);
228 
229  close(*cdp);
230  delete cdp;
231 }
232 
233 template<typename charT> /*static*/ void
234 IconvStringConverter<charT>::close(std::pair<iconv_t, iconv_t> cdp)
235 {
236 #ifndef NDEBUG
237  int rs = iconv_close(cdp.first);
238  assert(rs == 0);
239 
240  rs = iconv_close(cdp.second);
241  assert(rs == 0);
242 #else
243  iconv_close(cdp.first);
244  iconv_close(cdp.second);
245 #endif
246 }
247 
248 template<typename charT> Ice::Byte*
249 IconvStringConverter<charT>::toUTF8(const charT* sourceStart,
250  const charT* sourceEnd,
251  Ice::UTF8Buffer& buf) const
252 {
253  iconv_t cd = getDescriptors().second;
254 
255  //
256  // Reset cd
257  //
258 #ifdef NDEBUG
259  iconv(cd, 0, 0, 0, 0);
260 #else
261  size_t rs = iconv(cd, 0, 0, 0, 0);
262  assert(rs == 0);
263 #endif
264 
265 #ifdef ICE_CONST_ICONV_INBUF
266  const char* inbuf = reinterpret_cast<const char*>(sourceStart);
267 #else
268  char* inbuf = reinterpret_cast<char*>(const_cast<charT*>(sourceStart));
269 #endif
270  size_t inbytesleft = static_cast<size_t>(sourceEnd - sourceStart) * sizeof(charT);
271  char* outbuf = 0;
272 
273  size_t count = 0;
274  //
275  // Loop while we need more buffer space
276  //
277  do
278  {
279  size_t howMany = std::max(inbytesleft, size_t(4));
280  outbuf = reinterpret_cast<char*>(buf.getMoreBytes(howMany,
281  reinterpret_cast<Ice::Byte*>(outbuf)));
282  count = iconv(cd, &inbuf, &inbytesleft, &outbuf, &howMany);
283  } while(count == size_t(-1) && errno == E2BIG);
284 
285  if(count == size_t(-1))
286  {
287  throw Ice::IllegalConversionException(__FILE__, __LINE__,
288  errno == 0 ? "Unknown error" : IceUtilInternal::errorToString(errno));
289  }
290  return reinterpret_cast<Ice::Byte*>(outbuf);
291 }
292 
293 template<typename charT> void
294 IconvStringConverter<charT>::fromUTF8(const Ice::Byte* sourceStart, const Ice::Byte* sourceEnd,
295  std::basic_string<charT>& target) const
296 {
297  iconv_t cd = getDescriptors().first;
298 
299  //
300  // Reset cd
301  //
302 #ifdef NDEBUG
303  iconv(cd, 0, 0, 0, 0);
304 #else
305  size_t rs = iconv(cd, 0, 0, 0, 0);
306  assert(rs == 0);
307 #endif
308 
309 #ifdef ICE_CONST_ICONV_INBUF
310  const char* inbuf = reinterpret_cast<const char*>(sourceStart);
311 #else
312  char* inbuf = reinterpret_cast<char*>(const_cast<Ice::Byte*>(sourceStart));
313 #endif
314  assert(sourceEnd > sourceStart);
315  size_t inbytesleft = static_cast<size_t>(sourceEnd - sourceStart);
316 
317  char* outbuf = 0;
318  size_t outbytesleft = 0;
319  size_t count = 0;
320 
321  //
322  // Loop while we need more buffer space
323  //
324  do
325  {
326  size_t bytesused = 0;
327  if(outbuf != 0)
328  {
329  bytesused = static_cast<size_t>(outbuf - reinterpret_cast<const char*>(target.data()));
330  }
331 
332  const size_t increment = std::max<size_t>(inbytesleft, 4);
333  target.resize(target.size() + increment);
334  outbuf = const_cast<char*>(reinterpret_cast<const char*>(target.data())) + bytesused;
335  outbytesleft += increment * sizeof(charT);
336 
337  count = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
338 
339  } while(count == size_t(-1) && errno == E2BIG);
340 
341  if(count == size_t(-1))
342  {
343  throw Ice::IllegalConversionException(__FILE__, __LINE__,
344  errno == 0 ? "Unknown error" : IceUtilInternal::errorToString(errno));
345  }
346 
347  target.resize(target.size() - (outbytesleft / sizeof(charT)));
348 }
349 }
350 
351 namespace Ice
352 {
353 
360 template<typename charT>
361 ICE_HANDLE<IceUtil::BasicStringConverter<charT> >
362 createIconvStringConverter(const std::string& internalCodeWithDefault = "")
363 {
364  std::string internalCode = internalCodeWithDefault;
365 
366  if(internalCode.empty())
367  {
368  internalCode = nl_langinfo(CODESET);
369  }
370 
371  return ICE_MAKE_SHARED(IceInternal::IconvStringConverter<charT>, internalCode);
372 }
373 
374 }
375 
376 #endif
377 #endif
IceUtil::IllegalConversionException
This exception indicates the failure of a string conversion.
Definition: Exception.h:237
Ice::IconvInitializationException::ice_print
virtual void ice_print(std::ostream &str) const
Prints a description of this exception to the given stream.
StringUtil.h
Ice::createIconvStringConverter
::std::shared_ptr< IceUtil::BasicStringConverter< charT > > createIconvStringConverter(const std::string &internalCodeWithDefault="")
Creates a string converter for the given code.
Definition: IconvStringConverter.h:362
ICE_API
#define ICE_API
Definition: Config.h:197
Ice::Byte
unsigned char Byte
The mapping for the Slice byte type.
Definition: Config.h:50
Ice::IconvInitializationException
Indicates that Iconv does not support the code.
Definition: IconvStringConverter.h:39
Ice::IconvInitializationException::reason
std::string reason() const
Obtains the reason for the failure.
IceUtil::IllegalConversionException::reason
std::string reason() const
Provides the reason this exception was thrown.
IceUtil::UTF8Buffer::getMoreBytes
virtual Byte * getMoreBytes(size_t howMany, Byte *firstUnused)=0
Obtains more bytes.
IceUtil::UTF8Buffer
Provides bytes to toUTF8.
Definition: StringConverter.h:24
IceUtil::ThreadSyscallException
Definition: ThreadException.h:15
UndefSysMacros.h
Config.h
Ice::IconvInitializationException::~IconvInitializationException
virtual ~IconvInitializationException()
Ice
Definition: BuiltinSequences.h:56
Ice::IconvInitializationException::IconvInitializationException
IconvInitializationException(const char *file, int line, const std::string &reason)
Constructs the exception with a reason.
ICE_MAKE_SHARED
#define ICE_MAKE_SHARED(T,...)
Definition: Config.h:358
Ice::IllegalConversionException
IceUtil::IllegalConversionException IllegalConversionException
Indicates an error occurred during string conversion.
Definition: StringConverter.h:27
ThreadException.h
IceUtil::ExceptionHelper
Helper template for the implementation of Ice::Exception.
Definition: Exception.h:128
Ice::IconvInitializationException::ice_id
virtual std::string ice_id() const
Obtains the Slice type ID of this exception.
IceUtil::BasicStringConverter
A StringConverter converts narrow or wide-strings to and from UTF-8 byte sequences.
Definition: StringConverter.h:50
StringConverter.h