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