1 | // |
2 | // basic_streambuf.hpp |
3 | // ~~~~~~~~~~~~~~~~~~~ |
4 | // |
5 | // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
6 | // |
7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying |
8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
9 | // |
10 | |
11 | #ifndef BOOST_ASIO_BASIC_STREAMBUF_HPP |
12 | #define BOOST_ASIO_BASIC_STREAMBUF_HPP |
13 | |
14 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) |
15 | # pragma once |
16 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) |
17 | |
18 | #include <boost/asio/detail/config.hpp> |
19 | |
20 | #if !defined(BOOST_ASIO_NO_IOSTREAM) |
21 | |
22 | #include <algorithm> |
23 | #include <cstring> |
24 | #include <stdexcept> |
25 | #include <streambuf> |
26 | #include <vector> |
27 | #include <boost/asio/basic_streambuf_fwd.hpp> |
28 | #include <boost/asio/buffer.hpp> |
29 | #include <boost/asio/detail/limits.hpp> |
30 | #include <boost/asio/detail/noncopyable.hpp> |
31 | #include <boost/asio/detail/throw_exception.hpp> |
32 | |
33 | #include <boost/asio/detail/push_options.hpp> |
34 | |
35 | namespace boost { |
36 | namespace asio { |
37 | |
38 | /// Automatically resizable buffer class based on std::streambuf. |
39 | /** |
40 | * The @c basic_streambuf class is derived from @c std::streambuf to associate |
41 | * the streambuf's input and output sequences with one or more character |
42 | * arrays. These character arrays are internal to the @c basic_streambuf |
43 | * object, but direct access to the array elements is provided to permit them |
44 | * to be used efficiently with I/O operations. Characters written to the output |
45 | * sequence of a @c basic_streambuf object are appended to the input sequence |
46 | * of the same object. |
47 | * |
48 | * The @c basic_streambuf class's public interface is intended to permit the |
49 | * following implementation strategies: |
50 | * |
51 | * @li A single contiguous character array, which is reallocated as necessary |
52 | * to accommodate changes in the size of the character sequence. This is the |
53 | * implementation approach currently used in Asio. |
54 | * |
55 | * @li A sequence of one or more character arrays, where each array is of the |
56 | * same size. Additional character array objects are appended to the sequence |
57 | * to accommodate changes in the size of the character sequence. |
58 | * |
59 | * @li A sequence of one or more character arrays of varying sizes. Additional |
60 | * character array objects are appended to the sequence to accommodate changes |
61 | * in the size of the character sequence. |
62 | * |
63 | * The constructor for basic_streambuf accepts a @c size_t argument specifying |
64 | * the maximum of the sum of the sizes of the input sequence and output |
65 | * sequence. During the lifetime of the @c basic_streambuf object, the following |
66 | * invariant holds: |
67 | * @code size() <= max_size()@endcode |
68 | * Any member function that would, if successful, cause the invariant to be |
69 | * violated shall throw an exception of class @c std::length_error. |
70 | * |
71 | * The constructor for @c basic_streambuf takes an Allocator argument. A copy |
72 | * of this argument is used for any memory allocation performed, by the |
73 | * constructor and by all member functions, during the lifetime of each @c |
74 | * basic_streambuf object. |
75 | * |
76 | * @par Examples |
77 | * Writing directly from an streambuf to a socket: |
78 | * @code |
79 | * boost::asio::streambuf b; |
80 | * std::ostream os(&b); |
81 | * os << "Hello, World!\n"; |
82 | * |
83 | * // try sending some data in input sequence |
84 | * size_t n = sock.send(b.data()); |
85 | * |
86 | * b.consume(n); // sent data is removed from input sequence |
87 | * @endcode |
88 | * |
89 | * Reading from a socket directly into a streambuf: |
90 | * @code |
91 | * boost::asio::streambuf b; |
92 | * |
93 | * // reserve 512 bytes in output sequence |
94 | * boost::asio::streambuf::mutable_buffers_type bufs = b.prepare(512); |
95 | * |
96 | * size_t n = sock.receive(bufs); |
97 | * |
98 | * // received data is "committed" from output sequence to input sequence |
99 | * b.commit(n); |
100 | * |
101 | * std::istream is(&b); |
102 | * std::string s; |
103 | * is >> s; |
104 | * @endcode |
105 | */ |
106 | #if defined(GENERATING_DOCUMENTATION) |
107 | template <typename Allocator = std::allocator<char> > |
108 | #else |
109 | template <typename Allocator> |
110 | #endif |
111 | class basic_streambuf |
112 | : public std::streambuf, |
113 | private noncopyable |
114 | { |
115 | public: |
116 | #if defined(GENERATING_DOCUMENTATION) |
117 | /// The type used to represent the input sequence as a list of buffers. |
118 | typedef implementation_defined const_buffers_type; |
119 | |
120 | /// The type used to represent the output sequence as a list of buffers. |
121 | typedef implementation_defined mutable_buffers_type; |
122 | #else |
123 | typedef boost::asio::const_buffers_1 const_buffers_type; |
124 | typedef boost::asio::mutable_buffers_1 mutable_buffers_type; |
125 | #endif |
126 | |
127 | /// Construct a basic_streambuf object. |
128 | /** |
129 | * Constructs a streambuf with the specified maximum size. The initial size |
130 | * of the streambuf's input sequence is 0. |
131 | */ |
132 | explicit basic_streambuf( |
133 | std::size_t maximum_size = (std::numeric_limits<std::size_t>::max)(), |
134 | const Allocator& allocator = Allocator()) |
135 | : max_size_(maximum_size), |
136 | buffer_(allocator) |
137 | { |
138 | std::size_t pend = (std::min<std::size_t>)(max_size_, buffer_delta); |
139 | buffer_.resize((std::max<std::size_t>)(a: pend, b: 1)); |
140 | setg(gbeg: &buffer_[0], gnext: &buffer_[0], gend: &buffer_[0]); |
141 | setp(pbeg: &buffer_[0], pend: &buffer_[0] + pend); |
142 | } |
143 | |
144 | /// Get the size of the input sequence. |
145 | /** |
146 | * @returns The size of the input sequence. The value is equal to that |
147 | * calculated for @c s in the following code: |
148 | * @code |
149 | * size_t s = 0; |
150 | * const_buffers_type bufs = data(); |
151 | * const_buffers_type::const_iterator i = bufs.begin(); |
152 | * while (i != bufs.end()) |
153 | * { |
154 | * const_buffer buf(*i++); |
155 | * s += buffer_size(buf); |
156 | * } |
157 | * @endcode |
158 | */ |
159 | std::size_t size() const |
160 | { |
161 | return pptr() - gptr(); |
162 | } |
163 | |
164 | /// Get the maximum size of the basic_streambuf. |
165 | /** |
166 | * @returns The allowed maximum of the sum of the sizes of the input sequence |
167 | * and output sequence. |
168 | */ |
169 | std::size_t max_size() const |
170 | { |
171 | return max_size_; |
172 | } |
173 | |
174 | /// Get a list of buffers that represents the input sequence. |
175 | /** |
176 | * @returns An object of type @c const_buffers_type that satisfies |
177 | * ConstBufferSequence requirements, representing all character arrays in the |
178 | * input sequence. |
179 | * |
180 | * @note The returned object is invalidated by any @c basic_streambuf member |
181 | * function that modifies the input sequence or output sequence. |
182 | */ |
183 | const_buffers_type data() const |
184 | { |
185 | return boost::asio::buffer(b: boost::asio::const_buffer(gptr(), |
186 | (pptr() - gptr()) * sizeof(char_type))); |
187 | } |
188 | |
189 | /// Get a list of buffers that represents the output sequence, with the given |
190 | /// size. |
191 | /** |
192 | * Ensures that the output sequence can accommodate @c n characters, |
193 | * reallocating character array objects as necessary. |
194 | * |
195 | * @returns An object of type @c mutable_buffers_type that satisfies |
196 | * MutableBufferSequence requirements, representing character array objects |
197 | * at the start of the output sequence such that the sum of the buffer sizes |
198 | * is @c n. |
199 | * |
200 | * @throws std::length_error If <tt>size() + n > max_size()</tt>. |
201 | * |
202 | * @note The returned object is invalidated by any @c basic_streambuf member |
203 | * function that modifies the input sequence or output sequence. |
204 | */ |
205 | mutable_buffers_type prepare(std::size_t n) |
206 | { |
207 | reserve(n); |
208 | return boost::asio::buffer(b: boost::asio::mutable_buffer( |
209 | pptr(), n * sizeof(char_type))); |
210 | } |
211 | |
212 | /// Move characters from the output sequence to the input sequence. |
213 | /** |
214 | * Appends @c n characters from the start of the output sequence to the input |
215 | * sequence. The beginning of the output sequence is advanced by @c n |
216 | * characters. |
217 | * |
218 | * Requires a preceding call <tt>prepare(x)</tt> where <tt>x >= n</tt>, and |
219 | * no intervening operations that modify the input or output sequence. |
220 | * |
221 | * @note If @c n is greater than the size of the output sequence, the entire |
222 | * output sequence is moved to the input sequence and no error is issued. |
223 | */ |
224 | void commit(std::size_t n) |
225 | { |
226 | if (pptr() + n > epptr()) |
227 | n = epptr() - pptr(); |
228 | pbump(n: static_cast<int>(n)); |
229 | setg(gbeg: eback(), gnext: gptr(), gend: pptr()); |
230 | } |
231 | |
232 | /// Remove characters from the input sequence. |
233 | /** |
234 | * Removes @c n characters from the beginning of the input sequence. |
235 | * |
236 | * @note If @c n is greater than the size of the input sequence, the entire |
237 | * input sequence is consumed and no error is issued. |
238 | */ |
239 | void consume(std::size_t n) |
240 | { |
241 | if (egptr() < pptr()) |
242 | setg(gbeg: &buffer_[0], gnext: gptr(), gend: pptr()); |
243 | if (gptr() + n > pptr()) |
244 | n = pptr() - gptr(); |
245 | gbump(n: static_cast<int>(n)); |
246 | } |
247 | |
248 | protected: |
249 | enum { buffer_delta = 128 }; |
250 | |
251 | /// Override std::streambuf behaviour. |
252 | /** |
253 | * Behaves according to the specification of @c std::streambuf::underflow(). |
254 | */ |
255 | int_type underflow() |
256 | { |
257 | if (gptr() < pptr()) |
258 | { |
259 | setg(gbeg: &buffer_[0], gnext: gptr(), gend: pptr()); |
260 | return traits_type::to_int_type(c: *gptr()); |
261 | } |
262 | else |
263 | { |
264 | return traits_type::eof(); |
265 | } |
266 | } |
267 | |
268 | /// Override std::streambuf behaviour. |
269 | /** |
270 | * Behaves according to the specification of @c std::streambuf::overflow(), |
271 | * with the specialisation that @c std::length_error is thrown if appending |
272 | * the character to the input sequence would require the condition |
273 | * <tt>size() > max_size()</tt> to be true. |
274 | */ |
275 | int_type overflow(int_type c) |
276 | { |
277 | if (!traits_type::eq_int_type(c1: c, c2: traits_type::eof())) |
278 | { |
279 | if (pptr() == epptr()) |
280 | { |
281 | std::size_t buffer_size = pptr() - gptr(); |
282 | if (buffer_size < max_size_ && max_size_ - buffer_size < buffer_delta) |
283 | { |
284 | reserve(n: max_size_ - buffer_size); |
285 | } |
286 | else |
287 | { |
288 | reserve(n: buffer_delta); |
289 | } |
290 | } |
291 | |
292 | *pptr() = traits_type::to_char_type(c: c); |
293 | pbump(n: 1); |
294 | return c; |
295 | } |
296 | |
297 | return traits_type::not_eof(c: c); |
298 | } |
299 | |
300 | void reserve(std::size_t n) |
301 | { |
302 | // Get current stream positions as offsets. |
303 | std::size_t gnext = gptr() - &buffer_[0]; |
304 | std::size_t pnext = pptr() - &buffer_[0]; |
305 | std::size_t pend = epptr() - &buffer_[0]; |
306 | |
307 | // Check if there is already enough space in the put area. |
308 | if (n <= pend - pnext) |
309 | { |
310 | return; |
311 | } |
312 | |
313 | // Shift existing contents of get area to start of buffer. |
314 | if (gnext > 0) |
315 | { |
316 | pnext -= gnext; |
317 | std::memmove(dest: &buffer_[0], src: &buffer_[0] + gnext, n: pnext); |
318 | } |
319 | |
320 | // Ensure buffer is large enough to hold at least the specified size. |
321 | if (n > pend - pnext) |
322 | { |
323 | if (n <= max_size_ && pnext <= max_size_ - n) |
324 | { |
325 | pend = pnext + n; |
326 | buffer_.resize((std::max<std::size_t>)(a: pend, b: 1)); |
327 | } |
328 | else |
329 | { |
330 | std::length_error ex("boost::asio::streambuf too long" ); |
331 | boost::asio::detail::throw_exception(e: ex); |
332 | } |
333 | } |
334 | |
335 | // Update stream positions. |
336 | setg(gbeg: &buffer_[0], gnext: &buffer_[0], gend: &buffer_[0] + pnext); |
337 | setp(pbeg: &buffer_[0] + pnext, pend: &buffer_[0] + pend); |
338 | } |
339 | |
340 | private: |
341 | std::size_t max_size_; |
342 | std::vector<char_type, Allocator> buffer_; |
343 | |
344 | // Helper function to get the preferred size for reading data. |
345 | friend std::size_t read_size_helper( |
346 | basic_streambuf& sb, std::size_t max_size) |
347 | { |
348 | return std::min<std::size_t>( |
349 | std::max<std::size_t>(512, sb.buffer_.capacity() - sb.size()), |
350 | std::min<std::size_t>(max_size, sb.max_size() - sb.size())); |
351 | } |
352 | }; |
353 | |
354 | // Helper function to get the preferred size for reading data. Used for any |
355 | // user-provided specialisations of basic_streambuf. |
356 | template <typename Allocator> |
357 | inline std::size_t read_size_helper( |
358 | basic_streambuf<Allocator>& sb, std::size_t max_size) |
359 | { |
360 | return std::min<std::size_t>(512, |
361 | std::min<std::size_t>(max_size, sb.max_size() - sb.size())); |
362 | } |
363 | |
364 | } // namespace asio |
365 | } // namespace boost |
366 | |
367 | #include <boost/asio/detail/pop_options.hpp> |
368 | |
369 | #endif // !defined(BOOST_ASIO_NO_IOSTREAM) |
370 | |
371 | #endif // BOOST_ASIO_BASIC_STREAMBUF_HPP |
372 | |