]>
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 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, auto_ptr. | |
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/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 | ||
65 | namespace boost { namespace iostreams { | |
66 | ||
67 | template< typename SymmetricFilter, | |
68 | typename Alloc = | |
69 | std::allocator< | |
70 | BOOST_DEDUCED_TYPENAME char_type_of<SymmetricFilter>::type | |
71 | > > | |
72 | class symmetric_filter { | |
73 | public: | |
74 | typedef typename char_type_of<SymmetricFilter>::type char_type; | |
75 | typedef BOOST_IOSTREAMS_CHAR_TRAITS(char_type) traits_type; | |
76 | typedef std::basic_string<char_type, traits_type, Alloc> string_type; | |
77 | struct category | |
78 | : dual_use, | |
79 | filter_tag, | |
80 | multichar_tag, | |
81 | closable_tag | |
82 | { }; | |
83 | ||
84 | // Expands to a sequence of ctors which forward to impl. | |
85 | #define BOOST_PP_LOCAL_MACRO(n) \ | |
86 | BOOST_IOSTREAMS_TEMPLATE_PARAMS(n, T) \ | |
87 | explicit symmetric_filter( \ | |
88 | int buffer_size BOOST_PP_COMMA_IF(n) \ | |
89 | BOOST_PP_ENUM_BINARY_PARAMS(n, const T, &t) ) \ | |
90 | : pimpl_(new impl(buffer_size BOOST_PP_COMMA_IF(n) \ | |
91 | BOOST_PP_ENUM_PARAMS(n, t))) \ | |
92 | { BOOST_ASSERT(buffer_size > 0); } \ | |
93 | /**/ | |
94 | #define BOOST_PP_LOCAL_LIMITS (0, BOOST_IOSTREAMS_MAX_FORWARDING_ARITY) | |
95 | #include BOOST_PP_LOCAL_ITERATE() | |
96 | #undef BOOST_PP_LOCAL_MACRO | |
97 | ||
98 | template<typename Source> | |
99 | std::streamsize read(Source& src, char_type* s, std::streamsize n) | |
100 | { | |
101 | using namespace std; | |
102 | if (!(state() & f_read)) | |
103 | begin_read(); | |
104 | ||
105 | buffer_type& buf = pimpl_->buf_; | |
106 | int status = (state() & f_eof) != 0 ? f_eof : f_good; | |
107 | char_type *next_s = s, | |
108 | *end_s = s + n; | |
109 | while (true) | |
110 | { | |
111 | // Invoke filter if there are unconsumed characters in buffer or if | |
112 | // filter must be flushed. | |
113 | bool flush = status == f_eof; | |
114 | if (buf.ptr() != buf.eptr() || flush) { | |
115 | const char_type* next = buf.ptr(); | |
116 | bool done = | |
117 | !filter().filter(next, buf.eptr(), next_s, end_s, flush); | |
118 | buf.ptr() = buf.data() + (next - buf.data()); | |
119 | if (done) | |
120 | return detail::check_eof( | |
121 | static_cast<std::streamsize>(next_s - s) | |
122 | ); | |
123 | } | |
124 | ||
125 | // If no more characters are available without blocking, or | |
126 | // if read request has been satisfied, return. | |
127 | if ( (status == f_would_block && buf.ptr() == buf.eptr()) || | |
128 | next_s == end_s ) | |
129 | { | |
130 | return static_cast<std::streamsize>(next_s - s); | |
131 | } | |
132 | ||
133 | // Fill buffer. | |
134 | if (status == f_good) | |
135 | status = fill(src); | |
136 | } | |
137 | } | |
138 | ||
139 | template<typename Sink> | |
140 | std::streamsize write(Sink& snk, const char_type* s, std::streamsize n) | |
141 | { | |
142 | if (!(state() & f_write)) | |
143 | begin_write(); | |
144 | ||
145 | buffer_type& buf = pimpl_->buf_; | |
146 | const char_type *next_s, *end_s; | |
147 | for (next_s = s, end_s = s + n; next_s != end_s; ) { | |
148 | if (buf.ptr() == buf.eptr() && !flush(snk)) | |
149 | break; | |
150 | if(!filter().filter(next_s, end_s, buf.ptr(), buf.eptr(), false)) { | |
151 | flush(snk); | |
152 | break; | |
153 | } | |
154 | } | |
155 | return static_cast<std::streamsize>(next_s - s); | |
156 | } | |
157 | ||
158 | template<typename Sink> | |
159 | void close(Sink& snk, BOOST_IOS::openmode mode) | |
160 | { | |
161 | if (mode == BOOST_IOS::out) { | |
162 | ||
163 | if (!(state() & f_write)) | |
164 | begin_write(); | |
165 | ||
166 | // Repeatedly invoke filter() with no input. | |
167 | try { | |
168 | buffer_type& buf = pimpl_->buf_; | |
169 | char_type dummy; | |
170 | const char_type* end = &dummy; | |
171 | bool again = true; | |
172 | while (again) { | |
173 | if (buf.ptr() != buf.eptr()) | |
174 | again = filter().filter( end, end, buf.ptr(), | |
175 | buf.eptr(), true ); | |
176 | flush(snk); | |
177 | } | |
178 | } catch (...) { | |
179 | try { close_impl(); } catch (...) { } | |
180 | throw; | |
181 | } | |
182 | close_impl(); | |
183 | } else { | |
184 | close_impl(); | |
185 | } | |
186 | } | |
187 | SymmetricFilter& filter() { return *pimpl_; } | |
188 | string_type unconsumed_input() const; | |
189 | ||
190 | // Give impl access to buffer_type on Tru64 | |
191 | #if !BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042)) | |
192 | private: | |
193 | #endif | |
194 | typedef detail::buffer<char_type, Alloc> buffer_type; | |
195 | private: | |
196 | buffer_type& buf() { return pimpl_->buf_; } | |
197 | const buffer_type& buf() const { return pimpl_->buf_; } | |
198 | int& state() { return pimpl_->state_; } | |
199 | void begin_read(); | |
200 | void begin_write(); | |
201 | ||
202 | template<typename Source> | |
203 | int fill(Source& src) | |
204 | { | |
205 | std::streamsize amt = iostreams::read(src, buf().data(), buf().size()); | |
206 | if (amt == -1) { | |
207 | state() |= f_eof; | |
208 | return f_eof; | |
209 | } | |
210 | buf().set(0, amt); | |
211 | return amt != 0 ? f_good : f_would_block; | |
212 | } | |
213 | ||
214 | // Attempts to write the contents of the buffer the given Sink. | |
215 | // Returns true if at least on character was written. | |
216 | template<typename Sink> | |
217 | bool flush(Sink& snk) | |
218 | { | |
219 | typedef typename iostreams::category_of<Sink>::type category; | |
220 | typedef is_convertible<category, output> can_write; | |
221 | return flush(snk, can_write()); | |
222 | } | |
223 | ||
224 | template<typename Sink> | |
225 | bool flush(Sink& snk, mpl::true_) | |
226 | { | |
227 | std::streamsize amt = | |
228 | static_cast<std::streamsize>(buf().ptr() - buf().data()); | |
229 | std::streamsize result = | |
230 | boost::iostreams::write(snk, buf().data(), amt); | |
231 | if (result < amt && result > 0) | |
232 | traits_type::move(buf().data(), buf().data() + result, amt - result); | |
233 | buf().set(amt - result, buf().size()); | |
234 | return result != 0; | |
235 | } | |
236 | ||
237 | template<typename Sink> | |
238 | bool flush(Sink&, mpl::false_) { return true;} | |
239 | ||
240 | void close_impl(); | |
241 | ||
242 | enum flag_type { | |
243 | f_read = 1, | |
244 | f_write = f_read << 1, | |
245 | f_eof = f_write << 1, | |
246 | f_good, | |
247 | f_would_block | |
248 | }; | |
249 | ||
250 | struct impl : SymmetricFilter { | |
251 | ||
252 | // Expands to a sequence of ctors which forward to SymmetricFilter. | |
253 | #define BOOST_PP_LOCAL_MACRO(n) \ | |
254 | BOOST_IOSTREAMS_TEMPLATE_PARAMS(n, T) \ | |
255 | impl( int buffer_size BOOST_PP_COMMA_IF(n) \ | |
256 | BOOST_PP_ENUM_BINARY_PARAMS(n, const T, &t) ) \ | |
257 | : SymmetricFilter(BOOST_PP_ENUM_PARAMS(n, t)), \ | |
258 | buf_(buffer_size), state_(0) \ | |
259 | { } \ | |
260 | /**/ | |
261 | #define BOOST_PP_LOCAL_LIMITS (0, BOOST_IOSTREAMS_MAX_FORWARDING_ARITY) | |
262 | #include BOOST_PP_LOCAL_ITERATE() | |
263 | #undef BOOST_PP_LOCAL_MACRO | |
264 | ||
265 | buffer_type buf_; | |
266 | int state_; | |
267 | }; | |
268 | ||
269 | shared_ptr<impl> pimpl_; | |
270 | }; | |
271 | BOOST_IOSTREAMS_PIPABLE(symmetric_filter, 2) | |
272 | ||
273 | //------------------Implementation of symmetric_filter----------------// | |
274 | ||
275 | template<typename SymmetricFilter, typename Alloc> | |
276 | void symmetric_filter<SymmetricFilter, Alloc>::begin_read() | |
277 | { | |
278 | BOOST_ASSERT(!(state() & f_write)); | |
279 | state() |= f_read; | |
280 | buf().set(0, 0); | |
281 | } | |
282 | ||
283 | template<typename SymmetricFilter, typename Alloc> | |
284 | void symmetric_filter<SymmetricFilter, Alloc>::begin_write() | |
285 | { | |
286 | BOOST_ASSERT(!(state() & f_read)); | |
287 | state() |= f_write; | |
288 | buf().set(0, buf().size()); | |
289 | } | |
290 | ||
291 | template<typename SymmetricFilter, typename Alloc> | |
292 | void symmetric_filter<SymmetricFilter, Alloc>::close_impl() | |
293 | { | |
294 | state() = 0; | |
295 | buf().set(0, 0); | |
296 | filter().close(); | |
297 | } | |
298 | ||
299 | template<typename SymmetricFilter, typename Alloc> | |
300 | typename symmetric_filter<SymmetricFilter, Alloc>::string_type | |
301 | symmetric_filter<SymmetricFilter, Alloc>::unconsumed_input() const | |
302 | { return string_type(buf().ptr(), buf().eptr()); } | |
303 | ||
304 | //----------------------------------------------------------------------------// | |
305 | ||
306 | } } // End namespaces iostreams, boost. | |
307 | ||
308 | #include <boost/iostreams/detail/config/enable_warnings.hpp> // MSVC. | |
309 | ||
310 | #endif // #ifndef BOOST_IOSTREAMS_SYMMETRIC_FILTER_HPP_INCLUDED |