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