Chris@16: // Chris@16: // basic_streambuf.hpp Chris@16: // ~~~~~~~~~~~~~~~~~~~ Chris@16: // Chris@101: // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) Chris@16: // Chris@16: // Distributed under the Boost Software License, Version 1.0. (See accompanying Chris@16: // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: // Chris@16: Chris@16: #ifndef BOOST_ASIO_BASIC_STREAMBUF_HPP Chris@16: #define BOOST_ASIO_BASIC_STREAMBUF_HPP Chris@16: Chris@16: #if defined(_MSC_VER) && (_MSC_VER >= 1200) Chris@16: # pragma once Chris@16: #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) Chris@16: Chris@16: #include Chris@16: Chris@16: #if !defined(BOOST_ASIO_NO_IOSTREAM) Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: Chris@16: namespace boost { Chris@16: namespace asio { Chris@16: Chris@16: /// Automatically resizable buffer class based on std::streambuf. Chris@16: /** Chris@16: * The @c basic_streambuf class is derived from @c std::streambuf to associate Chris@16: * the streambuf's input and output sequences with one or more character Chris@16: * arrays. These character arrays are internal to the @c basic_streambuf Chris@16: * object, but direct access to the array elements is provided to permit them Chris@16: * to be used efficiently with I/O operations. Characters written to the output Chris@16: * sequence of a @c basic_streambuf object are appended to the input sequence Chris@16: * of the same object. Chris@16: * Chris@16: * The @c basic_streambuf class's public interface is intended to permit the Chris@16: * following implementation strategies: Chris@16: * Chris@16: * @li A single contiguous character array, which is reallocated as necessary Chris@16: * to accommodate changes in the size of the character sequence. This is the Chris@16: * implementation approach currently used in Asio. Chris@16: * Chris@16: * @li A sequence of one or more character arrays, where each array is of the Chris@16: * same size. Additional character array objects are appended to the sequence Chris@16: * to accommodate changes in the size of the character sequence. Chris@16: * Chris@16: * @li A sequence of one or more character arrays of varying sizes. Additional Chris@16: * character array objects are appended to the sequence to accommodate changes Chris@16: * in the size of the character sequence. Chris@16: * Chris@16: * The constructor for basic_streambuf accepts a @c size_t argument specifying Chris@16: * the maximum of the sum of the sizes of the input sequence and output Chris@16: * sequence. During the lifetime of the @c basic_streambuf object, the following Chris@16: * invariant holds: Chris@16: * @code size() <= max_size()@endcode Chris@16: * Any member function that would, if successful, cause the invariant to be Chris@16: * violated shall throw an exception of class @c std::length_error. Chris@16: * Chris@16: * The constructor for @c basic_streambuf takes an Allocator argument. A copy Chris@16: * of this argument is used for any memory allocation performed, by the Chris@16: * constructor and by all member functions, during the lifetime of each @c Chris@16: * basic_streambuf object. Chris@16: * Chris@16: * @par Examples Chris@16: * Writing directly from an streambuf to a socket: Chris@16: * @code Chris@16: * boost::asio::streambuf b; Chris@16: * std::ostream os(&b); Chris@16: * os << "Hello, World!\n"; Chris@16: * Chris@16: * // try sending some data in input sequence Chris@16: * size_t n = sock.send(b.data()); Chris@16: * Chris@16: * b.consume(n); // sent data is removed from input sequence Chris@16: * @endcode Chris@16: * Chris@16: * Reading from a socket directly into a streambuf: Chris@16: * @code Chris@16: * boost::asio::streambuf b; Chris@16: * Chris@16: * // reserve 512 bytes in output sequence Chris@16: * boost::asio::streambuf::mutable_buffers_type bufs = b.prepare(512); Chris@16: * Chris@16: * size_t n = sock.receive(bufs); Chris@16: * Chris@16: * // received data is "committed" from output sequence to input sequence Chris@16: * b.commit(n); Chris@16: * Chris@16: * std::istream is(&b); Chris@16: * std::string s; Chris@16: * is >> s; Chris@16: * @endcode Chris@16: */ Chris@16: #if defined(GENERATING_DOCUMENTATION) Chris@16: template > Chris@16: #else Chris@16: template Chris@16: #endif Chris@16: class basic_streambuf Chris@16: : public std::streambuf, Chris@16: private noncopyable Chris@16: { Chris@16: public: Chris@16: #if defined(GENERATING_DOCUMENTATION) Chris@16: /// The type used to represent the input sequence as a list of buffers. Chris@16: typedef implementation_defined const_buffers_type; Chris@16: Chris@16: /// The type used to represent the output sequence as a list of buffers. Chris@16: typedef implementation_defined mutable_buffers_type; Chris@16: #else Chris@16: typedef boost::asio::const_buffers_1 const_buffers_type; Chris@16: typedef boost::asio::mutable_buffers_1 mutable_buffers_type; Chris@16: #endif Chris@16: Chris@16: /// Construct a basic_streambuf object. Chris@16: /** Chris@16: * Constructs a streambuf with the specified maximum size. The initial size Chris@16: * of the streambuf's input sequence is 0. Chris@16: */ Chris@16: explicit basic_streambuf( Chris@16: std::size_t maximum_size = (std::numeric_limits::max)(), Chris@16: const Allocator& allocator = Allocator()) Chris@16: : max_size_(maximum_size), Chris@16: buffer_(allocator) Chris@16: { Chris@16: std::size_t pend = (std::min)(max_size_, buffer_delta); Chris@16: buffer_.resize((std::max)(pend, 1)); Chris@16: setg(&buffer_[0], &buffer_[0], &buffer_[0]); Chris@16: setp(&buffer_[0], &buffer_[0] + pend); Chris@16: } Chris@16: Chris@16: /// Get the size of the input sequence. Chris@16: /** Chris@16: * @returns The size of the input sequence. The value is equal to that Chris@16: * calculated for @c s in the following code: Chris@16: * @code Chris@16: * size_t s = 0; Chris@16: * const_buffers_type bufs = data(); Chris@16: * const_buffers_type::const_iterator i = bufs.begin(); Chris@16: * while (i != bufs.end()) Chris@16: * { Chris@16: * const_buffer buf(*i++); Chris@16: * s += buffer_size(buf); Chris@16: * } Chris@16: * @endcode Chris@16: */ Chris@16: std::size_t size() const Chris@16: { Chris@16: return pptr() - gptr(); Chris@16: } Chris@16: Chris@16: /// Get the maximum size of the basic_streambuf. Chris@16: /** Chris@16: * @returns The allowed maximum of the sum of the sizes of the input sequence Chris@16: * and output sequence. Chris@16: */ Chris@16: std::size_t max_size() const Chris@16: { Chris@16: return max_size_; Chris@16: } Chris@16: Chris@16: /// Get a list of buffers that represents the input sequence. Chris@16: /** Chris@16: * @returns An object of type @c const_buffers_type that satisfies Chris@16: * ConstBufferSequence requirements, representing all character arrays in the Chris@16: * input sequence. Chris@16: * Chris@16: * @note The returned object is invalidated by any @c basic_streambuf member Chris@16: * function that modifies the input sequence or output sequence. Chris@16: */ Chris@16: const_buffers_type data() const Chris@16: { Chris@16: return boost::asio::buffer(boost::asio::const_buffer(gptr(), Chris@16: (pptr() - gptr()) * sizeof(char_type))); Chris@16: } Chris@16: Chris@16: /// Get a list of buffers that represents the output sequence, with the given Chris@16: /// size. Chris@16: /** Chris@16: * Ensures that the output sequence can accommodate @c n characters, Chris@16: * reallocating character array objects as necessary. Chris@16: * Chris@16: * @returns An object of type @c mutable_buffers_type that satisfies Chris@16: * MutableBufferSequence requirements, representing character array objects Chris@16: * at the start of the output sequence such that the sum of the buffer sizes Chris@16: * is @c n. Chris@16: * Chris@16: * @throws std::length_error If size() + n > max_size(). Chris@16: * Chris@16: * @note The returned object is invalidated by any @c basic_streambuf member Chris@16: * function that modifies the input sequence or output sequence. Chris@16: */ Chris@16: mutable_buffers_type prepare(std::size_t n) Chris@16: { Chris@16: reserve(n); Chris@16: return boost::asio::buffer(boost::asio::mutable_buffer( Chris@16: pptr(), n * sizeof(char_type))); Chris@16: } Chris@16: Chris@16: /// Move characters from the output sequence to the input sequence. Chris@16: /** Chris@16: * Appends @c n characters from the start of the output sequence to the input Chris@16: * sequence. The beginning of the output sequence is advanced by @c n Chris@16: * characters. Chris@16: * Chris@16: * Requires a preceding call prepare(x) where x >= n, and Chris@16: * no intervening operations that modify the input or output sequence. Chris@16: * Chris@101: * @note If @c n is greater than the size of the output sequence, the entire Chris@101: * output sequence is moved to the input sequence and no error is issued. Chris@16: */ Chris@16: void commit(std::size_t n) Chris@16: { Chris@16: if (pptr() + n > epptr()) Chris@16: n = epptr() - pptr(); Chris@16: pbump(static_cast(n)); Chris@16: setg(eback(), gptr(), pptr()); Chris@16: } Chris@16: Chris@16: /// Remove characters from the input sequence. Chris@16: /** Chris@16: * Removes @c n characters from the beginning of the input sequence. Chris@16: * Chris@101: * @note If @c n is greater than the size of the input sequence, the entire Chris@101: * input sequence is consumed and no error is issued. Chris@16: */ Chris@16: void consume(std::size_t n) Chris@16: { Chris@16: if (egptr() < pptr()) Chris@16: setg(&buffer_[0], gptr(), pptr()); Chris@16: if (gptr() + n > pptr()) Chris@16: n = pptr() - gptr(); Chris@16: gbump(static_cast(n)); Chris@16: } Chris@16: Chris@16: protected: Chris@16: enum { buffer_delta = 128 }; Chris@16: Chris@16: /// Override std::streambuf behaviour. Chris@16: /** Chris@16: * Behaves according to the specification of @c std::streambuf::underflow(). Chris@16: */ Chris@16: int_type underflow() Chris@16: { Chris@16: if (gptr() < pptr()) Chris@16: { Chris@16: setg(&buffer_[0], gptr(), pptr()); Chris@16: return traits_type::to_int_type(*gptr()); Chris@16: } Chris@16: else Chris@16: { Chris@16: return traits_type::eof(); Chris@16: } Chris@16: } Chris@16: Chris@16: /// Override std::streambuf behaviour. Chris@16: /** Chris@16: * Behaves according to the specification of @c std::streambuf::overflow(), Chris@16: * with the specialisation that @c std::length_error is thrown if appending Chris@16: * the character to the input sequence would require the condition Chris@16: * size() > max_size() to be true. Chris@16: */ Chris@16: int_type overflow(int_type c) Chris@16: { Chris@16: if (!traits_type::eq_int_type(c, traits_type::eof())) Chris@16: { Chris@16: if (pptr() == epptr()) Chris@16: { Chris@16: std::size_t buffer_size = pptr() - gptr(); Chris@16: if (buffer_size < max_size_ && max_size_ - buffer_size < buffer_delta) Chris@16: { Chris@16: reserve(max_size_ - buffer_size); Chris@16: } Chris@16: else Chris@16: { Chris@16: reserve(buffer_delta); Chris@16: } Chris@16: } Chris@16: Chris@16: *pptr() = traits_type::to_char_type(c); Chris@16: pbump(1); Chris@16: return c; Chris@16: } Chris@16: Chris@16: return traits_type::not_eof(c); Chris@16: } Chris@16: Chris@16: void reserve(std::size_t n) Chris@16: { Chris@16: // Get current stream positions as offsets. Chris@16: std::size_t gnext = gptr() - &buffer_[0]; Chris@16: std::size_t pnext = pptr() - &buffer_[0]; Chris@16: std::size_t pend = epptr() - &buffer_[0]; Chris@16: Chris@16: // Check if there is already enough space in the put area. Chris@16: if (n <= pend - pnext) Chris@16: { Chris@16: return; Chris@16: } Chris@16: Chris@16: // Shift existing contents of get area to start of buffer. Chris@16: if (gnext > 0) Chris@16: { Chris@16: pnext -= gnext; Chris@16: std::memmove(&buffer_[0], &buffer_[0] + gnext, pnext); Chris@16: } Chris@16: Chris@16: // Ensure buffer is large enough to hold at least the specified size. Chris@16: if (n > pend - pnext) Chris@16: { Chris@16: if (n <= max_size_ && pnext <= max_size_ - n) Chris@16: { Chris@16: pend = pnext + n; Chris@16: buffer_.resize((std::max)(pend, 1)); Chris@16: } Chris@16: else Chris@16: { Chris@16: std::length_error ex("boost::asio::streambuf too long"); Chris@16: boost::asio::detail::throw_exception(ex); Chris@16: } Chris@16: } Chris@16: Chris@16: // Update stream positions. Chris@16: setg(&buffer_[0], &buffer_[0], &buffer_[0] + pnext); Chris@16: setp(&buffer_[0] + pnext, &buffer_[0] + pend); Chris@16: } Chris@16: Chris@16: private: Chris@16: std::size_t max_size_; Chris@16: std::vector buffer_; Chris@16: Chris@16: // Helper function to get the preferred size for reading data. Chris@16: friend std::size_t read_size_helper( Chris@16: basic_streambuf& sb, std::size_t max_size) Chris@16: { Chris@16: return std::min( Chris@16: std::max(512, sb.buffer_.capacity() - sb.size()), Chris@16: std::min(max_size, sb.max_size() - sb.size())); Chris@16: } Chris@16: }; Chris@16: Chris@16: // Helper function to get the preferred size for reading data. Used for any Chris@16: // user-provided specialisations of basic_streambuf. Chris@16: template Chris@16: inline std::size_t read_size_helper( Chris@16: basic_streambuf& sb, std::size_t max_size) Chris@16: { Chris@16: return std::min(512, Chris@16: std::min(max_size, sb.max_size() - sb.size())); Chris@16: } Chris@16: Chris@16: } // namespace asio Chris@16: } // namespace boost Chris@16: Chris@16: #include Chris@16: Chris@16: #endif // !defined(BOOST_ASIO_NO_IOSTREAM) Chris@16: Chris@16: #endif // BOOST_ASIO_BASIC_STREAMBUF_HPP