source: NonGTP/Boost/boost/iostreams/code_converter.hpp @ 857

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