]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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. | |
74 | T* component() { return &*obj(); } | |
75 | protected: | |
76 | BOOST_IOSTREAMS_USING_PROTECTED_STREAMBUF_MEMBERS(base_type) | |
77 | ||
78 | //----------virtual functions---------------------------------------------// | |
79 | ||
80 | #ifndef BOOST_IOSTREAMS_NO_LOCALE | |
81 | void imbue(const std::locale& loc); | |
82 | #endif | |
83 | #ifdef BOOST_IOSTREAMS_NO_STREAM_TEMPLATES | |
84 | public: | |
85 | #endif | |
86 | int_type underflow(); | |
87 | int_type pbackfail(int_type c); | |
88 | int_type overflow(int_type c); | |
89 | int sync(); | |
90 | pos_type seekoff( off_type off, BOOST_IOS::seekdir way, | |
91 | BOOST_IOS::openmode which ); | |
92 | pos_type seekpos(pos_type sp, BOOST_IOS::openmode which); | |
93 | ||
94 | // Declared in linked_streambuf. | |
95 | void set_next(streambuf_type* next); | |
96 | void close_impl(BOOST_IOS::openmode m); | |
97 | const std::type_info& component_type() const { return typeid(T); } | |
98 | void* component_impl() { return component(); } | |
99 | private: | |
100 | ||
101 | //----------Accessor functions--------------------------------------------// | |
102 | ||
103 | wrapper& obj() { return *storage_; } | |
104 | streambuf_type* next() const { return next_; } | |
105 | buffer_type& in() { return buffer_.first(); } | |
106 | buffer_type& out() { return buffer_.second(); } | |
107 | bool can_read() const { return is_convertible<Mode, input>::value; } | |
108 | bool can_write() const { return is_convertible<Mode, output>::value; } | |
109 | bool output_buffered() const { return (flags_ & f_output_buffered) != 0; } | |
110 | bool shared_buffer() const { return is_convertible<Mode, seekable>::value; } | |
111 | void set_flags(int f) { flags_ = f; } | |
112 | ||
113 | //----------State changing functions--------------------------------------// | |
114 | ||
115 | virtual void init_get_area(); | |
116 | virtual void init_put_area(); | |
117 | ||
118 | //----------Utility function----------------------------------------------// | |
119 | ||
120 | pos_type seek_impl( stream_offset off, BOOST_IOS::seekdir way, | |
121 | BOOST_IOS::openmode which ); | |
122 | void sync_impl(); | |
123 | ||
124 | enum flag_type { | |
125 | f_open = 1, | |
126 | f_output_buffered = f_open << 1, | |
127 | f_auto_close = f_output_buffered << 1 | |
128 | }; | |
129 | ||
130 | optional<wrapper> storage_; | |
131 | streambuf_type* next_; | |
132 | double_object< | |
133 | buffer_type, | |
134 | is_convertible< | |
135 | Mode, | |
136 | two_sequence | |
137 | > | |
138 | > buffer_; | |
139 | std::streamsize pback_size_; | |
140 | int flags_; | |
141 | }; | |
142 | ||
143 | //--------------Implementation of indirect_streambuf--------------------------// | |
144 | ||
145 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
146 | indirect_streambuf<T, Tr, Alloc, Mode>::indirect_streambuf() | |
147 | : next_(0), pback_size_(0), flags_(f_auto_close) { } | |
148 | ||
149 | //--------------Implementation of open, is_open and close---------------------// | |
150 | ||
151 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
152 | void indirect_streambuf<T, Tr, Alloc, Mode>::open | |
153 | (const T& t, std::streamsize buffer_size, std::streamsize pback_size) | |
154 | { | |
155 | using namespace std; | |
156 | ||
157 | // Normalize buffer sizes. | |
158 | buffer_size = | |
159 | (buffer_size != -1) ? | |
160 | buffer_size : | |
161 | iostreams::optimal_buffer_size(t); | |
162 | pback_size = | |
163 | (pback_size != -1) ? | |
164 | pback_size : | |
165 | default_pback_buffer_size; | |
166 | ||
167 | // Construct input buffer. | |
168 | if (can_read()) { | |
169 | pback_size_ = (std::max)(std::streamsize(2), pback_size); // STLPort needs 2. | |
170 | std::streamsize size = | |
171 | pback_size_ + | |
172 | ( buffer_size ? buffer_size: 1 ); | |
173 | in().resize(size); | |
174 | if (!shared_buffer()) | |
175 | init_get_area(); | |
176 | } | |
177 | ||
178 | // Construct output buffer. | |
179 | if (can_write() && !shared_buffer()) { | |
180 | if (buffer_size != 0) | |
181 | out().resize(buffer_size); | |
182 | init_put_area(); | |
183 | } | |
184 | ||
185 | storage_.reset(wrapper(t)); | |
186 | flags_ |= f_open; | |
187 | if (can_write() && buffer_size > 1) | |
188 | flags_ |= f_output_buffered; | |
189 | this->set_true_eof(false); | |
190 | this->set_needs_close(); | |
191 | } | |
192 | ||
193 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
194 | inline bool indirect_streambuf<T, Tr, Alloc, Mode>::is_open() const | |
195 | { return (flags_ & f_open) != 0; } | |
196 | ||
197 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
198 | void indirect_streambuf<T, Tr, Alloc, Mode>::close() | |
199 | { | |
200 | using namespace std; | |
201 | base_type* self = this; | |
202 | detail::execute_all( | |
203 | detail::call_member_close(*self, BOOST_IOS::in), | |
204 | detail::call_member_close(*self, BOOST_IOS::out), | |
205 | detail::call_reset(storage_), | |
206 | detail::clear_flags(flags_) | |
207 | ); | |
208 | } | |
209 | ||
210 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
211 | bool indirect_streambuf<T, Tr, Alloc, Mode>::auto_close() const | |
212 | { return (flags_ & f_auto_close) != 0; } | |
213 | ||
214 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
215 | void indirect_streambuf<T, Tr, Alloc, Mode>::set_auto_close(bool close) | |
216 | { flags_ = (flags_ & ~f_auto_close) | (close ? f_auto_close : 0); } | |
217 | ||
218 | //--------------Implementation virtual functions------------------------------// | |
219 | ||
220 | #ifndef BOOST_IOSTREAMS_NO_LOCALE | |
221 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
222 | void indirect_streambuf<T, Tr, Alloc, Mode>::imbue(const std::locale& loc) | |
223 | { | |
224 | if (is_open()) { | |
225 | obj().imbue(loc); | |
226 | if (next_) | |
227 | next_->pubimbue(loc); | |
228 | } | |
229 | } | |
230 | #endif | |
231 | ||
232 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
233 | typename indirect_streambuf<T, Tr, Alloc, Mode>::int_type | |
234 | indirect_streambuf<T, Tr, Alloc, Mode>::underflow() | |
235 | { | |
236 | using namespace std; | |
237 | if (!gptr()) init_get_area(); | |
238 | buffer_type& buf = in(); | |
239 | if (gptr() < egptr()) return traits_type::to_int_type(*gptr()); | |
240 | ||
241 | // Fill putback buffer. | |
242 | std::streamsize keep = | |
243 | (std::min)( static_cast<std::streamsize>(gptr() - eback()), | |
244 | pback_size_ ); | |
245 | if (keep) | |
246 | traits_type::move( buf.data() + (pback_size_ - keep), | |
247 | gptr() - keep, keep ); | |
248 | ||
249 | // Set pointers to reasonable values in case read throws. | |
250 | setg( buf.data() + pback_size_ - keep, | |
251 | buf.data() + pback_size_, | |
252 | buf.data() + pback_size_ ); | |
253 | ||
254 | // Read from source. | |
255 | std::streamsize chars = | |
256 | obj().read(buf.data() + pback_size_, buf.size() - pback_size_, next_); | |
257 | if (chars == -1) { | |
258 | this->set_true_eof(true); | |
259 | chars = 0; | |
260 | } | |
261 | setg(eback(), gptr(), buf.data() + pback_size_ + chars); | |
262 | return chars != 0 ? | |
263 | traits_type::to_int_type(*gptr()) : | |
264 | traits_type::eof(); | |
265 | } | |
266 | ||
267 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
268 | typename indirect_streambuf<T, Tr, Alloc, Mode>::int_type | |
269 | indirect_streambuf<T, Tr, Alloc, Mode>::pbackfail(int_type c) | |
270 | { | |
271 | if (gptr() != eback()) { | |
272 | gbump(-1); | |
273 | if (!traits_type::eq_int_type(c, traits_type::eof())) | |
274 | *gptr() = traits_type::to_char_type(c); | |
275 | return traits_type::not_eof(c); | |
276 | } else { | |
277 | boost::throw_exception(bad_putback()); | |
278 | } | |
279 | } | |
280 | ||
281 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
282 | typename indirect_streambuf<T, Tr, Alloc, Mode>::int_type | |
283 | indirect_streambuf<T, Tr, Alloc, Mode>::overflow(int_type c) | |
284 | { | |
285 | if ( (output_buffered() && pptr() == 0) || | |
286 | (shared_buffer() && gptr() != 0) ) | |
287 | { | |
288 | init_put_area(); | |
289 | } | |
290 | if (!traits_type::eq_int_type(c, traits_type::eof())) { | |
291 | if (output_buffered()) { | |
292 | if (pptr() == epptr()) { | |
293 | sync_impl(); | |
294 | if (pptr() == epptr()) | |
295 | return traits_type::eof(); | |
296 | } | |
297 | *pptr() = traits_type::to_char_type(c); | |
298 | pbump(1); | |
299 | } else { | |
300 | char_type d = traits_type::to_char_type(c); | |
301 | if (obj().write(&d, 1, next_) != 1) | |
302 | return traits_type::eof(); | |
303 | } | |
304 | } | |
305 | return traits_type::not_eof(c); | |
306 | } | |
307 | ||
308 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
309 | int indirect_streambuf<T, Tr, Alloc, Mode>::sync() | |
310 | { | |
311 | try { // sync() is no-throw. | |
312 | sync_impl(); | |
313 | obj().flush(next_); | |
314 | return 0; | |
315 | } catch (...) { return -1; } | |
316 | } | |
317 | ||
318 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
319 | bool indirect_streambuf<T, Tr, Alloc, Mode>::strict_sync() | |
320 | { | |
321 | try { // sync() is no-throw. | |
322 | sync_impl(); | |
323 | return obj().flush(next_); | |
324 | } catch (...) { return false; } | |
325 | } | |
326 | ||
327 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
328 | inline typename indirect_streambuf<T, Tr, Alloc, Mode>::pos_type | |
329 | indirect_streambuf<T, Tr, Alloc, Mode>::seekoff | |
330 | (off_type off, BOOST_IOS::seekdir way, BOOST_IOS::openmode which) | |
331 | { return seek_impl(off, way, which); } | |
332 | ||
333 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
334 | inline typename indirect_streambuf<T, Tr, Alloc, Mode>::pos_type | |
335 | indirect_streambuf<T, Tr, Alloc, Mode>::seekpos | |
336 | (pos_type sp, BOOST_IOS::openmode which) | |
337 | { | |
338 | return seek_impl(position_to_offset(sp), BOOST_IOS::beg, which); | |
339 | } | |
340 | ||
341 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
342 | typename indirect_streambuf<T, Tr, Alloc, Mode>::pos_type | |
343 | indirect_streambuf<T, Tr, Alloc, Mode>::seek_impl | |
344 | (stream_offset off, BOOST_IOS::seekdir way, BOOST_IOS::openmode which) | |
345 | { | |
346 | if ( gptr() != 0 && way == BOOST_IOS::cur && which == BOOST_IOS::in && | |
347 | eback() - gptr() <= off && off <= egptr() - gptr() ) | |
348 | { // Small seek optimization | |
349 | gbump(off); | |
350 | return obj().seek(0, BOOST_IOS::cur, BOOST_IOS::in, next_) - | |
351 | static_cast<off_type>(egptr() - gptr()); | |
352 | } | |
353 | if (pptr() != 0) | |
354 | this->BOOST_IOSTREAMS_PUBSYNC(); // sync() confuses VisualAge 6. | |
355 | if (way == BOOST_IOS::cur && gptr()) | |
356 | off -= static_cast<off_type>(egptr() - gptr()); | |
357 | setg(0, 0, 0); | |
358 | setp(0, 0); | |
359 | return obj().seek(off, way, which, next_); | |
360 | } | |
361 | ||
362 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
363 | inline void indirect_streambuf<T, Tr, Alloc, Mode>::set_next | |
364 | (streambuf_type* next) | |
365 | { next_ = next; } | |
366 | ||
367 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
368 | inline void indirect_streambuf<T, Tr, Alloc, Mode>::close_impl | |
369 | (BOOST_IOS::openmode which) | |
370 | { | |
371 | if (which == BOOST_IOS::in && is_convertible<Mode, input>::value) { | |
372 | setg(0, 0, 0); | |
373 | } | |
374 | if (which == BOOST_IOS::out && is_convertible<Mode, output>::value) { | |
375 | sync(); | |
376 | setp(0, 0); | |
377 | } | |
378 | if ( !is_convertible<category, dual_use>::value || | |
379 | is_convertible<Mode, input>::value == (which == BOOST_IOS::in) ) | |
380 | { | |
381 | obj().close(which, next_); | |
382 | } | |
383 | } | |
384 | ||
385 | //----------State changing functions------------------------------------------// | |
386 | ||
387 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
388 | void indirect_streambuf<T, Tr, Alloc, Mode>::sync_impl() | |
389 | { | |
390 | std::streamsize avail, amt; | |
391 | if ((avail = static_cast<std::streamsize>(pptr() - pbase())) > 0) { | |
392 | if ((amt = obj().write(pbase(), avail, next())) == avail) | |
393 | setp(out().begin(), out().end()); | |
394 | else { | |
395 | const char_type* ptr = pptr(); | |
396 | setp(out().begin() + amt, out().end()); | |
397 | pbump(ptr - pptr()); | |
398 | } | |
399 | } | |
400 | } | |
401 | ||
402 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
403 | void indirect_streambuf<T, Tr, Alloc, Mode>::init_get_area() | |
404 | { | |
405 | if (shared_buffer() && pptr() != 0) { | |
406 | sync_impl(); | |
407 | setp(0, 0); | |
408 | } | |
409 | setg(in().begin(), in().begin(), in().begin()); | |
410 | } | |
411 | ||
412 | template<typename T, typename Tr, typename Alloc, typename Mode> | |
413 | void indirect_streambuf<T, Tr, Alloc, Mode>::init_put_area() | |
414 | { | |
415 | using namespace std; | |
416 | if (shared_buffer() && gptr() != 0) | |
417 | setg(0, 0, 0); | |
418 | if (output_buffered()) | |
419 | setp(out().begin(), out().end()); | |
420 | else | |
421 | setp(0, 0); | |
422 | } | |
423 | ||
424 | //----------------------------------------------------------------------------// | |
425 | ||
426 | } } } // End namespaces detail, iostreams, boost. | |
427 | ||
428 | #include <boost/iostreams/detail/config/enable_warnings.hpp> // MSVC, BCC 5.x | |
429 | ||
430 | #endif // #ifndef BOOST_IOSTREAMS_DETAIL_INDIRECT_STREAMBUF_HPP_INCLUDED |