Clementine
basic_socket_streambuf.hpp
1 //
2 // basic_socket_streambuf.hpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2020 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_SOCKET_STREAMBUF_HPP
12 #define ASIO_BASIC_SOCKET_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 <streambuf>
23 #include <vector>
24 #include "asio/basic_socket.hpp"
25 #include "asio/basic_stream_socket.hpp"
26 #include "asio/detail/buffer_sequence_adapter.hpp"
27 #include "asio/detail/memory.hpp"
28 #include "asio/detail/throw_error.hpp"
29 #include "asio/io_context.hpp"
30 
31 #if defined(ASIO_HAS_BOOST_DATE_TIME) \
32  && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
33 # include "asio/detail/deadline_timer_service.hpp"
34 #else // defined(ASIO_HAS_BOOST_DATE_TIME)
35  // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
36 # include "asio/steady_timer.hpp"
37 #endif // defined(ASIO_HAS_BOOST_DATE_TIME)
38  // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
39 
40 #if !defined(ASIO_HAS_VARIADIC_TEMPLATES)
41 
42 # include "asio/detail/variadic_templates.hpp"
43 
44 // A macro that should expand to:
45 // template <typename T1, ..., typename Tn>
46 // basic_socket_streambuf* connect(T1 x1, ..., Tn xn)
47 // {
48 // init_buffers();
49 // typedef typename Protocol::resolver resolver_type;
50 // resolver_type resolver(socket().get_executor());
51 // connect_to_endpoints(
52 // resolver.resolve(x1, ..., xn, ec_));
53 // return !ec_ ? this : 0;
54 // }
55 // This macro should only persist within this file.
56 
57 # define ASIO_PRIVATE_CONNECT_DEF(n) \
58  template <ASIO_VARIADIC_TPARAMS(n)> \
59  basic_socket_streambuf* connect(ASIO_VARIADIC_BYVAL_PARAMS(n)) \
60  { \
61  init_buffers(); \
62  typedef typename Protocol::resolver resolver_type; \
63  resolver_type resolver(socket().get_executor()); \
64  connect_to_endpoints( \
65  resolver.resolve(ASIO_VARIADIC_BYVAL_ARGS(n), ec_)); \
66  return !ec_ ? this : 0; \
67  } \
68 
69 
70 #endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES)
71 
72 #include "asio/detail/push_options.hpp"
73 
74 namespace asio {
75 namespace detail {
76 
77 // A separate base class is used to ensure that the io_context member is
78 // initialised prior to the basic_socket_streambuf's basic_socket base class.
80 {
81 protected:
83  : default_io_context_(ctx)
84  {
85  }
86 
87  shared_ptr<io_context> default_io_context_;
88 };
89 
90 // A separate base class is used to ensure that the dynamically allocated
91 // buffers are constructed prior to the basic_socket_streambuf's basic_socket
92 // base class. This makes moving the socket is the last potentially throwing
93 // step in the streambuf's move constructor, giving the constructor a strong
94 // exception safety guarantee.
96 {
97 protected:
99  : get_buffer_(buffer_size),
100  put_buffer_(buffer_size)
101  {
102  }
103 
104  enum { buffer_size = 512 };
105  std::vector<char> get_buffer_;
106  std::vector<char> put_buffer_;
107 };
108 
109 } // namespace detail
110 
111 #if !defined(ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL)
112 #define ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL
113 
114 // Forward declaration with defaulted arguments.
115 template <typename Protocol,
116 #if defined(ASIO_HAS_BOOST_DATE_TIME) \
117  && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
118  typename Clock = boost::posix_time::ptime,
119  typename WaitTraits = time_traits<Clock> >
120 #else // defined(ASIO_HAS_BOOST_DATE_TIME)
121  // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
122  typename Clock = chrono::steady_clock,
123  typename WaitTraits = wait_traits<Clock> >
124 #endif // defined(ASIO_HAS_BOOST_DATE_TIME)
125  // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
127 
128 #endif // !defined(ASIO_BASIC_SOCKET_STREAMBUF_FWD_DECL)
129 
131 #if defined(GENERATING_DOCUMENTATION)
132 template <typename Protocol,
133  typename Clock = chrono::steady_clock,
134  typename WaitTraits = wait_traits<Clock> >
135 #else // defined(GENERATING_DOCUMENTATION)
136 template <typename Protocol, typename Clock, typename WaitTraits>
137 #endif // defined(GENERATING_DOCUMENTATION)
139  : public std::streambuf,
142 #if defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
143  private basic_socket<Protocol>
144 #else // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
145  public basic_socket<Protocol>
146 #endif // defined(ASIO_NO_DEPRECATED) || defined(GENERATING_DOCUMENTATION)
147 {
148 private:
149  // These typedefs are intended keep this class's implementation independent
150  // of whether it's using Boost.DateClock, Boost.Chrono or std::chrono.
151 #if defined(ASIO_HAS_BOOST_DATE_TIME) \
152  && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
153  typedef WaitTraits traits_helper;
154 #else // defined(ASIO_HAS_BOOST_DATE_TIME)
155  // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
156  typedef detail::chrono_time_traits<Clock, WaitTraits> traits_helper;
157 #endif // defined(ASIO_HAS_BOOST_DATE_TIME)
158  // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
159 
160 public:
162  typedef Protocol protocol_type;
163 
165  typedef typename Protocol::endpoint endpoint_type;
166 
168  typedef Clock clock_type;
169 
170 #if defined(GENERATING_DOCUMENTATION)
171  typedef typename WaitTraits::time_type time_type;
173 
175  typedef typename WaitTraits::time_point time_point;
176 
178  typedef typename WaitTraits::duration_type duration_type;
179 
181  typedef typename WaitTraits::duration duration;
182 #else
183 # if !defined(ASIO_NO_DEPRECATED)
184  typedef typename traits_helper::time_type time_type;
185  typedef typename traits_helper::duration_type duration_type;
186 # endif // !defined(ASIO_NO_DEPRECATED)
187  typedef typename traits_helper::time_type time_point;
188  typedef typename traits_helper::duration_type duration;
189 #endif
190 
193  : detail::socket_streambuf_io_context(new io_context),
194  basic_socket<Protocol>(*default_io_context_),
195  expiry_time_(max_expiry_time())
196  {
197  init_buffers();
198  }
199 
200 #if defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
204  basic_socket<Protocol>(std::move(s)),
205  expiry_time_(max_expiry_time())
206  {
207  init_buffers();
208  }
209 
213  basic_socket<Protocol>(std::move(other.socket())),
214  ec_(other.ec_),
215  expiry_time_(other.expiry_time_)
216  {
217  get_buffer_.swap(other.get_buffer_);
218  put_buffer_.swap(other.put_buffer_);
219  setg(other.eback(), other.gptr(), other.egptr());
220  setp(other.pptr(), other.epptr());
221  other.ec_ = asio::error_code();
222  other.expiry_time_ = max_expiry_time();
223  other.init_buffers();
224  }
225 
228  {
229  this->close();
230  socket() = std::move(other.socket());
231  detail::socket_streambuf_io_context::operator=(other);
232  ec_ = other.ec_;
233  expiry_time_ = other.expiry_time_;
234  get_buffer_.swap(other.get_buffer_);
235  put_buffer_.swap(other.put_buffer_);
236  setg(other.eback(), other.gptr(), other.egptr());
237  setp(other.pptr(), other.epptr());
238  other.ec_ = asio::error_code();
239  other.expiry_time_ = max_expiry_time();
240  other.put_buffer_.resize(buffer_size);
241  other.init_buffers();
242  return *this;
243  }
244 #endif // defined(ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
245 
248  {
249  if (pptr() != pbase())
250  overflow(traits_type::eof());
251  }
252 
254 
260  basic_socket_streambuf* connect(const endpoint_type& endpoint)
261  {
262  init_buffers();
263  ec_ = asio::error_code();
264  this->connect_to_endpoints(&endpoint, &endpoint + 1);
265  return !ec_ ? this : 0;
266  }
267 
268 #if defined(GENERATING_DOCUMENTATION)
269 
278  template <typename T1, ..., typename TN>
279  basic_socket_streambuf* connect(T1 t1, ..., TN tn);
280 #elif defined(ASIO_HAS_VARIADIC_TEMPLATES)
281  template <typename... T>
283  {
284  init_buffers();
285  typedef typename Protocol::resolver resolver_type;
286  resolver_type resolver(socket().get_executor());
287  connect_to_endpoints(resolver.resolve(x..., ec_));
288  return !ec_ ? this : 0;
289  }
290 #else
291  ASIO_VARIADIC_GENERATE(ASIO_PRIVATE_CONNECT_DEF)
292 #endif
293 
295 
300  {
301  sync();
302  socket().close(ec_);
303  if (!ec_)
304  init_buffers();
305  return !ec_ ? this : 0;
306  }
307 
310  {
311  return *this;
312  }
313 
315 
319  const asio::error_code& error() const
320  {
321  return ec_;
322  }
323 
324 #if !defined(ASIO_NO_DEPRECATED)
325 
331  const asio::error_code& puberror() const
332  {
333  return error();
334  }
335 
338 
342  time_point expires_at() const
343  {
344  return expiry_time_;
345  }
346 #endif // !defined(ASIO_NO_DEPRECATED)
347 
349 
353  time_point expiry() const
354  {
355  return expiry_time_;
356  }
357 
359 
367  void expires_at(const time_point& expiry_time)
368  {
369  expiry_time_ = expiry_time;
370  }
371 
373 
381  void expires_after(const duration& expiry_time)
382  {
383  expiry_time_ = traits_helper::add(traits_helper::now(), expiry_time);
384  }
385 
386 #if !defined(ASIO_NO_DEPRECATED)
387 
392  duration expires_from_now() const
393  {
394  return traits_helper::subtract(expires_at(), traits_helper::now());
395  }
396 
399 
407  void expires_from_now(const duration& expiry_time)
408  {
409  expiry_time_ = traits_helper::add(traits_helper::now(), expiry_time);
410  }
411 #endif // !defined(ASIO_NO_DEPRECATED)
412 
413 protected:
414  int_type underflow()
415  {
416 #if defined(ASIO_WINDOWS_RUNTIME)
417  ec_ = asio::error::operation_not_supported;
418  return traits_type::eof();
419 #else // defined(ASIO_WINDOWS_RUNTIME)
420  if (gptr() != egptr())
421  return traits_type::eof();
422 
423  for (;;)
424  {
425  // Check if we are past the expiry time.
426  if (traits_helper::less_than(expiry_time_, traits_helper::now()))
427  {
428  ec_ = asio::error::timed_out;
429  return traits_type::eof();
430  }
431 
432  // Try to complete the operation without blocking.
433  if (!socket().native_non_blocking())
434  socket().native_non_blocking(true, ec_);
436  bufs(asio::buffer(get_buffer_) + putback_max);
437  detail::signed_size_type bytes = detail::socket_ops::recv(
438  socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_);
439 
440  // Check if operation succeeded.
441  if (bytes > 0)
442  {
443  setg(&get_buffer_[0], &get_buffer_[0] + putback_max,
444  &get_buffer_[0] + putback_max + bytes);
445  return traits_type::to_int_type(*gptr());
446  }
447 
448  // Check for EOF.
449  if (bytes == 0)
450  {
451  ec_ = asio::error::eof;
452  return traits_type::eof();
453  }
454 
455  // Operation failed.
456  if (ec_ != asio::error::would_block
457  && ec_ != asio::error::try_again)
458  return traits_type::eof();
459 
460  // Wait for socket to become ready.
461  if (detail::socket_ops::poll_read(
462  socket().native_handle(), 0, timeout(), ec_) < 0)
463  return traits_type::eof();
464  }
465 #endif // defined(ASIO_WINDOWS_RUNTIME)
466  }
467 
468  int_type overflow(int_type c)
469  {
470 #if defined(ASIO_WINDOWS_RUNTIME)
471  ec_ = asio::error::operation_not_supported;
472  return traits_type::eof();
473 #else // defined(ASIO_WINDOWS_RUNTIME)
474  char_type ch = traits_type::to_char_type(c);
475 
476  // Determine what needs to be sent.
477  const_buffer output_buffer;
478  if (put_buffer_.empty())
479  {
480  if (traits_type::eq_int_type(c, traits_type::eof()))
481  return traits_type::not_eof(c); // Nothing to do.
482  output_buffer = asio::buffer(&ch, sizeof(char_type));
483  }
484  else
485  {
486  output_buffer = asio::buffer(pbase(),
487  (pptr() - pbase()) * sizeof(char_type));
488  }
489 
490  while (output_buffer.size() > 0)
491  {
492  // Check if we are past the expiry time.
493  if (traits_helper::less_than(expiry_time_, traits_helper::now()))
494  {
495  ec_ = asio::error::timed_out;
496  return traits_type::eof();
497  }
498 
499  // Try to complete the operation without blocking.
500  if (!socket().native_non_blocking())
501  socket().native_non_blocking(true, ec_);
503  const_buffer, const_buffer> bufs(output_buffer);
504  detail::signed_size_type bytes = detail::socket_ops::send(
505  socket().native_handle(), bufs.buffers(), bufs.count(), 0, ec_);
506 
507  // Check if operation succeeded.
508  if (bytes > 0)
509  {
510  output_buffer += static_cast<std::size_t>(bytes);
511  continue;
512  }
513 
514  // Operation failed.
515  if (ec_ != asio::error::would_block
516  && ec_ != asio::error::try_again)
517  return traits_type::eof();
518 
519  // Wait for socket to become ready.
520  if (detail::socket_ops::poll_write(
521  socket().native_handle(), 0, timeout(), ec_) < 0)
522  return traits_type::eof();
523  }
524 
525  if (!put_buffer_.empty())
526  {
527  setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());
528 
529  // If the new character is eof then our work here is done.
530  if (traits_type::eq_int_type(c, traits_type::eof()))
531  return traits_type::not_eof(c);
532 
533  // Add the new character to the output buffer.
534  *pptr() = ch;
535  pbump(1);
536  }
537 
538  return c;
539 #endif // defined(ASIO_WINDOWS_RUNTIME)
540  }
541 
542  int sync()
543  {
544  return overflow(traits_type::eof());
545  }
546 
547  std::streambuf* setbuf(char_type* s, std::streamsize n)
548  {
549  if (pptr() == pbase() && s == 0 && n == 0)
550  {
551  put_buffer_.clear();
552  setp(0, 0);
553  sync();
554  return this;
555  }
556 
557  return 0;
558  }
559 
560 private:
561  // Disallow copying and assignment.
562  basic_socket_streambuf(const basic_socket_streambuf&) ASIO_DELETED;
563  basic_socket_streambuf& operator=(
564  const basic_socket_streambuf&) ASIO_DELETED;
565 
566  void init_buffers()
567  {
568  setg(&get_buffer_[0],
569  &get_buffer_[0] + putback_max,
570  &get_buffer_[0] + putback_max);
571 
572  if (put_buffer_.empty())
573  setp(0, 0);
574  else
575  setp(&put_buffer_[0], &put_buffer_[0] + put_buffer_.size());
576  }
577 
578  int timeout() const
579  {
580  int64_t msec = traits_helper::to_posix_duration(
581  traits_helper::subtract(expiry_time_,
582  traits_helper::now())).total_milliseconds();
583  if (msec > (std::numeric_limits<int>::max)())
584  msec = (std::numeric_limits<int>::max)();
585  else if (msec < 0)
586  msec = 0;
587  return static_cast<int>(msec);
588  }
589 
590  template <typename EndpointSequence>
591  void connect_to_endpoints(const EndpointSequence& endpoints)
592  {
593  this->connect_to_endpoints(endpoints.begin(), endpoints.end());
594  }
595 
596  template <typename EndpointIterator>
597  void connect_to_endpoints(EndpointIterator begin, EndpointIterator end)
598  {
599 #if defined(ASIO_WINDOWS_RUNTIME)
600  ec_ = asio::error::operation_not_supported;
601 #else // defined(ASIO_WINDOWS_RUNTIME)
602  if (ec_)
603  return;
604 
605  ec_ = asio::error::not_found;
606  for (EndpointIterator i = begin; i != end; ++i)
607  {
608  // Check if we are past the expiry time.
609  if (traits_helper::less_than(expiry_time_, traits_helper::now()))
610  {
611  ec_ = asio::error::timed_out;
612  return;
613  }
614 
615  // Close and reopen the socket.
616  typename Protocol::endpoint ep(*i);
617  socket().close(ec_);
618  socket().open(ep.protocol(), ec_);
619  if (ec_)
620  continue;
621 
622  // Try to complete the operation without blocking.
623  if (!socket().native_non_blocking())
624  socket().native_non_blocking(true, ec_);
625  detail::socket_ops::connect(socket().native_handle(),
626  ep.data(), ep.size(), ec_);
627 
628  // Check if operation succeeded.
629  if (!ec_)
630  return;
631 
632  // Operation failed.
633  if (ec_ != asio::error::in_progress
634  && ec_ != asio::error::would_block)
635  continue;
636 
637  // Wait for socket to become ready.
638  if (detail::socket_ops::poll_connect(
639  socket().native_handle(), timeout(), ec_) < 0)
640  continue;
641 
642  // Get the error code from the connect operation.
643  int connect_error = 0;
644  size_t connect_error_len = sizeof(connect_error);
645  if (detail::socket_ops::getsockopt(socket().native_handle(), 0,
646  SOL_SOCKET, SO_ERROR, &connect_error, &connect_error_len, ec_)
647  == detail::socket_error_retval)
648  return;
649 
650  // Check the result of the connect operation.
651  ec_ = asio::error_code(connect_error,
652  asio::error::get_system_category());
653  if (!ec_)
654  return;
655  }
656 #endif // defined(ASIO_WINDOWS_RUNTIME)
657  }
658 
659  // Helper function to get the maximum expiry time.
660  static time_point max_expiry_time()
661  {
662 #if defined(ASIO_HAS_BOOST_DATE_TIME) \
663  && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
664  return boost::posix_time::pos_infin;
665 #else // defined(ASIO_HAS_BOOST_DATE_TIME)
666  // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
667  return (time_point::max)();
668 #endif // defined(ASIO_HAS_BOOST_DATE_TIME)
669  // && defined(ASIO_USE_BOOST_DATE_TIME_FOR_SOCKET_IOSTREAM)
670  }
671 
672  enum { putback_max = 8 };
673  asio::error_code ec_;
674  time_point expiry_time_;
675 };
676 
677 } // namespace asio
678 
679 #include "asio/detail/pop_options.hpp"
680 
681 #if !defined(ASIO_HAS_VARIADIC_TEMPLATES)
682 # undef ASIO_PRIVATE_CONNECT_DEF
683 #endif // !defined(ASIO_HAS_VARIADIC_TEMPLATES)
684 
685 #endif // !defined(ASIO_NO_IOSTREAM)
686 
687 #endif // ASIO_BASIC_SOCKET_STREAMBUF_HPP
Protocol protocol_type
The protocol type.
Definition: basic_socket_streambuf.hpp:162
const asio::error_code & puberror() const
(Deprecated: Use error().) Get the last error associated with the stream buffer.
Definition: basic_socket_streambuf.hpp:331
time_point expires_at() const
(Deprecated: Use expiry().) Get the stream buffer&#39;s expiry time as an absolute time.
Definition: basic_socket_streambuf.hpp:342
time_point expiry() const
Get the stream buffer&#39;s expiry time as an absolute time.
Definition: basic_socket_streambuf.hpp:353
Definition: basic_socket_streambuf.hpp:79
Holds a buffer that cannot be modified.
Definition: buffer.hpp:226
basic_socket< Protocol > & socket()
Get a reference to the underlying socket.
Definition: basic_socket_streambuf.hpp:309
Definition: blocking.hpp:208
Provides stream-oriented socket functionality.
Definition: basic_stream_socket.hpp:36
Definition: chrono_time_traits.hpp:34
duration expires_from_now() const
(Deprecated: Use expiry().) Get the stream buffer&#39;s expiry time relative to now.
Definition: basic_socket_streambuf.hpp:392
basic_socket_streambuf()
Construct a basic_socket_streambuf without establishing a connection.
Definition: basic_socket_streambuf.hpp:192
void expires_from_now(const duration &expiry_time)
(Deprecated: Use expires_after().) Set the stream buffer&#39;s expiry time relative to now...
Definition: basic_socket_streambuf.hpp:407
void expires_after(const duration &expiry_time)
Set the stream buffer&#39;s expiry time relative to now.
Definition: basic_socket_streambuf.hpp:381
std::size_t size() const ASIO_NOEXCEPT
Get the size of the memory range.
Definition: buffer.hpp:279
Provides core I/O functionality.
Definition: io_context.hpp:211
Definition: chrono.h:284
ASIO_MUTABLE_BUFFER buffer(const mutable_buffer &b) ASIO_NOEXCEPT
Create a new modifiable buffer from an existing buffer.
Definition: buffer.hpp:909
Definition: buffer_sequence_adapter.hpp:103
Protocol::endpoint endpoint_type
The endpoint type.
Definition: basic_socket_streambuf.hpp:165
Protocol::endpoint connect(basic_socket< Protocol, Executor > &s, const EndpointSequence &endpoints, typename enable_if< is_endpoint_sequence< EndpointSequence >::value >::type *=0)
Establishes a socket connection by trying each endpoint in a sequence.
Definition: connect.hpp:106
Wait traits suitable for use with the basic_waitable_timer class template.
Definition: netfwd.hpp:111
virtual ~basic_socket_streambuf()
Destructor flushes buffered data.
Definition: basic_socket_streambuf.hpp:247
Class to represent an error code value.
Definition: error_code.hpp:80
void expires_at(const time_point &expiry_time)
Set the stream buffer&#39;s expiry time as an absolute time.
Definition: basic_socket_streambuf.hpp:367
basic_socket_streambuf * connect(const endpoint_type &endpoint)
Establish a connection.
Definition: basic_socket_streambuf.hpp:260
Provides socket functionality.
Definition: basic_socket.hpp:52
Definition: basic_socket_streambuf.hpp:95
Iostream streambuf for a socket.
Definition: basic_socket_streambuf.hpp:126
Clock clock_type
The clock type.
Definition: basic_socket_streambuf.hpp:168
Definition: any_io_executor.hpp:28
basic_socket_streambuf * close()
Close the connection.
Definition: basic_socket_streambuf.hpp:299
const asio::error_code & error() const
Get the last error associated with the stream buffer.
Definition: basic_socket_streambuf.hpp:319
Definition: format.h:3611