source: NonGTP/Boost/boost/iostreams/filter/symmetric.hpp @ 857

Revision 857, 10.6 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 the definitions of the class templates symmetric_filter,
8// which models DualUseFilter based on a model of the Symmetric Filter.
9
10//
11// Roughly, a Symmetric Filter is a class type with the following interface:
12//
13//   struct symmetric_filter {
14//       typedef xxx char_type;
15//
16//       bool filter( const char*& begin_in, const char* end_in,
17//                    char*& begin_out, char* end_out, bool flush )
18//       {
19//          // Consume as many characters as possible from the interval
20//          // [begin_in, end_in), without exhausting the output range
21//          // [begin_out, end_out). If flush is true, write as mush output
22//          // as possible.
23//          // A return value of true indicates that filter should be called
24//          // again. More precisely, if flush is false, a return value of
25//          // false indicates that the natural end of stream has been reached
26//          // and that all filtered data has been forwarded; if flush is
27//          // true, a return value of false indicates that all filtered data
28//          // has been forwarded.
29//       }
30//       void close() { /* Reset filter's state. */ }
31//   };
32//
33// Symmetric Filter filters need not be CopyConstructable.
34//
35
36#ifndef BOOST_IOSTREAMS_SYMMETRIC_FILTER_HPP_INCLUDED
37#define BOOST_IOSTREAMS_SYMMETRIC_FILTER_HPP_INCLUDED
38
39#if defined(_MSC_VER) && (_MSC_VER >= 1020)
40# pragma once
41#endif
42
43#include <cassert>
44#include <memory>                               // allocator, auto_ptr.
45#include <boost/config.hpp>                     // BOOST_DEDUCED_TYPENAME.
46#include <boost/iostreams/char_traits.hpp>
47#include <boost/iostreams/constants.hpp>        // buffer size.
48#include <boost/iostreams/detail/buffer.hpp>
49#include <boost/iostreams/detail/char_traits.hpp>
50#include <boost/iostreams/detail/closer.hpp>
51#include <boost/iostreams/detail/config/limits.hpp>
52#include <boost/iostreams/detail/template_params.hpp>
53#include <boost/iostreams/traits.hpp>
54#include <boost/iostreams/operations.hpp>       // read, write.
55#include <boost/iostreams/pipeline.hpp>
56#include <boost/preprocessor/iteration/local.hpp>
57#include <boost/preprocessor/punctuation/comma_if.hpp>
58#include <boost/preprocessor/repetition/enum_binary_params.hpp>
59#include <boost/preprocessor/repetition/enum_params.hpp>
60#include <boost/shared_ptr.hpp>
61
62// Must come last.
63#include <boost/iostreams/detail/config/disable_warnings.hpp>  // MSVC.
64
65namespace boost { namespace iostreams {
66
67template< typename SymmetricFilter,
68          typename Alloc =
69              std::allocator<
70                  BOOST_DEDUCED_TYPENAME char_type_of<SymmetricFilter>::type
71              > >
72class symmetric_filter {
73public:
74    typedef typename char_type_of<SymmetricFilter>::type  char_type;
75    typedef std::basic_string<char_type>                  string_type;
76    struct category
77        : dual_use,
78          filter_tag,
79          multichar_tag,
80          closable_tag
81        { };
82
83    // Expands to a sequence of ctors which forward to impl.
84    #define BOOST_PP_LOCAL_MACRO(n) \
85        BOOST_IOSTREAMS_TEMPLATE_PARAMS(n, T) \
86        explicit symmetric_filter( \
87              int buffer_size BOOST_PP_COMMA_IF(n) \
88              BOOST_PP_ENUM_BINARY_PARAMS(n, const T, &t) ) \
89            : pimpl_(new impl(buffer_size BOOST_PP_COMMA_IF(n) \
90                     BOOST_PP_ENUM_PARAMS(n, t))) \
91            { } \
92        /**/
93    #define BOOST_PP_LOCAL_LIMITS (0, BOOST_IOSTREAMS_MAX_FORWARDING_ARITY)
94    #include BOOST_PP_LOCAL_ITERATE()
95    #undef BOOST_PP_LOCAL_MACRO
96
97    template<typename Source>
98    std::streamsize read(Source& src, char_type* s, std::streamsize n)
99    {
100        using namespace std;
101        if (!(state() & f_read))
102            begin_read();
103
104        buffer_type&  buf = pimpl_->buf_;
105        int           status = (state() & f_eof) != 0 ? f_eof : f_good;
106        char_type    *next_s = s,
107                     *end_s = s + n;
108        while (true)
109        {
110            // Invoke filter if there are unconsumed characters in buffer or if
111            // filter must be flushed.
112            bool flush = status == f_eof;
113            if (buf.ptr() != buf.eptr() || flush) {
114                const char_type* next = buf.ptr();
115                bool done =
116                    !filter().filter(next, buf.eptr(), next_s, end_s, flush);
117                buf.ptr() = buf.data() + (next - buf.data());
118                if (done)
119                    return detail::check_eof(static_cast<streamsize>(next_s - s));
120            }
121
122            // If no more characters are available without blocking, or
123            // if read request has been satisfied, return.
124            if ( status == f_would_block && buf.ptr() == buf.eptr() ||
125                 next_s == end_s )
126            {
127                return static_cast<streamsize>(next_s - s);
128            }
129
130            // Fill buffer.
131            if (status == f_good)
132                status = fill(src);
133        }
134    }
135
136    template<typename Sink>
137    std::streamsize write(Sink& snk, const char_type* s, std::streamsize n)
138    {
139        if (!(state() & f_write))
140            begin_write();
141
142        buffer_type&     buf = pimpl_->buf_;
143        const char_type *next_s, *end_s;
144        for (next_s = s, end_s = s + n; next_s != end_s; ) {
145            if (buf.ptr() == buf.eptr() && !flush(snk))
146                break;
147            filter().filter(next_s, end_s, buf.ptr(), buf.eptr(), false);
148        }
149        return static_cast<std::streamsize>(next_s - s);
150    }
151
152    // Give detail::closer<> permission to call close().
153    typedef symmetric_filter<SymmetricFilter, Alloc> self;
154    friend struct detail::closer<self>;
155
156    template<typename Sink>
157    void close(Sink& snk, BOOST_IOS::openmode which)
158    {
159        using namespace std;
160        if ((state() & f_read) && (which & BOOST_IOS::in))
161            close();
162        if ((state() & f_write) && (which & BOOST_IOS::out)) {
163
164            // Repeatedly invoke filter() with no input.
165            detail::closer<self>  closer(*this);
166            buffer_type&          buf = pimpl_->buf_;
167            char                  dummy;
168            const char*           end = &dummy;
169            bool                  again = true;
170            while (again) {
171                if (buf.ptr() != buf.eptr())
172                    again = filter().filter(end, end, buf.ptr(), buf.eptr(), true);
173                flush(snk);
174            }
175        }
176    }
177    SymmetricFilter& filter() { return *pimpl_; }
178    string_type unconsumed_input() const;
179
180// Give impl access to buffer_type on Tru64
181#if !BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042))
182    private:
183#endif
184    typedef detail::buffer<char_type, Alloc> buffer_type;
185private:
186    buffer_type& buf() { return pimpl_->buf_; }
187    const buffer_type& buf() const { return pimpl_->buf_; }
188    int& state() { return pimpl_->state_; }
189    void begin_read();
190    void begin_write();
191
192    template<typename Source>
193    int fill(Source& src)
194    {
195        std::streamsize amt = iostreams::read(src, buf().data(), buf().size());
196        if (amt == -1) {
197            state() |= f_eof;
198            return f_eof;
199        }
200        buf().set(0, amt);
201        return amt == buf().size() ? f_good : f_would_block;
202    }
203
204    // Attempts to write the contents of the buffer the given Sink.
205    // Returns true if at least on character was written.
206    template<typename Sink>
207    bool flush(Sink& snk)
208    {
209        typedef typename iostreams::category_of<Sink>::type  category;
210        typedef is_convertible<category, output>             can_write;
211        return flush(snk, can_write());
212    }
213
214    template<typename Sink>
215    bool flush(Sink& snk, mpl::true_)
216    {
217        using std::streamsize;
218        typedef char_traits<char_type> traits_type;
219        streamsize amt =
220            static_cast<streamsize>(buf().ptr() - buf().data());
221        streamsize result =
222            boost::iostreams::write(snk, buf().data(), amt);
223        if (result < amt && result > 0)
224            traits_type::move(buf().data(), buf().data() + result, amt - result);
225        buf().set(amt - result, buf().size());
226        return result != 0;
227    }
228
229    template<typename Sink>
230    bool flush(Sink& snk, mpl::false_) { return true;}
231
232    void close();
233
234    enum flag_type {
235        f_read   = 1,
236        f_write  = f_read << 1,
237        f_eof    = f_write << 1,
238        f_good,
239        f_would_block
240    };
241
242    struct impl : SymmetricFilter {
243
244    // Expands to a sequence of ctors which forward to SymmetricFilter.
245    #define BOOST_PP_LOCAL_MACRO(n) \
246        BOOST_IOSTREAMS_TEMPLATE_PARAMS(n, T) \
247        impl( int buffer_size BOOST_PP_COMMA_IF(n) \
248              BOOST_PP_ENUM_BINARY_PARAMS(n, const T, &t) ) \
249            : SymmetricFilter(BOOST_PP_ENUM_PARAMS(n, t)), \
250              buf_(buffer_size), state_(0) \
251            { } \
252        /**/
253    #define BOOST_PP_LOCAL_LIMITS (0, BOOST_IOSTREAMS_MAX_FORWARDING_ARITY)
254    #include BOOST_PP_LOCAL_ITERATE()
255    #undef BOOST_PP_LOCAL_MACRO
256
257        buffer_type  buf_;
258        int          state_;
259    };
260
261    shared_ptr<impl> pimpl_;
262};
263BOOST_IOSTREAMS_PIPABLE(symmetric_filter, 2)
264
265//------------------Implementation of symmetric_filter----------------//
266
267template<typename SymmetricFilter, typename Alloc>
268void symmetric_filter<SymmetricFilter, Alloc>::begin_read()
269{
270    assert(!(state() & f_write));
271    state() |= f_read;
272    buf().set(0, 0);
273}
274
275template<typename SymmetricFilter, typename Alloc>
276void symmetric_filter<SymmetricFilter, Alloc>::begin_write()
277{
278    assert(!(state() & f_read));
279    state() |= f_write;
280    buf().set(0, buf().size());
281}
282
283template<typename SymmetricFilter, typename Alloc>
284void symmetric_filter<SymmetricFilter, Alloc>::close()
285{
286    state() = 0;
287    buf().set(0, 0);
288    filter().close();
289}
290
291template<typename SymmetricFilter, typename Alloc>
292typename symmetric_filter<SymmetricFilter, Alloc>::string_type
293symmetric_filter<SymmetricFilter, Alloc>::unconsumed_input() const
294{ return string_type(buf().ptr(), buf().eptr()); }
295
296//----------------------------------------------------------------------------//
297
298} } // End namespaces iostreams, boost.
299
300#include <boost/iostreams/detail/config/enable_warnings.hpp>  // MSVC.
301
302#endif // #ifndef BOOST_IOSTREAMS_SYMMETRIC_FILTER_HPP_INCLUDED
Note: See TracBrowser for help on using the repository browser.