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 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>
testboost::iostreams::finite_state_machine_base::is_any46         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 {
testboost::iostreams::finite_state_machine_base_ex::is78         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;
imbue(const std::locale & loc)99     void imbue(const std::locale& loc) { loc_ = loc; }
getloc() const100     const std::locale& getloc() const { return loc_; }
101 protected:
finite_state_machine()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;
executeboost::iostreams::finite_state_machine::row114         static void execute(Derived& d, char_type event)
115         {
116             (d.*Action)(event);
117         }
118     };
119 
120     // Stack interface.
121 
empty() const122     bool empty() const
123     {
124         return off_ == buf_.size();
125     }
push(char c)126     void push(char c) { buf_ += c; }
pop()127     char_type pop()
128     {
129         char_type result = buf_[off_++];
130         if (off_ == buf_.size())
131             clear();
132         return result;
133     }
top()134     char_type& top() { return buf_[off_]; }
clear()135     void clear()
136     {
137         buf_.clear();
138         off_ = 0;
139     }
140 
141     // Default event handlers.
142 
on_eof()143     void on_eof() { }
skip(char_type)144     void skip(char_type) { }
145 
146 #if BOOST_WORKAROUND(__MWERKS__, <= 0x3206)
147     template<typename Ch>
_push_impl(Ch c)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
on_any(char_type)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 
finite_state_filter_impl()196     finite_state_filter_impl() : state_(FiniteStateMachine::initial_state) { }
197 
198     template<typename T0>
finite_state_filter_impl(const T0 & 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>
finite_state_filter_impl(const T0 & t0,const T1 & 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>
finite_state_filter_impl(const T0 & t0,const T1 & t1,const T2 & 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:
process_event(char_type c)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     }
state()221     int& state() { return state_; }
reset()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 {
executeboost::iostreams::detail::finite_state_filter_impl::process_event_impl230         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> {
executeboost::iostreams::detail::finite_state_filter_impl::process_event_impl251         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>
on_any(FSM & fsm,char_type c)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 
finite_state_filter()285     finite_state_filter() : flags_(0) { }
286 
287     template<typename T0>
finite_state_filter(const T0 & t0)288     finite_state_filter(const T0& t0)
289         : base_type(t0), flags_(0)
290         { }
291 
292     template<typename T0, typename T1>
finite_state_filter(const T0 & t0,const T1 & 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>
finite_state_filter(const T0 & t0,const T1 & t1,const T2 & 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>
get(Source & src)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>
put(Sink & dest,char_type c)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>
close(Device & dev,BOOST_IOS::openmode which)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