1 | // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com) |
2 | // (C) Copyright 2005-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 | // Adapted from an example of James Kanze, with suggestions from Peter Dimov. |
9 | // See https://web.archive.org/web/20041222094942/http://www.gabi-soft.fr/codebase-en.html. |
10 | |
11 | #ifndef BOOST_IOSTREAMS_FINITE_STATE_FILTER_HPP_INCLUDED |
12 | #define BOOST_IOSTREAMS_FINITE_STATE_FILTER_HPP_INCLUDED |
13 | |
14 | #include <cassert> |
15 | #include <cstdio> // EOF. |
16 | #include <iostream> // cin, cout. |
17 | #include <locale> |
18 | #include <string> |
19 | #include <boost/config.hpp> // JOIN, member template friends. |
20 | #include <boost/detail/workaround.hpp> |
21 | #include <boost/iostreams/categories.hpp> |
22 | #include <boost/iostreams/char_traits.hpp> |
23 | #include <boost/iostreams/checked_operations.hpp> // put_if. |
24 | #include <boost/iostreams/concepts.hpp> |
25 | #include <boost/iostreams/detail/ios.hpp> // openmode. |
26 | #include <boost/iostreams/filter/stdio.hpp> |
27 | #include <boost/iostreams/operations.hpp> |
28 | #include <boost/mpl/begin_end.hpp> |
29 | #include <boost/mpl/deref.hpp> |
30 | #include <boost/preprocessor/control/expr_if.hpp> |
31 | #include <boost/static_assert.hpp> |
32 | #include <boost/type_traits/is_base_and_derived.hpp> |
33 | |
34 | namespace boost { namespace iostreams { |
35 | |
36 | //------------------Definition of basic character classes---------------------// |
37 | |
38 | struct finite_state_machine_base { |
39 | |
40 | static const int initial_state = 0; |
41 | |
42 | // All-inclusive character class. |
43 | |
44 | struct is_any { |
45 | template<typename Ch> |
46 | static bool test(Ch, const std::locale&) { return true; } |
47 | }; |
48 | |
49 | // Locale-sensitive character classes. |
50 | |
51 | #define BOOST_IOSTREAMS_CHARACTER_CLASS(class) \ |
52 | struct BOOST_JOIN(is_, class) { \ |
53 | template<typename Ch> \ |
54 | static bool test(Ch event, const std::locale& loc) \ |
55 | { return std::BOOST_JOIN(is, class)(event, loc); } \ |
56 | }; \ |
57 | /**/ |
58 | |
59 | BOOST_IOSTREAMS_CHARACTER_CLASS(alnum) |
60 | BOOST_IOSTREAMS_CHARACTER_CLASS(alpha) |
61 | BOOST_IOSTREAMS_CHARACTER_CLASS(cntrl) |
62 | BOOST_IOSTREAMS_CHARACTER_CLASS(digit) |
63 | BOOST_IOSTREAMS_CHARACTER_CLASS(graph) |
64 | BOOST_IOSTREAMS_CHARACTER_CLASS(lower) |
65 | BOOST_IOSTREAMS_CHARACTER_CLASS(print) |
66 | BOOST_IOSTREAMS_CHARACTER_CLASS(punct) |
67 | BOOST_IOSTREAMS_CHARACTER_CLASS(space) |
68 | BOOST_IOSTREAMS_CHARACTER_CLASS(upper) |
69 | BOOST_IOSTREAMS_CHARACTER_CLASS(xdigit) |
70 | |
71 | #undef BOOST_IOSTREAMS_CHARACTER_CLASS |
72 | }; |
73 | |
74 | template<typename Ch> |
75 | struct finite_state_machine_base_ex : finite_state_machine_base { |
76 | template<Ch C> |
77 | struct is { |
78 | static bool test(Ch event, const std::locale&) |
79 | { |
80 | return event == C; |
81 | } |
82 | }; |
83 | }; |
84 | |
85 | //------------------Definition of base class for finite state filters---------// |
86 | |
87 | namespace detail { |
88 | |
89 | template<typename FiniteStateMachine> |
90 | class finite_state_filter_impl; |
91 | |
92 | } // End namespace detail. |
93 | |
94 | template<typename Derived, typename Ch = char> |
95 | class finite_state_machine : public finite_state_machine_base_ex<Ch> { |
96 | public: |
97 | typedef Ch char_type; |
98 | typedef typename char_traits<Ch>::int_type int_type; |
99 | void imbue(const std::locale& loc) { loc_ = loc; } |
100 | const std::locale& getloc() const { return loc_; } |
101 | protected: |
102 | finite_state_machine() : off_(0) { } |
103 | |
104 | // Template whose instantiations make up transition table. |
105 | |
106 | template< int State, |
107 | typename CharacterClass, |
108 | int NextState, |
109 | void (Derived::*Action)(char_type) > |
110 | struct row { |
111 | typedef CharacterClass character_class; |
112 | static const int state = State; |
113 | static const int next_state = NextState; |
114 | static void execute(Derived& d, char_type event) |
115 | { |
116 | (d.*Action)(event); |
117 | } |
118 | }; |
119 | |
120 | // Stack interface. |
121 | |
122 | bool empty() const |
123 | { |
124 | return off_ == buf_.size(); |
125 | } |
126 | void push(char c) { buf_ += c; } |
127 | char_type pop() |
128 | { |
129 | char_type result = buf_[off_++]; |
130 | if (off_ == buf_.size()) |
131 | clear(); |
132 | return result; |
133 | } |
134 | char_type& top() { return buf_[off_]; } |
135 | void clear() |
136 | { |
137 | buf_.clear(); |
138 | off_ = 0; |
139 | } |
140 | |
141 | // Default event handlers. |
142 | |
143 | void on_eof() { } |
144 | void skip(char_type) { } |
145 | |
146 | #if BOOST_WORKAROUND(__MWERKS__, <= 0x3206) |
147 | template<typename Ch> |
148 | void _push_impl(Ch c) { push(c); } |
149 | #endif |
150 | |
151 | #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS |
152 | template<typename FiniteStateFilter> |
153 | friend class detail::finite_state_filter_impl; |
154 | #else |
155 | public: |
156 | #endif |
157 | void on_any(char_type) { } |
158 | private: |
159 | typedef std::basic_string<char_type> string_type; |
160 | typedef typename string_type::size_type size_type; |
161 | std::locale loc_; |
162 | string_type buf_; |
163 | size_type off_; |
164 | }; |
165 | |
166 | #if !BOOST_WORKAROUND(__MWERKS__, <= 0x3206) |
167 | # define BOOST_IOSTREAMS_FSM(fsm) \ |
168 | template<typename Ch> \ |
169 | void push(Ch c) \ |
170 | { ::boost::iostreams::finite_state_machine<fsm, Ch>::push(c); } \ |
171 | template<typename Ch> \ |
172 | void skip(Ch c) { (void) c; } \ |
173 | /**/ |
174 | #else // #ifndef __MWERKS__ |
175 | # define BOOST_IOSTREAMS_FSM(fsm) \ |
176 | void push(char c) { this->_push_impl(c); } \ |
177 | void push(wchar_t c) { this->_push_impl(c); } \ |
178 | void skip(char c) { (void) c; } \ |
179 | void skip(wchar_t c) { (void) c; } \ |
180 | /**/ |
181 | #endif |
182 | |
183 | //------------------Definition of finite_state_filter_impl--------------------// |
184 | |
185 | namespace detail { |
186 | |
187 | template<typename FiniteStateMachine> |
188 | class finite_state_filter_impl : protected FiniteStateMachine |
189 | { |
190 | private: |
191 | template<typename First, typename Last> |
192 | struct process_event_impl; |
193 | public: |
194 | typedef typename char_type_of<FiniteStateMachine>::type char_type; |
195 | |
196 | finite_state_filter_impl() : state_(FiniteStateMachine::initial_state) { } |
197 | |
198 | template<typename T0> |
199 | explicit finite_state_filter_impl(const T0& t0) |
200 | : FiniteStateMachine(t0), state_(FiniteStateMachine::initial_state) |
201 | { } |
202 | |
203 | template<typename T0, typename T1> |
204 | finite_state_filter_impl(const T0& t0, const T1& t1) |
205 | : FiniteStateMachine(t0, t1), state_(FiniteStateMachine::initial_state) |
206 | { } |
207 | |
208 | template<typename T0, typename T1, typename T2> |
209 | finite_state_filter_impl(const T0& t0, const T1& t1, const T2& t2) |
210 | : FiniteStateMachine(t0, t1, t2), |
211 | state_(FiniteStateMachine::initial_state) |
212 | { } |
213 | protected: |
214 | void process_event(char_type c) |
215 | { |
216 | typedef typename FiniteStateMachine::transition_table transitions; |
217 | typedef typename mpl::begin<transitions>::type first; |
218 | typedef typename mpl::end<transitions>::type last; |
219 | state_ = process_event_impl<first, last>::execute(*this, state_, c); |
220 | } |
221 | int& state() { return state_; } |
222 | void reset() |
223 | { |
224 | state_ = FiniteStateMachine::initial_state; |
225 | this->clear(); |
226 | } |
227 | private: |
228 | template<typename First, typename Last> |
229 | struct process_event_impl { |
230 | static int execute(FiniteStateMachine& fsm, int state, char_type event) |
231 | { |
232 | typedef typename mpl::deref<First>::type rule; |
233 | typedef typename mpl::next<First>::type next; |
234 | typedef typename rule::character_class character_class; |
235 | |
236 | if ( state == rule::state && |
237 | character_class::test(event, fsm.getloc()) ) |
238 | { |
239 | // Rule applies. |
240 | rule::execute(fsm, event); |
241 | return rule::next_state; |
242 | } |
243 | |
244 | // Rule is inapplicable: try next rule. |
245 | return process_event_impl<next, Last>::execute(fsm, state, event); |
246 | } |
247 | }; |
248 | |
249 | template<typename Last> |
250 | struct process_event_impl<Last, Last> { |
251 | static int execute(FiniteStateMachine& fsm, int state, char_type c) |
252 | { |
253 | on_any(fsm, c); |
254 | return state; |
255 | } |
256 | }; |
257 | #if BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042)) /* Tru64 */ \ |
258 | || BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3205)) /* CW9.4 */ |
259 | public: |
260 | #endif |
261 | template<typename FSM> |
262 | static void on_any(FSM& fsm, char_type c) { fsm.on_any(c); } |
263 | private: |
264 | int state_; |
265 | }; |
266 | |
267 | } // End namespace detail. |
268 | |
269 | //------------------Definition of finite_state_filter-------------------------// |
270 | |
271 | template<typename FiniteStateMachine> |
272 | class finite_state_filter |
273 | : public detail::finite_state_filter_impl<FiniteStateMachine> |
274 | { |
275 | private: |
276 | typedef detail::finite_state_filter_impl<FiniteStateMachine> base_type; |
277 | public: |
278 | typedef typename base_type::char_type char_type; |
279 | typedef char_traits<char_type> traits_type; |
280 | typedef typename base_type::int_type int_type; |
281 | struct category |
282 | : dual_use, filter_tag, closable_tag, localizable_tag |
283 | { }; |
284 | |
285 | finite_state_filter() : flags_(0) { } |
286 | |
287 | template<typename T0> |
288 | finite_state_filter(const T0& t0) |
289 | : base_type(t0), flags_(0) |
290 | { } |
291 | |
292 | template<typename T0, typename T1> |
293 | finite_state_filter(const T0& t0, const T1& t1) |
294 | : base_type(t0, t1), flags_(0) |
295 | { } |
296 | |
297 | template<typename T0, typename T1, typename T2> |
298 | finite_state_filter(const T0& t0, const T1& t1, const T2& t2) |
299 | : base_type(t0, t1, t2), flags_(0) |
300 | { } |
301 | |
302 | template<typename Source> |
303 | int_type get(Source& src) |
304 | { |
305 | assert((flags_ & f_write) == 0); |
306 | flags_ |= f_read; |
307 | |
308 | while (true) { |
309 | if ((flags_ & f_eof) == 0) { |
310 | |
311 | // Read a character and process it. |
312 | int_type c; |
313 | if (traits_type::is_eof(c = iostreams::get(src))) { |
314 | flags_ |= f_eof; |
315 | this->on_eof(); |
316 | } else if (!traits_type::would_block(c)) { |
317 | this->process_event(c); |
318 | } |
319 | } |
320 | |
321 | // Return a character, if available. |
322 | if (!this->empty()) |
323 | return this->pop(); |
324 | else if ((flags_ & f_eof) != 0) |
325 | return traits_type::eof(); |
326 | } |
327 | } |
328 | |
329 | template<typename Sink> |
330 | bool put(Sink& dest, char_type c) |
331 | { |
332 | assert((flags_ & f_read) == 0); |
333 | flags_ |= f_write; |
334 | |
335 | this->process_event(c); |
336 | while (!this->empty() && iostreams::put(dest, this->top())) |
337 | this->pop(); |
338 | |
339 | return true; |
340 | } |
341 | |
342 | template<typename Device> |
343 | void close(Device& dev, BOOST_IOS::openmode which) |
344 | { |
345 | if (which == BOOST_IOS::out) { |
346 | if (flags_ & f_write) |
347 | while (!this->empty()) |
348 | iostreams::put_if(dev, this->pop()); |
349 | this->reset(); |
350 | flags_ = 0; |
351 | } |
352 | } |
353 | private: |
354 | enum flags { |
355 | f_read = 1, |
356 | f_write = f_read << 1, |
357 | f_eof = f_write << 1 |
358 | }; |
359 | |
360 | int flags_; |
361 | }; |
362 | |
363 | } } // End namespaces iostreams, boost. |
364 | |
365 | #endif // #ifndef BOOST_IOSTREAMS_FINITE_STATE_FILTER_HPP_INCLUDED |
366 | |