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