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