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