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