]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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) | |
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)(buffer_size, 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 | BOOST_IOSTREAMS_FORWARD( code_converter, open_impl, Device, | |
268 | BOOST_IOSTREAMS_CONVERTER_PARAMS, | |
269 | BOOST_IOSTREAMS_CONVERTER_ARGS ) | |
270 | ||
271 | // fstream-like interface. | |
272 | ||
273 | bool is_open() const { return this->pimpl_->is_open(); } | |
274 | void close(BOOST_IOS::openmode which = BOOST_IOS::in | BOOST_IOS::out ) | |
275 | { impl().close(which); } | |
276 | ||
277 | // Device interface. | |
278 | ||
279 | std::streamsize read(char_type*, std::streamsize); | |
280 | std::streamsize write(const char_type*, std::streamsize); | |
281 | void imbue(const std::locale& loc) { impl().cvt_.imbue(loc); } | |
282 | ||
283 | // Direct device access. | |
284 | ||
285 | Device& operator*() { return detail::unwrap_direct(dev()); } | |
286 | Device* operator->() { return &detail::unwrap_direct(dev()); } | |
287 | private: | |
288 | template<typename T> // Used for forwarding. | |
289 | void open_impl(const T& t BOOST_IOSTREAMS_CONVERTER_PARAMS()) | |
290 | { | |
291 | impl().open(t BOOST_IOSTREAMS_CONVERTER_ARGS()); | |
292 | } | |
293 | ||
294 | const codecvt_type& cvt() { return impl().cvt_.get(); } | |
295 | device_type& dev() { return impl().dev(); } | |
296 | buffer_type& in() { return impl().buf_.first(); } | |
297 | buffer_type& out() { return impl().buf_.second(); } | |
298 | impl_type& impl() { return *this->pimpl_; } | |
299 | }; | |
300 | ||
301 | //--------------Implementation of converter-----------------------------------// | |
302 | ||
303 | // Implementation note: if end of stream contains a partial character, | |
304 | // it is ignored. | |
305 | template<typename Device, typename Codevt, typename Alloc> | |
306 | std::streamsize code_converter<Device, Codevt, Alloc>::read | |
307 | (char_type* s, std::streamsize n) | |
308 | { | |
309 | const extern_type* next; // Next external char. | |
310 | intern_type* nint; // Next internal char. | |
311 | std::streamsize total = 0; // Characters read. | |
312 | int status = iostreams::char_traits<char>::good(); | |
313 | bool partial = false; | |
314 | buffer_type& buf = in(); | |
315 | ||
316 | do { | |
317 | ||
318 | // Fill buffer. | |
319 | if (buf.ptr() == buf.eptr() || partial) { | |
320 | status = buf.fill(dev()); | |
321 | if (buf.ptr() == buf.eptr()) | |
322 | break; | |
323 | partial = false; | |
324 | } | |
325 | ||
326 | // Convert. | |
327 | std::codecvt_base::result result = | |
328 | cvt().in( buf.state(), | |
329 | buf.ptr(), buf.eptr(), next, | |
330 | s + total, s + n, nint ); | |
331 | buf.ptr() += next - buf.ptr(); | |
332 | total = static_cast<std::streamsize>(nint - s); | |
333 | ||
334 | switch (result) { | |
335 | case std::codecvt_base::partial: | |
336 | partial = true; | |
337 | break; | |
338 | case std::codecvt_base::ok: | |
339 | break; | |
340 | case std::codecvt_base::noconv: | |
341 | { | |
342 | std::streamsize amt = | |
343 | std::min<std::streamsize>(next - buf.ptr(), n - total); | |
344 | detail::strncpy_if_same(s + total, buf.ptr(), amt); | |
345 | total += amt; | |
346 | } | |
347 | break; | |
348 | case std::codecvt_base::error: | |
349 | default: | |
350 | buf.state() = state_type(); | |
351 | boost::throw_exception(code_conversion_error()); | |
352 | } | |
353 | ||
354 | } while (total < n && status != EOF && status != WOULD_BLOCK); | |
355 | ||
356 | return total == 0 && status == EOF ? -1 : total; | |
357 | } | |
358 | ||
359 | template<typename Device, typename Codevt, typename Alloc> | |
360 | std::streamsize code_converter<Device, Codevt, Alloc>::write | |
361 | (const char_type* s, std::streamsize n) | |
362 | { | |
363 | buffer_type& buf = out(); | |
364 | extern_type* next; // Next external char. | |
365 | const intern_type* nint; // Next internal char. | |
366 | std::streamsize total = 0; // Characters written. | |
367 | bool partial = false; | |
368 | ||
369 | while (total < n) { | |
370 | ||
371 | // Empty buffer. | |
372 | if (buf.eptr() == buf.end() || partial) { | |
373 | if (!buf.flush(dev())) | |
374 | break; | |
375 | partial = false; | |
376 | } | |
377 | ||
378 | // Convert. | |
379 | std::codecvt_base::result result = | |
380 | cvt().out( buf.state(), | |
381 | s + total, s + n, nint, | |
382 | buf.eptr(), buf.end(), next ); | |
383 | int progress = (int) (next - buf.eptr()); | |
384 | buf.eptr() += progress; | |
385 | ||
386 | switch (result) { | |
387 | case std::codecvt_base::partial: | |
388 | partial = true; | |
389 | BOOST_FALLTHROUGH; | |
390 | case std::codecvt_base::ok: | |
391 | total = static_cast<std::streamsize>(nint - s); | |
392 | break; | |
393 | case std::codecvt_base::noconv: | |
394 | { | |
395 | std::streamsize amt = | |
396 | std::min<std::streamsize>( nint - total - s, | |
397 | buf.end() - buf.eptr() ); | |
398 | detail::strncpy_if_same(buf.eptr(), s + total, amt); | |
399 | total += amt; | |
400 | } | |
401 | break; | |
402 | case std::codecvt_base::error: | |
403 | default: | |
404 | buf.state() = state_type(); | |
405 | boost::throw_exception(code_conversion_error()); | |
406 | } | |
407 | } | |
408 | return total; | |
409 | } | |
410 | ||
411 | //----------------------------------------------------------------------------// | |
412 | ||
413 | } } // End namespaces iostreams, boost. | |
414 | ||
415 | #include <boost/iostreams/detail/config/enable_warnings.hpp> // Borland 5.x | |
416 | ||
417 | #endif // #ifndef BOOST_IOSTREAMS_CODE_CONVERTER_HPP_INCLUDED |