Ice 3.8
C++ API Reference
Loading...
Searching...
No Matches
IconvStringConverter.h
1// Copyright (c) ZeroC, Inc.
2
3#ifndef ICE_ICONV_STRING_CONVERTER
4#define ICE_ICONV_STRING_CONVERTER
5
6//
7// For all platforms except Windows
8//
9#ifndef _WIN32
10# include "Config.h"
11# include "LocalExceptions.h"
12# include "StringConverter.h"
13# include "StringUtil.h"
14
15# include <algorithm>
16# include <cassert>
17# include <iconv.h>
18# include <langinfo.h>
19# include <memory>
20# include <sstream>
21
22namespace IceInternal
23{
24 //
25 // Converts charT encoded with internalCode to and from UTF-8 byte sequences
26 //
27 // The implementation allocates a pair of iconv_t on each thread, to avoid
28 // opening / closing iconv_t objects all the time.
29 //
30 //
31 template<typename charT> class IconvStringConverter final : public Ice::BasicStringConverter<charT>
32 {
33 public:
34 IconvStringConverter(const std::string&);
35
36 std::byte* toUTF8(const charT*, const charT*, Ice::UTF8Buffer&) const final;
37
38 void fromUTF8(const std::byte*, const std::byte*, std::basic_string<charT>&) const final;
39
40 private:
41 struct DescriptorHolder
42 {
43 std::pair<iconv_t, iconv_t> descriptor;
44
45 // NOLINTNEXTLINE(performance-no-int-to-ptr)
46 DescriptorHolder(const std::string& internalCode) : descriptor(iconv_t(-1), iconv_t(-1))
47 {
48 const char* externalCode = "UTF-8";
49
50 descriptor.first = iconv_open(internalCode.c_str(), externalCode);
51 if (descriptor.first == iconv_t(-1)) // NOLINT(performance-no-int-to-ptr)
52 {
53 std::ostringstream os;
54 os << "iconv cannot convert from " << externalCode << " to " << internalCode;
55 throw Ice::FeatureNotSupportedException{__FILE__, __LINE__, os.str()};
56 }
57
58 descriptor.second = iconv_open(externalCode, internalCode.c_str());
59 if (descriptor.second == iconv_t(-1)) // NOLINT(performance-no-int-to-ptr)
60 {
61 iconv_close(descriptor.first);
62 std::ostringstream os;
63 os << "iconv cannot convert from " << internalCode << " to " << externalCode;
64 throw Ice::FeatureNotSupportedException{__FILE__, __LINE__, os.str()};
65 }
66 }
67
68 ~DescriptorHolder()
69 {
70 [[maybe_unused]] int rs = iconv_close(descriptor.first);
71 assert(rs == 0);
72
73 rs = iconv_close(descriptor.second);
74 assert(rs == 0);
75 }
76
77 DescriptorHolder(const DescriptorHolder&) = delete;
78 DescriptorHolder& operator=(const DescriptorHolder&) = delete;
79 };
80
81 [[nodiscard]] std::pair<iconv_t, iconv_t> getDescriptors() const;
82
83 const std::string _internalCode;
84 };
85
86 //
87 // Implementation
88 //
89
90 template<typename charT>
91 IconvStringConverter<charT>::IconvStringConverter(const std::string& internalCode) : _internalCode(internalCode)
92 {
93 //
94 // Verify that iconv supports conversion to/from internalCode
95 //
96 const DescriptorHolder descriptorHolder(internalCode);
97 }
98
99 template<typename charT> std::pair<iconv_t, iconv_t> IconvStringConverter<charT>::getDescriptors() const
100 {
101 static const thread_local DescriptorHolder descriptorHolder(_internalCode);
102 return descriptorHolder.descriptor;
103 }
104
105 template<typename charT>
106 std::byte*
107 IconvStringConverter<charT>::toUTF8(const charT* sourceStart, const charT* sourceEnd, Ice::UTF8Buffer& buf) const
108 {
109 iconv_t cd = getDescriptors().second;
110
111 //
112 // Reset cd
113 //
114 [[maybe_unused]] size_t rs = iconv(cd, nullptr, nullptr, nullptr, nullptr);
115
116 char* inbuf = reinterpret_cast<char*>(const_cast<charT*>(sourceStart));
117 size_t inbytesleft = static_cast<size_t>(sourceEnd - sourceStart) * sizeof(charT);
118 std::byte* outbuf = nullptr;
119
120 size_t count = 0;
121 //
122 // Loop while we need more buffer space
123 //
124 do
125 {
126 size_t howMany = std::max(inbytesleft, size_t(4));
127 outbuf = buf.getMoreBytes(howMany, outbuf);
128 count = iconv(cd, &inbuf, &inbytesleft, reinterpret_cast<char**>(&outbuf), &howMany);
129 } while (count == size_t(-1) && errno == E2BIG);
130
131 if (count == size_t(-1))
132 {
133 int errorCode = errno;
134 std::ostringstream os;
135 os << "iconv failed with: " << (errorCode == 0 ? "unknown error" : IceInternal::errorToString(errorCode));
136 throw Ice::IllegalConversionException(__FILE__, __LINE__, os.str());
137 }
138 return outbuf;
139 }
140
141 template<typename charT>
142 void IconvStringConverter<charT>::fromUTF8(
143 const std::byte* sourceStart,
144 const std::byte* sourceEnd,
145 std::basic_string<charT>& target) const
146 {
147 iconv_t cd = getDescriptors().first;
148
149 //
150 // Reset cd
151 //
152 [[maybe_unused]] size_t rs = iconv(cd, nullptr, nullptr, nullptr, nullptr);
153 assert(rs == 0);
154 char* inbuf = reinterpret_cast<char*>(const_cast<std::byte*>(sourceStart));
155 assert(sourceEnd > sourceStart);
156 auto inbytesleft = static_cast<size_t>(sourceEnd - sourceStart);
157
158 char* outbuf = nullptr;
159 size_t outbytesleft = 0;
160 size_t count = 0;
161
162 //
163 // Loop while we need more buffer space
164 //
165 do
166 {
167 size_t bytesUsed = 0;
168 if (outbuf)
169 {
170 bytesUsed = static_cast<size_t>(outbuf - reinterpret_cast<const char*>(target.data()));
171 }
172
173 const size_t increment = std::max<size_t>(inbytesleft, 4);
174 target.resize(target.size() + increment);
175 outbuf = const_cast<char*>(reinterpret_cast<const char*>(target.data())) + bytesUsed;
176 outbytesleft += increment * sizeof(charT);
177
178 count = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
179
180 } while (count == size_t(-1) && errno == E2BIG);
181
182 if (count == size_t(-1))
183 {
184 int errorCode = errno;
185 std::ostringstream os;
186 os << "iconv failed with: " << (errorCode == 0 ? "unknown error" : IceInternal::errorToString(errorCode));
187 throw Ice::IllegalConversionException(__FILE__, __LINE__, os.str());
188 }
189
190 target.resize(target.size() - (outbytesleft / sizeof(charT)));
191 }
192}
193
194namespace Ice
195{
196 /// Creates a string converter for the given code.
197 /// @param internalCodeWithDefault The desired code. If empty or not provided, a default code is used.
198 /// @return The converter object.
199 /// @throws FeatureNotSupportedException If the code is not supported.
200 template<typename charT>
201 std::shared_ptr<Ice::BasicStringConverter<charT>>
202 createIconvStringConverter(const std::string& internalCodeWithDefault = "")
203 {
204 std::string internalCode = internalCodeWithDefault;
205
206 if (internalCode.empty())
207 {
208 internalCode = nl_langinfo(CODESET);
209 }
210
211 return std::make_shared<IceInternal::IconvStringConverter<charT>>(internalCode);
212 }
213}
214
215#endif
216#endif
This exception indicates the failure of a string conversion.
virtual std::byte * getMoreBytes(size_t howMany, std::byte *firstUnused)=0
Obtains more bytes.
Provides bytes to toUTF8.
std::shared_ptr< Ice::BasicStringConverter< charT > > createIconvStringConverter(const std::string &internalCodeWithDefault="")
Creates a string converter for the given code.
The Ice RPC framework.
Definition SampleEvent.h:59