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 
7 // This material is heavily indebted to the discussion and code samples in
8 // A. Langer and K. Kreft, "Standard C++ IOStreams and Locales",
9 // Addison-Wesley, 2000, pp. 228-43.
10 
11 // User "GMSB" provided an optimization for small seeks.
12 
13 #ifndef BOOST_IOSTREAMS_DETAIL_INDIRECT_STREAMBUF_HPP_INCLUDED
14 #define BOOST_IOSTREAMS_DETAIL_INDIRECT_STREAMBUF_HPP_INCLUDED
15 
16 #include <algorithm>                             // min, max.
17 #include <cassert>
18 #include <exception>
19 #include <typeinfo>
20 #include <boost/config.hpp>                      // Member template friends.
21 #include <boost/detail/workaround.hpp>
22 #include <boost/iostreams/constants.hpp>
23 #include <boost/iostreams/detail/adapter/concept_adapter.hpp>
24 #include <boost/iostreams/detail/buffer.hpp>
25 #include <boost/iostreams/detail/config/wide_streams.hpp>
26 #include <boost/iostreams/detail/double_object.hpp>
27 #include <boost/iostreams/detail/execute.hpp>
28 #include <boost/iostreams/detail/functional.hpp>
29 #include <boost/iostreams/detail/ios.hpp>
30 #include <boost/iostreams/detail/optional.hpp>
31 #include <boost/iostreams/detail/push.hpp>
32 #include <boost/iostreams/detail/streambuf/linked_streambuf.hpp>
33 #include <boost/iostreams/operations.hpp>
34 #include <boost/iostreams/positioning.hpp>
35 #include <boost/iostreams/traits.hpp>
36 #include <boost/iostreams/operations.hpp>
37 #include <boost/mpl/if.hpp>
38 #include <boost/throw_exception.hpp>
39 #include <boost/type_traits/is_convertible.hpp>
40 
41 // Must come last.
42 #include <boost/iostreams/detail/config/disable_warnings.hpp>  // MSVC, BCC 5.x
43 
44 namespace boost { namespace iostreams { namespace detail {
45 
46 //
47 // Description: The implementation of basic_streambuf used by chains.
48 //
49 template<typename T, typename Tr, typename Alloc, typename Mode>
50 class indirect_streambuf
51     : public linked_streambuf<BOOST_DEDUCED_TYPENAME char_type_of<T>::type, Tr>
52 {
53 public:
54     typedef typename char_type_of<T>::type                    char_type;
55     BOOST_IOSTREAMS_STREAMBUF_TYPEDEFS(Tr)
56 private:
57     typedef typename category_of<T>::type                     category;
58     typedef concept_adapter<T>                                wrapper;
59     typedef detail::basic_buffer<char_type, Alloc>            buffer_type;
60     typedef indirect_streambuf<T, Tr, Alloc, Mode>            my_type;
61     typedef detail::linked_streambuf<char_type, traits_type>  base_type;
62     typedef linked_streambuf<char_type, Tr>                   streambuf_type;
63 public:
64     indirect_streambuf();
65 
66     void open(const T& t BOOST_IOSTREAMS_PUSH_PARAMS());
67     bool is_open() const;
68     void close();
69     bool auto_close() const;
70     void set_auto_close(bool close);
71     bool strict_sync();
72 
73     // Declared in linked_streambuf.
component()74     T* component() { return &*obj(); }
75 protected:
76 #if !BOOST_WORKAROUND(__GNUC__, == 2)
77     BOOST_IOSTREAMS_USING_PROTECTED_STREAMBUF_MEMBERS(base_type)
78 #endif
79 
80     //----------virtual functions---------------------------------------------//
81 
82 #ifndef BOOST_IOSTREAMS_NO_LOCALE
83     void imbue(const std::locale& loc);
84 #endif
85 #ifdef BOOST_IOSTREAMS_NO_STREAM_TEMPLATES
86     public:
87 #endif
88     int_type underflow();
89     int_type pbackfail(int_type c);
90     int_type overflow(int_type c);
91     int sync();
92     pos_type seekoff( off_type off, BOOST_IOS::seekdir way,
93                       BOOST_IOS::openmode which );
94     pos_type seekpos(pos_type sp, BOOST_IOS::openmode which);
95 
96     // Declared in linked_streambuf.
97     void set_next(streambuf_type* next);
98     void close_impl(BOOST_IOS::openmode m);
component_type() const99     const std::type_info& component_type() const { return typeid(T); }
component_impl()100     void* component_impl() { return component(); }
101 private:
102 
103     //----------Accessor functions--------------------------------------------//
104 
obj()105     wrapper& obj() { return *storage_; }
next() const106     streambuf_type* next() const { return next_; }
in()107     buffer_type& in() { return buffer_.first(); }
out()108     buffer_type& out() { return buffer_.second(); }
can_read() const109     bool can_read() const { return is_convertible<Mode, input>::value; }
can_write() const110     bool can_write() const { return is_convertible<Mode, output>::value; }
output_buffered() const111     bool output_buffered() const { return (flags_ & f_output_buffered) != 0; }
shared_buffer() const112     bool shared_buffer() const { return is_convertible<Mode, seekable>::value; }
set_flags(int f)113     void set_flags(int f) { flags_ = f; }
114 
115     //----------State changing functions--------------------------------------//
116 
117     virtual void init_get_area();
118     virtual void init_put_area();
119 
120     //----------Utility function----------------------------------------------//
121 
122     pos_type seek_impl( stream_offset off, BOOST_IOS::seekdir way,
123                         BOOST_IOS::openmode which );
124     void sync_impl();
125 
126     enum flag_type {
127         f_open             = 1,
128         f_output_buffered  = f_open << 1,
129         f_auto_close       = f_output_buffered << 1
130     };
131 
132     optional<wrapper>           storage_;
133     streambuf_type*             next_;
134     double_object<
135         buffer_type,
136         is_convertible<
137             Mode,
138             two_sequence
139         >
140     >                           buffer_;
141     std::streamsize             pback_size_;
142     int                         flags_;
143 };
144 
145 //--------------Implementation of indirect_streambuf--------------------------//
146 
147 template<typename T, typename Tr, typename Alloc, typename Mode>
indirect_streambuf()148 indirect_streambuf<T, Tr, Alloc, Mode>::indirect_streambuf()
149     : next_(0), pback_size_(0), flags_(f_auto_close) { }
150 
151 //--------------Implementation of open, is_open and close---------------------//
152 
153 template<typename T, typename Tr, typename Alloc, typename Mode>
open(const T & t,std::streamsize buffer_size,std::streamsize pback_size)154 void indirect_streambuf<T, Tr, Alloc, Mode>::open
155     (const T& t, std::streamsize buffer_size, std::streamsize pback_size)
156 {
157     using namespace std;
158 
159     // Normalize buffer sizes.
160     buffer_size =
161         (buffer_size != -1) ?
162         buffer_size :
163         iostreams::optimal_buffer_size(t);
164     pback_size =
165         (pback_size != -1) ?
166         pback_size :
167         default_pback_buffer_size;
168 
169     // Construct input buffer.
170     if (can_read()) {
171         pback_size_ = (std::max)(std::streamsize(2), pback_size); // STLPort needs 2.
172         std::streamsize size =
173             pback_size_ +
174             ( buffer_size ? buffer_size: 1 );
175         in().resize(size);
176         if (!shared_buffer())
177             init_get_area();
178     }
179 
180     // Construct output buffer.
181     if (can_write() && !shared_buffer()) {
182         if (buffer_size != 0)
183             out().resize(buffer_size);
184         init_put_area();
185     }
186 
187     storage_.reset(wrapper(t));
188     flags_ |= f_open;
189     if (can_write() && buffer_size > 1)
190         flags_ |= f_output_buffered;
191     this->set_true_eof(false);
192     this->set_needs_close();
193 }
194 
195 template<typename T, typename Tr, typename Alloc, typename Mode>
is_open() const196 inline bool indirect_streambuf<T, Tr, Alloc, Mode>::is_open() const
197 { return (flags_ & f_open) != 0; }
198 
199 template<typename T, typename Tr, typename Alloc, typename Mode>
close()200 void indirect_streambuf<T, Tr, Alloc, Mode>::close()
201 {
202     using namespace std;
203     base_type* self = this;
204     detail::execute_all(
205         detail::call_member_close(*self, BOOST_IOS::in),
206         detail::call_member_close(*self, BOOST_IOS::out),
207         detail::call_reset(storage_),
208         detail::clear_flags(flags_)
209     );
210 }
211 
212 template<typename T, typename Tr, typename Alloc, typename Mode>
auto_close() const213 bool indirect_streambuf<T, Tr, Alloc, Mode>::auto_close() const
214 { return (flags_ & f_auto_close) != 0; }
215 
216 template<typename T, typename Tr, typename Alloc, typename Mode>
set_auto_close(bool close)217 void indirect_streambuf<T, Tr, Alloc, Mode>::set_auto_close(bool close)
218 { flags_ = (flags_ & ~f_auto_close) | (close ? f_auto_close : 0); }
219 
220 //--------------Implementation virtual functions------------------------------//
221 
222 #ifndef BOOST_IOSTREAMS_NO_LOCALE
223 template<typename T, typename Tr, typename Alloc, typename Mode>
imbue(const std::locale & loc)224 void indirect_streambuf<T, Tr, Alloc, Mode>::imbue(const std::locale& loc)
225 {
226     if (is_open()) {
227         obj().imbue(loc);
228         if (next_)
229             next_->pubimbue(loc);
230     }
231 }
232 #endif
233 
234 template<typename T, typename Tr, typename Alloc, typename Mode>
235 typename indirect_streambuf<T, Tr, Alloc, Mode>::int_type
underflow()236 indirect_streambuf<T, Tr, Alloc, Mode>::underflow()
237 {
238     using namespace std;
239     if (!gptr()) init_get_area();
240     buffer_type& buf = in();
241     if (gptr() < egptr()) return traits_type::to_int_type(*gptr());
242 
243     // Fill putback buffer.
244     std::streamsize keep =
245         (std::min)( static_cast<std::streamsize>(gptr() - eback()),
246                     pback_size_ );
247     if (keep)
248         traits_type::move( buf.data() + (pback_size_ - keep),
249                            gptr() - keep, keep );
250 
251     // Set pointers to reasonable values in case read throws.
252     setg( buf.data() + pback_size_ - keep,
253           buf.data() + pback_size_,
254           buf.data() + pback_size_ );
255 
256     // Read from source.
257     std::streamsize chars =
258         obj().read(buf.data() + pback_size_, buf.size() - pback_size_, next_);
259     if (chars == -1) {
260         this->set_true_eof(true);
261         chars = 0;
262     }
263     setg(eback(), gptr(), buf.data() + pback_size_ + chars);
264     return chars != 0 ?
265         traits_type::to_int_type(*gptr()) :
266         traits_type::eof();
267 }
268 
269 template<typename T, typename Tr, typename Alloc, typename Mode>
270 typename indirect_streambuf<T, Tr, Alloc, Mode>::int_type
pbackfail(int_type c)271 indirect_streambuf<T, Tr, Alloc, Mode>::pbackfail(int_type c)
272 {
273     if (gptr() != eback()) {
274         gbump(-1);
275         if (!traits_type::eq_int_type(c, traits_type::eof()))
276             *gptr() = traits_type::to_char_type(c);
277         return traits_type::not_eof(c);
278     } else {
279         boost::throw_exception(bad_putback());
280     }
281 }
282 
283 template<typename T, typename Tr, typename Alloc, typename Mode>
284 typename indirect_streambuf<T, Tr, Alloc, Mode>::int_type
overflow(int_type c)285 indirect_streambuf<T, Tr, Alloc, Mode>::overflow(int_type c)
286 {
287     if ( (output_buffered() && pptr() == 0) ||
288          (shared_buffer() && gptr() != 0) )
289     {
290         init_put_area();
291     }
292     if (!traits_type::eq_int_type(c, traits_type::eof())) {
293         if (output_buffered()) {
294             if (pptr() == epptr()) {
295                 sync_impl();
296                 if (pptr() == epptr())
297                     return traits_type::eof();
298             }
299             *pptr() = traits_type::to_char_type(c);
300             pbump(1);
301         } else {
302             char_type d = traits_type::to_char_type(c);
303             if (obj().write(&d, 1, next_) != 1)
304                 return traits_type::eof();
305         }
306     }
307     return traits_type::not_eof(c);
308 }
309 
310 template<typename T, typename Tr, typename Alloc, typename Mode>
sync()311 int indirect_streambuf<T, Tr, Alloc, Mode>::sync()
312 {
313     try { // sync() is no-throw.
314         sync_impl();
315         obj().flush(next_);
316         return 0;
317     } catch (...) { return -1; }
318 }
319 
320 template<typename T, typename Tr, typename Alloc, typename Mode>
strict_sync()321 bool indirect_streambuf<T, Tr, Alloc, Mode>::strict_sync()
322 {
323     try { // sync() is no-throw.
324         sync_impl();
325         return obj().flush(next_);
326     } catch (...) { return false; }
327 }
328 
329 template<typename T, typename Tr, typename Alloc, typename Mode>
330 inline typename indirect_streambuf<T, Tr, Alloc, Mode>::pos_type
seekoff(off_type off,BOOST_IOS::seekdir way,BOOST_IOS::openmode which)331 indirect_streambuf<T, Tr, Alloc, Mode>::seekoff
332     (off_type off, BOOST_IOS::seekdir way, BOOST_IOS::openmode which)
333 { return seek_impl(off, way, which); }
334 
335 template<typename T, typename Tr, typename Alloc, typename Mode>
336 inline typename indirect_streambuf<T, Tr, Alloc, Mode>::pos_type
seekpos(pos_type sp,BOOST_IOS::openmode which)337 indirect_streambuf<T, Tr, Alloc, Mode>::seekpos
338     (pos_type sp, BOOST_IOS::openmode which)
339 {
340     return seek_impl(position_to_offset(sp), BOOST_IOS::beg, which);
341 }
342 
343 template<typename T, typename Tr, typename Alloc, typename Mode>
344 typename indirect_streambuf<T, Tr, Alloc, Mode>::pos_type
seek_impl(stream_offset off,BOOST_IOS::seekdir way,BOOST_IOS::openmode which)345 indirect_streambuf<T, Tr, Alloc, Mode>::seek_impl
346     (stream_offset off, BOOST_IOS::seekdir way, BOOST_IOS::openmode which)
347 {
348     if ( gptr() != 0 && way == BOOST_IOS::cur && which == BOOST_IOS::in &&
349          eback() - gptr() <= off && off <= egptr() - gptr() )
350     {   // Small seek optimization
351         gbump(off);
352         return obj().seek(0, BOOST_IOS::cur, BOOST_IOS::in, next_) -
353                static_cast<off_type>(egptr() - gptr());
354     }
355     if (pptr() != 0)
356         this->BOOST_IOSTREAMS_PUBSYNC(); // sync() confuses VisualAge 6.
357     if (way == BOOST_IOS::cur && gptr())
358         off -= static_cast<off_type>(egptr() - gptr());
359     setg(0, 0, 0);
360     setp(0, 0);
361     return obj().seek(off, way, which, next_);
362 }
363 
364 template<typename T, typename Tr, typename Alloc, typename Mode>
set_next(streambuf_type * next)365 inline void indirect_streambuf<T, Tr, Alloc, Mode>::set_next
366     (streambuf_type* next)
367 { next_ = next; }
368 
369 template<typename T, typename Tr, typename Alloc, typename Mode>
close_impl(BOOST_IOS::openmode which)370 inline void indirect_streambuf<T, Tr, Alloc, Mode>::close_impl
371     (BOOST_IOS::openmode which)
372 {
373     if (which == BOOST_IOS::in && is_convertible<Mode, input>::value) {
374         setg(0, 0, 0);
375     }
376     if (which == BOOST_IOS::out && is_convertible<Mode, output>::value) {
377         sync();
378         setp(0, 0);
379     }
380     if ( !is_convertible<category, dual_use>::value ||
381          is_convertible<Mode, input>::value == (which == BOOST_IOS::in) )
382     {
383         obj().close(which, next_);
384     }
385 }
386 
387 //----------State changing functions------------------------------------------//
388 
389 template<typename T, typename Tr, typename Alloc, typename Mode>
sync_impl()390 void indirect_streambuf<T, Tr, Alloc, Mode>::sync_impl()
391 {
392     std::streamsize avail, amt;
393     if ((avail = static_cast<std::streamsize>(pptr() - pbase())) > 0) {
394         if ((amt = obj().write(pbase(), avail, next())) == avail)
395             setp(out().begin(), out().end());
396         else {
397             const char_type* ptr = pptr();
398             setp(out().begin() + amt, out().end());
399             pbump(ptr - pptr());
400         }
401     }
402 }
403 
404 template<typename T, typename Tr, typename Alloc, typename Mode>
init_get_area()405 void indirect_streambuf<T, Tr, Alloc, Mode>::init_get_area()
406 {
407     if (shared_buffer() && pptr() != 0) {
408         sync_impl();
409         setp(0, 0);
410     }
411     setg(in().begin(), in().begin(), in().begin());
412 }
413 
414 template<typename T, typename Tr, typename Alloc, typename Mode>
init_put_area()415 void indirect_streambuf<T, Tr, Alloc, Mode>::init_put_area()
416 {
417     using namespace std;
418     if (shared_buffer() && gptr() != 0)
419         setg(0, 0, 0);
420     if (output_buffered())
421         setp(out().begin(), out().end());
422     else
423         setp(0, 0);
424 }
425 
426 //----------------------------------------------------------------------------//
427 
428 } } } // End namespaces detail, iostreams, boost.
429 
430 #include <boost/iostreams/detail/config/enable_warnings.hpp> // MSVC, BCC 5.x
431 
432 #endif // #ifndef BOOST_IOSTREAMS_DETAIL_INDIRECT_STREAMBUF_HPP_INCLUDED
433