1 | // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com) |
2 | // (C) Copyright 2003-2007 Jonathan Turkanis |
3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying |
4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.) |
5 | |
6 | // See http://www.boost.org/libs/iostreams for documentation. |
7 | |
8 | // Contains machinery for performing code conversion. |
9 | |
10 | #ifndef BOOST_IOSTREAMS_CODE_CONVERTER_HPP_INCLUDED |
11 | #define BOOST_IOSTREAMS_CODE_CONVERTER_HPP_INCLUDED |
12 | |
13 | #if defined(_MSC_VER) && (_MSC_VER >= 1020) |
14 | # pragma once |
15 | #endif |
16 | |
17 | #include <boost/iostreams/detail/config/wide_streams.hpp> |
18 | #if defined(BOOST_IOSTREAMS_NO_WIDE_STREAMS) || \ |
19 | defined(BOOST_IOSTREAMS_NO_LOCALE) \ |
20 | /**/ |
21 | # error code conversion not supported on this platform |
22 | #endif |
23 | |
24 | #include <algorithm> // max. |
25 | #include <cstring> // memcpy. |
26 | #include <exception> |
27 | #include <boost/config.hpp> // DEDUCED_TYPENAME, |
28 | #include <boost/iostreams/char_traits.hpp> |
29 | #include <boost/iostreams/constants.hpp> // default_filter_buffer_size. |
30 | #include <boost/iostreams/detail/adapter/concept_adapter.hpp> |
31 | #include <boost/iostreams/detail/adapter/direct_adapter.hpp> |
32 | #include <boost/iostreams/detail/buffer.hpp> |
33 | #include <boost/iostreams/detail/call_traits.hpp> |
34 | #include <boost/iostreams/detail/codecvt_holder.hpp> |
35 | #include <boost/iostreams/detail/codecvt_helper.hpp> |
36 | #include <boost/iostreams/detail/double_object.hpp> |
37 | #include <boost/iostreams/detail/execute.hpp> |
38 | #include <boost/iostreams/detail/forward.hpp> |
39 | #include <boost/iostreams/detail/functional.hpp> |
40 | #include <boost/iostreams/detail/ios.hpp> // failure, openmode, int types. |
41 | #include <boost/iostreams/detail/optional.hpp> |
42 | #include <boost/iostreams/detail/select.hpp> |
43 | #include <boost/iostreams/traits.hpp> |
44 | #include <boost/iostreams/operations.hpp> |
45 | #include <boost/shared_ptr.hpp> |
46 | #include <boost/static_assert.hpp> |
47 | #include <boost/throw_exception.hpp> |
48 | #include <boost/type_traits/is_convertible.hpp> |
49 | #include <boost/type_traits/is_same.hpp> |
50 | |
51 | // Must come last. |
52 | #include <boost/iostreams/detail/config/disable_warnings.hpp> // Borland 5.x |
53 | |
54 | namespace boost { namespace iostreams { |
55 | |
56 | struct code_conversion_error : BOOST_IOSTREAMS_FAILURE { |
57 | code_conversion_error() |
58 | : BOOST_IOSTREAMS_FAILURE("code conversion error" ) |
59 | { } |
60 | }; |
61 | |
62 | namespace detail { |
63 | |
64 | //--------------Definition of strncpy_if_same---------------------------------// |
65 | |
66 | // Helper template for strncpy_if_same, below. |
67 | template<bool B> |
68 | struct strncpy_if_same_impl; |
69 | |
70 | template<> |
71 | struct strncpy_if_same_impl<true> { |
72 | template<typename Ch> |
73 | static Ch* copy(Ch* tgt, const Ch* src, std::streamsize n) |
74 | { return BOOST_IOSTREAMS_CHAR_TRAITS(Ch)::copy(tgt, src, n); } |
75 | }; |
76 | |
77 | template<> |
78 | struct strncpy_if_same_impl<false> { |
79 | template<typename Src, typename Tgt> |
80 | static Tgt* copy(Tgt* tgt, const Src*, std::streamsize) { return tgt; } |
81 | }; |
82 | |
83 | template<typename Src, typename Tgt> |
84 | Tgt* strncpy_if_same(Tgt* tgt, const Src* src, std::streamsize n) |
85 | { |
86 | typedef strncpy_if_same_impl<is_same<Src, Tgt>::value> impl; |
87 | return impl::copy(tgt, src, n); |
88 | } |
89 | |
90 | //--------------Definition of conversion_buffer-------------------------------// |
91 | |
92 | // Buffer and conversion state for reading. |
93 | template<typename Codecvt, typename Alloc> |
94 | class conversion_buffer |
95 | : public buffer< |
96 | BOOST_DEDUCED_TYPENAME detail::codecvt_extern<Codecvt>::type, |
97 | Alloc |
98 | > |
99 | { |
100 | public: |
101 | typedef typename Codecvt::state_type state_type; |
102 | conversion_buffer() |
103 | : buffer< |
104 | BOOST_DEDUCED_TYPENAME detail::codecvt_extern<Codecvt>::type, |
105 | Alloc |
106 | >(0) |
107 | { |
108 | reset(); |
109 | } |
110 | state_type& state() { return state_; } |
111 | void reset() |
112 | { |
113 | if (this->size()) |
114 | this->set(0, 0); |
115 | state_ = state_type(); |
116 | } |
117 | private: |
118 | state_type state_; |
119 | }; |
120 | |
121 | //--------------Definition of converter_impl----------------------------------// |
122 | |
123 | // Contains member data, open/is_open/close and buffer management functions. |
124 | template<typename Device, typename Codecvt, typename Alloc> |
125 | struct code_converter_impl { |
126 | typedef typename codecvt_extern<Codecvt>::type extern_type; |
127 | typedef typename category_of<Device>::type device_category; |
128 | typedef is_convertible<device_category, input> can_read; |
129 | typedef is_convertible<device_category, output> can_write; |
130 | typedef is_convertible<device_category, bidirectional> is_bidir; |
131 | typedef typename |
132 | iostreams::select< // Disambiguation for Tru64. |
133 | is_bidir, bidirectional, |
134 | can_read, input, |
135 | can_write, output |
136 | >::type mode; |
137 | typedef typename |
138 | mpl::if_< |
139 | is_direct<Device>, |
140 | direct_adapter<Device>, |
141 | Device |
142 | >::type device_type; |
143 | typedef optional< concept_adapter<device_type> > storage_type; |
144 | typedef is_convertible<device_category, two_sequence> is_double; |
145 | typedef conversion_buffer<Codecvt, Alloc> buffer_type; |
146 | |
147 | code_converter_impl() : cvt_(), flags_(0) { } |
148 | |
149 | ~code_converter_impl() |
150 | { |
151 | try { |
152 | if (flags_ & f_open) close(); |
153 | } catch (...) { /* */ } |
154 | } |
155 | |
156 | template <class T> |
157 | void open(const T& dev, int buffer_size) |
158 | { |
159 | if (flags_ & f_open) |
160 | boost::throw_exception(BOOST_IOSTREAMS_FAILURE("already open" )); |
161 | if (buffer_size == -1) |
162 | buffer_size = default_filter_buffer_size; |
163 | int max_length = cvt_.get().max_length(); |
164 | buffer_size = (std::max)(a: buffer_size, b: 2 * max_length); |
165 | if (can_read::value) { |
166 | buf_.first().resize(buffer_size); |
167 | buf_.first().set(0, 0); |
168 | } |
169 | if (can_write::value && !is_double::value) { |
170 | buf_.second().resize(buffer_size); |
171 | buf_.second().set(0, 0); |
172 | } |
173 | dev_.reset(concept_adapter<device_type>(dev)); |
174 | flags_ = f_open; |
175 | } |
176 | |
177 | void close() |
178 | { |
179 | detail::execute_all( |
180 | detail::call_member_close(*this, BOOST_IOS::in), |
181 | detail::call_member_close(*this, BOOST_IOS::out) |
182 | ); |
183 | } |
184 | |
185 | void close(BOOST_IOS::openmode which) |
186 | { |
187 | if (which == BOOST_IOS::in && (flags_ & f_input_closed) == 0) { |
188 | flags_ |= f_input_closed; |
189 | iostreams::close(dev(), BOOST_IOS::in); |
190 | } |
191 | if (which == BOOST_IOS::out && (flags_ & f_output_closed) == 0) { |
192 | flags_ |= f_output_closed; |
193 | detail::execute_all( |
194 | detail::flush_buffer(buf_.second(), dev(), can_write::value), |
195 | detail::call_close(dev(), BOOST_IOS::out), |
196 | detail::call_reset(dev_), |
197 | detail::call_reset(buf_.first()), |
198 | detail::call_reset(buf_.second()) |
199 | ); |
200 | } |
201 | } |
202 | |
203 | bool is_open() const { return (flags_ & f_open) != 0;} |
204 | |
205 | device_type& dev() { return **dev_; } |
206 | |
207 | enum flag_type { |
208 | f_open = 1, |
209 | f_input_closed = f_open << 1, |
210 | f_output_closed = f_input_closed << 1 |
211 | }; |
212 | |
213 | codecvt_holder<Codecvt> cvt_; |
214 | storage_type dev_; |
215 | double_object< |
216 | buffer_type, |
217 | is_double |
218 | > buf_; |
219 | int flags_; |
220 | }; |
221 | |
222 | } // End namespace detail. |
223 | |
224 | //--------------Definition of converter---------------------------------------// |
225 | |
226 | #define BOOST_IOSTREAMS_CONVERTER_PARAMS() , int buffer_size = -1 |
227 | #define BOOST_IOSTREAMS_CONVERTER_ARGS() , buffer_size |
228 | |
229 | template<typename Device, typename Codecvt, typename Alloc> |
230 | struct code_converter_base { |
231 | typedef detail::code_converter_impl< |
232 | Device, Codecvt, Alloc |
233 | > impl_type; |
234 | code_converter_base() : pimpl_(new impl_type) { } |
235 | shared_ptr<impl_type> pimpl_; |
236 | }; |
237 | |
238 | template< typename Device, |
239 | typename Codecvt = detail::default_codecvt, |
240 | typename Alloc = std::allocator<char> > |
241 | class code_converter |
242 | : protected code_converter_base<Device, Codecvt, Alloc> |
243 | { |
244 | private: |
245 | typedef detail::code_converter_impl< |
246 | Device, Codecvt, Alloc |
247 | > impl_type; |
248 | typedef typename impl_type::device_type device_type; |
249 | typedef typename impl_type::buffer_type buffer_type; |
250 | typedef typename detail::codecvt_holder<Codecvt>::codecvt_type codecvt_type; |
251 | typedef typename detail::codecvt_intern<Codecvt>::type intern_type; |
252 | typedef typename detail::codecvt_extern<Codecvt>::type extern_type; |
253 | typedef typename detail::codecvt_state<Codecvt>::type state_type; |
254 | public: |
255 | typedef intern_type char_type; |
256 | struct category |
257 | : impl_type::mode, device_tag, closable_tag, localizable_tag |
258 | { }; |
259 | BOOST_STATIC_ASSERT(( |
260 | is_same< |
261 | extern_type, |
262 | BOOST_DEDUCED_TYPENAME char_type_of<Device>::type |
263 | >::value |
264 | )); |
265 | public: |
266 | code_converter() { } |
267 | #if BOOST_WORKAROUND(__GNUC__, < 3) |
268 | code_converter(code_converter& rhs) |
269 | : code_converter_base<Device, Codecvt, Alloc>(rhs) |
270 | { } |
271 | code_converter(const code_converter& rhs) |
272 | : code_converter_base<Device, Codecvt, Alloc>(rhs) |
273 | { } |
274 | #endif |
275 | BOOST_IOSTREAMS_FORWARD( code_converter, open_impl, Device, |
276 | BOOST_IOSTREAMS_CONVERTER_PARAMS, |
277 | BOOST_IOSTREAMS_CONVERTER_ARGS ) |
278 | |
279 | // fstream-like interface. |
280 | |
281 | bool is_open() const { return this->pimpl_->is_open(); } |
282 | void close(BOOST_IOS::openmode which = BOOST_IOS::in | BOOST_IOS::out ) |
283 | { impl().close(which); } |
284 | |
285 | // Device interface. |
286 | |
287 | std::streamsize read(char_type*, std::streamsize); |
288 | std::streamsize write(const char_type*, std::streamsize); |
289 | void imbue(const std::locale& loc) { impl().cvt_.imbue(loc); } |
290 | |
291 | // Direct device access. |
292 | |
293 | Device& operator*() { return detail::unwrap_direct(dev()); } |
294 | Device* operator->() { return &detail::unwrap_direct(dev()); } |
295 | private: |
296 | template<typename T> // Used for forwarding. |
297 | void open_impl(const T& t BOOST_IOSTREAMS_CONVERTER_PARAMS()) |
298 | { |
299 | impl().open(t BOOST_IOSTREAMS_CONVERTER_ARGS()); |
300 | } |
301 | |
302 | const codecvt_type& cvt() { return impl().cvt_.get(); } |
303 | device_type& dev() { return impl().dev(); } |
304 | buffer_type& in() { return impl().buf_.first(); } |
305 | buffer_type& out() { return impl().buf_.second(); } |
306 | impl_type& impl() { return *this->pimpl_; } |
307 | }; |
308 | |
309 | //--------------Implementation of converter-----------------------------------// |
310 | |
311 | // Implementation note: if end of stream contains a partial character, |
312 | // it is ignored. |
313 | template<typename Device, typename Codevt, typename Alloc> |
314 | std::streamsize code_converter<Device, Codevt, Alloc>::read |
315 | (char_type* s, std::streamsize n) |
316 | { |
317 | const extern_type* next; // Next external char. |
318 | intern_type* nint; // Next internal char. |
319 | std::streamsize total = 0; // Characters read. |
320 | int status = iostreams::char_traits<char>::good(); |
321 | bool partial = false; |
322 | buffer_type& buf = in(); |
323 | |
324 | do { |
325 | |
326 | // Fill buffer. |
327 | if (buf.ptr() == buf.eptr() || partial) { |
328 | status = buf.fill(dev()); |
329 | if (buf.ptr() == buf.eptr()) |
330 | break; |
331 | partial = false; |
332 | } |
333 | |
334 | // Convert. |
335 | std::codecvt_base::result result = |
336 | cvt().in( buf.state(), |
337 | buf.ptr(), buf.eptr(), next, |
338 | s + total, s + n, nint ); |
339 | buf.ptr() += next - buf.ptr(); |
340 | total = static_cast<std::streamsize>(nint - s); |
341 | |
342 | switch (result) { |
343 | case std::codecvt_base::partial: |
344 | partial = true; |
345 | break; |
346 | case std::codecvt_base::ok: |
347 | break; |
348 | case std::codecvt_base::noconv: |
349 | { |
350 | std::streamsize amt = |
351 | std::min<std::streamsize>(next - buf.ptr(), n - total); |
352 | detail::strncpy_if_same(s + total, buf.ptr(), amt); |
353 | total += amt; |
354 | } |
355 | break; |
356 | case std::codecvt_base::error: |
357 | default: |
358 | buf.state() = state_type(); |
359 | boost::throw_exception(e: code_conversion_error()); |
360 | } |
361 | |
362 | } while (total < n && status != EOF && status != WOULD_BLOCK); |
363 | |
364 | return total == 0 && status == EOF ? -1 : total; |
365 | } |
366 | |
367 | template<typename Device, typename Codevt, typename Alloc> |
368 | std::streamsize code_converter<Device, Codevt, Alloc>::write |
369 | (const char_type* s, std::streamsize n) |
370 | { |
371 | buffer_type& buf = out(); |
372 | extern_type* next; // Next external char. |
373 | const intern_type* nint; // Next internal char. |
374 | std::streamsize total = 0; // Characters written. |
375 | bool partial = false; |
376 | |
377 | while (total < n) { |
378 | |
379 | // Empty buffer. |
380 | if (buf.eptr() == buf.end() || partial) { |
381 | if (!buf.flush(dev())) |
382 | break; |
383 | partial = false; |
384 | } |
385 | |
386 | // Convert. |
387 | std::codecvt_base::result result = |
388 | cvt().out( buf.state(), |
389 | s + total, s + n, nint, |
390 | buf.eptr(), buf.end(), next ); |
391 | int progress = (int) (next - buf.eptr()); |
392 | buf.eptr() += progress; |
393 | |
394 | switch (result) { |
395 | case std::codecvt_base::partial: |
396 | partial = true; |
397 | BOOST_FALLTHROUGH; |
398 | case std::codecvt_base::ok: |
399 | total = static_cast<std::streamsize>(nint - s); |
400 | break; |
401 | case std::codecvt_base::noconv: |
402 | { |
403 | std::streamsize amt = |
404 | std::min<std::streamsize>( nint - total - s, |
405 | buf.end() - buf.eptr() ); |
406 | detail::strncpy_if_same(buf.eptr(), s + total, amt); |
407 | total += amt; |
408 | } |
409 | break; |
410 | case std::codecvt_base::error: |
411 | default: |
412 | buf.state() = state_type(); |
413 | boost::throw_exception(e: code_conversion_error()); |
414 | } |
415 | } |
416 | return total; |
417 | } |
418 | |
419 | //----------------------------------------------------------------------------// |
420 | |
421 | } } // End namespaces iostreams, boost. |
422 | |
423 | #include <boost/iostreams/detail/config/enable_warnings.hpp> // Borland 5.x |
424 | |
425 | #endif // #ifndef BOOST_IOSTREAMS_CODE_CONVERTER_HPP_INCLUDED |
426 | |