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, streamsize.
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 {
code_conversion_errorboost::iostreams::code_conversion_error57     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>
copyboost::iostreams::detail::strncpy_if_same_impl73     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>
copyboost::iostreams::detail::strncpy_if_same_impl80     static Tgt* copy(Tgt* tgt, const Src*, std::streamsize) { return tgt; }
81 };
82 
83 template<typename Src, typename Tgt>
strncpy_if_same(Tgt * tgt,const Src * src,std::streamsize n)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;
conversion_buffer()102     conversion_buffer()
103         : buffer<
104               BOOST_DEDUCED_TYPENAME detail::codecvt_extern<Codecvt>::type,
105               Alloc
106           >(0)
107     {
108         reset();
109     }
state()110     state_type& state() { return state_; }
reset()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 
code_converter_implboost::iostreams::detail::code_converter_impl147     code_converter_impl() : cvt_(), flags_(0) { }
148 
~code_converter_implboost::iostreams::detail::code_converter_impl149     ~code_converter_impl()
150     {
151         try {
152             if (flags_ & f_open) close();
153         } catch (...) { /* */ }
154     }
155 
156     template <class T>
openboost::iostreams::detail::code_converter_impl157     void open(const T& dev, std::streamsize 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         std::streamsize 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 
closeboost::iostreams::detail::code_converter_impl177     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 
closeboost::iostreams::detail::code_converter_impl185     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 
is_openboost::iostreams::detail::code_converter_impl203     bool is_open() const { return (flags_ & f_open) != 0;}
204 
devboost::iostreams::detail::code_converter_impl205     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() , std::streamsize 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;
code_converter_baseboost::iostreams::code_converter_base234     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:
code_converter()266     code_converter() { }
BOOST_IOSTREAMS_FORWARD(code_converter,open_impl,Device,BOOST_IOSTREAMS_CONVERTER_PARAMS,BOOST_IOSTREAMS_CONVERTER_ARGS)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(); }
close(BOOST_IOS::openmode which=BOOST_IOS::in|BOOST_IOS::out)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);
imbue(const std::locale & loc)281     void imbue(const std::locale& loc) { impl().cvt_.imbue(loc); }
282 
283         // Direct device access.
284 
operator *()285     Device& operator*() { return detail::unwrap_direct(dev()); }
operator ->()286     Device* operator->() { return &detail::unwrap_direct(dev()); }
287 private:
288     template<typename T> // Used for forwarding.
open_impl(const T & t BOOST_IOSTREAMS_CONVERTER_PARAMS ())289     void open_impl(const T& t BOOST_IOSTREAMS_CONVERTER_PARAMS())
290     {
291         impl().open(t BOOST_IOSTREAMS_CONVERTER_ARGS());
292     }
293 
cvt()294     const codecvt_type& cvt() { return impl().cvt_.get(); }
dev()295     device_type& dev() { return impl().dev(); }
in()296     buffer_type& in() { return impl().buf_.first(); }
out()297     buffer_type& out() { return impl().buf_.second(); }
impl()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>
read(char_type * s,std::streamsize n)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>
write(const char_type * s,std::streamsize n)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
418