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