Ticket #10561: indirect_streambuf.hpp

File indirect_streambuf.hpp, 15.6 KB (added by jlodos@…, 8 years ago)
Line 
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
44namespace boost { namespace iostreams { namespace detail {
45
46//
47// Description: The implementation of basic_streambuf used by chains.
48//
49template<typename T, typename Tr, typename Alloc, typename Mode>
50class indirect_streambuf
51 : public linked_streambuf<BOOST_DEDUCED_TYPENAME char_type_of<T>::type, Tr>
52{
53public:
54 typedef typename char_type_of<T>::type char_type;
55 BOOST_IOSTREAMS_STREAMBUF_TYPEDEFS(Tr)
56private:
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;
63public:
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.
74 T* component() { return &*obj(); }
75protected:
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);
99 const std::type_info& component_type() const { return typeid(T); }
100 void* component_impl() { return component(); }
101private:
102
103 //----------Accessor functions--------------------------------------------//
104
105 wrapper& obj() { return *storage_; }
106 streambuf_type* next() const { return next_; }
107 buffer_type& in() { return buffer_.first(); }
108 buffer_type& out() { return buffer_.second(); }
109 bool can_read() const { return is_convertible<Mode, input>::value; }
110 bool can_write() const { return is_convertible<Mode, output>::value; }
111 bool output_buffered() const { return (flags_ & f_output_buffered) != 0; }
112 bool shared_buffer() const { return is_convertible<Mode, seekable>::value || is_convertible<Mode, dual_seekable>::value; }
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
147template<typename T, typename Tr, typename Alloc, typename Mode>
148indirect_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
153template<typename T, typename Tr, typename Alloc, typename Mode>
154void 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
195template<typename T, typename Tr, typename Alloc, typename Mode>
196inline bool indirect_streambuf<T, Tr, Alloc, Mode>::is_open() const
197{ return (flags_ & f_open) != 0; }
198
199template<typename T, typename Tr, typename Alloc, typename Mode>
200void 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
212template<typename T, typename Tr, typename Alloc, typename Mode>
213bool indirect_streambuf<T, Tr, Alloc, Mode>::auto_close() const
214{ return (flags_ & f_auto_close) != 0; }
215
216template<typename T, typename Tr, typename Alloc, typename Mode>
217void 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
223template<typename T, typename Tr, typename Alloc, typename Mode>
224void 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
234template<typename T, typename Tr, typename Alloc, typename Mode>
235typename indirect_streambuf<T, Tr, Alloc, Mode>::int_type
236indirect_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
269template<typename T, typename Tr, typename Alloc, typename Mode>
270typename indirect_streambuf<T, Tr, Alloc, Mode>::int_type
271indirect_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
283template<typename T, typename Tr, typename Alloc, typename Mode>
284typename indirect_streambuf<T, Tr, Alloc, Mode>::int_type
285indirect_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
310template<typename T, typename Tr, typename Alloc, typename Mode>
311int 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
320template<typename T, typename Tr, typename Alloc, typename Mode>
321bool 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
329template<typename T, typename Tr, typename Alloc, typename Mode>
330inline typename indirect_streambuf<T, Tr, Alloc, Mode>::pos_type
331indirect_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
335template<typename T, typename Tr, typename Alloc, typename Mode>
336inline typename indirect_streambuf<T, Tr, Alloc, Mode>::pos_type
337indirect_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
343template<typename T, typename Tr, typename Alloc, typename Mode>
344typename indirect_streambuf<T, Tr, Alloc, Mode>::pos_type
345indirect_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 bool two_head = is_convertible<category, dual_seekable>::value ||
360 is_convertible<category, bidirectional_seekable>::value;
361 if (two_head) {
362 BOOST_IOS::openmode both = BOOST_IOS::in | BOOST_IOS::out;
363 if ((which & both) == both)
364 boost::throw_exception(bad_seek());
365 if (which & BOOST_IOS::in) {
366 setg(0, 0, 0);
367 }
368 if (which & BOOST_IOS::out) {
369 setp(0, 0);
370 }
371 }
372 else {
373 setg(0, 0, 0);
374 setp(0, 0);
375 }
376 return obj().seek(off, way, which, next_);
377}
378
379template<typename T, typename Tr, typename Alloc, typename Mode>
380inline void indirect_streambuf<T, Tr, Alloc, Mode>::set_next
381 (streambuf_type* next)
382{ next_ = next; }
383
384template<typename T, typename Tr, typename Alloc, typename Mode>
385inline void indirect_streambuf<T, Tr, Alloc, Mode>::close_impl
386 (BOOST_IOS::openmode which)
387{
388 if (which == BOOST_IOS::in && is_convertible<Mode, input>::value) {
389 setg(0, 0, 0);
390 }
391 if (which == BOOST_IOS::out && is_convertible<Mode, output>::value) {
392 sync();
393 setp(0, 0);
394 }
395 if ( !is_convertible<category, dual_use>::value ||
396 is_convertible<Mode, input>::value == (which == BOOST_IOS::in) )
397 {
398 obj().close(which, next_);
399 }
400}
401
402//----------State changing functions------------------------------------------//
403
404template<typename T, typename Tr, typename Alloc, typename Mode>
405void indirect_streambuf<T, Tr, Alloc, Mode>::sync_impl()
406{
407 std::streamsize avail, amt;
408 if ((avail = static_cast<std::streamsize>(pptr() - pbase())) > 0) {
409 if ((amt = obj().write(pbase(), avail, next())) == avail)
410 setp(out().begin(), out().end());
411 else {
412 const char_type* ptr = pptr();
413 setp(out().begin() + amt, out().end());
414 pbump(ptr - pptr());
415 }
416 }
417}
418
419template<typename T, typename Tr, typename Alloc, typename Mode>
420void indirect_streambuf<T, Tr, Alloc, Mode>::init_get_area()
421{
422 if (shared_buffer() && pptr() != 0) {
423 sync_impl();
424 setp(0, 0);
425 }
426 setg(in().begin(), in().begin(), in().begin());
427}
428
429template<typename T, typename Tr, typename Alloc, typename Mode>
430void indirect_streambuf<T, Tr, Alloc, Mode>::init_put_area()
431{
432 using namespace std;
433 if (shared_buffer() && gptr() != 0) {
434 obj().seek(static_cast<off_type>(gptr() - egptr()), BOOST_IOS::cur, BOOST_IOS::in, next_);
435 setg(0, 0, 0);
436 }
437 if (output_buffered())
438 setp(out().begin(), out().end());
439 else
440 setp(0, 0);
441}
442
443//----------------------------------------------------------------------------//
444
445} } } // End namespaces detail, iostreams, boost.
446
447#include <boost/iostreams/detail/config/enable_warnings.hpp> // MSVC, BCC 5.x
448
449#endif // #ifndef BOOST_IOSTREAMS_DETAIL_INDIRECT_STREAMBUF_HPP_INCLUDED