chain.hpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  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. #ifndef BOOST_IOSTREAMS_DETAIL_CHAIN_HPP_INCLUDED
  7. #define BOOST_IOSTREAMS_DETAIL_CHAIN_HPP_INCLUDED
  8. #if defined(_MSC_VER)
  9. # pragma once
  10. #endif
  11. #include <boost/assert.hpp>
  12. #include <exception>
  13. #include <iterator> // advance.
  14. #include <list>
  15. #include <memory> // allocator, auto_ptr or unique_ptr.
  16. #include <stdexcept> // logic_error, out_of_range.
  17. #include <boost/checked_delete.hpp>
  18. #include <boost/config.hpp> // BOOST_MSVC, template friends,
  19. #include <boost/detail/workaround.hpp> // BOOST_NESTED_TEMPLATE
  20. #include <boost/core/typeinfo.hpp>
  21. #include <boost/iostreams/constants.hpp>
  22. #include <boost/iostreams/detail/access_control.hpp>
  23. #include <boost/iostreams/detail/char_traits.hpp>
  24. #include <boost/iostreams/detail/push.hpp>
  25. #include <boost/iostreams/detail/streambuf.hpp> // pubsync.
  26. #include <boost/iostreams/detail/wrap_unwrap.hpp>
  27. #include <boost/iostreams/device/null.hpp>
  28. #include <boost/iostreams/positioning.hpp>
  29. #include <boost/iostreams/traits.hpp> // is_filter.
  30. #include <boost/iostreams/stream_buffer.hpp>
  31. #include <boost/next_prior.hpp>
  32. #include <boost/shared_ptr.hpp>
  33. #include <boost/static_assert.hpp>
  34. #include <boost/throw_exception.hpp>
  35. #include <boost/type_traits/is_convertible.hpp>
  36. #include <boost/type.hpp>
  37. #include <boost/iostreams/detail/execute.hpp>
  38. // Sometimes type_info objects must be compared by name. Borrowed from
  39. // Boost.Python and Boost.Function.
  40. #if defined(__GNUC__) || \
  41. defined(_AIX) || \
  42. (defined(__sgi) && defined(__host_mips)) || \
  43. (defined(linux) && defined(__INTEL_COMPILER) && defined(__ICC)) \
  44. /**/
  45. # include <cstring>
  46. # define BOOST_IOSTREAMS_COMPARE_TYPE_ID(X,Y) \
  47. (std::strcmp((X).name(),(Y).name()) == 0)
  48. #else
  49. # define BOOST_IOSTREAMS_COMPARE_TYPE_ID(X,Y) ((X)==(Y))
  50. #endif
  51. // Deprecated. Unused.
  52. #define BOOST_IOSTREAMS_COMPONENT_TYPE(chain, index) \
  53. chain.component_type( index ) \
  54. /**/
  55. // Deprecated. Unused.
  56. #define BOOST_IOSTREAMS_COMPONENT(chain, index, target) \
  57. chain.component< target >( index ) \
  58. /**/
  59. namespace boost { namespace iostreams {
  60. //--------------Definition of chain and wchain--------------------------------//
  61. namespace detail {
  62. template<typename Chain> class chain_client;
  63. //
  64. // Concept name: Chain.
  65. // Description: Represents a chain of stream buffers which provides access
  66. // to the first buffer in the chain and sends notifications when the
  67. // streambufs are added to or removed from chain.
  68. // Refines: Closable device with mode equal to typename Chain::mode.
  69. // Models: chain, converting_chain.
  70. // Example:
  71. //
  72. // class chain {
  73. // public:
  74. // typedef xxx chain_type;
  75. // typedef xxx client_type;
  76. // typedef xxx mode;
  77. // bool is_complete() const; // Ready for i/o.
  78. // template<typename T>
  79. // void push( const T& t, // Adds a stream buffer to
  80. // streamsize, // chain, based on t, with
  81. // streamsize ); // given buffer and putback
  82. // // buffer sizes. Pass -1 to
  83. // // request default size.
  84. // protected:
  85. // void register_client(client_type* client); // Associate client.
  86. // void notify(); // Notify client.
  87. // };
  88. //
  89. //
  90. // Description: Represents a chain of filters with an optional device at the
  91. // end.
  92. // Template parameters:
  93. // Self - A class deriving from the current instantiation of this template.
  94. // This is an example of the Curiously Recurring Template Pattern.
  95. // Ch - The character type.
  96. // Tr - The character traits type.
  97. // Alloc - The allocator type.
  98. // Mode - A mode tag.
  99. //
  100. template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
  101. class chain_base {
  102. public:
  103. typedef Ch char_type;
  104. BOOST_IOSTREAMS_STREAMBUF_TYPEDEFS(Tr)
  105. typedef Alloc allocator_type;
  106. typedef Mode mode;
  107. struct category
  108. : Mode,
  109. device_tag
  110. { };
  111. typedef chain_client<Self> client_type;
  112. friend class chain_client<Self>;
  113. private:
  114. typedef linked_streambuf<Ch> streambuf_type;
  115. typedef std::list<streambuf_type*> list_type;
  116. typedef chain_base<Self, Ch, Tr, Alloc, Mode> my_type;
  117. protected:
  118. chain_base() : pimpl_(new chain_impl) { }
  119. chain_base(const chain_base& rhs): pimpl_(rhs.pimpl_) { }
  120. public:
  121. // dual_use is a pseudo-mode to facilitate filter writing,
  122. // not a genuine mode.
  123. BOOST_STATIC_ASSERT((!is_convertible<mode, dual_use>::value));
  124. //----------Buffer sizing-------------------------------------------------//
  125. // Sets the size of the buffer created for the devices to be added to this
  126. // chain. Does not affect the size of the buffer for devices already
  127. // added.
  128. void set_device_buffer_size(std::streamsize n)
  129. { pimpl_->device_buffer_size_ = n; }
  130. // Sets the size of the buffer created for the filters to be added
  131. // to this chain. Does not affect the size of the buffer for filters already
  132. // added.
  133. void set_filter_buffer_size(std::streamsize n)
  134. { pimpl_->filter_buffer_size_ = n; }
  135. // Sets the size of the putback buffer for filters and devices to be added
  136. // to this chain. Does not affect the size of the buffer for filters or
  137. // devices already added.
  138. void set_pback_size(std::streamsize n)
  139. { pimpl_->pback_size_ = n; }
  140. //----------Device interface----------------------------------------------//
  141. std::streamsize read(char_type* s, std::streamsize n);
  142. std::streamsize write(const char_type* s, std::streamsize n);
  143. std::streampos seek(stream_offset off, BOOST_IOS::seekdir way);
  144. //----------Direct component access---------------------------------------//
  145. const boost::core::typeinfo& component_type(int n) const
  146. {
  147. if (static_cast<size_type>(n) >= size())
  148. boost::throw_exception(std::out_of_range("bad chain offset"));
  149. return (*boost::next(list().begin(), n))->component_type();
  150. }
  151. // Deprecated.
  152. template<int N>
  153. const boost::core::typeinfo& component_type() const { return component_type(N); }
  154. template<typename T>
  155. T* component(int n) const { return component(n, boost::type<T>()); }
  156. // Deprecated.
  157. template<int N, typename T>
  158. T* component() const { return component<T>(N); }
  159. #if !BOOST_WORKAROUND(BOOST_MSVC, == 1310)
  160. private:
  161. #endif
  162. template<typename T>
  163. T* component(int n, boost::type<T>) const
  164. {
  165. if (static_cast<size_type>(n) >= size())
  166. boost::throw_exception(std::out_of_range("bad chain offset"));
  167. streambuf_type* link = *boost::next(list().begin(), n);
  168. if (BOOST_IOSTREAMS_COMPARE_TYPE_ID(link->component_type(), BOOST_CORE_TYPEID(T)))
  169. return static_cast<T*>(link->component_impl());
  170. else
  171. return 0;
  172. }
  173. public:
  174. //----------Container-like interface--------------------------------------//
  175. typedef typename list_type::size_type size_type;
  176. streambuf_type& front() { return *list().front(); }
  177. BOOST_IOSTREAMS_DEFINE_PUSH(push, mode, char_type, push_impl)
  178. void pop();
  179. bool empty() const { return list().empty(); }
  180. size_type size() const { return list().size(); }
  181. void reset();
  182. //----------Additional i/o functions--------------------------------------//
  183. // Returns true if this chain is non-empty and its final link
  184. // is a source or sink, i.e., if it is ready to perform i/o.
  185. bool is_complete() const;
  186. bool auto_close() const;
  187. void set_auto_close(bool close);
  188. bool sync() { return front().BOOST_IOSTREAMS_PUBSYNC() != -1; }
  189. bool strict_sync();
  190. private:
  191. template<typename T>
  192. void push_impl(const T& t, std::streamsize buffer_size = -1,
  193. std::streamsize pback_size = -1)
  194. {
  195. typedef typename iostreams::category_of<T>::type category;
  196. typedef typename unwrap_ios<T>::type component_type;
  197. typedef stream_buffer<
  198. component_type,
  199. BOOST_IOSTREAMS_CHAR_TRAITS(char_type),
  200. Alloc, Mode
  201. > streambuf_t;
  202. typedef typename list_type::iterator iterator;
  203. BOOST_STATIC_ASSERT((is_convertible<category, Mode>::value));
  204. if (is_complete())
  205. boost::throw_exception(std::logic_error("chain complete"));
  206. streambuf_type* prev = !empty() ? list().back() : 0;
  207. buffer_size =
  208. buffer_size != -1 ?
  209. buffer_size :
  210. iostreams::optimal_buffer_size(t);
  211. pback_size =
  212. pback_size != -1 ?
  213. pback_size :
  214. pimpl_->pback_size_;
  215. #if defined(BOOST_NO_CXX11_SMART_PTR)
  216. std::auto_ptr<streambuf_t>
  217. buf(new streambuf_t(t, buffer_size, pback_size));
  218. #else
  219. std::unique_ptr<streambuf_t>
  220. buf(new streambuf_t(t, buffer_size, pback_size));
  221. #endif
  222. list().push_back(buf.get());
  223. buf.release();
  224. if (is_device<component_type>::value) {
  225. pimpl_->flags_ |= f_complete | f_open;
  226. for ( iterator first = list().begin(),
  227. last = list().end();
  228. first != last;
  229. ++first )
  230. {
  231. (*first)->set_needs_close();
  232. }
  233. }
  234. if (prev) prev->set_next(list().back());
  235. notify();
  236. }
  237. list_type& list() { return pimpl_->links_; }
  238. const list_type& list() const { return pimpl_->links_; }
  239. void register_client(client_type* client) { pimpl_->client_ = client; }
  240. void notify() { if (pimpl_->client_) pimpl_->client_->notify(); }
  241. //----------Nested classes------------------------------------------------//
  242. static void close(streambuf_type* b, BOOST_IOS::openmode m)
  243. {
  244. if (m == BOOST_IOS::out && is_convertible<Mode, output>::value)
  245. b->BOOST_IOSTREAMS_PUBSYNC();
  246. b->close(m);
  247. }
  248. static void set_next(streambuf_type* b, streambuf_type* next)
  249. { b->set_next(next); }
  250. static void set_auto_close(streambuf_type* b, bool close)
  251. { b->set_auto_close(close); }
  252. struct closer {
  253. typedef streambuf_type* argument_type;
  254. typedef void result_type;
  255. closer(BOOST_IOS::openmode m) : mode_(m) { }
  256. void operator() (streambuf_type* b)
  257. {
  258. close(b, mode_);
  259. }
  260. BOOST_IOS::openmode mode_;
  261. };
  262. friend struct closer;
  263. enum flags {
  264. f_complete = 1,
  265. f_open = 2,
  266. f_auto_close = 4
  267. };
  268. struct chain_impl {
  269. chain_impl()
  270. : client_(0), device_buffer_size_(default_device_buffer_size),
  271. filter_buffer_size_(default_filter_buffer_size),
  272. pback_size_(default_pback_buffer_size),
  273. flags_(f_auto_close)
  274. { }
  275. ~chain_impl()
  276. {
  277. try { close(); } catch (...) { }
  278. try { reset(); } catch (...) { }
  279. }
  280. void close()
  281. {
  282. if ((flags_ & f_open) != 0) {
  283. flags_ &= ~f_open;
  284. stream_buffer< basic_null_device<Ch, Mode> > null;
  285. if ((flags_ & f_complete) == 0) {
  286. null.open(basic_null_device<Ch, Mode>());
  287. set_next(links_.back(), &null);
  288. }
  289. links_.front()->BOOST_IOSTREAMS_PUBSYNC();
  290. try {
  291. boost::iostreams::detail::execute_foreach(
  292. links_.rbegin(), links_.rend(),
  293. closer(BOOST_IOS::in)
  294. );
  295. } catch (...) {
  296. try {
  297. boost::iostreams::detail::execute_foreach(
  298. links_.begin(), links_.end(),
  299. closer(BOOST_IOS::out)
  300. );
  301. } catch (...) { }
  302. throw;
  303. }
  304. boost::iostreams::detail::execute_foreach(
  305. links_.begin(), links_.end(),
  306. closer(BOOST_IOS::out)
  307. );
  308. }
  309. }
  310. void reset()
  311. {
  312. typedef typename list_type::iterator iterator;
  313. for ( iterator first = links_.begin(),
  314. last = links_.end();
  315. first != last;
  316. ++first )
  317. {
  318. if ( (flags_ & f_complete) == 0 ||
  319. (flags_ & f_auto_close) == 0 )
  320. {
  321. set_auto_close(*first, false);
  322. }
  323. streambuf_type* buf = 0;
  324. std::swap(buf, *first);
  325. delete buf;
  326. }
  327. links_.clear();
  328. flags_ &= ~f_complete;
  329. flags_ &= ~f_open;
  330. }
  331. list_type links_;
  332. client_type* client_;
  333. std::streamsize device_buffer_size_,
  334. filter_buffer_size_,
  335. pback_size_;
  336. int flags_;
  337. };
  338. friend struct chain_impl;
  339. //----------Member data---------------------------------------------------//
  340. private:
  341. shared_ptr<chain_impl> pimpl_;
  342. };
  343. } // End namespace detail.
  344. //
  345. // Macro: BOOST_IOSTREAMS_DECL_CHAIN(name, category)
  346. // Description: Defines a template derived from chain_base appropriate for a
  347. // particular i/o category. The template has the following parameters:
  348. // Ch - The character type.
  349. // Tr - The character traits type.
  350. // Alloc - The allocator type.
  351. // Macro parameters:
  352. // name_ - The name of the template to be defined.
  353. // category_ - The i/o category of the template to be defined.
  354. //
  355. #define BOOST_IOSTREAMS_DECL_CHAIN(name_, default_char_) \
  356. template< typename Mode, typename Ch = default_char_, \
  357. typename Tr = BOOST_IOSTREAMS_CHAR_TRAITS(Ch), \
  358. typename Alloc = std::allocator<Ch> > \
  359. class name_ : public boost::iostreams::detail::chain_base< \
  360. name_<Mode, Ch, Tr, Alloc>, \
  361. Ch, Tr, Alloc, Mode \
  362. > \
  363. { \
  364. public: \
  365. struct category : device_tag, Mode { }; \
  366. typedef Mode mode; \
  367. private: \
  368. typedef boost::iostreams::detail::chain_base< \
  369. name_<Mode, Ch, Tr, Alloc>, \
  370. Ch, Tr, Alloc, Mode \
  371. > base_type; \
  372. public: \
  373. typedef Ch char_type; \
  374. typedef Tr traits_type; \
  375. typedef typename traits_type::int_type int_type; \
  376. typedef typename traits_type::off_type off_type; \
  377. name_() { } \
  378. name_(const name_& rhs) : base_type(rhs) { } \
  379. name_& operator=(const name_& rhs) \
  380. { base_type::operator=(rhs); return *this; } \
  381. }; \
  382. /**/
  383. BOOST_IOSTREAMS_DECL_CHAIN(chain, char)
  384. BOOST_IOSTREAMS_DECL_CHAIN(wchain, wchar_t)
  385. #undef BOOST_IOSTREAMS_DECL_CHAIN
  386. //--------------Definition of chain_client------------------------------------//
  387. namespace detail {
  388. //
  389. // Template name: chain_client
  390. // Description: Class whose instances provide access to an underlying chain
  391. // using an interface similar to the chains.
  392. // Subclasses: the various stream and stream buffer templates.
  393. //
  394. template<typename Chain>
  395. class chain_client {
  396. public:
  397. typedef Chain chain_type;
  398. typedef typename chain_type::char_type char_type;
  399. typedef typename chain_type::traits_type traits_type;
  400. typedef typename chain_type::size_type size_type;
  401. typedef typename chain_type::mode mode;
  402. chain_client(chain_type* chn = 0) : chain_(chn ) { }
  403. chain_client(chain_client* client) : chain_(client->chain_) { }
  404. virtual ~chain_client() { }
  405. const boost::core::typeinfo& component_type(int n) const
  406. { return chain_->component_type(n); }
  407. // Deprecated.
  408. template<int N>
  409. const boost::core::typeinfo& component_type() const
  410. { return chain_->BOOST_NESTED_TEMPLATE component_type<N>(); }
  411. template<typename T>
  412. T* component(int n) const
  413. { return chain_->BOOST_NESTED_TEMPLATE component<T>(n); }
  414. // Deprecated.
  415. template<int N, typename T>
  416. T* component() const
  417. { return chain_->BOOST_NESTED_TEMPLATE component<N, T>(); }
  418. bool is_complete() const { return chain_->is_complete(); }
  419. bool auto_close() const { return chain_->auto_close(); }
  420. void set_auto_close(bool close) { chain_->set_auto_close(close); }
  421. bool strict_sync() { return chain_->strict_sync(); }
  422. void set_device_buffer_size(std::streamsize n)
  423. { chain_->set_device_buffer_size(n); }
  424. void set_filter_buffer_size(std::streamsize n)
  425. { chain_->set_filter_buffer_size(n); }
  426. void set_pback_size(std::streamsize n) { chain_->set_pback_size(n); }
  427. BOOST_IOSTREAMS_DEFINE_PUSH(push, mode, char_type, push_impl)
  428. void pop() { chain_->pop(); }
  429. bool empty() const { return chain_->empty(); }
  430. size_type size() const { return chain_->size(); }
  431. void reset() { chain_->reset(); }
  432. // Returns a copy of the underlying chain.
  433. chain_type filters() { return *chain_; }
  434. chain_type filters() const { return *chain_; }
  435. protected:
  436. template<typename T>
  437. void push_impl(const T& t BOOST_IOSTREAMS_PUSH_PARAMS())
  438. { chain_->push(t BOOST_IOSTREAMS_PUSH_ARGS()); }
  439. chain_type& ref() { return *chain_; }
  440. void set_chain(chain_type* c)
  441. { chain_ = c; chain_->register_client(this); }
  442. #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) && \
  443. (!BOOST_WORKAROUND(BOOST_BORLANDC, < 0x600))
  444. template<typename S, typename C, typename T, typename A, typename M>
  445. friend class chain_base;
  446. #else
  447. public:
  448. #endif
  449. virtual void notify() { }
  450. private:
  451. chain_type* chain_;
  452. };
  453. //--------------Implementation of chain_base----------------------------------//
  454. template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
  455. inline std::streamsize chain_base<Self, Ch, Tr, Alloc, Mode>::read
  456. (char_type* s, std::streamsize n)
  457. { return iostreams::read(*list().front(), s, n); }
  458. template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
  459. inline std::streamsize chain_base<Self, Ch, Tr, Alloc, Mode>::write
  460. (const char_type* s, std::streamsize n)
  461. { return iostreams::write(*list().front(), s, n); }
  462. template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
  463. inline std::streampos chain_base<Self, Ch, Tr, Alloc, Mode>::seek
  464. (stream_offset off, BOOST_IOS::seekdir way)
  465. { return iostreams::seek(*list().front(), off, way); }
  466. template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
  467. void chain_base<Self, Ch, Tr, Alloc, Mode>::reset()
  468. {
  469. using namespace std;
  470. pimpl_->close();
  471. pimpl_->reset();
  472. }
  473. template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
  474. bool chain_base<Self, Ch, Tr, Alloc, Mode>::is_complete() const
  475. {
  476. return (pimpl_->flags_ & f_complete) != 0;
  477. }
  478. template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
  479. bool chain_base<Self, Ch, Tr, Alloc, Mode>::auto_close() const
  480. {
  481. return (pimpl_->flags_ & f_auto_close) != 0;
  482. }
  483. template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
  484. void chain_base<Self, Ch, Tr, Alloc, Mode>::set_auto_close(bool close)
  485. {
  486. pimpl_->flags_ =
  487. (pimpl_->flags_ & ~f_auto_close) |
  488. (close ? f_auto_close : 0);
  489. }
  490. template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
  491. bool chain_base<Self, Ch, Tr, Alloc, Mode>::strict_sync()
  492. {
  493. typedef typename list_type::iterator iterator;
  494. bool result = true;
  495. for ( iterator first = list().begin(),
  496. last = list().end();
  497. first != last;
  498. ++first )
  499. {
  500. bool s = (*first)->strict_sync();
  501. result = result && s;
  502. }
  503. return result;
  504. }
  505. template<typename Self, typename Ch, typename Tr, typename Alloc, typename Mode>
  506. void chain_base<Self, Ch, Tr, Alloc, Mode>::pop()
  507. {
  508. BOOST_ASSERT(!empty());
  509. if (auto_close())
  510. pimpl_->close();
  511. streambuf_type* buf = 0;
  512. std::swap(buf, list().back());
  513. buf->set_auto_close(false);
  514. buf->set_next(0);
  515. delete buf;
  516. list().pop_back();
  517. pimpl_->flags_ &= ~f_complete;
  518. if (auto_close() || list().empty())
  519. pimpl_->flags_ &= ~f_open;
  520. }
  521. } // End namespace detail.
  522. } } // End namespaces iostreams, boost.
  523. #endif // #ifndef BOOST_IOSTREAMS_DETAIL_CHAIN_HPP_INCLUDED