| 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 | |