symmetric.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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. // See http://www.boost.org/libs/iostreams for documentation.
  6. // Contains the definitions of the class templates symmetric_filter,
  7. // which models DualUseFilter based on a model of the Symmetric Filter.
  8. //
  9. // Roughly, a Symmetric Filter is a class type with the following interface:
  10. //
  11. // struct symmetric_filter {
  12. // typedef xxx char_type;
  13. //
  14. // bool filter( const char*& begin_in, const char* end_in,
  15. // char*& begin_out, char* end_out, bool flush )
  16. // {
  17. // // Consume as many characters as possible from the interval
  18. // // [begin_in, end_in), without exhausting the output range
  19. // // [begin_out, end_out). If flush is true, write as mush output
  20. // // as possible.
  21. // // A return value of true indicates that filter should be called
  22. // // again. More precisely, if flush is false, a return value of
  23. // // false indicates that the natural end of stream has been reached
  24. // // and that all filtered data has been forwarded; if flush is
  25. // // true, a return value of false indicates that all filtered data
  26. // // has been forwarded.
  27. // }
  28. // void close() { /* Reset filter's state. */ }
  29. // };
  30. //
  31. // Symmetric Filter filters need not be CopyConstructable.
  32. //
  33. #ifndef BOOST_IOSTREAMS_SYMMETRIC_FILTER_HPP_INCLUDED
  34. #define BOOST_IOSTREAMS_SYMMETRIC_FILTER_HPP_INCLUDED
  35. #if defined(_MSC_VER)
  36. # pragma once
  37. #endif
  38. #include <boost/assert.hpp>
  39. #include <memory> // allocator.
  40. #include <boost/config.hpp> // BOOST_DEDUCED_TYPENAME.
  41. #include <boost/iostreams/char_traits.hpp>
  42. #include <boost/iostreams/constants.hpp> // buffer size.
  43. #include <boost/iostreams/detail/buffer.hpp>
  44. #include <boost/iostreams/detail/char_traits.hpp>
  45. #include <boost/iostreams/detail/config/limits.hpp>
  46. #include <boost/iostreams/detail/ios.hpp> // streamsize.
  47. #include <boost/iostreams/detail/template_params.hpp>
  48. #include <boost/iostreams/traits.hpp>
  49. #include <boost/iostreams/operations.hpp> // read, write.
  50. #include <boost/iostreams/pipeline.hpp>
  51. #include <boost/preprocessor/iteration/local.hpp>
  52. #include <boost/preprocessor/punctuation/comma_if.hpp>
  53. #include <boost/preprocessor/repetition/enum_binary_params.hpp>
  54. #include <boost/preprocessor/repetition/enum_params.hpp>
  55. #include <boost/shared_ptr.hpp>
  56. // Must come last.
  57. #include <boost/iostreams/detail/config/disable_warnings.hpp> // MSVC.
  58. namespace boost { namespace iostreams {
  59. template< typename SymmetricFilter,
  60. typename Alloc =
  61. std::allocator<
  62. BOOST_DEDUCED_TYPENAME char_type_of<SymmetricFilter>::type
  63. > >
  64. class symmetric_filter {
  65. public:
  66. typedef typename char_type_of<SymmetricFilter>::type char_type;
  67. typedef BOOST_IOSTREAMS_CHAR_TRAITS(char_type) traits_type;
  68. typedef std::basic_string<char_type, traits_type, Alloc> string_type;
  69. struct category
  70. : dual_use,
  71. filter_tag,
  72. multichar_tag,
  73. closable_tag
  74. { };
  75. // Expands to a sequence of ctors which forward to impl.
  76. #define BOOST_PP_LOCAL_MACRO(n) \
  77. BOOST_IOSTREAMS_TEMPLATE_PARAMS(n, T) \
  78. explicit symmetric_filter( \
  79. std::streamsize buffer_size BOOST_PP_COMMA_IF(n) \
  80. BOOST_PP_ENUM_BINARY_PARAMS(n, const T, &t) ) \
  81. : pimpl_(new impl(buffer_size BOOST_PP_COMMA_IF(n) \
  82. BOOST_PP_ENUM_PARAMS(n, t))) \
  83. { BOOST_ASSERT(buffer_size > 0); } \
  84. /**/
  85. #define BOOST_PP_LOCAL_LIMITS (0, BOOST_IOSTREAMS_MAX_FORWARDING_ARITY)
  86. #include BOOST_PP_LOCAL_ITERATE()
  87. #undef BOOST_PP_LOCAL_MACRO
  88. template<typename Source>
  89. std::streamsize read(Source& src, char_type* s, std::streamsize n)
  90. {
  91. using namespace std;
  92. if (!(state() & f_read))
  93. begin_read();
  94. buffer_type& buf = pimpl_->buf_;
  95. int status = (state() & f_eof) != 0 ? f_eof : f_good;
  96. char_type *next_s = s,
  97. *end_s = s + n;
  98. while (true)
  99. {
  100. // Invoke filter if there are unconsumed characters in buffer or if
  101. // filter must be flushed.
  102. bool flush = status == f_eof;
  103. if (buf.ptr() != buf.eptr() || flush) {
  104. const char_type* next = buf.ptr();
  105. bool done =
  106. !filter().filter(next, buf.eptr(), next_s, end_s, flush);
  107. buf.ptr() = buf.data() + (next - buf.data());
  108. if (done)
  109. return detail::check_eof(
  110. static_cast<std::streamsize>(next_s - s)
  111. );
  112. }
  113. // If no more characters are available without blocking, or
  114. // if read request has been satisfied, return.
  115. if ( (status == f_would_block && buf.ptr() == buf.eptr()) ||
  116. next_s == end_s )
  117. {
  118. return static_cast<std::streamsize>(next_s - s);
  119. }
  120. // Fill buffer.
  121. if (status == f_good)
  122. status = fill(src);
  123. }
  124. }
  125. template<typename Sink>
  126. std::streamsize write(Sink& snk, const char_type* s, std::streamsize n)
  127. {
  128. if (!(state() & f_write))
  129. begin_write();
  130. buffer_type& buf = pimpl_->buf_;
  131. const char_type *next_s, *end_s;
  132. for (next_s = s, end_s = s + n; next_s != end_s; ) {
  133. if (buf.ptr() == buf.eptr() && !flush(snk))
  134. break;
  135. if(!filter().filter(next_s, end_s, buf.ptr(), buf.eptr(), false)) {
  136. flush(snk);
  137. break;
  138. }
  139. }
  140. return static_cast<std::streamsize>(next_s - s);
  141. }
  142. template<typename Sink>
  143. void close(Sink& snk, BOOST_IOS::openmode mode)
  144. {
  145. if (mode == BOOST_IOS::out) {
  146. if (!(state() & f_write))
  147. begin_write();
  148. // Repeatedly invoke filter() with no input.
  149. try {
  150. buffer_type& buf = pimpl_->buf_;
  151. char_type dummy;
  152. const char_type* end = &dummy;
  153. bool again = true;
  154. while (again) {
  155. if (buf.ptr() != buf.eptr())
  156. again = filter().filter( end, end, buf.ptr(),
  157. buf.eptr(), true );
  158. flush(snk);
  159. }
  160. } catch (...) {
  161. try { close_impl(); } catch (...) { }
  162. throw;
  163. }
  164. close_impl();
  165. } else {
  166. close_impl();
  167. }
  168. }
  169. SymmetricFilter& filter() { return *pimpl_; }
  170. string_type unconsumed_input() const;
  171. // Give impl access to buffer_type on Tru64
  172. #if !BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042))
  173. private:
  174. #endif
  175. typedef detail::buffer<char_type, Alloc> buffer_type;
  176. private:
  177. buffer_type& buf() { return pimpl_->buf_; }
  178. const buffer_type& buf() const { return pimpl_->buf_; }
  179. int& state() { return pimpl_->state_; }
  180. void begin_read();
  181. void begin_write();
  182. template<typename Source>
  183. int fill(Source& src)
  184. {
  185. std::streamsize amt = iostreams::read(src, buf().data(), buf().size());
  186. if (amt == -1) {
  187. state() |= f_eof;
  188. return f_eof;
  189. }
  190. buf().set(0, amt);
  191. return amt != 0 ? f_good : f_would_block;
  192. }
  193. // Attempts to write the contents of the buffer the given Sink.
  194. // Returns true if at least on character was written.
  195. template<typename Sink>
  196. bool flush(Sink& snk)
  197. {
  198. typedef typename iostreams::category_of<Sink>::type category;
  199. typedef is_convertible<category, output> can_write;
  200. return flush(snk, can_write());
  201. }
  202. template<typename Sink>
  203. bool flush(Sink& snk, mpl::true_)
  204. {
  205. std::streamsize amt =
  206. static_cast<std::streamsize>(buf().ptr() - buf().data());
  207. std::streamsize result =
  208. boost::iostreams::write(snk, buf().data(), amt);
  209. if (result < amt && result > 0)
  210. traits_type::move(buf().data(), buf().data() + result, amt - result);
  211. buf().set(amt - result, buf().size());
  212. return result != 0;
  213. }
  214. template<typename Sink>
  215. bool flush(Sink&, mpl::false_) { return true;}
  216. void close_impl();
  217. enum flag_type {
  218. f_read = 1,
  219. f_write = f_read << 1,
  220. f_eof = f_write << 1,
  221. f_good,
  222. f_would_block
  223. };
  224. struct impl : SymmetricFilter {
  225. // Expands to a sequence of ctors which forward to SymmetricFilter.
  226. #define BOOST_PP_LOCAL_MACRO(n) \
  227. BOOST_IOSTREAMS_TEMPLATE_PARAMS(n, T) \
  228. impl( std::streamsize buffer_size BOOST_PP_COMMA_IF(n) \
  229. BOOST_PP_ENUM_BINARY_PARAMS(n, const T, &t) ) \
  230. : SymmetricFilter(BOOST_PP_ENUM_PARAMS(n, t)), \
  231. buf_(buffer_size), state_(0) \
  232. { } \
  233. /**/
  234. #define BOOST_PP_LOCAL_LIMITS (0, BOOST_IOSTREAMS_MAX_FORWARDING_ARITY)
  235. #include BOOST_PP_LOCAL_ITERATE()
  236. #undef BOOST_PP_LOCAL_MACRO
  237. buffer_type buf_;
  238. int state_;
  239. };
  240. shared_ptr<impl> pimpl_;
  241. };
  242. BOOST_IOSTREAMS_PIPABLE(symmetric_filter, 2)
  243. //------------------Implementation of symmetric_filter----------------//
  244. template<typename SymmetricFilter, typename Alloc>
  245. void symmetric_filter<SymmetricFilter, Alloc>::begin_read()
  246. {
  247. BOOST_ASSERT(!(state() & f_write));
  248. state() |= f_read;
  249. buf().set(0, 0);
  250. }
  251. template<typename SymmetricFilter, typename Alloc>
  252. void symmetric_filter<SymmetricFilter, Alloc>::begin_write()
  253. {
  254. BOOST_ASSERT(!(state() & f_read));
  255. state() |= f_write;
  256. buf().set(0, buf().size());
  257. }
  258. template<typename SymmetricFilter, typename Alloc>
  259. void symmetric_filter<SymmetricFilter, Alloc>::close_impl()
  260. {
  261. state() = 0;
  262. buf().set(0, 0);
  263. filter().close();
  264. }
  265. template<typename SymmetricFilter, typename Alloc>
  266. typename symmetric_filter<SymmetricFilter, Alloc>::string_type
  267. symmetric_filter<SymmetricFilter, Alloc>::unconsumed_input() const
  268. { return string_type(buf().ptr(), buf().eptr()); }
  269. //----------------------------------------------------------------------------//
  270. } } // End namespaces iostreams, boost.
  271. #include <boost/iostreams/detail/config/enable_warnings.hpp> // MSVC.
  272. #endif // #ifndef BOOST_IOSTREAMS_SYMMETRIC_FILTER_HPP_INCLUDED