diff --git a/Jamroot b/Jamroot new file mode 100644 index 0000000000..f7198a97c8 --- /dev/null +++ b/Jamroot @@ -0,0 +1,68 @@ +# +# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +# + +import os ; +import feature ; +import boost ; + +boost.use-project ; + +if [ os.name ] = SOLARIS +{ + lib socket ; + lib nsl ; +} +else if [ os.name ] = NT +{ + lib ws2_32 ; + lib mswsock ; +} +else if [ os.name ] = HPUX +{ + lib ipv6 ; +} +else if [ os.name ] = QNXNTO +{ + lib socket ; +} +else if [ os.name ] = HAIKU +{ + lib network ; +} + +build-project test/asio ; + +project beast + : requirements + . + #/boost//headers + /boost/system//boost_system + BOOST_ALL_NO_LIB=1 + multi + static + static + LINUX:_XOPEN_SOURCE=600 + LINUX:_GNU_SOURCE=1 + SOLARIS:_XOPEN_SOURCE=500 + SOLARIS:__EXTENSIONS__ + SOLARIS:socket + SOLARIS:nsl + NT:_WIN32_WINNT=0x0501 + NT,cw:ws2_32 + NT,cw:mswsock + NT,gcc:ws2_32 + NT,gcc:mswsock + NT,gcc-cygwin:__USE_W32_SOCKETS + HPUX,gcc:_XOPEN_SOURCE_EXTENDED + HPUX:ipv6 + QNXNTO:socket + HAIKU:network + : usage-requirements + . + : + build-dir bin + ; diff --git a/ReadMe.md b/ReadMe0.md similarity index 100% rename from ReadMe.md rename to ReadMe0.md diff --git a/beast/asio/error.h b/beast/asio.h similarity index 63% rename from beast/asio/error.h rename to beast/asio.h index 18ab67cc94..607b12a7c3 100644 --- a/beast/asio/error.h +++ b/beast/asio.h @@ -17,29 +17,22 @@ */ //============================================================================== -#ifndef BEAST_ASIO_ERROR_H_INCLUDED -#define BEAST_ASIO_ERROR_H_INCLUDED +#ifndef BEAST_ASIO_H_INCLUDED +#define BEAST_ASIO_H_INCLUDED -#include -#include - -namespace beast { -namespace asio { - -/** Returns `true` if the error code is a SSL "short read." */ -inline -bool -is_short_read (boost::system::error_code const& ec) -{ - return (ec.category() == boost::asio::error::get_ssl_category()) - && (ERR_GET_REASON(ec.value()) == SSL_R_SHORT_READ); -} - -/** Returns a human readable message if the error code is SSL related. */ -std::string -asio_message(boost::system::error_code const& ec); - -} -} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #endif diff --git a/beast/asio/append_buffers.h b/beast/asio/append_buffers.h new file mode 100644 index 0000000000..297ca41396 --- /dev/null +++ b/beast/asio/append_buffers.h @@ -0,0 +1,517 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_APPEND_BUFFERS_H_INLUDED +#define BEAST_ASIO_APPEND_BUFFERS_H_INLUDED + +#include +#include +#include +#include +#include +#include +#include + +namespace beast { + +namespace detail { + +template +class append_buffers_helper +{ + std::tuple bs_; + +public: + using value_type = ValueType; + + class const_iterator; + + append_buffers_helper(append_buffers_helper&&) = default; + append_buffers_helper(append_buffers_helper const&) = default; + append_buffers_helper& operator=(append_buffers_helper&&) = default; + append_buffers_helper& operator=(append_buffers_helper const&) = default; + + explicit + append_buffers_helper(Bs const&... bs) + : bs_(bs...) + { + } + + const_iterator + begin() const; + + const_iterator + end() const; +}; + +template +std::size_t constexpr +max_sizeof() +{ + return sizeof(U); +} + +template +std::size_t constexpr +max_sizeof() +{ + return + max_sizeof() > max_sizeof() ? + max_sizeof() : max_sizeof(); +} + +template +class append_buffers_helper< + ValueType, Bs...>::const_iterator +{ + std::size_t n_; + std::tuple const* bs_; + std::array()> buf_; + + friend class append_buffers_helper; + + template + using C = std::integral_constant; + + template + using iter_t = typename std::tuple_element_t< + I, std::tuple>::const_iterator; + + template + iter_t& + iter() + { + return *reinterpret_cast< + iter_t*>(buf_.data()); + } + + template + iter_t const& + iter() const + { + return *reinterpret_cast< + iter_t const*>(buf_.data()); + } + +public: + using value_type = ValueType; + using pointer = value_type const*; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + ~const_iterator(); + const_iterator(); + const_iterator(const_iterator&& other); + const_iterator(const_iterator const& other); + const_iterator& operator=(const_iterator&& other); + const_iterator& operator=(const_iterator const& other); + + bool + operator==(const_iterator const& other) const; + + bool + operator!=(const_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*() const; + + pointer + operator->() const = delete; + + const_iterator& + operator++(); + + const_iterator + operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + + const_iterator& + operator--(); + + const_iterator + operator--(int) + { + auto temp = *this; + --(*this); + return temp; + } + +private: + const_iterator( + std::tuple const& bs, bool at_end); + + void + construct(C) + { + auto constexpr I = sizeof...(Bs); + n_ = I; + } + + template + void + construct(C) + { + if(std::get(*bs_).begin() != + std::get(*bs_).end()) + { + n_ = I; + new(buf_.data()) iter_t{ + std::get(*bs_).begin()}; + return; + } + construct(C{}); + } + + void + destroy(C) + { + return; + } + + template + void + destroy(C) + { + if(n_ == I) + { + using Iter = iter_t; + iter().~Iter(); + return; + } + destroy(C{}); + } + + void + move(C, const_iterator&&) + { + return; + } + + template + void + move(C, const_iterator&& other) + { + if(n_ == I) + { + new(buf_.data()) iter_t{ + std::move(other.iter())}; + return; + } + move(C{}, std::move(other)); + } + + void + copy(C, const_iterator const&) + { + return; + } + + template + void + copy(C, const_iterator const& other) + { + if(n_ == I) + { + new(buf_.data()) iter_t{ + other.iter()}; + return; + } + copy(C{}, other); + } + + bool + equal(C, + const_iterator const&) const + { + return true; + } + + template + bool + equal(C, const_iterator const& other) const + { + if(n_ == I) + return iter() == other.iter(); + return equal(C{}, other); + } + + [[noreturn]] + reference + dereference(C) const + { + throw std::logic_error("invalid iterator"); + } + + template + reference + dereference(C) const + { + if(n_ == I) + return *iter(); + return dereference(C{}); + } + + [[noreturn]] + void + increment(C) + { + throw std::logic_error("invalid iterator"); + } + + template + void + increment(C) + { + if(n_ == I) + { + if(++iter() != + std::get(*bs_).end()) + return; + using Iter = iter_t; + iter().~Iter(); + return construct(C{}); + } + increment(C{}); + } + + void + decrement(C) + { + auto constexpr I = sizeof...(Bs); + if(n_ == I) + { + --n_; + new(buf_.data()) iter_t{ + std::get(*bs_).end()}; + } + decrement(C{}); + } + + void + decrement(C<0>) + { + auto constexpr I = 0; + if(iter() != std::get(*bs_).begin()) + { + --iter(); + return; + } + throw std::logic_error("invalid iterator"); + } + + template + void + decrement(C) + { + if(n_ == I) + { + if(iter() != std::get(*bs_).begin()) + { + --iter(); + return; + } + --n_; + using Iter = iter_t; + iter().~Iter(); + new(buf_.data()) iter_t{ + std::get(*bs_).end()}; + } + decrement(C{}); + } +}; + +//------------------------------------------------------------------------------ + +template +append_buffers_helper:: +const_iterator::~const_iterator() +{ + destroy(C<0>{}); +} + +template +append_buffers_helper:: +const_iterator::const_iterator() + : n_(sizeof...(Bs)) + , bs_(nullptr) +{ +} + +template +append_buffers_helper:: +const_iterator::const_iterator( + std::tuple const& bs, bool at_end) + : bs_(&bs) +{ + if(at_end) + n_ = sizeof...(Bs); + else + construct(C<0>{}); +} + +template +append_buffers_helper:: +const_iterator::const_iterator(const_iterator&& other) + : n_(other.n_) + , bs_(other.bs_) +{ + move(C<0>{}, std::move(other)); +} + +template +append_buffers_helper:: +const_iterator::const_iterator(const_iterator const& other) + : n_(other.n_) + , bs_(other.bs_) +{ + copy(C<0>{}, other); +} + +template +auto +append_buffers_helper:: +const_iterator::operator=(const_iterator&& other) -> + const_iterator& +{ + if(&other == this) + return *this; + destroy(C<0>{}); + n_ = other.n_; + bs_ = other.bs_; + move(C<0>{}, std::move(other)); + return *this; +} + +template +auto +append_buffers_helper:: +const_iterator::operator=(const_iterator const& other) -> +const_iterator& +{ + if(&other == this) + return *this; + destroy(C<0>{}); + n_ = other.n_; + bs_ = other.bs_; + copy(C<0>{}, other); + return *this; +} + +template +bool +append_buffers_helper:: +const_iterator::operator==(const_iterator const& other) const +{ + if(bs_ != other.bs_) + return false; + if(n_ != other.n_) + return false; + return equal(C<0>{}, other); +} + +template +auto +append_buffers_helper:: +const_iterator::operator*() const -> + reference +{ + return dereference(C<0>{}); +} + +template +auto +append_buffers_helper:: +const_iterator::operator++() -> + const_iterator& +{ + increment(C<0>{}); + return *this; +} + +template +auto +append_buffers_helper:: +const_iterator::operator--() -> + const_iterator& +{ + decrement(C{}); + return *this; +} + +template +auto +append_buffers_helper::begin() const -> + const_iterator +{ + return const_iterator(bs_, false); +} + +template +auto +append_buffers_helper::end() const -> + const_iterator +{ + return const_iterator(bs_, true); +} + +} // detail + +//------------------------------------------------------------------------------ + +/** Concatenate 2 or more buffer sequences to form a `ConstBufferSequence`. + + This function returns a `ConstBufferSequence` that when iterated, + efficiently concatenates the input buffer sequences. Copies of the + arguments passed will be made; however, the returned object does + not take ownership of the underlying memory. The application is still + responsible for managing the lifetime of the referenced memory. + + @param buffers The list of buffer sequences to concatenate. + + @return A new `ConstBufferSequence` that represents the concatenation + of the input buffer sequences. +*/ +#if GENERATING_DOCS +template +implementation_defined +append_buffers(BufferSequence const&... buffers) +#else +template +auto +append_buffers(B1 const& b1, B2 const& b2, Bn const&... bn) +#endif +{ + return detail::append_buffers_helper< + boost::asio::const_buffer, + B1, B2, Bn...>(b1, b2, bn...); +} + +} // beast + +#endif diff --git a/beast/asio/async_completion.h b/beast/asio/async_completion.h new file mode 100644 index 0000000000..da9493ff1f --- /dev/null +++ b/beast/asio/async_completion.h @@ -0,0 +1,96 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_ASYNC_COMPLETION_H_INLUDED +#define BEAST_ASIO_ASYNC_COMPLETION_H_INLUDED + +#include +#include +#include +#include +#include + +namespace beast { + +/** Completion helper for implementing the extensible asynchronous model. + + This class template is used to transform caller-provided + completion tokens in calls to asynchronous initiation + functions. The transformation allows customization of + the return type of the initiating function, and the type + of the final handler. + + @tparam CompletionToken A CompletionHandler, or a user defined type + with specializations for customizing the return type (for example, + `boost::asio::use_future` or `boost::asio::yield_context`). + + @tparam Signature The callable signature of the final completion handler. + + Usage: + @code + template + auto + async_initfn(..., CompletionToken&& token) + { + async_completion completion(token); + ... + return completion.result.get(); + } + @endcode + + See + Library Foundations For Asynchronous Operations +*/ + +template +struct async_completion +{ + /** The type of the final handler. + + Objects of this type will be callable with the + specified signature. + */ + using handler_type = + typename boost::asio::handler_type< + CompletionToken, Signature>::type; + + /** Construct the completion helper. + + @param token The completion token. Copies will be made as + required. If `CompletionToken` is movable, it may also be moved. + */ + async_completion(std::remove_reference_t& token) + : handler(std::forward(token)) + , result(handler) + { + static_assert(is_Handler::value, + "Handler requirements not met"); + } + + /** The final completion handler, callable with the specified signature. */ + handler_type handler; + + /** The return value of the asynchronous initiation function. */ + boost::asio::async_result result; +}; + +} // beast + +#endif diff --git a/beast/asio/basic_streambuf.h b/beast/asio/basic_streambuf.h new file mode 100644 index 0000000000..e87999d1e7 --- /dev/null +++ b/beast/asio/basic_streambuf.h @@ -0,0 +1,350 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_BASIC_STREAMBUF_H_INCLUDED +#define BEAST_ASIO_BASIC_STREAMBUF_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include + +namespace beast { + +/** A `Streambuf` that uses multiple buffers internally. + + The implementation uses 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. + + @tparam Allocator The allocator to use for managing memory. +*/ +template +class basic_streambuf +#if ! GENERATING_DOCS + : private empty_base_optimization< + typename std::allocator_traits:: + template rebind_alloc> +#endif +{ +public: + using allocator_type = typename + std::allocator_traits:: + template rebind_alloc; + +private: + class element; + + using alloc_traits = std::allocator_traits; + using list_type = typename boost::intrusive::make_list>::type; + using iterator = typename list_type::iterator; + using const_iterator = typename list_type::const_iterator; + + using size_type = typename std::allocator_traits::size_type; + using const_buffer = boost::asio::const_buffer; + using mutable_buffer = boost::asio::mutable_buffer; + + static_assert(std::is_base_of::iterator_category>::value, + "BidirectionalIterator requirements not met"); + + static_assert(std::is_base_of::iterator_category>::value, + "BidirectionalIterator requirements not met"); + + list_type list_; // list of allocated buffers + iterator out_; // element that contains out_pos_ + size_type alloc_size_; // min amount to allocate + size_type in_size_ = 0; // size of the input sequence + size_type in_pos_ = 0; // input offset in list_.front() + size_type out_pos_ = 0; // output offset in *out_ + size_type out_end_ = 0; // output end offset in list_.back() + +public: + class const_buffers_type; + + class mutable_buffers_type; + + /// Destructor. + ~basic_streambuf(); + + /** Move constructor. + + The output sequence of this object will be empty. + + After the move, the moved-from object will have an + empty input and output sequence, with no internal + buffers allocated. + + @param other The stream buffer to move from. + */ + basic_streambuf(basic_streambuf&& other); + + /** Move constructor. + + The output sequence of this object will be empty. + + After the move, the moved-from object will have an + empty input and output sequence, with no internal + buffers allocated. + + @param other The stream buffer to move from. + + @param alloc The allocator to associate with the + stream buffer. + */ + basic_streambuf(basic_streambuf&& other, + allocator_type const& alloc); + + /** Move assignment. + + The output sequence of this object will be empty. + + After the move, the moved-from object will have an + empty input and output sequence, with no internal + buffers allocated. + + @param other The stream buffer to move from. + */ + basic_streambuf& + operator=(basic_streambuf&& other); + + /// Copy constructor. + basic_streambuf(basic_streambuf const& other); + + /** Copy constructor. + + The output sequence of this object will be empty. + + @param other The stream buffer to copy. + + @param alloc The allocator to associate with the + stream buffer. + */ + basic_streambuf(basic_streambuf const& other, + allocator_type const& alloc); + + /** Copy assignment. + + The output sequence of this object will be empty. + + @param other The stream buffer to copy. + */ + basic_streambuf& operator=(basic_streambuf const& other); + + /** Copy constructor. + + The output sequence of this object will be empty. + + @param other The stream buffer to copy. + */ + template + basic_streambuf(basic_streambuf const& other); + + /** Copy constructor. + + The output sequence of this object will be empty. + + @param other The stream buffer to copy. + + @param alloc The allocator to associate with the + stream buffer. + */ + template + basic_streambuf(basic_streambuf const& other, + allocator_type const& alloc); + + /** Copy assignment. + + The output sequence of this object will be empty. + + @param other The stream buffer to copy. + */ + template + basic_streambuf& operator=(basic_streambuf const& other); + + /** Construct a stream buffer. + + @param alloc_size The size of buffer to allocate. This is a soft + limit, calls to prepare for buffers exceeding this size will allocate + the larger size. + + @param alloc The allocator to use. + */ + explicit + basic_streambuf(std::size_t alloc_size = 1024, + Allocator const& alloc = allocator_type{}); + + /// Get the associated allocator + allocator_type + get_allocator() const + { + return this->member(); + } + + /// Get the maximum size of the basic_streambuf. + size_type + max_size() const + { + return std::numeric_limits::max(); + } + + /// Get the size of the input sequence. + size_type + size() const + { + return in_size_; + } + + /// Get a list of buffers that represents the output sequence, with the given size. + mutable_buffers_type + prepare(size_type n); + + /// Move bytes from the output sequence to the input sequence. + void + commit(size_type n); + + /// Get a list of buffers that represents the input sequence. + const_buffers_type + data() const; + + /// Remove bytes from the input sequence. + void + consume(size_type n); + + /// Clear everything. + void + clear(); + + // Helper for read_until + template + friend + std::size_t + read_size_helper(basic_streambuf< + OtherAllocator> const& streambuf, std::size_t max_size); + +private: + void + move_assign(basic_streambuf& other, std::false_type); + + void + move_assign(basic_streambuf& other, std::true_type); + + void + copy_assign(basic_streambuf const& other, std::false_type); + + void + copy_assign(basic_streambuf const& other, std::true_type); + + void + delete_list(); + + std::size_t + prepare_size() const; + + void + debug_check() const; +}; + +/// The type used to represent the input sequence as a list of buffers. +template +class basic_streambuf::const_buffers_type +{ + basic_streambuf const* sb_ = nullptr; + + friend class basic_streambuf; + + explicit + const_buffers_type(basic_streambuf const& sb); + +public: + using value_type = boost::asio::const_buffer; + + class const_iterator; + + const_buffers_type() = default; + const_buffers_type(const_buffers_type const&) = default; + const_buffers_type& operator=(const_buffers_type const&) = default; + + const_iterator + begin() const; + + const_iterator + end() const; +}; + +/// The type used to represent the output sequence as a list of buffers. +template +class basic_streambuf::mutable_buffers_type +{ + basic_streambuf const* sb_; + + friend class basic_streambuf; + + explicit + mutable_buffers_type(basic_streambuf const& sb); + +public: + using value_type = mutable_buffer; + + class const_iterator; + + mutable_buffers_type() = default; + mutable_buffers_type(mutable_buffers_type const&) = default; + mutable_buffers_type& operator=(mutable_buffers_type const&) = default; + + const_iterator + begin() const; + + const_iterator + end() const; +}; + +/** Format output to a stream buffer. + + @param streambuf The streambuf to write to. + + @param t The object to write. + + @return The stream buffer. +*/ +template +basic_streambuf& +operator<<(basic_streambuf& buf, T const& t); + +/** Convert the entire basic_streambuf to a string. + + @param streambuf The streambuf to convert. + + @return A string representing the contents of the input sequence. +*/ +template +std::string +to_string(basic_streambuf const& streambuf); + +} // beast + +#include + +#endif diff --git a/beast/asio/bind_handler.h b/beast/asio/bind_handler.h index 22c3855c1d..afa964a907 100644 --- a/beast/asio/bind_handler.h +++ b/beast/asio/bind_handler.h @@ -23,142 +23,151 @@ #include #include #include - #include #include #include namespace beast { -namespace asio { namespace detail { /** Nullary handler that calls Handler with bound arguments. - The rebound handler provides the same io_service execution + + The bound handler provides the same io_service execution guarantees as the original handler. */ -template +template class bound_handler { private: - using args_type = std::tuple ...>; + using args_type = std::tuple...>; - std::decay_t m_handler; - args_type m_args; + Handler h_; + args_type args_; - template - static void invoke (Handler& h, Tuple& args, + template + static void invoke(Handler& h, Tuple& args, std::index_sequence ) { - h (std::get (args)...); + h(std::get(args)...); } public: using result_type = void; + template explicit - bound_handler (DeducedHandler&& handler, Args&&... args) - : m_handler (std::forward (handler)) - , m_args (std::forward (args)...) + bound_handler(DeducedHandler&& handler, Args&&... args) + : h_(std::forward(handler)) + , args_(std::forward(args)...) { } void - operator() () + operator()() { - invoke (m_handler, m_args, - std::index_sequence_for ()); + invoke(h_, args_, + std::index_sequence_for ()); } void - operator() () const + operator()() const { - invoke (m_handler, m_args, - std::index_sequence_for ()); - } - - template - friend - void - asio_handler_invoke (Function& f, bound_handler* h) - { - boost_asio_handler_invoke_helpers:: - invoke (f, h->m_handler); - } - - template - friend - void - asio_handler_invoke (Function const& f, bound_handler* h) - { - boost_asio_handler_invoke_helpers:: - invoke (f, h->m_handler); + invoke(h_, args_, + std::index_sequence_for ()); } friend void* - asio_handler_allocate (std::size_t size, bound_handler* h) + asio_handler_allocate( + std::size_t size, bound_handler* h) { return boost_asio_handler_alloc_helpers:: - allocate (size, h->m_handler); + allocate(size, h->h_); } friend void - asio_handler_deallocate (void* p, std::size_t size, bound_handler* h) + asio_handler_deallocate( + void* p, std::size_t size, bound_handler* h) { boost_asio_handler_alloc_helpers:: - deallocate (p, size, h->m_handler); + deallocate(p, size, h->h_); } friend bool - asio_handler_is_continuation (bound_handler* h) + asio_handler_is_continuation(bound_handler* h) { return boost_asio_handler_cont_helpers:: - is_continuation (h->m_handler); + is_continuation (h->h_); + } + + template + friend + void + asio_handler_invoke(F&& f, bound_handler* h) + { + boost_asio_handler_invoke_helpers:: + invoke(f, h->h_); } }; -} +} // detail //------------------------------------------------------------------------------ -/** Binds parameters to a handler to produce a nullary functor. - The returned handler provides the same io_service execution guarantees - as the original handler. This is designed to use as a replacement for - io_service::wrap, to ensure that the handler will not be invoked - immediately by the calling function. +/** Bind parameters to a completion handler, creating a wrapped handler. + + This function creates a new handler which invoked with no parameters + calls the original handler with the list of bound arguments. The passed + handler and arguments are forwarded into the returned handler, which + provides the same `io_service` execution guarantees as the original + handler. + + Unlike `io_service::wrap`, the returned handler can be used in a + subsequent call to `io_service::post` instead of `io_service::dispatch`, + to ensure that the handler will not be invoked immediately by the + calling function. + + Example: + @code + template + void + do_cancel(AsyncReadStream& stream, ReadHandler&& handler) + { + stream.get_io_service().post( + bind_handler(std::forward(handler), + boost::asio::error::operation_aborted, 0)); + } + @endcode + + @param handler The handler to wrap. + + @param args A list of arguments to bind to the handler. The + arguments are forwarded into the returned + */ -template -detail::bound_handler -bind_handler (DeducedHandler&& handler, Args&&... args) +template +#if GENERATING_DOCS +implementation_defined +#else +detail::bound_handler, Args...> +#endif +bind_handler(CompletionHandler&& handler, Args&&... args) { - return detail::bound_handler ( - std::forward (handler), - std::forward (args)...); + return detail::bound_handler, Args...>(std::forward< + CompletionHandler>(handler), + std::forward(args)...); } -} -} - -//------------------------------------------------------------------------------ +} // beast namespace std { - -template -void bind (beast::asio::detail::bound_handler < - Handler, Args...>, ...) = delete; - -#if 0 -template -struct is_bind_expression < - beast::asio::detail::bound_handler -> : std::true_type -{ -}; -#endif - -} +template +void bind(beast::detail::bound_handler< + Handler, Args...>, ...) = delete; +} // std #endif diff --git a/beast/asio/buffers_adapter.h b/beast/asio/buffers_adapter.h new file mode 100644 index 0000000000..a2212a7463 --- /dev/null +++ b/beast/asio/buffers_adapter.h @@ -0,0 +1,648 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_BUFFERS_ADAPTER_H_INLUDED +#define BEAST_ASIO_BUFFERS_ADAPTER_H_INLUDED + +#include +#include +#include +#include +#include +#include +#include + +namespace beast { + +/** Adapts a `MutableBufferSequence` into a `Streambuf`. + + This class wraps a `MutableBufferSequence` to meet the requirements + of `Streambuf`. Upon construction the input and output sequences are + empty. A copy of the mutable buffer sequence object is stored; however, + ownership of the underlying memory is not transferred. The caller is + responsible for making sure that referenced memory remains valid + for the duration of any operations. + + The size of the mutable buffer sequence determines the maximum + number of bytes which may be prepared and committed. + + @tparam Buffers The type of mutable buffer sequence to wrap. +*/ +template +class buffers_adapter +{ +private: + using buffers_type = std::decay_t; + using iter_type = typename buffers_type::const_iterator; + + static auto constexpr is_mutable = + std::is_constructible::value_type>::value; + + Buffers bs_; + iter_type begin_; + iter_type out_; + iter_type end_; + std::size_t max_size_; + std::size_t in_pos_ = 0; // offset in *begin_ + std::size_t in_size_ = 0; // size of input sequence + std::size_t out_pos_ = 0; // offset in *out_ + std::size_t out_end_ = 0; // output end offset + + template + buffers_adapter(Deduced&& other, + std::size_t nbegin, std::size_t nout, + std::size_t nend) + : bs_(std::forward(other).bs_) + , begin_(std::next(bs_.begin(), nbegin)) + , out_(std::next(bs_.begin(), nout)) + , end_(std::next(bs_.begin(), nend)) + , max_size_(other.max_size_) + , in_pos_(other.in_pos_) + , in_size_(other.in_size_) + , out_pos_(other.out_pos_) + , out_end_(other.out_end_) + { + } + +public: + class const_buffers_type; + class mutable_buffers_type; + + // Move constructor. + buffers_adapter(buffers_adapter&& other); + + // Copy constructor. + buffers_adapter(buffers_adapter const& other); + + // Move assignment. + buffers_adapter& operator=(buffers_adapter&& other); + + // Copy assignment. + buffers_adapter& operator=(buffers_adapter const&); + + /** Construct a buffers adapter. + + @param buffers The mutable buffer sequence to wrap. A copy of + the object will be made, but ownership of the memory is not + transferred. + */ + explicit + buffers_adapter(Buffers const& buffers); + + /// Returns the largest size output sequence possible. + std::size_t + max_size() const + { + return max_size_; + } + + /// Get the size of the input sequence. + std::size_t + size() const + { + return in_size_; + } + + /** Get a list of buffers that represents the output sequence, with the given size. + + @throws std::length_error if the size would exceed the limit + imposed by the underlying mutable buffer sequence. + */ + mutable_buffers_type + prepare(std::size_t n); + + /// Move bytes from the output sequence to the input sequence. + void + commit(std::size_t n); + + /// Get a list of buffers that represents the input sequence. + const_buffers_type + data() const; + + /// Remove bytes from the input sequence. + void + consume(std::size_t n); +}; + +//------------------------------------------------------------------------------ + +/// The type used to represent the input sequence as a list of buffers. +template +class buffers_adapter::const_buffers_type +{ + buffers_adapter const* ba_; + +public: + using value_type = boost::asio::const_buffer; + + class const_iterator; + + const_buffers_type() = default; + const_buffers_type( + const_buffers_type const&) = default; + const_buffers_type& operator=( + const_buffers_type const&) = default; + + const_iterator + begin() const; + + const_iterator + end() const; + +private: + friend class buffers_adapter; + + const_buffers_type(buffers_adapter const& ba) + : ba_(&ba) + { + } +}; + +template +class buffers_adapter::const_buffers_type::const_iterator +{ + iter_type it_; + buffers_adapter const* ba_; + +public: + using value_type = boost::asio::const_buffer; + using pointer = value_type const*; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + const_iterator() = default; + const_iterator(const_iterator&& other) = default; + const_iterator(const_iterator const& other) = default; + const_iterator& operator=(const_iterator&& other) = default; + const_iterator& operator=(const_iterator const& other) = default; + + bool + operator==(const_iterator const& other) const + { + return ba_ == other.ba_ && + it_ == other.it_; + } + + bool + operator!=(const_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*() const + { + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + return value_type{buffer_cast(*it_), + (ba_->out_ == ba_->bs_.end() || + it_ != ba_->out_) ? buffer_size(*it_) : ba_->out_pos_} + + (it_ == ba_->begin_ ? ba_->in_pos_ : 0); + } + + pointer + operator->() const = delete; + + const_iterator& + operator++() + { + ++it_; + return *this; + } + + const_iterator + operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + + const_iterator& + operator--() + { + --it_; + return *this; + } + + const_iterator + operator--(int) + { + auto temp = *this; + --(*this); + return temp; + } + +private: + friend class const_buffers_type; + + const_iterator(buffers_adapter const& ba, + iter_type iter) + : it_(iter) + , ba_(&ba) + { + } +}; + +template +inline +auto +buffers_adapter::const_buffers_type::begin() const -> + const_iterator +{ + return const_iterator{*ba_, ba_->begin_}; +} + +template +inline +auto +buffers_adapter::const_buffers_type::end() const -> + const_iterator +{ + return const_iterator{*ba_, ba_->out_ == + ba_->end_ ? ba_->end_ : std::next(ba_->out_)}; +} + +//------------------------------------------------------------------------------ + +/// The type used to represent the output sequence as a list of buffers. +template +class buffers_adapter::mutable_buffers_type +{ + buffers_adapter const* ba_; + +public: + using value_type = boost::asio::mutable_buffer; + + class const_iterator; + + mutable_buffers_type() = default; + mutable_buffers_type( + mutable_buffers_type const&) = default; + mutable_buffers_type& operator=( + mutable_buffers_type const&) = default; + + const_iterator + begin() const; + + const_iterator + end() const; + +private: + friend class buffers_adapter; + + mutable_buffers_type( + buffers_adapter const& ba) + : ba_(&ba) + { + } +}; + +template +class buffers_adapter::mutable_buffers_type::const_iterator +{ + iter_type it_; + buffers_adapter const* ba_; + +public: + using value_type = boost::asio::mutable_buffer; + using pointer = value_type const*; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + const_iterator() = default; + const_iterator(const_iterator&& other) = default; + const_iterator(const_iterator const& other) = default; + const_iterator& operator=(const_iterator&& other) = default; + const_iterator& operator=(const_iterator const& other) = default; + + bool + operator==(const_iterator const& other) const + { + return ba_ == other.ba_ && + it_ == other.it_; + } + + bool + operator!=(const_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*() const + { + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + return value_type{buffer_cast(*it_), + it_ == std::prev(ba_->end_) ? + ba_->out_end_ : buffer_size(*it_)} + + (it_ == ba_->out_ ? ba_->out_pos_ : 0); + } + + pointer + operator->() const = delete; + + const_iterator& + operator++() + { + ++it_; + return *this; + } + + const_iterator + operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + + const_iterator& + operator--() + { + --it_; + return *this; + } + + const_iterator + operator--(int) + { + auto temp = *this; + --(*this); + return temp; + } + +private: + friend class mutable_buffers_type; + + const_iterator(buffers_adapter const& ba, + iter_type iter) + : it_(iter) + , ba_(&ba) + { + } +}; + +template +inline +auto +buffers_adapter::mutable_buffers_type::begin() const -> + const_iterator +{ + return const_iterator{*ba_, ba_->out_}; +} + +template +inline +auto +buffers_adapter::mutable_buffers_type::end() const -> + const_iterator +{ + return const_iterator{*ba_, ba_->end_}; +} + +//------------------------------------------------------------------------------ + +template +buffers_adapter::buffers_adapter( + buffers_adapter&& other) + : buffers_adapter(std::move(other), + std::distance(other.bs_.begin(), other.begin_), + std::distance(other.bs_.begin(), other.out_), + std::distance(other.bs_.begin(), other.end_)) +{ +} + +template +buffers_adapter::buffers_adapter( + buffers_adapter const& other) + : buffers_adapter(other, + std::distance(other.bs_.begin(), other.begin_), + std::distance(other.bs_.begin(), other.out_), + std::distance(other.bs_.begin(), other.end_)) +{ +} + +template +auto +buffers_adapter::operator=( + buffers_adapter&& other) -> buffers_adapter& +{ + auto const nbegin = std::distance( + other.bs_.begin(), other.begin_); + auto const nout = std::distance( + other.bs_.begin(), other.out_); + auto const nend = std::distance( + other.bs_.begin(), other.end_); + bs_ = std::move(other.bs_); + begin_ = std::next(bs_.begin(), nbegin); + out_ = std::next(bs_.begin(), nout); + end_ = std::next(bs_.begin(), nend); + max_size_ = other.max_size_; + in_pos_ = other.in_pos_; + in_size_ = other.in_size_; + out_pos_ = other.out_pos_; + out_end_ = other.out_end_; + return *this; +} + +template +auto +buffers_adapter::operator=( + buffers_adapter const& other) -> buffers_adapter& +{ + auto const nbegin = std::distance( + other.bs_.begin(), other.begin_); + auto const nout = std::distance( + other.bs_.begin(), other.out_); + auto const nend = std::distance( + other.bs_.begin(), other.end_); + bs_ = other.bs_; + begin_ = std::next(bs_.begin(), nbegin); + out_ = std::next(bs_.begin(), nout); + end_ = std::next(bs_.begin(), nend); + max_size_ = other.max_size_; + in_pos_ = other.in_pos_; + in_size_ = other.in_size_; + out_pos_ = other.out_pos_; + out_end_ = other.out_end_; + return *this; +} + +template +buffers_adapter::buffers_adapter( + Buffers const& bs) + : bs_(bs) + , begin_(bs_.begin()) + , out_(bs_.begin()) + , end_(bs_.begin()) + , max_size_(boost::asio::buffer_size(bs_)) +{ +} + +template +auto +buffers_adapter::prepare(std::size_t n) -> + mutable_buffers_type +{ + using boost::asio::buffer_size; + static_assert(is_mutable, + "Operation not valid for ConstBufferSequence"); + end_ = out_; + if(end_ != bs_.end()) + { + auto size = buffer_size(*end_) - out_pos_; + if(n > size) + { + n -= size; + while(++end_ != bs_.end()) + { + size = buffer_size(*end_); + if(n < size) + { + out_end_ = n; + n = 0; + ++end_; + break; + } + n -= size; + out_end_ = size; + } + } + else + { + ++end_; + out_end_ = out_pos_ + n; + n = 0; + } + } + if(n > 0) + throw std::length_error( + "no space in buffers_adapter"); + return mutable_buffers_type{*this}; +} + +template +void +buffers_adapter::commit(std::size_t n) +{ + using boost::asio::buffer_size; + static_assert(is_mutable, + "Operation not valid for ConstBufferSequence"); + if(out_ == end_) + return; + auto const last = std::prev(end_); + while(out_ != last) + { + auto const avail = + buffer_size(*out_) - out_pos_; + if(n < avail) + { + out_pos_ += n; + in_size_ += n; + max_size_ -= n; + return; + } + ++out_; + n -= avail; + out_pos_ = 0; + in_size_ += avail; + max_size_ -= avail; + } + + n = std::min(n, out_end_ - out_pos_); + out_pos_ += n; + in_size_ += n; + max_size_ -= n; + if(out_pos_ == buffer_size(*out_)) + { + ++out_; + out_pos_ = 0; + out_end_ = 0; + } +} + +template +inline +auto +buffers_adapter::data() const -> + const_buffers_type +{ + return const_buffers_type{*this}; +} + +template +void +buffers_adapter::consume(std::size_t n) +{ + for(;;) + { + if(begin_ != out_) + { + auto const avail = + buffer_size(*begin_) - in_pos_; + if(n < avail) + { + in_size_ -= n; + in_pos_ += n; + break; + } + n -= avail; + in_size_ -= avail; + in_pos_ = 0; + ++begin_; + } + else + { + auto const avail = out_pos_ - in_pos_; + if(n < avail) + { + in_size_ -= n; + in_pos_ += n; + } + else + { + in_size_ -= avail; + if(out_pos_ != out_end_|| + out_ != std::prev(bs_.end())) + { + in_pos_ = out_pos_; + } + else + { + // Use the whole buffer now. + in_pos_ = 0; + out_pos_ = 0; + out_end_ = 0; + } + } + break; + } + } +} + +} // beast + +#endif diff --git a/beast/asio/buffers_debug.h b/beast/asio/buffers_debug.h new file mode 100644 index 0000000000..19689c372d --- /dev/null +++ b/beast/asio/buffers_debug.h @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_BUFFERS_DEBUG_H_INLUDED +#define BEAST_ASIO_BUFFERS_DEBUG_H_INLUDED + +#include +#include + +namespace beast { +namespace debug { + +template +static +std::string +buffers_to_string(Buffers const& bs) +{ + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + std::string s; + s.reserve(buffer_size(bs)); + for(auto const& b : bs) + s.append(buffer_cast(b), + buffer_size(b)); + for(auto i = s.size(); i-- > 0;) + if(s[i] == '\r') + s.replace(i, 1, "\\r"); + else if(s[i] == '\n') + s.replace(i, 1, "\\n\n"); + return s; +} + +} // debug +} // beast + +#endif diff --git a/beast/asio/consuming_buffers.h b/beast/asio/consuming_buffers.h new file mode 100644 index 0000000000..f20ad7fa7f --- /dev/null +++ b/beast/asio/consuming_buffers.h @@ -0,0 +1,309 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_CONSUMING_BUFFERS_H_INLUDED +#define BEAST_ASIO_CONSUMING_BUFFERS_H_INLUDED + +#include +#include +#include +#include +#include +#include + +namespace beast { + +/** Adapter to trim the front of a `BufferSequence`. + + This adapter wraps a buffer sequence to create a new sequence + which may be incrementally consumed. Bytes consumed are removed + from the front of the buffer. The underlying memory is not changed, + instead the adapter efficiently iterates through a subset of + the buffers wrapped. + + The wrapped buffer is not modified, a copy is made instead. + Ownership of the underlying memory is not transferred, the application + is still responsible for managing its lifetime. + + @tparam Buffers The buffer sequence to wrap. + + @ptaram ValueType The type of buffer of the final buffer sequence. This + can be different from the buffer type of the wrapped sequence. For + example, a `MutableBufferSequence` can be transformed into a + consumable `ConstBufferSequence`. Violations of buffer const safety + are not permitted, and will result in a compile error. +*/ +template +class consuming_buffers +{ + using iter_type = + typename Buffers::const_iterator; + + static_assert(std::is_constructible::value_type>::value, + "ValueType requirements not met"); + + Buffers bs_; + iter_type begin_; + std::size_t skip_ = 0; + + template + consuming_buffers(Deduced&& other, std::size_t nbegin) + : bs_(std::forward(other).bs_) + , begin_(std::next(bs_.begin(), nbegin)) + , skip_(other.skip_) + { + } + +public: + /// The type for each element in the list of buffers. + using value_type = ValueType; + + class const_iterator; + + /// Move constructor. + consuming_buffers(consuming_buffers&&); + + /// Copy constructor. + consuming_buffers(consuming_buffers const&); + + /// Move assignment. + consuming_buffers& operator=(consuming_buffers&&); + + /// Copy assignment. + consuming_buffers& operator=(consuming_buffers const&); + + /** Construct to represent a buffer sequence. + + A copy of the buffer sequence is made. Ownership of the + underlying memory is not transferred or copied. + */ + explicit + consuming_buffers(Buffers const& buffers); + + /// Get a bidirectional iterator to the first element. + const_iterator + begin() const; + + /// Get a bidirectional iterator for one past the last element. + const_iterator + end() const; + + /** Remove bytes from the beginning of the sequence. + + @param n The number of bytes to remove. If this is + larger than the number of bytes remaining, all the + bytes remaining are removed. + */ + void + consume(std::size_t n); +}; + +/// A bidirectional iterator type that may be used to read elements. +template +class consuming_buffers::const_iterator +{ + friend class consuming_buffers; + + using iter_type = + typename Buffers::const_iterator; + + iter_type it_; + consuming_buffers const* b_; + +public: + using value_type = + typename std::iterator_traits::value_type; + using pointer = value_type const*; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + const_iterator() = default; + const_iterator(const_iterator&& other) = default; + const_iterator(const_iterator const& other) = default; + const_iterator& operator=(const_iterator&& other) = default; + const_iterator& operator=(const_iterator const& other) = default; + + bool + operator==(const_iterator const& other) const + { + return b_ == other.b_ && it_ == other.it_; + } + + bool + operator!=(const_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*() const + { + if(it_ == b_->begin_) + return *it_ + b_->skip_; + return *it_; + } + + pointer + operator->() const = delete; + + const_iterator& + operator++() + { + ++it_; + return *this; + } + + const_iterator + operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + + const_iterator& + operator--() + { + --it_; + return *this; + } + + const_iterator + operator--(int) + { + auto temp = *this; + --(*this); + return temp; + } + +private: + const_iterator(consuming_buffers const& b, + iter_type it) + : it_(it) + , b_(&b) + { + } +}; + +template +consuming_buffers:: +consuming_buffers(consuming_buffers&& other) + : consuming_buffers(std::move(other), + std::distance( + other.bs_.begin(), other.begin_)) +{ +} + +template +consuming_buffers:: +consuming_buffers(consuming_buffers const& other) + : consuming_buffers(other, + std::distance( + other.bs_.begin(), other.begin_)) +{ +} + +template +auto +consuming_buffers:: +operator=(consuming_buffers&& other) -> + consuming_buffers& +{ + auto const nbegin = std::distance( + other.bs_.begin(), other.begin_); + bs_ = std::move(other.bs_); + begin_ = std::next(bs_.begin(), nbegin); + skip_ = other.skip_; + return *this; +} + +template +auto +consuming_buffers:: +operator=(consuming_buffers const& other) -> + consuming_buffers& +{ + auto const nbegin = std::distance( + other.bs_.begin(), other.begin_); + bs_ = other.bs_; + begin_ = std::next(bs_.begin(), nbegin); + skip_ = other.skip_; + return *this; +} + +template +consuming_buffers:: +consuming_buffers(Buffers const& bs) + : bs_(bs) + , begin_(bs_.begin()) +{ +} + +template +auto +consuming_buffers::begin() const -> + const_iterator +{ + return const_iterator{*this, begin_}; +} + +template +auto +consuming_buffers::end() const -> + const_iterator +{ + return const_iterator{*this, bs_.end()}; +} + +template +void +consuming_buffers::consume(std::size_t n) +{ + using boost::asio::buffer_size; + for(;n > 0 && begin_ != bs_.end(); ++begin_) + { + auto const len = + buffer_size(*begin_) - skip_; + if(n < len) + { + skip_ += n; + break; + } + n -= len; + skip_ = 0; + } +} + +/// Returns a consumed buffer +template +consuming_buffers +consumed_buffers(Buffers const& bs, std::size_t n) +{ + consuming_buffers cb(bs); + cb.consume(n); + return cb; +} + +} // beast + +#endif diff --git a/beast/asio/handler_alloc.h b/beast/asio/handler_alloc.h new file mode 100644 index 0000000000..d76f62d993 --- /dev/null +++ b/beast/asio/handler_alloc.h @@ -0,0 +1,150 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_HANDLER_ALLOC_H_INCLUDED +#define BEAST_ASIO_HANDLER_ALLOC_H_INCLUDED + +#include +#include +#include +#include +#include + +namespace beast { + +// Guidance from +// http://howardhinnant.github.io/allocator_boilerplate.html + +/** An allocator that uses handler customizations. + + This allocator uses the handler customizations `asio_handler_allocate` + and `asio_handler_deallocate` to manage memory. + + @tparam T The type of object + + @tparam Handler The type of handler. +*/ +template +class handler_alloc +{ +private: + // We want a partial template specialization as a friend + // but that isn't allowed so we friend all versions. This + // should produce a compile error if Handler is not + // constructible from H. + // + template + friend class handler_alloc; + + Handler h_; + +public: + using value_type = T; + using is_always_equal = std::true_type; + + handler_alloc() = delete; + handler_alloc(handler_alloc&&) = default; + handler_alloc(handler_alloc const&) = default; + handler_alloc& operator=(handler_alloc&&) = default; + handler_alloc& operator=(handler_alloc const&) = default; + + /** Construct the allocator. + + The handler is moved or copied into the allocator. + */ + explicit + handler_alloc(Handler&& h) + : h_(std::move(h)) + { + } + + /** Construct the allocator. + + A copy of the handler is made. + */ + explicit + handler_alloc(Handler const& h) + : h_(h) + { + } + + template + handler_alloc( + handler_alloc&& other) + : h_(std::move(other.h_)) + { + } + + template + handler_alloc( + handler_alloc const& other) + : h_(other.h_) + { + } + + value_type* + allocate(std::ptrdiff_t n) + { + auto const size = n * sizeof(T); + return static_cast( + boost_asio_handler_alloc_helpers::allocate( + size, h_)); + } + + void + deallocate(value_type* p, std::ptrdiff_t n) + { + auto const size = n * sizeof(T); + boost_asio_handler_alloc_helpers::deallocate( + p, size, h_); + } + +#ifdef _MSC_VER + // Work-around for MSVC not using allocator_traits + // in the implementation of shared_ptr + // + void + destroy(T* t) + { + t->~T(); + } +#endif + + template + friend + bool + operator==(handler_alloc const& lhs, + handler_alloc const& rhs) + { + return true; + } + + template + friend + bool + operator!=(handler_alloc const& lhs, + handler_alloc const& rhs) + { + return !(lhs == rhs); + } +}; + +} // beast + +#endif diff --git a/beast/asio/impl/basic_streambuf.ipp b/beast/asio/impl/basic_streambuf.ipp new file mode 100644 index 0000000000..afec27bf7e --- /dev/null +++ b/beast/asio/impl/basic_streambuf.ipp @@ -0,0 +1,847 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_BASIC_STREAMBUF_IPP_INCLUDED +#define BEAST_ASIO_BASIC_STREAMBUF_IPP_INCLUDED + +#include +#include +#include +#include +#include +#include + +namespace beast { + +/* These diagrams illustrate the layout and state variables. + + Input and output contained entirely in one element: + + 0 out_ + |<-------------+------------------------------------------->| + in_pos_ out_pos_ out_end_ + + + Output contained in first and second elements: + + out_ + |<------+----------+------->| |<----------+-------------->| + in_pos_ out_pos_ out_end_ + + + Output contained in the second element: + + out_ + |<------------+------------>| |<----+-------------------->| + in_pos_ out_pos_ out_end_ + + + Output contained in second and third elements: + + out_ + |<-----+-------->| |<-------+------>| |<--------------->| + in_pos_ out_pos_ out_end_ + + + Input sequence is empty: + + out_ + |<------+------------------>| |<-----------+------------->| + out_pos_ out_end_ + in_pos_ + + + Output sequence is empty: + + out_ + |<------+------------------>| |<------+------------------>| + in_pos_ out_pos_ + out_end_ + + + The end of output can point to the end of an element. + But out_pos_ should never point to the end: + + out_ + |<------+------------------>| |<------+------------------>| + in_pos_ out_pos_ out_end_ + + + When the input sequence entirely fills the last element and + the output sequence is empty, out_ will point to the end of + the list of buffers, and out_pos_ and out_end_ will be 0: + + + |<------+------------------>| out_ == list_.end() + in_pos_ out_pos_ == 0 + out_end_ == 0 +*/ + +template +class basic_streambuf::element + : public boost::intrusive::list_base_hook< + boost::intrusive::link_mode< + boost::intrusive::normal_link>> +{ + using size_type = typename std::allocator_traits::size_type; + + size_type const size_; + +public: + element(element const&) = delete; + element& operator=(element const&) = delete; + + explicit + element(size_type n) + : size_(n) + { + } + + size_type + size() const + { + return size_; + } + + char* + data() const + { + return const_cast( + reinterpret_cast(this+1)); + } +}; + +//------------------------------------------------------------------------------ + +template +class basic_streambuf::const_buffers_type::const_iterator +{ + basic_streambuf const* sb_ = nullptr; + typename list_type::const_iterator it_; + +public: + using value_type = + typename const_buffers_type::value_type; + using pointer = value_type const*; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + const_iterator() = default; + const_iterator(const_iterator&& other) = default; + const_iterator(const_iterator const& other) = default; + const_iterator& operator=(const_iterator&& other) = default; + const_iterator& operator=(const_iterator const& other) = default; + + const_iterator(basic_streambuf const& sb, + typename list_type::const_iterator const& it) + : sb_(&sb) + , it_(it) + { + } + + bool + operator==(const_iterator const& other) const + { + return sb_ == other.sb_ && it_ == other.it_; + } + + bool + operator!=(const_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*() const + { + auto const& e = *it_; + return value_type{e.data(), + (sb_->out_ == sb_->list_.end() || + &e != &*sb_->out_) ? e.size() : sb_->out_pos_} + + (&e == &*sb_->list_.begin() ? sb_->in_pos_ : 0); + } + + pointer + operator->() const = delete; + + const_iterator& + operator++() + { + ++it_; + return *this; + } + + const_iterator + operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + + const_iterator& + operator--() + { + --it_; + return *this; + } + + const_iterator + operator--(int) + { + auto temp = *this; + --(*this); + return temp; + } +}; + +template +basic_streambuf::const_buffers_type::const_buffers_type( + basic_streambuf const& sb) + : sb_(&sb) +{ +} + +template +auto +basic_streambuf::const_buffers_type::begin() const -> + const_iterator +{ + return const_iterator{*sb_, sb_->list_.begin()}; +} + +template +auto +basic_streambuf::const_buffers_type::end() const -> + const_iterator +{ + return const_iterator{*sb_, sb_->out_ == + sb_->list_.end() ? sb_->list_.end() : + std::next(sb_->out_)}; +} + +//------------------------------------------------------------------------------ + +template +class basic_streambuf::mutable_buffers_type::const_iterator +{ + basic_streambuf const* sb_ = nullptr; + typename list_type::const_iterator it_; + +public: + using value_type = + typename mutable_buffers_type::value_type; + using pointer = value_type const*; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + const_iterator() = default; + const_iterator(const_iterator&& other) = default; + const_iterator(const_iterator const& other) = default; + const_iterator& operator=(const_iterator&& other) = default; + const_iterator& operator=(const_iterator const& other) = default; + + const_iterator(basic_streambuf const& sb, + typename list_type::const_iterator const& it) + : sb_(&sb) + , it_(it) + { + } + + bool + operator==(const_iterator const& other) const + { + return sb_ == other.sb_ && it_ == other.it_; + } + + bool + operator!=(const_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*() const + { + auto const& e = *it_; + return value_type{e.data(), + &e == &*std::prev(sb_->list_.end()) ? + sb_->out_end_ : e.size()} + + (&e == &*sb_->out_ ? sb_->out_pos_ : 0); + } + + pointer + operator->() const = delete; + + const_iterator& + operator++() + { + ++it_; + return *this; + } + + const_iterator + operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + + const_iterator& + operator--() + { + --it_; + return *this; + } + + const_iterator + operator--(int) + { + auto temp = *this; + --(*this); + return temp; + } +}; + +template +basic_streambuf::mutable_buffers_type::mutable_buffers_type( + basic_streambuf const& sb) + : sb_(&sb) +{ +} + +template +auto +basic_streambuf::mutable_buffers_type::begin() const -> + const_iterator +{ + return const_iterator{*sb_, sb_->out_}; +} + +template +auto +basic_streambuf::mutable_buffers_type::end() const -> + const_iterator +{ + return const_iterator{*sb_, sb_->list_.end()}; +} + +//------------------------------------------------------------------------------ + +template +basic_streambuf::~basic_streambuf() +{ + delete_list(); +} + +template +basic_streambuf:: +basic_streambuf(basic_streambuf&& other) + : empty_base_optimization( + std::move(other.member())) + , alloc_size_(other.alloc_size_) + , in_size_(other.in_size_) + , in_pos_(other.in_pos_) + , out_pos_(other.out_pos_) + , out_end_(other.out_end_) +{ + auto const at_end = + other.out_ == other.list_.end(); + list_ = std::move(other.list_); + out_ = at_end ? list_.end() : other.out_; + other.in_size_ = 0; + other.out_ = other.list_.end(); + other.in_pos_ = 0; + other.out_pos_ = 0; + other.out_end_ = 0; +} + +template +basic_streambuf:: +basic_streambuf(basic_streambuf&& other, + allocator_type const& alloc) + : basic_streambuf(other.alloc_size_, alloc) +{ + using boost::asio::buffer_copy; + if(this->member() != other.member()) + commit(buffer_copy(prepare(other.size()), other.data())); + else + move_assign(other, std::true_type{}); +} + +template +auto +basic_streambuf::operator=( + basic_streambuf&& other) -> basic_streambuf& +{ + if(this == &other) + return *this; + // VFALCO If any memory allocated we could use it first? + clear(); + alloc_size_ = other.alloc_size_; + move_assign(other, std::integral_constant{}); + return *this; +} + +template +basic_streambuf:: +basic_streambuf(basic_streambuf const& other) + : basic_streambuf(other.alloc_size_, + alloc_traits::select_on_container_copy_construction(other.member())) +{ + commit(boost::asio::buffer_copy(prepare(other.size()), other.data())); +} + +template +basic_streambuf:: +basic_streambuf(basic_streambuf const& other, + allocator_type const& alloc) + : basic_streambuf(other.alloc_size_, alloc) +{ + commit(boost::asio::buffer_copy(prepare(other.size()), other.data())); +} + +template +auto +basic_streambuf::operator=( + basic_streambuf const& other) -> + basic_streambuf& +{ + if(this == &other) + return *this; + using boost::asio::buffer_copy; + clear(); + copy_assign(other, std::integral_constant{}); + commit(buffer_copy(prepare(other.size()), other.data())); + return *this; +} + +template +template +basic_streambuf::basic_streambuf( + basic_streambuf const& other) + : basic_streambuf(other.alloc_size_) +{ + using boost::asio::buffer_copy; + commit(buffer_copy(prepare(other.size()), other.data())); +} + +template +template +basic_streambuf::basic_streambuf( + basic_streambuf const& other, + allocator_type const& alloc) + : basic_streambuf(other.alloc_size_, alloc) +{ + using boost::asio::buffer_copy; + commit(buffer_copy(prepare(other.size()), other.data())); +} + +template +template +auto +basic_streambuf::operator=( + basic_streambuf const& other) -> + basic_streambuf& +{ + using boost::asio::buffer_copy; + clear(); + commit(buffer_copy(prepare(other.size()), other.data())); + return *this; +} + +template +basic_streambuf::basic_streambuf( + std::size_t alloc_size, Allocator const& alloc) + : empty_base_optimization(alloc) + , out_(list_.end()) + , alloc_size_(alloc_size) +{ + if(alloc_size <= 0) + throw std::invalid_argument( + "basic_streambuf: invalid alloc_size"); +} + +template +auto +basic_streambuf::prepare(size_type n) -> + mutable_buffers_type +{ + iterator pos = out_; + if(pos != list_.end()) + { + auto const avail = pos->size() - out_pos_; + if(n > avail) + { + n -= avail; + out_end_ = pos->size(); + while(++pos != list_.end()) + { + if(n < pos->size()) + { + out_end_ = n; + n = 0; + ++pos; + break; + } + n -= pos->size(); + } + } + else + { + ++pos; + out_end_ = out_pos_ + n; + n = 0; + } + } + + if(n > 0) + { + assert(pos == list_.end()); + for(;;) + { + auto const size = std::max(alloc_size_, n); + auto& e = *reinterpret_cast( + alloc_traits::allocate(this->member(), + size + sizeof(element))); + alloc_traits::construct(this->member(), &e, size); + list_.push_back(e); + if(out_ == list_.end()) + { + out_ = list_.iterator_to(e); + debug_check(); + } + if(n <= size) + { + out_end_ = n; + debug_check(); + break; + } + n -= size; + debug_check(); + } + } + else + { + while(pos != list_.end()) + { + auto& e = *pos++; + list_.erase(list_.iterator_to(e)); + auto const len = e.size() + sizeof(e); + alloc_traits::destroy(this->member(), &e); + alloc_traits::deallocate(this->member(), + reinterpret_cast(&e), len); + } + debug_check(); + } + + return mutable_buffers_type(*this); +} + +template +void +basic_streambuf::commit(size_type n) +{ + if(list_.empty()) + return; + if(out_ == list_.end()) + return; + auto const last = std::prev(list_.end()); + while(out_ != last) + { + auto const avail = + out_->size() - out_pos_; + if(n < avail) + { + out_pos_ += n; + in_size_ += n; + debug_check(); + return; + } + ++out_; + n -= avail; + out_pos_ = 0; + in_size_ += avail; + debug_check(); + } + + n = std::min(n, out_end_ - out_pos_); + out_pos_ += n; + in_size_ += n; + if(out_pos_ == out_->size()) + { + ++out_; + out_pos_ = 0; + out_end_ = 0; + } + debug_check(); +} + +template +auto +basic_streambuf::data() const -> + const_buffers_type +{ + return const_buffers_type(*this); +} + +template +void +basic_streambuf::consume(size_type n) +{ + if(list_.empty()) + return; + + auto pos = list_.begin(); + for(;;) + { + if(pos != out_) + { + auto const avail = pos->size() - in_pos_; + if(n < avail) + { + in_size_ -= n; + in_pos_ += n; + debug_check(); + break; + } + n -= avail; + in_size_ -= avail; + in_pos_ = 0; + debug_check(); + + element& e = *pos++; + list_.erase(list_.iterator_to(e)); + auto const len = e.size() + sizeof(e); + alloc_traits::destroy(this->member(), &e); + alloc_traits::deallocate(this->member(), + reinterpret_cast(&e), len); + } + else + { + auto const avail = out_pos_ - in_pos_; + if(n < avail) + { + in_size_ -= n; + in_pos_ += n; + } + else + { + in_size_ -= avail; + if(out_pos_ != out_end_|| + out_ != list_.iterator_to(list_.back())) + { + in_pos_ = out_pos_; + } + else + { + // Use the whole buffer now. + // Alternatively we could deallocate it. + in_pos_ = 0; + out_pos_ = 0; + out_end_ = 0; + } + } + debug_check(); + break; + } + } +} + +template +void +basic_streambuf::clear() +{ + delete_list(); + list_.clear(); + out_ = list_.begin(); + in_size_ = 0; + in_pos_ = 0; + out_pos_ = 0; + out_end_ = 0; +} + +template +void +basic_streambuf:: +move_assign(basic_streambuf& other, std::false_type) +{ + using boost::asio::buffer_copy; + if(this->member() != other.member()) + { + commit(buffer_copy(prepare(other.size()), other.data())); + other.clear(); + } + else + move_assign(other, std::true_type{}); +} + +template +void +basic_streambuf:: +move_assign(basic_streambuf& other, std::true_type) +{ + this->member() = std::move(other.member()); + auto const at_end = + other.out_ == other.list_.end(); + list_ = std::move(other.list_); + out_ = at_end ? list_.end() : other.out_; + + in_size_ = other.in_size_; + in_pos_ = other.in_pos_; + out_pos_ = other.out_pos_; + out_end_ = other.out_end_; + + other.in_size_ = 0; + other.out_ = other.list_.end(); + other.in_pos_ = 0; + other.out_pos_ = 0; + other.out_end_ = 0; +} + +template +void +basic_streambuf:: +copy_assign(basic_streambuf const& other, std::false_type) +{ +} + +template +void +basic_streambuf:: +copy_assign(basic_streambuf const& other, std::true_type) +{ + this->member() = other.member(); +} + +template +void +basic_streambuf::delete_list() +{ + for(auto iter = list_.begin(); iter != list_.end();) + { + auto& e = *iter++; + auto const n = e.size() + sizeof(e); + alloc_traits::destroy(this->member(), &e); + alloc_traits::deallocate(this->member(), + reinterpret_cast(&e), n); + } +} + +// Returns the number of bytes which can be +// prepared without causing a memory allocation. +template +std::size_t +basic_streambuf::prepare_size() const +{ + auto pos = out_; + if(pos == list_.end()) + return 0; + auto n = pos->size() - out_pos_; + while(++pos != list_.end()) + n += pos->size(); + return n; +} + +template +void +basic_streambuf::debug_check() const +{ +#ifndef NDEBUG + if(list_.empty()) + { + assert(in_pos_ == 0); + assert(in_size_ == 0); + assert(out_pos_ == 0); + assert(out_end_ == 0); + assert(out_ == list_.end()); + return; + } + + auto const& front = list_.front(); + + assert(in_pos_ < front.size()); + + if(out_ == list_.end()) + { + assert(out_pos_ == 0); + assert(out_end_ == 0); + } + else + { + auto const& out = *out_; + auto const& back = list_.back(); + + assert(out_end_ <= back.size()); + assert(out_pos_ < out.size()); + assert(&out != &front || out_pos_ >= in_pos_); + assert(&out != &front || out_pos_ - in_pos_ == in_size_); + assert(&out != &back || out_pos_ <= out_end_); + } +#endif +} + +template +basic_streambuf& +operator<<(basic_streambuf& buf, T const& t) +{ + using boost::asio::buffer; + using boost::asio::buffer_copy; + std::stringstream ss; + ss << t; + auto const& s = ss.str(); + buf.commit(buffer_copy(buf.prepare(s.size()), + boost::asio::buffer(s))); + return buf; +} + +//------------------------------------------------------------------------------ + +template +std::size_t +read_size_helper(basic_streambuf< + Allocator> const& streambuf, std::size_t max_size) +{ + return std::min(max_size, + std::max(512, streambuf.prepare_size())); +} + +template +std::string +to_string(basic_streambuf const& streambuf) +{ + using boost::asio::buffer; + using boost::asio::buffer_copy; + std::string s; + s.resize(streambuf.size()); + buffer_copy( + buffer(&s[0], s.size()), streambuf.data()); + return s; +} + +} // beast + +#endif diff --git a/beast/asio/impl/streambuf_readstream.ipp b/beast/asio/impl/streambuf_readstream.ipp new file mode 100644 index 0000000000..fd1e8d9147 --- /dev/null +++ b/beast/asio/impl/streambuf_readstream.ipp @@ -0,0 +1,265 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_STREAMBUF_READSTREAM_IPP_INLUDED +#define BEAST_ASIO_STREAMBUF_READSTREAM_IPP_INLUDED + +#include +#include +#include +#include + +namespace beast { + +template +template +class streambuf_readstream< + Stream, Streambuf>::read_some_op +{ + using alloc_type = + handler_alloc; + + struct data + { + streambuf_readstream& brs; + MutableBufferSequence bs; + Handler h; + int state = 0; + + template + data(DeducedHandler&& h_, + streambuf_readstream& brs_, + MutableBufferSequence const& bs_) + : brs(brs_) + , bs(bs_) + , h(std::forward(h_)) + { + } + }; + + std::shared_ptr d_; + +public: + read_some_op(read_some_op&&) = default; + read_some_op(read_some_op const&) = default; + + template + read_some_op(DeducedHandler&& h, Args&&... args) + : d_(std::allocate_shared(alloc_type{h}, + std::forward(h), + std::forward(args)...)) + { + (*this)(error_code{}, 0); + } + + void + operator()(error_code const& ec, + std::size_t bytes_transferred); + + friend + auto asio_handler_allocate( + std::size_t size, read_some_op* op) + { + return boost_asio_handler_alloc_helpers:: + allocate(size, op->d_->h); + } + + friend + auto asio_handler_deallocate( + void* p, std::size_t size, read_some_op* op) + { + return boost_asio_handler_alloc_helpers:: + deallocate(p, size, op->d_->h); + } + + friend + auto asio_handler_is_continuation(read_some_op* op) + { + return boost_asio_handler_cont_helpers:: + is_continuation(op->d_->h); + } + + template + friend + auto asio_handler_invoke(Function&& f, read_some_op* op) + { + return boost_asio_handler_invoke_helpers:: + invoke(f, op->d_->h); + } +}; + +template +template +void +streambuf_readstream:: +read_some_op::operator()( + error_code const& ec, std::size_t bytes_transferred) +{ + auto& d = *d_; + while(! ec && d.state != 99) + { + switch(d.state) + { + case 0: + if(d.brs.sb_.size() == 0) + { + d.state = + d.brs.size_ > 0 ? 2 : 1; + break; + } + d.state = 4; + d.brs.get_io_service().post( + bind_handler(std::move(*this), ec, 0)); + return; + + case 1: + // read (unbuffered) + d.state = 99; + d.brs.next_layer_.async_read_some( + d.bs, std::move(*this)); + return; + + case 2: + // read + d.state = 3; + d.brs.next_layer_.async_read_some( + d.brs.sb_.prepare(d.brs.size_), + std::move(*this)); + return; + + // got data + case 3: + d.state = 4; + d.brs.sb_.commit(bytes_transferred); + break; + + // copy + case 4: + bytes_transferred = + boost::asio::buffer_copy( + d.bs, d.brs.sb_.data()); + d.brs.sb_.consume(bytes_transferred); + // call handler + d.state = 99; + break; + } + } + d.h(ec, bytes_transferred); +} + +//------------------------------------------------------------------------------ + +template +template +streambuf_readstream:: +streambuf_readstream(Args&&... args) + : next_layer_(std::forward(args)...) +{ + static_assert(is_Stream::value, + "Stream requirements not met"); + static_assert(is_Streambuf::value, + "Streambuf requirements not met"); +} + +template +template +auto +streambuf_readstream:: +async_write_some(ConstBufferSequence const& buffers, + WriteHandler&& handler) +{ + static_assert(is_ConstBufferSequence< + ConstBufferSequence>::value, + "ConstBufferSequence requirements not met"); + static_assert(is_Handler::value, + "WriteHandler requirements not met"); + return next_layer_.async_write_some(buffers, + std::forward(handler)); +} + +template +template +std::size_t +streambuf_readstream:: +read_some( + MutableBufferSequence const& buffers) +{ + static_assert(is_MutableBufferSequence< + MutableBufferSequence>::value, + "MutableBufferSequence requirements not met"); + error_code ec; + auto n = read_some(buffers, ec); + if(ec) + throw boost::system::system_error{ec}; + return n; +} + +template +template +std::size_t +streambuf_readstream:: +read_some(MutableBufferSequence const& buffers, + error_code& ec) +{ + static_assert(is_MutableBufferSequence< + MutableBufferSequence>::value, + "MutableBufferSequence requirements not met"); + using boost::asio::buffer_size; + using boost::asio::buffer_copy; + if(buffer_size(buffers) == 0) + return 0; + if(size_ == 0) + return next_layer_.read_some(buffers, ec); + if(sb_.size() == 0) + { + sb_.commit(next_layer_.read_some( + sb_.prepare(size_), ec)); + if(ec) + return 0; + } + auto bytes_transferred = + buffer_copy(buffers, sb_.data()); + sb_.consume(bytes_transferred); + return bytes_transferred; +} + +template +template +auto +streambuf_readstream:: +async_read_some( + MutableBufferSequence const& buffers, + ReadHandler&& handler) +{ + static_assert(is_MutableBufferSequence< + MutableBufferSequence>::value, + "MutableBufferSequence requirements not met"); + beast::async_completion< + ReadHandler, void(error_code, std::size_t) + > completion(handler); + read_some_op{ + completion.handler, *this, buffers}; + return completion.result.get(); +} + +} // beast + +#endif diff --git a/beast/asio/io_latency_probe.h b/beast/asio/io_latency_probe.h index 158ed9ea37..2ecd25248e 100644 --- a/beast/asio/io_latency_probe.h +++ b/beast/asio/io_latency_probe.h @@ -20,15 +20,14 @@ #ifndef BEAST_ASIO_IO_LATENCY_PROBE_H_INCLUDED #define BEAST_ASIO_IO_LATENCY_PROBE_H_INCLUDED +#include +#include +#include #include #include #include #include -#include -#include -#include - namespace beast { /** Measures handler latency on an io_service queue. */ diff --git a/beast/asio/prepare_buffers.h b/beast/asio/prepare_buffers.h new file mode 100644 index 0000000000..724e56b26e --- /dev/null +++ b/beast/asio/prepare_buffers.h @@ -0,0 +1,345 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_PREPARE_BUFFERS_H_INLUDED +#define BEAST_ASIO_PREPARE_BUFFERS_H_INLUDED + +#include +#include +#include +#include +#include +#include + +namespace beast { + +/** Get a trimmed const buffer. + + The new buffer starts at the beginning of the passed + buffer. Ownership of the underlying memory is not + transferred. +*/ +inline +boost::asio::const_buffer +prepare_buffer(std::size_t n, + boost::asio::const_buffer buffer) +{ + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + return { buffer_cast(buffer), + std::min(n, buffer_size(buffer)) }; +} + +/** Get a trimmed mutable buffer. + + The new buffer starts at the beginning of the passed + buffer. Ownership of the underlying memory is not + transferred. +*/ +inline +boost::asio::mutable_buffer +prepare_buffer(std::size_t n, + boost::asio::mutable_buffer buffer) +{ + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + return { buffer_cast(buffer), + std::min(n, buffer_size(buffer)) }; +} + +/** Wrapper to produce a trimmed buffer sequence. + + This wraps a buffer sequence to efficiently present a shorter + subset of the original list of buffers starting with the first + byte of the original sequence. + + @tparam BufferSequence The buffer sequence to wrap. +*/ +template +class prepared_buffers +{ + using iter_type = + typename BufferSequence::const_iterator; + + BufferSequence bs_; + iter_type back_; + iter_type end_; + std::size_t size_; + + template + prepared_buffers(Deduced&& other, + std::size_t nback, std::size_t nend) + : bs_(std::forward(other).bs_) + , back_(std::next(bs_.begin(), nback)) + , end_(std::next(bs_.begin(), nend)) + , size_(other.size_) + { + } + +public: + /// The type for each element in the list of buffers. + using value_type = + typename std::iterator_traits::value_type; + + class const_iterator; + + /// Move constructor. + prepared_buffers(prepared_buffers&&); + + /// Copy constructor. + prepared_buffers(prepared_buffers const&); + + /// Move assignment. + prepared_buffers& operator=(prepared_buffers&&); + + /// Copy assignment. + prepared_buffers& operator=(prepared_buffers const&); + + /** Construct a wrapped buffer sequence. + + @param n The maximum number of bytes in the wrapped sequence. + If this is larger than the size of buffers, the wrapped + sequence will represent the entire input sequence. + + @param buffers The buffer sequence to wrap. A copy of the sequence + will be made, but ownership of the underlying memory is not transferred. + */ + prepared_buffers(std::size_t n, BufferSequence const& buffers); + + /// Get a bidirectional iterator to the first element. + const_iterator + begin() const; + + /// Get a bidirectional iterator for one past the last element. + const_iterator + end() const; + +private: + void + setup(std::size_t n) + { + for(end_ = bs_.begin(); end_ != bs_.end(); ++end_) + { + auto const len = + boost::asio::buffer_size(*end_); + if(n <= len) + { + size_ = n; + back_ = end_++; + return; + } + n -= len; + } + size_ = 0; + back_ = end_; + } +}; + +/// A bidirectional iterator type that may be used to read elements. +template +class prepared_buffers::const_iterator +{ + friend class prepared_buffers; + + using iter_type = + typename BufferSequence::const_iterator; + + prepared_buffers const* b_; + typename BufferSequence::const_iterator it_; + +public: + using value_type = + typename std::iterator_traits::value_type; + using pointer = value_type const*; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + const_iterator() = default; + const_iterator(const_iterator&& other) = default; + const_iterator(const_iterator const& other) = default; + const_iterator& operator=(const_iterator&& other) = default; + const_iterator& operator=(const_iterator const& other) = default; + + bool + operator==(const_iterator const& other) const + { + return b_ == other.b_ && it_ == other.it_; + } + + bool + operator!=(const_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*() const + { + if(it_ == b_->back_) + return prepare_buffer(b_->size_, *it_); + return *it_; + } + + pointer + operator->() const = delete; + + const_iterator& + operator++() + { + ++it_; + return *this; + } + + const_iterator + operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + + const_iterator& + operator--() + { + --it_; + return *this; + } + + const_iterator + operator--(int) + { + auto temp = *this; + --(*this); + return temp; + } + +private: + const_iterator(prepared_buffers const& b, + bool at_end) + : b_(&b) + , it_(at_end ? b.end_ : b.bs_.begin()) + { + } +}; + +template +prepared_buffers:: +prepared_buffers(prepared_buffers&& other) + : prepared_buffers(std::move(other), + std::distance(other.bs_.begin(), other.back_), + std::distance(other.bs_.begin(), other.end_)) +{ +} + +template +prepared_buffers:: +prepared_buffers(prepared_buffers const& other) + : prepared_buffers(other, + std::distance(other.bs_.begin(), other.back_), + std::distance(other.bs_.begin(), other.end_)) +{ +} + +template +auto +prepared_buffers:: +operator=(prepared_buffers&& other) -> + prepared_buffers& +{ + auto const nback = std::distance( + other.bs_.begin(), other.back_); + auto const nend = std::distance( + other.bs_.begin(), other.end_); + bs_ = std::move(other.bs_); + back_ = std::next(bs_.begin(), nback); + end_ = std::next(bs_.begin(), nend); + size_ = other.size_; + return *this; +} + +template +auto +prepared_buffers:: +operator=(prepared_buffers const& other) -> + prepared_buffers& +{ + auto const nback = std::distance( + other.bs_.begin(), other.back_); + auto const nend = std::distance( + other.bs_.begin(), other.end_); + bs_ = other.bs_; + back_ = std::next(bs_.begin(), nback); + end_ = std::next(bs_.begin(), nend); + size_ = other.size_; + return *this; +} + +template +prepared_buffers:: +prepared_buffers(std::size_t n, BufferSequence const& bs) + : bs_(bs) +{ + setup(n); +} + +template +auto +prepared_buffers::begin() const -> + const_iterator +{ + return const_iterator{*this, false}; +} + +template +auto +prepared_buffers::end() const -> + const_iterator +{ + return const_iterator{*this, true}; +} + +//------------------------------------------------------------------------------ + +/** Return a trimmed, wrapped buffer sequence. + + This function returns a new buffer sequence which wraps the provided + buffer sequence and efficiently presents a shorter subset of the + original list of buffers starting with the first byte of the original + sequence. + + @param n The maximum number of bytes in the wrapped sequence. If this + is larger than the size of buffers, the wrapped sequence will represent + the entire input sequence. + + @param buffers The buffer sequence to wrap. A copy of the sequence + will be made, but ownership of the underlying memory is not transferred. +*/ +template +inline +prepared_buffers +prepare_buffers(std::size_t n, BufferSequence const& buffers) +{ + return prepared_buffers(n, buffers); +} + +} // beast + +#endif diff --git a/beast/asio/tests/bind_handler.test.cpp b/beast/asio/src/test/beast_asio_bind_handler_test.cpp similarity index 93% rename from beast/asio/tests/bind_handler.test.cpp rename to beast/asio/src/test/beast_asio_bind_handler_test.cpp index 551d327562..826de07ac0 100644 --- a/beast/asio/tests/bind_handler.test.cpp +++ b/beast/asio/src/test/beast_asio_bind_handler_test.cpp @@ -17,20 +17,12 @@ */ //============================================================================== -// LIBS: boost_system - -#if BEAST_INCLUDE_BEASTCONFIG -#include -#endif - #include - #include - #include namespace beast { -namespace asio { +namespace test { class bind_handler_test : public unit_test::suite { @@ -51,5 +43,6 @@ public: BEAST_DEFINE_TESTSUITE(bind_handler,asio,beast); -} -} +} // test +} // beast + diff --git a/beast/asio/src/test/beast_asio_buffers_test.cpp b/beast/asio/src/test/beast_asio_buffers_test.cpp new file mode 100644 index 0000000000..c0686cec49 --- /dev/null +++ b/beast/asio/src/test/beast_asio_buffers_test.cpp @@ -0,0 +1,362 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace test { + +class buffers_test : public unit_test::suite +{ +public: + template + static + std::string + to_string(ConstBufferSequence const& bs) + { + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + std::string s; + s.reserve(buffer_size(bs)); + for(auto const& b : bs) + s.append(buffer_cast(b), + buffer_size(b)); + return s; + } + + void testStreambuf() + { + using boost::asio::buffer; + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + std::string const s = "Hello, world"; + expect(s.size() == 12); + for(std::size_t i = 1; i < 12; ++i) { + for(std::size_t x = 1; x < 4; ++x) { + for(std::size_t y = 1; y < 4; ++y) { + for(std::size_t t = 1; t < 4; ++ t) { + for(std::size_t u = 1; u < 4; ++ u) { + std::size_t z = s.size() - (x + y); + std::size_t v = s.size() - (t + u); + { + streambuf sb(i); + decltype(sb)::mutable_buffers_type d; + d = sb.prepare(z); expect(buffer_size(d) == z); + d = sb.prepare(0); expect(buffer_size(d) == 0); + d = sb.prepare(y); expect(buffer_size(d) == y); + d = sb.prepare(x); expect(buffer_size(d) == x); + sb.commit(buffer_copy(d, buffer(s.data(), x))); + expect(sb.size() == x); + expect(buffer_size(sb.data()) == sb.size()); + d = sb.prepare(x); expect(buffer_size(d) == x); + d = sb.prepare(0); expect(buffer_size(d) == 0); + d = sb.prepare(z); expect(buffer_size(d) == z); + d = sb.prepare(y); expect(buffer_size(d) == y); + sb.commit(buffer_copy(d, buffer(s.data()+x, y))); + sb.commit(1); + expect(sb.size() == x + y); + expect(buffer_size(sb.data()) == sb.size()); + d = sb.prepare(x); expect(buffer_size(d) == x); + d = sb.prepare(y); expect(buffer_size(d) == y); + d = sb.prepare(0); expect(buffer_size(d) == 0); + d = sb.prepare(z); expect(buffer_size(d) == z); + sb.commit(buffer_copy(d, buffer(s.data()+x+y, z))); + sb.commit(2); + expect(sb.size() == x + y + z); + expect(buffer_size(sb.data()) == sb.size()); + expect(to_string(sb.data()) == s); + sb.consume(t); + d = sb.prepare(0); expect(buffer_size(d) == 0); + expect(to_string(sb.data()) == s.substr(t, std::string::npos)); + sb.consume(u); + expect(to_string(sb.data()) == s.substr(t + u, std::string::npos)); + sb.consume(v); + expect(to_string(sb.data()) == ""); + sb.consume(1); + d = sb.prepare(0); expect(buffer_size(d) == 0); + } + }}}}} + } + + void testBuffersAdapter() + { + using boost::asio::buffer; + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + using boost::asio::const_buffer; + using boost::asio::mutable_buffer; + char buf[12]; + std::string const s = "Hello, world"; + expect(s.size() == sizeof(buf)); + for(std::size_t i = 1; i < 4; ++i) { + for(std::size_t j = 1; j < 4; ++j) { + for(std::size_t x = 1; x < 4; ++x) { + for(std::size_t y = 1; y < 4; ++y) { + for(std::size_t t = 1; t < 4; ++ t) { + for(std::size_t u = 1; u < 4; ++ u) { + std::size_t k = sizeof(buf) - (i + j); + std::size_t z = sizeof(buf) - (x + y); + std::size_t v = sizeof(buf) - (t + u); + { + std::memset(buf, 0, sizeof(buf)); + std::array bs{{ + mutable_buffer{&buf[0], i}, + mutable_buffer{&buf[i], j}, + mutable_buffer{&buf[i+j], k}}}; + buffers_adapter ba(std::move(bs)); + expect(ba.max_size() == sizeof(buf)); + decltype(ba)::mutable_buffers_type d; + d = ba.prepare(z); expect(buffer_size(d) == z); + d = ba.prepare(0); expect(buffer_size(d) == 0); + d = ba.prepare(y); expect(buffer_size(d) == y); + d = ba.prepare(x); expect(buffer_size(d) == x); + ba.commit(buffer_copy(d, buffer(s.data(), x))); + expect(ba.size() == x); + expect(ba.max_size() == sizeof(buf) - x); + expect(buffer_size(ba.data()) == ba.size()); + d = ba.prepare(x); expect(buffer_size(d) == x); + d = ba.prepare(0); expect(buffer_size(d) == 0); + d = ba.prepare(z); expect(buffer_size(d) == z); + d = ba.prepare(y); expect(buffer_size(d) == y); + ba.commit(buffer_copy(d, buffer(s.data()+x, y))); + ba.commit(1); + expect(ba.size() == x + y); + expect(ba.max_size() == sizeof(buf) - (x + y)); + expect(buffer_size(ba.data()) == ba.size()); + d = ba.prepare(x); expect(buffer_size(d) == x); + d = ba.prepare(y); expect(buffer_size(d) == y); + d = ba.prepare(0); expect(buffer_size(d) == 0); + d = ba.prepare(z); expect(buffer_size(d) == z); + ba.commit(buffer_copy(d, buffer(s.data()+x+y, z))); + ba.commit(2); + expect(ba.size() == x + y + z); + expect(ba.max_size() == 0); + expect(buffer_size(ba.data()) == ba.size()); + expect(to_string(ba.data()) == s); + ba.consume(t); + d = ba.prepare(0); expect(buffer_size(d) == 0); + expect(to_string(ba.data()) == s.substr(t, std::string::npos)); + ba.consume(u); + expect(to_string(ba.data()) == s.substr(t + u, std::string::npos)); + ba.consume(v); + expect(to_string(ba.data()) == ""); + ba.consume(1); + d = ba.prepare(0); expect(buffer_size(d) == 0); + try + { + ba.prepare(1); + fail(); + } + catch(...) + { + pass(); + } + } + }}}}}} + } + + void testConsuming() + { + using boost::asio::buffer; + using boost::asio::const_buffer; + char buf[12]; + std::string const s = "Hello, world"; + expect(s.size() == sizeof(buf)); + buffer_copy(buffer(buf), buffer(s)); + expect(to_string(buffer(buf)) == s); + for(std::size_t i = 1; i < 4; ++i) { + for(std::size_t j = 1; j < 4; ++j) { + for(std::size_t x = 1; x < 4; ++x) { + for(std::size_t y = 1; y < 4; ++y) { + std::size_t k = sizeof(buf) - (i + j); + std::size_t z = sizeof(buf) - (x + y); + { + std::array bs{{ + const_buffer{&buf[0], i}, + const_buffer{&buf[i], j}, + const_buffer{&buf[i+j], k}}}; + consuming_buffers cb(bs); + expect(to_string(cb) == s); + cb.consume(0); + expect(to_string(cb) == s); + cb.consume(x); + expect(to_string(cb) == s.substr(x)); + cb.consume(y); + expect(to_string(cb) == s.substr(x+y)); + cb.consume(z); + expect(to_string(cb) == ""); + cb.consume(1); + expect(to_string(cb) == ""); + } + }}}} + } + + void testStaticBuffers() + { + using boost::asio::buffer; + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + char buf[12]; + std::string const s = "Hello, world"; + expect(s.size() == sizeof(buf)); + for(std::size_t i = 1; i < 4; ++i) { + for(std::size_t j = 1; j < 4; ++j) { + for(std::size_t x = 1; x < 4; ++x) { + for(std::size_t y = 1; y < 4; ++y) { + for(std::size_t t = 1; t < 4; ++ t) { + for(std::size_t u = 1; u < 4; ++ u) { + std::size_t z = sizeof(buf) - (x + y); + std::size_t v = sizeof(buf) - (t + u); + { + std::memset(buf, 0, sizeof(buf)); + static_streambuf_n ba; + decltype(ba)::mutable_buffers_type d; + d = ba.prepare(z); expect(buffer_size(d) == z); + d = ba.prepare(0); expect(buffer_size(d) == 0); + d = ba.prepare(y); expect(buffer_size(d) == y); + d = ba.prepare(x); expect(buffer_size(d) == x); + ba.commit(buffer_copy(d, buffer(s.data(), x))); + expect(ba.size() == x); + expect(buffer_size(ba.data()) == ba.size()); + d = ba.prepare(x); expect(buffer_size(d) == x); + d = ba.prepare(0); expect(buffer_size(d) == 0); + d = ba.prepare(z); expect(buffer_size(d) == z); + d = ba.prepare(y); expect(buffer_size(d) == y); + ba.commit(buffer_copy(d, buffer(s.data()+x, y))); + ba.commit(1); + expect(ba.size() == x + y); + expect(buffer_size(ba.data()) == ba.size()); + d = ba.prepare(x); expect(buffer_size(d) == x); + d = ba.prepare(y); expect(buffer_size(d) == y); + d = ba.prepare(0); expect(buffer_size(d) == 0); + d = ba.prepare(z); expect(buffer_size(d) == z); + ba.commit(buffer_copy(d, buffer(s.data()+x+y, z))); + ba.commit(2); + expect(ba.size() == x + y + z); + expect(buffer_size(ba.data()) == ba.size()); + expect(to_string(ba.data()) == s); + ba.consume(t); + d = ba.prepare(0); expect(buffer_size(d) == 0); + expect(to_string(ba.data()) == s.substr(t, std::string::npos)); + ba.consume(u); + expect(to_string(ba.data()) == s.substr(t + u, std::string::npos)); + ba.consume(v); + expect(to_string(ba.data()) == ""); + ba.consume(1); + d = ba.prepare(0); expect(buffer_size(d) == 0); + try + { + ba.prepare(1); + fail(); + } + catch(...) + { + pass(); + } + } + }}}}}} + } + + void testAppendBuffers() + { + using boost::asio::buffer_size; + using boost::asio::const_buffer; + char buf[10]; + std::list b1; + std::vector b2{ + const_buffer{buf+0, 1}, + const_buffer{buf+1, 2}}; + std::list b3; + std::array b4{{ + const_buffer{buf+3, 1}, + const_buffer{buf+4, 2}, + const_buffer{buf+6, 3}}}; + std::list b5{ + const_buffer{buf+9, 1}}; + std::list b6; + auto bs = append_buffers( + b1, b2, b3, b4, b5, b6); + expect(buffer_size(bs) == 10); + std::vector v; + for(auto iter = std::make_reverse_iterator(bs.end()); + iter != std::make_reverse_iterator(bs.begin()); ++iter) + v.emplace_back(*iter); + expect(buffer_size(bs) == 10); + decltype(bs) bs2(bs); + auto bs3(std::move(bs)); + bs = bs2; + bs3 = std::move(bs2); + { + streambuf sb1, sb2; + expect(buffer_size(append_buffers( + sb1.prepare(5), sb2.prepare(7))) == 12); + sb1.commit(5); + sb2.commit(7); + expect(buffer_size(append_buffers( + sb1.data(), sb2.data())) == 12); + } + } + + void testClipBuffers() + { + using boost::asio::const_buffer; + std::string const s = "Hello, world"; + expect(s.size() == 12); + for(std::size_t x = 1; x < 4; ++x) { + for(std::size_t y = 1; y < 4; ++y) { + std::size_t z = s.size() - (x + y); + { + std::array bs{{ + const_buffer{&s[0], x}, + const_buffer{&s[x], y}, + const_buffer{&s[x+y], z}}}; + for(std::size_t i = 0; i <= s.size() + 1; ++i) + expect(to_string(prepare_buffers(i, bs)) == + s.substr(0, i)); + } + }} + } + + void run() override + { + testStreambuf(); + testBuffersAdapter(); + testConsuming(); + testStaticBuffers(); + + testAppendBuffers(); + testClipBuffers(); + } +}; + +BEAST_DEFINE_TESTSUITE(buffers,asio,beast); + +} // test +} // beast diff --git a/beast/asio/tests/error_test.cpp b/beast/asio/src/test/beast_asio_error_test.cpp similarity index 82% rename from beast/asio/tests/error_test.cpp rename to beast/asio/src/test/beast_asio_error_test.cpp index 32e43decfc..9026ea2bbb 100644 --- a/beast/asio/tests/error_test.cpp +++ b/beast/asio/src/test/beast_asio_error_test.cpp @@ -17,22 +17,22 @@ */ //============================================================================== -#include +#include #include #include namespace beast { -namespace asio { class error_test : public unit_test::suite { public: - void run () + void run() { { - boost::system::error_code ec = boost::system::error_code (335544539, - boost::asio::error::get_ssl_category ()); - std::string const s = beast::asio::asio_message (ec); + boost::system::error_code ec = + boost::system::error_code (335544539, + boost::asio::error::get_ssl_category ()); + std::string const s = beast::error_message_with_ssl(ec); expect(s == " (20,0,219) error:140000DB:SSL routines:SSL routines:short read"); } } @@ -40,5 +40,4 @@ public: BEAST_DEFINE_TESTSUITE(error,asio,beast); -} -} +} // beast diff --git a/beast/asio/impl/error.cpp b/beast/asio/ssl_error.h similarity index 52% rename from beast/asio/impl/error.cpp rename to beast/asio/ssl_error.h index 65ede2e831..65afc8ffcf 100644 --- a/beast/asio/impl/error.cpp +++ b/beast/asio/ssl_error.h @@ -17,43 +17,56 @@ */ //============================================================================== -#include +#ifndef BEAST_ASIO_SSL_ERROR_H_INCLUDED +#define BEAST_ASIO_SSL_ERROR_H_INCLUDED + +#include +//#include // Causes error with WinSock.h +#include #include namespace beast { -namespace asio { - -// This buffer must be at least 120 bytes, most examples use 256. -// https://www.openssl.org/docs/crypto/ERR_error_string.html -static std::uint32_t const errorBufferSize (256); +/** Returns a human readable message if the error code is SSL related. */ +template std::string -asio_message (boost::system::error_code const& ec) +error_message_with_ssl(boost::system::error_code const& ec) { std::string error; - if (ec.category () == boost::asio::error::get_ssl_category ()) + if (ec.category() == boost::asio::error::get_ssl_category()) { error = " (" - + boost::lexical_cast (ERR_GET_LIB (ec.value ())) + + boost::lexical_cast(ERR_GET_LIB (ec.value ())) + "," - + boost::lexical_cast (ERR_GET_FUNC (ec.value ())) + + boost::lexical_cast(ERR_GET_FUNC (ec.value ())) + "," - + boost::lexical_cast (ERR_GET_REASON (ec.value ())) + + boost::lexical_cast(ERR_GET_REASON (ec.value ())) + ") "; - // - char buf[errorBufferSize]; - ::ERR_error_string_n (ec.value (), buf, errorBufferSize); + // This buffer must be at least 120 bytes, most examples use 256. + // https://www.openssl.org/docs/crypto/ERR_error_string.html + char buf[256]; + ::ERR_error_string_n(ec.value (), buf, sizeof(buf)); error += buf; } else { - error = ec.message (); + error = ec.message(); } return error; } +/** Returns `true` if the error code is a SSL "short read." */ +inline +bool +is_short_read(boost::system::error_code const& ec) +{ + return (ec.category() == boost::asio::error::get_ssl_category()) + && (ERR_GET_REASON(ec.value()) == SSL_R_SHORT_READ); } -} + +} // beast + +#endif diff --git a/beast/asio/static_streambuf.h b/beast/asio/static_streambuf.h new file mode 100644 index 0000000000..349998d39e --- /dev/null +++ b/beast/asio/static_streambuf.h @@ -0,0 +1,471 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_STATIC_STREAMBUF_H_INLUDED +#define BEAST_ASIO_STATIC_STREAMBUF_H_INLUDED + +#include +#include +#include +#include +#include +#include + +namespace beast { + +/** A `Streambuf` with a fixed size internal buffer. + + Ownership of the underlying storage belongs to the derived class. + + @note Variables are usually declared using the template class + `static_streambuf_n`; however, to reduce the number of instantiations + of template functions receiving static stream buffer arguments in a + deduced context, the signature of the receiving function should use + `static_streambuf`. +*/ +class static_streambuf +{ +#if GENERATING_DOCS +private: +#else +protected: +#endif + std::uint8_t* in_; + std::uint8_t* out_; + std::uint8_t* last_; + std::uint8_t* end_; + +public: + class const_buffers_type; + class mutable_buffers_type; + +#if GENERATING_DOCS +private: +#endif + static_streambuf( + static_streambuf const& other) noexcept = delete; + static_streambuf& operator=( + static_streambuf const&) noexcept = delete; +#if GENERATING_DOCS +public: +#endif + + /// Returns the largest size output sequence possible. + std::size_t + max_size() const + { + return end_ - in_; + } + + /// Get the size of the input sequence. + std::size_t + size() const + { + return out_ - in_; + } + + /** Get a list of buffers that represents the output sequence, with the given size. + + @throws std::length_error if the size would exceed the limit + imposed by the underlying mutable buffer sequence. + */ + mutable_buffers_type + prepare(std::size_t n); + + /// Move bytes from the output sequence to the input sequence. + void + commit(std::size_t n) + { + out_ += std::min(n, last_ - out_); + } + + /// Get a list of buffers that represents the input sequence. + const_buffers_type + data() const; + + /// Remove bytes from the input sequence. + void + consume(std::size_t n) + { + in_ += std::min(n, out_ - in_); + } + +#if GENERATING_DOCS +private: +#else +protected: +#endif + static_streambuf(std::uint8_t* p, std::size_t n) + { + reset(p, n); + } + + void + reset(std::uint8_t* p, std::size_t n) + { + in_ = p; + out_ = p; + last_ = p; + end_ = p + n; + } +}; + +//------------------------------------------------------------------------------ + +/// The type used to represent the input sequence as a list of buffers. +class static_streambuf::const_buffers_type +{ + std::size_t n_; + std::uint8_t const* p_; + +public: + using value_type = boost::asio::const_buffer; + + class const_iterator; + + const_buffers_type() = default; + const_buffers_type( + const_buffers_type const&) = default; + const_buffers_type& operator=( + const_buffers_type const&) = default; + + const_iterator + begin() const; + + const_iterator + end() const; + +private: + friend class static_streambuf; + + const_buffers_type( + std::uint8_t const* p, std::size_t n) + : n_(n) + , p_(p) + { + } +}; + +class static_streambuf::const_buffers_type::const_iterator +{ + std::size_t n_; + std::uint8_t const* p_; + +public: + using value_type = boost::asio::const_buffer; + using pointer = value_type const*; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + const_iterator() = default; + const_iterator(const_iterator&& other) = default; + const_iterator(const_iterator const& other) = default; + const_iterator& operator=(const_iterator&& other) = default; + const_iterator& operator=(const_iterator const& other) = default; + + bool + operator==(const_iterator const& other) const + { + return p_ == other.p_; + } + + bool + operator!=(const_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*() const + { + return value_type{p_, n_}; + } + + pointer + operator->() const = delete; + + const_iterator& + operator++() + { + p_ += n_; + return *this; + } + + const_iterator + operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + + const_iterator& + operator--() + { + p_ -= n_; + return *this; + } + + const_iterator + operator--(int) + { + auto temp = *this; + --(*this); + return temp; + } + +private: + friend class const_buffers_type; + + const_iterator( + std::uint8_t const* p, std::size_t n) + : n_(n) + , p_(p) + { + } +}; + +inline +auto +static_streambuf::const_buffers_type::begin() const -> + const_iterator +{ + return const_iterator{p_, n_}; +} + +inline +auto +static_streambuf::const_buffers_type::end() const -> + const_iterator +{ + return const_iterator{p_ + n_, n_}; +} + +//------------------------------------------------------------------------------ + +/// The type used to represent the output sequence as a list of buffers. +class static_streambuf::mutable_buffers_type +{ + std::size_t n_; + std::uint8_t* p_; + +public: + using value_type = boost::asio::mutable_buffer; + + class const_iterator; + + mutable_buffers_type() = default; + mutable_buffers_type( + mutable_buffers_type const&) = default; + mutable_buffers_type& operator=( + mutable_buffers_type const&) = default; + + const_iterator + begin() const; + + const_iterator + end() const; + +private: + friend class static_streambuf; + + mutable_buffers_type( + std::uint8_t* p, std::size_t n) + : n_(n) + , p_(p) + { + } +}; + +class static_streambuf::mutable_buffers_type::const_iterator +{ + std::size_t n_; + std::uint8_t* p_; + +public: + using value_type = boost::asio::mutable_buffer; + using pointer = value_type const*; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + const_iterator() = default; + const_iterator(const_iterator&& other) = default; + const_iterator(const_iterator const& other) = default; + const_iterator& operator=(const_iterator&& other) = default; + const_iterator& operator=(const_iterator const& other) = default; + + bool + operator==(const_iterator const& other) const + { + return p_ == other.p_; + } + + bool + operator!=(const_iterator const& other) const + { + return !(*this == other); + } + + reference + operator*() const + { + return value_type{p_, n_}; + } + + pointer + operator->() const = delete; + + const_iterator& + operator++() + { + p_ += n_; + return *this; + } + + const_iterator + operator++(int) + { + auto temp = *this; + ++(*this); + return temp; + } + + const_iterator& + operator--() + { + p_ -= n_; + return *this; + } + + const_iterator + operator--(int) + { + auto temp = *this; + --(*this); + return temp; + } + +private: + friend class mutable_buffers_type; + + const_iterator(std::uint8_t* p, std::size_t n) + : n_(n) + , p_(p) + { + } +}; + +inline +auto +static_streambuf::mutable_buffers_type::begin() const -> + const_iterator +{ + return const_iterator{p_, n_}; +} + +inline +auto +static_streambuf::mutable_buffers_type::end() const -> + const_iterator +{ + return const_iterator{p_ + n_, n_}; +} + +//------------------------------------------------------------------------------ + +inline +auto +static_streambuf::prepare(std::size_t n) -> + mutable_buffers_type +{ + if(n > static_cast(end_ - out_)) + throw std::length_error("no space in streambuf"); + last_ = out_ + n; + return mutable_buffers_type{out_, n}; +} + +inline +auto +static_streambuf::data() const -> + const_buffers_type +{ + return const_buffers_type{in_, + static_cast(out_ - in_)}; +} + +//------------------------------------------------------------------------------ + +/** A `Streambuf` with a fixed size internal buffer. + + @tparam N The number of bytes in the internal buffer. + + @note To reduce the number of template instantiations when passing + objects of this type in a deduced context, the signature of the + receiving function should use `static_streambuf` instead. +*/ +template +class static_streambuf_n + : private boost::base_from_member< + std::array> + , public static_streambuf +{ + using member_type = boost::base_from_member< + std::array>; +public: +#if GENERATING_DOCS +private: +#endif + static_streambuf_n( + static_streambuf_n const&) = delete; + static_streambuf_n& operator=( + static_streambuf_n const&) = delete; +#if GENERATING_DOCS +public: +#endif + + /// Construct a static stream buffer. + static_streambuf_n() + : static_streambuf( + member_type::member.data(), + member_type::member.size()) + { + } + + /** Reset the stream buffer. + + Postconditions: + The input sequence and output sequence are empty, + `max_size()` returns `N`. + */ + void + reset() + { + static_streambuf::reset( + member_type::member.data(), + member_type::member.size()); + } +}; + +} // beast + +#endif diff --git a/beast/asio/streambuf.h b/beast/asio/streambuf.h index 6f7e5af767..22031930cc 100644 --- a/beast/asio/streambuf.h +++ b/beast/asio/streambuf.h @@ -20,661 +20,12 @@ #ifndef BEAST_ASIO_STREAMBUF_H_INCLUDED #define BEAST_ASIO_STREAMBUF_H_INCLUDED -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include namespace beast { -namespace asio { - -/** Implements asio::streambuf interface using multiple buffers. */ -template -class basic_streambuf - : private empty_base_optimization -{ -public: - using size_type = typename std::allocator_traits::size_type; - using const_buffer = boost::asio::const_buffer; - using mutable_buffer = boost::asio::mutable_buffer; - -private: - class element; - - using alloc_traits = std::allocator_traits; - using list_type = typename boost::intrusive::make_list >::type; - using iterator = typename list_type::iterator; - using const_iterator = typename list_type::const_iterator; - - /* These diagrams illustrate the layout and state variables. - - Input and output contained entirely in one element: - - 0 out_ - |<-------------+------------------------------------------->| - in_pos_ out_pos_ out_end_ - - - Output contained in first and second elements: - - out_ - |<------+----------+------->| |<----------+-------------->| - in_pos_ out_pos_ out_end_ - - - Output contained in the second element: - - out_ - |<------------+------------>| |<----+-------------------->| - in_pos_ out_pos_ out_end_ - - - Output contained in second and third elements: - - out_ - |<-----+-------->| |<-------+------>| |<--------------->| - in_pos_ out_pos_ out_end_ - - - Input sequence is empty: - - out_ - |<------+------------------>| |<-----------+------------->| - out_pos_ out_end_ - in_pos_ - - - Output sequence is empty: - - out_ - |<------+------------------>| |<------+------------------>| - in_pos_ out_pos_ - out_end_ - - - The end of output can point to the end of an element. - But out_pos_ should never point to the end: - - out_ - |<------+------------------>| |<------+------------------>| - in_pos_ out_pos_ out_end_ - - - When the input sequence entirely fills the last element and - the output sequence is empty, out_ will point to the end of - the list of buffers, and out_pos_ and out_end_ will be 0: - - - |<------+------------------>| out_ == list_.end() - in_pos_ out_pos_ == 0 - out_end_ == 0 - */ - - list_type list_; - size_type block_size_; - size_type block_size_next_; - size_type in_size_ = 0; // size of the input sequence - iterator out_; // element that contains out_pos_ - size_type in_pos_ = 0; // input offset in list_.front() - size_type out_pos_ = 0; // output offset in *out_ - size_type out_end_ = 0; // output end offset in list_.back() - -public: - class const_buffers_type; - class mutable_buffers_type; - - basic_streambuf (basic_streambuf const& other) = delete; - basic_streambuf& operator= (basic_streambuf const& other) = delete; - basic_streambuf& operator= (basic_streambuf&& other) = delete; - - ~basic_streambuf(); - - explicit - basic_streambuf(std::size_t block_size = 16*1024, - Allocator const& alloc = Allocator{}); - - basic_streambuf (basic_streambuf&& other); - - /** Get the maximum size of the basic_streambuf. */ - size_type - max_size() const - { - return std::numeric_limits::max(); - } - - /** Get the size of the input sequence. */ - size_type - size() const - { - return in_size_; - } - - /** Get a list of buffers that represents the output sequence, with the given size. */ - mutable_buffers_type - prepare (size_type n); - - /** Move bytes from the output sequence to the input sequence. */ - void - commit (size_type n); - - /** Get a list of buffers that represents the input sequence. */ - const_buffers_type - data() const; - - /** Remove bytes from the input sequence. */ - void - consume (size_type n); - -private: - void - debug_check() const; -}; - -//------------------------------------------------------------------------------ - -template -class basic_streambuf::element - : public boost::intrusive::list_base_hook < - boost::intrusive::link_mode > -{ -private: - size_type const size_; // size of the allocation minus sizeof(element) - -public: - element (element const&) = delete; - element& operator= (element const&) = delete; - - explicit - element (size_type block_size) - : size_(block_size) - { } - - size_type - size() const - { - return size_; - } - - size_type - alloc_size() const - { - return size_ + sizeof(*this); - } - - char* - data() const - { - return const_cast( - reinterpret_cast(this+1)); - } -}; - -//------------------------------------------------------------------------------ - -template -class basic_streambuf::const_buffers_type -{ -public: - using value_type = const_buffer; - -private: - struct transform - { - using argument_type = element; - using result_type = value_type; - - basic_streambuf const* streambuf_ = nullptr; - - transform() = default; - - explicit - transform (basic_streambuf const& streambuf) - : streambuf_ (&streambuf) - { - } - - value_type const - operator() (element const& e) const; - }; - - basic_streambuf const* streambuf_ = nullptr; - -public: - using const_iterator = boost::transform_iterator< - transform, typename list_type::const_iterator, - value_type, value_type>; - - const_buffers_type() = default; - const_buffers_type (const_buffers_type const&) = default; - const_buffers_type& operator= (const_buffers_type const&) = default; - - const_iterator - begin() const - { - return const_iterator (streambuf_->list_.begin(), - transform(*streambuf_)); - } - - const_iterator - end() const - { - return const_iterator (streambuf_->out_ == - streambuf_->list_.end() ? streambuf_->list_.end() : - std::next(streambuf_->out_), transform(*streambuf_)); - } - -private: - friend class basic_streambuf; - - explicit - const_buffers_type (basic_streambuf const& streambuf); -}; - -template -basic_streambuf::const_buffers_type::const_buffers_type ( - basic_streambuf const& streambuf) - : streambuf_ (&streambuf) -{ -} - -template -auto -basic_streambuf::const_buffers_type:: - transform::operator() (element const& e) const -> - value_type const -{ - return value_type (e.data(), - (streambuf_->out_ == streambuf_->list_.end() || - &e != &*streambuf_->out_) ? e.size() : streambuf_->out_pos_) + - (&e == &*streambuf_->list_.begin() ? - streambuf_->in_pos_ : 0); -} - -//------------------------------------------------------------------------------ - -template -class basic_streambuf::mutable_buffers_type -{ -public: - using value_type = mutable_buffer; - -private: - struct transform - { - using argument_type = element; - using result_type = value_type; - - basic_streambuf const* streambuf_ = nullptr; - - transform() = default; - - explicit - transform (basic_streambuf const& streambuf) - : streambuf_ (&streambuf) - { - } - - value_type const - operator() (element const& e) const; - }; - - basic_streambuf const* streambuf_; - -public: - using const_iterator = boost::transform_iterator< - transform, typename list_type::const_iterator, - value_type, value_type>; - - mutable_buffers_type() = default; - mutable_buffers_type (mutable_buffers_type const&) = default; - mutable_buffers_type& operator= (mutable_buffers_type const&) = default; - - const_iterator - begin() const - { - return const_iterator (streambuf_->out_, - transform(*streambuf_)); - } - - const_iterator - end() const - { - return const_iterator (streambuf_->list_.end(), - transform(*streambuf_)); - } - -private: - friend class basic_streambuf; - mutable_buffers_type (basic_streambuf const& streambuf); -}; - -template -basic_streambuf::mutable_buffers_type::mutable_buffers_type ( - basic_streambuf const& streambuf) - : streambuf_ (&streambuf) -{ -} - -template -auto -basic_streambuf::mutable_buffers_type:: - transform::operator() (element const& e) const -> - value_type const -{ - return value_type (e.data(), &e == &*std::prev(streambuf_->list_.end()) ? - streambuf_->out_end_ : e.size()) + (&e == &*streambuf_->out_ ? - streambuf_->out_pos_ : 0); -} - -//------------------------------------------------------------------------------ - -template -basic_streambuf::~basic_streambuf() -{ - for(auto iter = list_.begin(); iter != list_.end();) - { - auto& e = *iter++; - size_type const n = e.alloc_size(); - alloc_traits::destroy(this->member(), &e); - alloc_traits::deallocate(this->member(), - reinterpret_cast(&e), n); - } -} - -template -basic_streambuf::basic_streambuf(std::size_t block_size, - Allocator const& alloc) - : empty_base_optimization(alloc) - , block_size_ (block_size) - , block_size_next_ (block_size) - , out_ (list_.end()) -{ - if (! (block_size > 0)) - throw std::invalid_argument( - "basic_streambuf: invalid block_size"); -} - -template -basic_streambuf::basic_streambuf (basic_streambuf&& other) - : empty_base_optimization(other.member()) - , list_ (std::move(other.list_)) - , block_size_ (other.block_size_) - , block_size_next_ (other.block_size_next_) - , in_size_ (other.in_size_) - , out_ (other.out_) - , in_pos_ (other.in_pos_) - , out_pos_ (other.out_pos_) - , out_end_ (other.out_end_) -{ - other.in_size_ = 0; - other.out_ = other.list_.end(); - other.in_pos_ = 0; - other.out_pos_ = 0; - other.out_end_ = 0; -} - -template -auto -basic_streambuf::prepare (size_type n) -> - mutable_buffers_type -{ - iterator pos = out_; - if (pos != list_.end()) - { - auto const avail = pos->size() - out_pos_; - if (n > avail) - { - n -= avail; - while (++pos != list_.end()) - { - if (n < pos->size()) - { - out_end_ = n; - n = 0; - ++pos; - break; - } - out_end_ = pos->size(); - n -= pos->size(); - } - } - else - { - ++pos; - out_end_ = out_pos_ + n; - n = 0; - } - debug_check(); - } - - if (n > 0) - { - assert(pos == list_.end()); - for(;;) - { - auto const avail = block_size_next_; - auto& e = *reinterpret_cast(alloc_traits::allocate( - this->member(), avail + sizeof(element))); - alloc_traits::construct(this->member(), &e, avail); - list_.push_back(e); - if (out_ == list_.end()) - { - out_ = list_.iterator_to(e); - debug_check(); - } - if (n <= avail) - { - out_end_ = n; - debug_check(); - break; - } - n -= avail; - } - } - else - { - while (pos != list_.end()) - { - auto& e = *pos++; - list_.erase(list_.iterator_to(e)); - auto const len = e.alloc_size(); - alloc_traits::destroy(this->member(), &e); - alloc_traits::deallocate(this->member(), - reinterpret_cast(&e), len); - } - debug_check(); - } - - return mutable_buffers_type (*this); -} - -template -void -basic_streambuf::commit (size_type n) -{ - if (list_.empty()) - return; - if (out_ == list_.end()) - return; - auto const last = std::prev(list_.end()); - while (out_ != last) - { - auto const avail = - out_->size() - out_pos_; - if (n < avail) - { - out_pos_ += n; - in_size_ += n; - debug_check(); - return; - } - ++out_; - n -= avail; - out_pos_ = 0; - in_size_ += avail; - debug_check(); - } - - n = std::min (n, out_end_ - out_pos_); - out_pos_ += n; - in_size_ += n; - if (out_pos_ == out_->size()) - { - ++out_; - out_pos_ = 0; - out_end_ = 0; - } - debug_check(); -} - -template -auto -basic_streambuf::data() const -> - const_buffers_type -{ - return const_buffers_type(*this); -} - -template -void -basic_streambuf::consume (size_type n) -{ - if (list_.empty()) - return; - - auto pos = list_.begin(); - for(;;) - { - if (pos != out_) - { - auto const avail = pos->size() - in_pos_; - if (n < avail) - { - in_size_ -= n; - in_pos_ += n; - debug_check(); - break; - } - n -= avail; - in_size_ -= avail; - in_pos_ = 0; - debug_check(); - - element& e = *pos++; - list_.erase(list_.iterator_to(e)); - size_type const len = e.alloc_size(); - alloc_traits::destroy(this->member(), &e); - alloc_traits::deallocate(this->member(), - reinterpret_cast(&e), len); - } - else - { - auto const avail = out_pos_ - in_pos_; - if (n < avail) - { - in_size_ -= n; - in_pos_ += n; - } - else - { - in_size_ -= avail; - if (out_pos_ != out_end_|| - out_ != list_.iterator_to(list_.back())) - { - in_pos_ = out_pos_; - } - else - { - // Use the whole buffer now. - // Alternatively we could deallocate it. - in_pos_ = 0; - out_pos_ = 0; - out_end_ = 0; - } - } - debug_check(); - break; - } - } -} - -template -void -basic_streambuf::debug_check() const -{ -#ifndef NDEBUG - if (list_.empty()) - { - assert(in_pos_ == 0); - assert(in_size_ == 0); - assert(out_pos_ == 0); - assert(out_end_ == 0); - assert(out_ == list_.end()); - return; - } - - auto const& front = list_.front(); - - assert(in_pos_ < front.size()); - - if (out_ == list_.end()) - { - assert(out_pos_ == 0); - assert(out_end_ == 0); - } - else - { - auto const& out = *out_; - auto const& back = list_.back(); - - assert(out_end_ <= back.size()); - assert(out_pos_ < out.size()); - assert(&out != &front || out_pos_ >= in_pos_); - assert(&out != &front || out_pos_ - in_pos_ == in_size_); - assert(&out != &back || out_pos_ <= out_end_); - } -#endif -} - -template -basic_streambuf& -operator<< (basic_streambuf& buf, T const& t) -{ - std::stringstream ss; - ss << t; - auto const& s = ss.str(); - buf.commit(boost::asio::buffer_copy( - buf.prepare(s.size()), boost::asio::buffer(s))); - return buf; -} - -//------------------------------------------------------------------------------ using streambuf = basic_streambuf>; -/** Convert the entire basic_streambuf to a string. - @note It is more efficient to deal directly in the streambuf instead. -*/ -template -std::string -to_string (basic_streambuf const& buf) -{ - std::string s; - s.resize(buf.size()); - boost::asio::buffer_copy(boost::asio::buffer( - &s[0], s.size()), buf.data()); - return s; -} - -} -} +} // beast #endif diff --git a/beast/asio/streambuf_readstream.h b/beast/asio/streambuf_readstream.h new file mode 100644 index 0000000000..78b6db7815 --- /dev/null +++ b/beast/asio/streambuf_readstream.h @@ -0,0 +1,259 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_STREAMBUF_READSTREAM_H_INLUDED +#define BEAST_ASIO_STREAMBUF_READSTREAM_H_INLUDED + +#include +#include +#include +#include +#include +#include + +namespace beast { + +/** A `Stream` with attached `Streambuf` to buffer reads. + + This wraps a `Stream` implementation so that calls to write are + passed through to the underlying stream, while calls to read will + first consume the input sequence stored in a `Streambuf` which + is part of the object. + + The use-case for this class is different than that of the + `boost::asio::buffered_readstream`. It is designed to facilitate + the use of `boost::asio::read_until`, and to allow buffers + acquired during detection of handshakes to be made transparently + available to callers. A hypothetical implementation of the + buffered version of `boost::asio::ssl::stream::async_handshake` + could make use of this wrapper. + + Uses: + + * Transparently leave untouched input acquired in calls + to `boost::asio::read_until` behind for subsequent callers. + + * "Preload" a stream with handshake input data acquired + from other sources. + + Example: + @code + // Process the next HTTP headers on the stream, + // leaving excess bytes behind for the next call. + // + template + void process_http_message( + streambuf_readstream& stream) + { + // Read up to and including the end of the HTTP + // headers, leaving the sequence in the stream's + // buffer. read_until may read past the end of the + // headers; the return value will include only the + // part up to the end of the delimiter. + // + std::size_t bytes_transferred = + boost::asio::read_until( + stream.next_layer(), stream.buffer(), "\r\n\r\n"); + + // Use prepare_buffers() to limit the input + // sequence to only the data up to and including + // the trailing "\r\n\r\n". + // + auto header_buffers = prepare_buffers( + bytes_transferred, stream.buffer().data()); + + ... + + // Discard the portion of the input corresponding + // to the HTTP headers. + // + stream.buffer().consume(bytes_transferred); + + // Everything we read from the stream + // is part of the content-body. + } + @endcode + + @tparam Stream The type of stream to wrap. + + @tparam Streambuf The type of stream buffer to use. +*/ +template +class streambuf_readstream +{ + using error_code = boost::system::error_code; + + template + class read_some_op; + + Streambuf sb_; + std::size_t size_ = 0; + Stream next_layer_; + +public: + /// The type of the internal buffer + using streambuf_type = Streambuf; + + /// The type of the next layer. + using next_layer_type = + std::remove_reference_t; + + /// The type of the lowest layer. + using lowest_layer_type = + typename next_layer_type::lowest_layer_type; + + /// Move constructor. + streambuf_readstream(streambuf_readstream&&) = default; + + /** Construct the wrapping stream. + + @param args Parameters forwarded to the `Stream` constructor. + */ + template + explicit + streambuf_readstream(Args&&... args); + + /// Get a reference to the next layer. + next_layer_type& + next_layer() + { + return next_layer_; + } + + /// Get a reference to the lowest layer. + lowest_layer_type& + lowest_layer() + { + return next_layer_.lowest_layer(); + } + + /// Get a const reference to the lowest layer. + lowest_layer_type const& + lowest_layer() const + { + return next_layer_.lowest_layer(); + } + + /// Get the io_service associated with the object. + boost::asio::io_service& + get_io_service() + { + return next_layer_.get_io_service(); + } + + /** Access the internal buffer. + + The internal buffer is returned. It is possible for the + caller to break invariants with this function. For example, + by causing the internal buffer size to increase beyond + the caller defined maximum. + */ + Streambuf& + buffer() + { + return sb_; + } + + /** Access the internal buffer. + + The internal buffer is returned. It is possible for the + caller to break invariants with this function. For example, + by causing the internal buffer size to increase beyond + the caller defined maximum. + */ + Streambuf const& + buffer() const + { + return sb_; + } + + /** Set the maximum buffer size. + + This changes the maximum size of the internal buffer used + to hold read data. No bytes are discarded by this call. If + the buffer size is set to zero, no more data will be buffered. + + Thread safety: + The caller is responsible for making sure the call is + made from the same implicit or explicit strand. + + @param size The number of bytes in the read buffer. + + @note This is a soft limit. If the new maximum size is smaller + than the amount of data in the buffer, no bytes are discarded. + */ + void + reserve(std::size_t size) + { + size_ = size; + } + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t + write_some(ConstBufferSequence const& buffers) + { + return next_layer_.write_some(buffers); + } + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred. + template + std::size_t + write_some(ConstBufferSequence const& buffers, + error_code& ec) + { + return next_layer_.write_some(buffers, ec); + } + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + auto + async_write_some(ConstBufferSequence const& buffers, + WriteHandler&& handler); + + /// Read some data from the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t + read_some(MutableBufferSequence const& buffers); + + /// Read some data from the stream. Returns the number of bytes read + /// or 0 if an error occurred. + template + std::size_t + read_some(MutableBufferSequence const& buffers, + error_code& ec); + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + auto + async_read_some(MutableBufferSequence const& buffers, + ReadHandler&& handler); +}; + +} // beast + +#include + +#endif diff --git a/beast/asio/temp_buffer.h b/beast/asio/temp_buffer.h new file mode 100644 index 0000000000..a36b9fb621 --- /dev/null +++ b/beast/asio/temp_buffer.h @@ -0,0 +1,109 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_TEMP_BUFFER_H_INCLUDED +#define BEAST_ASIO_TEMP_BUFFER_H_INCLUDED + +#include +#include +#include +#include +#include + +namespace beast { + +template +class temp_buffer +{ + Handler& h_; + std::size_t n_ = 0; + std::uint8_t* p_ = nullptr; + +public: + explicit + temp_buffer(Handler& h) + : h_(h) + { + } + + ~temp_buffer() + { + if(p_) + dealloc(); + } + + operator + boost::asio::const_buffer() const + { + return boost::asio::const_buffer{p_, n_}; + } + + operator + boost::asio::mutable_buffer() const + { + return boost::asio::mutable_buffer{p_, n_}; + } + + std::uint8_t* + data() const + { + return p_; + } + + std::size_t + size() + { + return n_; + } + + boost::asio::mutable_buffers_1 + buffers() const + { + return boost::asio::mutable_buffers_1{ + p_, n_}; + } + + void + alloc(std::size_t size) + { + if(n_ != size) + { + if(p_) + dealloc(); + n_ = size; + if(n_ > 0) + p_ = reinterpret_cast( + boost_asio_handler_alloc_helpers:: + allocate(n_, h_)); + } + } + + void + dealloc() + { + boost_asio_handler_alloc_helpers:: + deallocate(p_, n_, h_); + p_ = nullptr; + n_ = 0; + } +}; + +} // beast + +#endif diff --git a/beast/asio/type_check.h b/beast/asio/type_check.h new file mode 100644 index 0000000000..00d0276171 --- /dev/null +++ b/beast/asio/type_check.h @@ -0,0 +1,329 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_ASIO_TYPE_CHECK_H_INCLUDED +#define BEAST_ASIO_TYPE_CHECK_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include + +namespace beast { + +//------------------------------------------------------------------------------ + +// Types that meet the requirements, +// for use with std::declval only. +// + +#if GENERATING_DOCS +namespace detail { +#else +namespace concept { +#endif + +template +struct BufferSequence +{ + using value_type = BufferType; + using const_iterator = BufferType const*; + ~BufferSequence(); + BufferSequence(BufferSequence const&) = default; + const_iterator + begin() const noexcept; + const_iterator + end() const noexcept; +}; + +using ConstBufferSequence = + BufferSequence; + +using MutableBufferSequence = + BufferSequence; + +struct StreamHandler +{ + StreamHandler(StreamHandler const&) = default; + void + operator()(boost::system::error_code ec, + std::size_t); +}; + +using ReadHandler = StreamHandler; +using WriteHandler = StreamHandler; + +} // concept + +// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/ConstBufferSequence.html +// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/MutableBufferSequence.html +// +/// Determine if `T` meets the requirements of `BufferSequence`. +template +class is_BufferSequence +{ + template > + static R check1(int); + template + static std::false_type check1(...); + using type1 = decltype(check1(0)); + + template::iterator_category>> + #else + // workaround: + // boost::asio::detail::consuming_buffers::const_iterator + // is not bidirectional + std::forward_iterator_tag, + typename std::iterator_traits< + typename U::const_iterator>::iterator_category>> + #endif + static R check2(int); + template + static std::false_type check2(...); + using type2 = decltype(check2(0)); + + template().begin()), + typename U::const_iterator>::type> + static R check3(int); + template + static std::false_type check3(...); + using type3 = decltype(check3(0)); + + template().end()), + typename U::const_iterator>::type> + static R check4(int); + template + static std::false_type check4(...); + using type4 = decltype(check4(0)); + +public: + /// `true` if `T` meets the requirements. + static bool const value = + std::is_copy_constructible::value && + std::is_destructible::value && + type1::value && type2::value && + type3::value && type4::value; +}; + +#if ! GENERATING_DOCS + +/// Determine if `T` meets the requirements of `ConstBufferSequence`. +template +using is_ConstBufferSequence = + is_BufferSequence; +static_assert(is_ConstBufferSequence::value, ""); +static_assert(! is_ConstBufferSequence::value, ""); + +/// Determine if `T` meets the requirements of `MutableBufferSequence`. +template +using is_MutableBufferSequence = + is_BufferSequence; +static_assert(is_MutableBufferSequence::value, ""); +static_assert(! is_MutableBufferSequence::value, ""); + +#endif + +//------------------------------------------------------------------------------ + +/// Determine if `T` has the `get_io_service` member. +template +class has_get_io_service +{ + template().get_io_service()), + boost::asio::io_service&>> + static R check(int); + template + static std::false_type check(...); + using type = decltype(check(0)); + +public: + /// `true` if `T` meets the requirements. + static bool const value = type::value; +}; +static_assert(! has_get_io_service::value, ""); + +// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncReadStream.html +// +/// Determine if `T` meets the requirements of `AsyncReadStream`. +template +class is_AsyncReadStream +{ + template().async_read_some( + std::declval(), + std::declval()), + std::true_type{})> + static R check(int); + template + static std::false_type check(...); + using type = decltype(check(0)); + +public: + /// `true` if `T` meets the requirements. + static bool const value = + has_get_io_service::value && type::value; +}; +static_assert(! is_AsyncReadStream::value, ""); + +// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/AsyncWriteStream.html +// +/// Determine if `T` meets the requirements of `AsyncWriteStream`. +template +class is_AsyncWriteStream +{ + template().async_write_some( + std::declval(), + std::declval()), + std::true_type{})> + static R check(int); + template + static std::false_type check(...); + using type = decltype(check(0)); + +public: + /// `true` if `T` meets the requirements. + static bool const value = + has_get_io_service::value && type::value; +}; +static_assert(! is_AsyncWriteStream::value, ""); + +// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncReadStream.html +// +/// Determine if `T` meets the requirements of `SyncReadStream`. +template +class is_SyncReadStream +{ + using error_code = + boost::system::error_code; + + template().read_some( + std::declval())), + std::size_t>> + static R check1(int); + template + static std::false_type check1(...); + using type1 = decltype(check1(0)); + + template().read_some( + std::declval(), + std::declval())), std::size_t>> + static R check2(int); + template + static std::false_type check2(...); + using type2 = decltype(check2(0)); + +public: + /// `true` if `T` meets the requirements. + static bool const value = + type1::value && type2::value; +}; +static_assert(! is_SyncReadStream::value, ""); + +// http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/SyncWriteStream.html +// +/// Determine if `T` meets the requirements of `SyncWriterStream`. +template +class is_SyncWriteStream +{ + using error_code = + boost::system::error_code; + + template().write_some( + std::declval())), + std::size_t>> + static R check1(int); + template + static std::false_type check1(...); + using type1 = decltype(check1(0)); + + template().write_some( + std::declval(), + std::declval())), std::size_t>> + static R check2(int); + template + static std::false_type check2(...); + using type2 = decltype(check2(0)); + +public: + /// `true` if `T` meets the requirements. + static bool const value = + type1::value && type2::value; +}; +static_assert(! is_SyncWriteStream::value, ""); + +/// Determine if `T` meets the requirements of `Stream`. +template +struct is_Stream +{ +/// `true` if `T` meets the requirements. + static bool const value = + is_AsyncReadStream::value && + is_AsyncWriteStream::value && + is_SyncReadStream::value && + is_SyncWriteStream::value; +}; + +/// Determine if `T` meets the requirements of `Streambuf`. +template +struct is_Streambuf : std::false_type {}; +template +struct is_Streambuf().prepare(1))>::value>, + std::integral_constant().data())>::value>, + decltype(std::declval().commit(1), std::true_type{}), + decltype(std::declval().consume(1), std::true_type{}), + std::is_same().size()), std::size_t> +>>:std::true_type{}; + +#if ! GENERATING_DOCS + +/// Determine if `T` meets the requirements of `CompletionHandler`. +template +using is_Handler = std::integral_constant>::value && + is_call_possible::value>; + +#endif + +} // beast + +#endif diff --git a/beast/http/tests/chunked_encoder.test.cpp b/beast/http/tests/chunked_encoder.test.cpp index 900b3ab58c..9684131e30 100644 --- a/beast/http/tests/chunked_encoder.test.cpp +++ b/beast/http/tests/chunked_encoder.test.cpp @@ -23,6 +23,7 @@ namespace beast { namespace http { +namespace test { class chunk_encode_test : public unit_test::suite { @@ -98,7 +99,7 @@ public: check (std::string const& in, std::string const& answer, bool final_chunk = true) { - asio::streambuf sb(3); + streambuf sb(3); sb << in; auto const out = streambuf_to_string (sb, final_chunk); if (! expect (out == answer)) @@ -108,7 +109,7 @@ public: void testStreambuf() { - asio::streambuf sb(3); + streambuf sb(3); std::string const s = "0123456789012345678901234567890123456789012345678901234567890123456789" "0123456789012345678901234567890123456789012345678901234567890123456789" @@ -147,5 +148,7 @@ public: BEAST_DEFINE_TESTSUITE(chunk_encode,http,beast); -} -} +} // test +} // http +} // beast + diff --git a/beast/unit_test/tests/main.cpp b/beast/unit_test/src/main.cpp similarity index 100% rename from beast/unit_test/tests/main.cpp rename to beast/unit_test/src/main.cpp diff --git a/beast/asio/Asio.unity.cpp b/beast/unity/beast_asio_unity.cpp similarity index 81% rename from beast/asio/Asio.unity.cpp rename to beast/unity/beast_asio_unity.cpp index 6334a818ae..74208df9d0 100644 --- a/beast/asio/Asio.unity.cpp +++ b/beast/unity/beast_asio_unity.cpp @@ -17,12 +17,6 @@ */ //============================================================================== -#if BEAST_INCLUDE_BEASTCONFIG -#include -#endif - -#include -#include -#include -#include - +#include +#include +#include diff --git a/test/asio/Jamfile b/test/asio/Jamfile new file mode 100644 index 0000000000..751b853806 --- /dev/null +++ b/test/asio/Jamfile @@ -0,0 +1,29 @@ +# +# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +# + +import os ; + +path-constant main : ../../beast/unit_test/src/main.cpp ; + +unit-test all : + append_buffers.cpp + asio.cpp + async_completion.cpp + basic_streambuf.cpp + bind_handler.cpp + buffers_adapter.cpp + buffers_debug.cpp + consuming_buffers.cpp + handler_alloc.cpp + placeholders.cpp + prepare_buffers.cpp + static_streambuf.cpp + streambuf.cpp + streambuf_readstream.cpp + type_check.cpp + $(main) + ; diff --git a/test/asio/append_buffers.cpp b/test/asio/append_buffers.cpp new file mode 100644 index 0000000000..468b8622a3 --- /dev/null +++ b/test/asio/append_buffers.cpp @@ -0,0 +1,76 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// + +// Test that header file is self-contained. +#include + +#include +#include +#include +#include +#include +#include + +namespace beast { +namespace asio { +namespace test { + +class append_buffers_test : public unit_test::suite +{ +public: + + void testAppendBuffers() + { + using boost::asio::buffer_size; + using boost::asio::const_buffer; + char buf[10]; + std::list b1; + std::vector b2{ + const_buffer{buf+0, 1}, + const_buffer{buf+1, 2}}; + std::list b3; + std::array b4{{ + const_buffer{buf+3, 1}, + const_buffer{buf+4, 2}, + const_buffer{buf+6, 3}}}; + std::list b5{ + const_buffer{buf+9, 1}}; + std::list b6; + auto bs = append_buffers( + b1, b2, b3, b4, b5, b6); + expect(buffer_size(bs) == 10); + std::vector v; + for(auto iter = std::make_reverse_iterator(bs.end()); + iter != std::make_reverse_iterator(bs.begin()); ++iter) + v.emplace_back(*iter); + expect(buffer_size(bs) == 10); + decltype(bs) bs2(bs); + auto bs3(std::move(bs)); + bs = bs2; + bs3 = std::move(bs2); + { + boost::asio::streambuf sb1, sb2; + expect(buffer_size(append_buffers( + sb1.prepare(5), sb2.prepare(7))) == 12); + sb1.commit(5); + sb2.commit(7); + expect(buffer_size(append_buffers( + sb1.data(), sb2.data())) == 12); + } + } + + void run() override + { + testAppendBuffers(); + } +}; + +BEAST_DEFINE_TESTSUITE(append_buffers,asio,beast); + +} // test +} // asio +} // beast diff --git a/test/asio/asio.cpp b/test/asio/asio.cpp new file mode 100644 index 0000000000..880a574428 --- /dev/null +++ b/test/asio/asio.cpp @@ -0,0 +1,9 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// + +// Test that header file is self-contained. +#include diff --git a/test/asio/async_completion.cpp b/test/asio/async_completion.cpp new file mode 100644 index 0000000000..1118ab8044 --- /dev/null +++ b/test/asio/async_completion.cpp @@ -0,0 +1,9 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// + +// Test that header file is self-contained. +#include diff --git a/test/asio/basic_streambuf.cpp b/test/asio/basic_streambuf.cpp new file mode 100644 index 0000000000..afe91596fa --- /dev/null +++ b/test/asio/basic_streambuf.cpp @@ -0,0 +1,9 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// + +// Test that header file is self-contained. +#include diff --git a/test/asio/bind_handler.cpp b/test/asio/bind_handler.cpp new file mode 100644 index 0000000000..540efa5164 --- /dev/null +++ b/test/asio/bind_handler.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// + +// Test that header file is self-contained. +#include + +#include diff --git a/test/asio/buffers_adapter.cpp b/test/asio/buffers_adapter.cpp new file mode 100644 index 0000000000..d2f3d73cf6 --- /dev/null +++ b/test/asio/buffers_adapter.cpp @@ -0,0 +1,124 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// + +// Test that header file is self-contained. +#include + +#include +#include +#include +#include + +namespace beast { +namespace asio { +namespace test { + +class buffers_adapter_test : public unit_test::suite +{ +public: + template + static + std::string + to_string(ConstBufferSequence const& bs) + { + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + std::string s; + s.reserve(buffer_size(bs)); + for(auto const& b : bs) + s.append(buffer_cast(b), + buffer_size(b)); + return s; + } + + void testBuffersAdapter() + { + using boost::asio::buffer; + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + using boost::asio::const_buffer; + using boost::asio::mutable_buffer; + char buf[12]; + std::string const s = "Hello, world"; + expect(s.size() == sizeof(buf)); + for(std::size_t i = 1; i < 4; ++i) { + for(std::size_t j = 1; j < 4; ++j) { + for(std::size_t x = 1; x < 4; ++x) { + for(std::size_t y = 1; y < 4; ++y) { + for(std::size_t t = 1; t < 4; ++ t) { + for(std::size_t u = 1; u < 4; ++ u) { + std::size_t k = sizeof(buf) - (i + j); + std::size_t z = sizeof(buf) - (x + y); + std::size_t v = sizeof(buf) - (t + u); + { + std::memset(buf, 0, sizeof(buf)); + std::array bs{{ + mutable_buffer{&buf[0], i}, + mutable_buffer{&buf[i], j}, + mutable_buffer{&buf[i+j], k}}}; + buffers_adapter ba(std::move(bs)); + expect(ba.max_size() == sizeof(buf)); + decltype(ba)::mutable_buffers_type d; + d = ba.prepare(z); expect(buffer_size(d) == z); + d = ba.prepare(0); expect(buffer_size(d) == 0); + d = ba.prepare(y); expect(buffer_size(d) == y); + d = ba.prepare(x); expect(buffer_size(d) == x); + ba.commit(buffer_copy(d, buffer(s.data(), x))); + expect(ba.size() == x); + expect(ba.max_size() == sizeof(buf) - x); + expect(buffer_size(ba.data()) == ba.size()); + d = ba.prepare(x); expect(buffer_size(d) == x); + d = ba.prepare(0); expect(buffer_size(d) == 0); + d = ba.prepare(z); expect(buffer_size(d) == z); + d = ba.prepare(y); expect(buffer_size(d) == y); + ba.commit(buffer_copy(d, buffer(s.data()+x, y))); + ba.commit(1); + expect(ba.size() == x + y); + expect(ba.max_size() == sizeof(buf) - (x + y)); + expect(buffer_size(ba.data()) == ba.size()); + d = ba.prepare(x); expect(buffer_size(d) == x); + d = ba.prepare(y); expect(buffer_size(d) == y); + d = ba.prepare(0); expect(buffer_size(d) == 0); + d = ba.prepare(z); expect(buffer_size(d) == z); + ba.commit(buffer_copy(d, buffer(s.data()+x+y, z))); + ba.commit(2); + expect(ba.size() == x + y + z); + expect(ba.max_size() == 0); + expect(buffer_size(ba.data()) == ba.size()); + expect(to_string(ba.data()) == s); + ba.consume(t); + d = ba.prepare(0); expect(buffer_size(d) == 0); + expect(to_string(ba.data()) == s.substr(t, std::string::npos)); + ba.consume(u); + expect(to_string(ba.data()) == s.substr(t + u, std::string::npos)); + ba.consume(v); + expect(to_string(ba.data()) == ""); + ba.consume(1); + d = ba.prepare(0); expect(buffer_size(d) == 0); + try + { + ba.prepare(1); + fail(); + } + catch(...) + { + pass(); + } + } + }}}}}} + } + void run() override + { + testBuffersAdapter(); + } +}; + +BEAST_DEFINE_TESTSUITE(buffers_adapter,asio,beast); + +} // test +} // asio +} // beast diff --git a/test/asio/buffers_debug.cpp b/test/asio/buffers_debug.cpp new file mode 100644 index 0000000000..231c49b499 --- /dev/null +++ b/test/asio/buffers_debug.cpp @@ -0,0 +1,9 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// + +// Test that header file is self-contained. +#include diff --git a/test/asio/consuming_buffers.cpp b/test/asio/consuming_buffers.cpp new file mode 100644 index 0000000000..c38355117b --- /dev/null +++ b/test/asio/consuming_buffers.cpp @@ -0,0 +1,97 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// + +// Test that header file is self-contained. +#include + +#include +#include +#include + +namespace beast { +namespace asio { +namespace test { + +class consuming_buffers_test : public unit_test::suite +{ +public: + template + static + std::string + to_string(ConstBufferSequence const& bs) + { + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + std::string s; + s.reserve(buffer_size(bs)); + for(auto const& b : bs) + s.append(buffer_cast(b), + buffer_size(b)); + return s; + } + + void testBuffers() + { + using boost::asio::buffer; + using boost::asio::const_buffer; + char buf[12]; + std::string const s = "Hello, world"; + expect(s.size() == sizeof(buf)); + buffer_copy(buffer(buf), buffer(s)); + expect(to_string(buffer(buf)) == s); + for(std::size_t i = 1; i < 4; ++i) { + for(std::size_t j = 1; j < 4; ++j) { + for(std::size_t x = 1; x < 4; ++x) { + for(std::size_t y = 1; y < 4; ++y) { + std::size_t k = sizeof(buf) - (i + j); + std::size_t z = sizeof(buf) - (x + y); + { + std::array bs{{ + const_buffer{&buf[0], i}, + const_buffer{&buf[i], j}, + const_buffer{&buf[i+j], k}}}; + consuming_buffers cb(bs); + expect(to_string(cb) == s); + cb.consume(0); + expect(to_string(cb) == s); + cb.consume(x); + expect(to_string(cb) == s.substr(x)); + cb.consume(y); + expect(to_string(cb) == s.substr(x+y)); + cb.consume(z); + expect(to_string(cb) == ""); + cb.consume(1); + expect(to_string(cb) == ""); + } + }}}} + } + + void testNullBuffers() + { + using boost::asio::buffer_copy; + using boost::asio::buffer_size; + using boost::asio::null_buffers; + consuming_buffers cb( + null_buffers{}); + expect(buffer_size(cb) == 0); + consuming_buffers cb2( + null_buffers{}); + expect(buffer_copy(cb2, cb) == 0); + } + + void run() override + { + testBuffers(); + testNullBuffers(); + } +}; + +BEAST_DEFINE_TESTSUITE(consuming_buffers,asio,beast); + +} // test +} // asio +} // beast diff --git a/test/asio/handler_alloc.cpp b/test/asio/handler_alloc.cpp new file mode 100644 index 0000000000..f90587b1dd --- /dev/null +++ b/test/asio/handler_alloc.cpp @@ -0,0 +1,9 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// + +// Test that header file is self-contained. +#include diff --git a/test/asio/placeholders.cpp b/test/asio/placeholders.cpp new file mode 100644 index 0000000000..b3df0f30e1 --- /dev/null +++ b/test/asio/placeholders.cpp @@ -0,0 +1,9 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// + +// Test that header file is self-contained. +#include diff --git a/test/asio/prepare_buffers.cpp b/test/asio/prepare_buffers.cpp new file mode 100644 index 0000000000..842ec85f0a --- /dev/null +++ b/test/asio/prepare_buffers.cpp @@ -0,0 +1,103 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// + +// Test that header file is self-contained. +#include + +#include +#include +#include +#include + +namespace beast { +namespace asio { +namespace test { + +class prepare_buffers_test : public unit_test::suite +{ +public: + template + static + std::string + to_string(ConstBufferSequence const& bs) + { + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + std::string s; + s.reserve(buffer_size(bs)); + for(auto const& b : bs) + s.append(buffer_cast(b), + buffer_size(b)); + return s; + } + + void testBuffers() + { + using boost::asio::buffer_size; + using boost::asio::const_buffer; + std::string const s = "Hello, world"; + expect(s.size() == 12); + for(std::size_t x = 1; x < 4; ++x) { + for(std::size_t y = 1; y < 4; ++y) { + std::size_t z = s.size() - (x + y); + { + std::array bs{{ + const_buffer{&s[0], x}, + const_buffer{&s[x], y}, + const_buffer{&s[x+y], z}}}; + for(std::size_t i = 0; i <= s.size() + 1; ++i) + { + auto pb = prepare_buffers(i, bs); + expect(to_string(pb) == s.substr(0, i)); + auto pb2 = pb; + expect(to_string(pb2) == to_string(pb)); + pb = prepare_buffers(0, bs); + pb2 = pb; + expect(buffer_size(pb2) == 0); + pb2 = prepare_buffers(i, bs); + expect(to_string(pb2) == s.substr(0, i)); + } + } + }} + } + + void testNullBuffers() + { + using boost::asio::buffer_copy; + using boost::asio::buffer_size; + using boost::asio::null_buffers; + auto pb0 = prepare_buffers(0, null_buffers{}); + expect(buffer_size(pb0) == 0); + auto pb1 = prepare_buffers(1, null_buffers{}); + expect(buffer_size(pb1) == 0); + expect(buffer_copy(pb0, pb1) == 0); + + using pb_type = decltype(pb0); + consuming_buffers cb(pb0); + expect(buffer_size(cb) == 0); + expect(buffer_copy(cb, pb1) == 0); + cb.consume(1); + expect(buffer_size(cb) == 0); + expect(buffer_copy(cb, pb1) == 0); + + auto pbc = prepare_buffers(2, cb); + expect(buffer_size(pbc) == 0); + expect(buffer_copy(pbc, cb) == 0); + } + + void run() override + { + testBuffers(); + testNullBuffers(); + } +}; + +BEAST_DEFINE_TESTSUITE(prepare_buffers,asio,beast); + +} // test +} // asio +} // beast diff --git a/beast/asio/tests/streambuf.test.cpp b/test/asio/static_streambuf.cpp similarity index 65% rename from beast/asio/tests/streambuf.test.cpp rename to test/asio/static_streambuf.cpp index f733a44c24..ce97965d58 100644 --- a/beast/asio/tests/streambuf.test.cpp +++ b/test/asio/static_streambuf.cpp @@ -1,29 +1,22 @@ -//------------------------------------------------------------------------------ -/* - This file is part of Beast: https://github.com/vinniefalco/Beast - Copyright 2013, Vinnie Falco +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. +// Test that header file is self-contained. +#include - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include #include +#include +#include namespace beast { namespace asio { +namespace test { -class streambuf_test : public unit_test::suite +class static_streambuf_test : public unit_test::suite { public: template @@ -31,7 +24,8 @@ public: std::string to_string(ConstBufferSequence const& bs) { - using namespace boost::asio; + using boost::asio::buffer_cast; + using boost::asio::buffer_size; std::string s; s.reserve(buffer_size(bs)); for(auto const& b : bs) @@ -40,13 +34,16 @@ public: return s; } - void testStreambuf() + void testStaticStreambuf() { - using namespace boost::asio; + using boost::asio::buffer; + using boost::asio::buffer_cast; + using boost::asio::buffer_size; char buf[12]; std::string const s = "Hello, world"; expect(s.size() == sizeof(buf)); - for(std::size_t i = 1; i < 12; ++i) { + for(std::size_t i = 1; i < 4; ++i) { + for(std::size_t j = 1; j < 4; ++j) { for(std::size_t x = 1; x < 4; ++x) { for(std::size_t y = 1; y < 4; ++y) { for(std::size_t t = 1; t < 4; ++ t) { @@ -55,7 +52,7 @@ public: std::size_t v = sizeof(buf) - (t + u); { std::memset(buf, 0, sizeof(buf)); - streambuf ba(i); + static_streambuf_n ba; decltype(ba)::mutable_buffers_type d; d = ba.prepare(z); expect(buffer_size(d) == z); d = ba.prepare(0); expect(buffer_size(d) == 0); @@ -90,17 +87,27 @@ public: expect(to_string(ba.data()) == ""); ba.consume(1); d = ba.prepare(0); expect(buffer_size(d) == 0); + try + { + ba.prepare(1); + fail(); + } + catch(...) + { + pass(); + } } - }}}}} + }}}}}} } - void run() + void run() override { - testStreambuf(); + testStaticStreambuf(); } }; -BEAST_DEFINE_TESTSUITE(streambuf,asio,beast); +BEAST_DEFINE_TESTSUITE(static_streambuf,asio,beast); -} -} +} // test +} // asio +} // beast diff --git a/test/asio/streambuf.cpp b/test/asio/streambuf.cpp new file mode 100644 index 0000000000..3e5e9c62fb --- /dev/null +++ b/test/asio/streambuf.cpp @@ -0,0 +1,296 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// + +// Test that header file is self-contained. +#include + +#include +#include +#include +#include +#include + +namespace beast { +namespace asio { +namespace test { + +struct test_allocator_info +{ + std::size_t ncopy = 0; + std::size_t nmove = 0; + std::size_t nselect = 0; +}; + +template +class test_allocator; + +template +struct test_allocator_base +{ +}; + +template +struct test_allocator_base +{ + static + test_allocator + select_on_container_copy_construction( + test_allocator const& a) + { + return test_allocator{}; + } +}; + +template +class test_allocator : public test_allocator_base< + T, Assign, Move, Swap, Select> +{ + std::size_t id_; + std::shared_ptr info_; + + template + friend class test_allocator; + +public: + using value_type = T; + using propagate_on_container_copy_assignment = + std::integral_constant; + using propagate_on_container_move_assignment = + std::integral_constant; + using propagate_on_container_swap = + std::integral_constant; + + template + struct rebind + { + using other = test_allocator< + U, Assign, Move, Swap, Select>; + }; + + test_allocator() + : id_([] + { + static std::atomic< + std::size_t> sid(0); + return ++sid; + }()) + , info_(std::make_shared()) + { + } + + test_allocator(test_allocator const& u) noexcept + : id_(u.id_) + , info_(u.info_) + { + ++info_->ncopy; + } + + template + test_allocator(test_allocator< + U, Assign, Move, Swap, Select> const& u) noexcept + : id_(u.id_) + , info_(u.info_) + { + ++info_->ncopy; + } + + test_allocator(test_allocator&& t) + : id_(t.id_) + , info_(t.info_) + { + ++info_->nmove; + } + + value_type* + allocate(std::size_t n) + { + return static_cast( + ::operator new (n*sizeof(value_type))); + } + + void + deallocate(value_type* p, std::size_t) noexcept + { + ::operator delete(p); + } + + std::size_t + id() const + { + return id_; + } + + test_allocator_info const* + operator->() const + { + return info_.get(); + } +}; + +class streambuf_test : public unit_test::suite +{ +public: + template + static + std::string + to_string(ConstBufferSequence const& bs) + { + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + std::string s; + s.reserve(buffer_size(bs)); + for(auto const& b : bs) + s.append(buffer_cast(b), + buffer_size(b)); + return s; + } + + void testStreambuf() + { + using boost::asio::buffer; + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + std::string const s = "Hello, world"; + expect(s.size() == 12); + for(std::size_t i = 1; i < 12; ++i) { + for(std::size_t x = 1; x < 4; ++x) { + for(std::size_t y = 1; y < 4; ++y) { + for(std::size_t t = 1; t < 4; ++ t) { + for(std::size_t u = 1; u < 4; ++ u) { + std::size_t z = s.size() - (x + y); + std::size_t v = s.size() - (t + u); + { + streambuf sb(i); + decltype(sb)::mutable_buffers_type d; + d = sb.prepare(z); expect(buffer_size(d) == z); + d = sb.prepare(0); expect(buffer_size(d) == 0); + d = sb.prepare(y); expect(buffer_size(d) == y); + d = sb.prepare(x); expect(buffer_size(d) == x); + sb.commit(buffer_copy(d, buffer(s.data(), x))); + expect(sb.size() == x); + expect(buffer_size(sb.data()) == sb.size()); + d = sb.prepare(x); expect(buffer_size(d) == x); + d = sb.prepare(0); expect(buffer_size(d) == 0); + d = sb.prepare(z); expect(buffer_size(d) == z); + d = sb.prepare(y); expect(buffer_size(d) == y); + sb.commit(buffer_copy(d, buffer(s.data()+x, y))); + sb.commit(1); + expect(sb.size() == x + y); + expect(buffer_size(sb.data()) == sb.size()); + d = sb.prepare(x); expect(buffer_size(d) == x); + d = sb.prepare(y); expect(buffer_size(d) == y); + d = sb.prepare(0); expect(buffer_size(d) == 0); + d = sb.prepare(z); expect(buffer_size(d) == z); + sb.commit(buffer_copy(d, buffer(s.data()+x+y, z))); + sb.commit(2); + expect(sb.size() == x + y + z); + expect(buffer_size(sb.data()) == sb.size()); + expect(to_string(sb.data()) == s); + sb.consume(t); + d = sb.prepare(0); expect(buffer_size(d) == 0); + expect(to_string(sb.data()) == s.substr(t, std::string::npos)); + sb.consume(u); + expect(to_string(sb.data()) == s.substr(t + u, std::string::npos)); + sb.consume(v); + expect(to_string(sb.data()) == ""); + sb.consume(1); + d = sb.prepare(0); expect(buffer_size(d) == 0); + } + }}}}} + } + + template + static + bool + eq(basic_streambuf const& sb1, + basic_streambuf const& sb2) + { + return to_string(sb1.data()) == to_string(sb2.data()); + } + + void testSpecial() + { + using boost::asio::buffer; + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + std::string const s = "Hello, world"; + expect(s.size() == 12); + for(std::size_t i = 1; i < 12; ++i) { + for(std::size_t x = 1; x < 4; ++x) { + for(std::size_t y = 1; y < 4; ++y) { + std::size_t z = s.size() - (x + y); + { + streambuf sb(i); + sb.commit(buffer_copy(sb.prepare(x), buffer(s.data(), x))); + sb.commit(buffer_copy(sb.prepare(y), buffer(s.data()+x, y))); + sb.commit(buffer_copy(sb.prepare(z), buffer(s.data()+x+y, z))); + expect(to_string(sb.data()) == s); + { + streambuf sb2(sb); + expect(eq(sb, sb2)); + } + { + streambuf sb2; + sb2 = sb; + expect(eq(sb, sb2)); + } + { + streambuf sb2(std::move(sb)); + expect(to_string(sb2.data()) == s); + expect(buffer_size(sb.data()) == 0); + sb = std::move(sb2); + expect(to_string(sb.data()) == s); + expect(buffer_size(sb2.data()) == 0); + } + } + }}} + } + + void testAllocator() + { + { + using alloc_type = + test_allocator; + using sb_type = basic_streambuf; + sb_type sb; + expect(sb.get_allocator().id() == 1); + } + { + using alloc_type = + test_allocator; + using sb_type = basic_streambuf; + sb_type sb; + expect(sb.get_allocator().id() == 2); + sb_type sb2(sb); + expect(sb2.get_allocator().id() == 2); + sb_type sb3(sb, alloc_type{}); + //expect(sb3.get_allocator().id() == 3); + } + { + using alloc_type = + test_allocator; + using sb_type = basic_streambuf; + } + } + + void run() override + { + testStreambuf(); + testSpecial(); + testAllocator(); + } +}; + +BEAST_DEFINE_TESTSUITE(streambuf,asio,beast); + +} // test +} // asio +} // beast diff --git a/test/asio/streambuf_readstream.cpp b/test/asio/streambuf_readstream.cpp new file mode 100644 index 0000000000..e35f5bfd8a --- /dev/null +++ b/test/asio/streambuf_readstream.cpp @@ -0,0 +1,9 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// + +// Test that header file is self-contained. +#include diff --git a/test/asio/temp_buffer.cpp b/test/asio/temp_buffer.cpp new file mode 100644 index 0000000000..536cf776f3 --- /dev/null +++ b/test/asio/temp_buffer.cpp @@ -0,0 +1,9 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// + +// Test that header file is self-contained. +#include diff --git a/test/asio/type_check.cpp b/test/asio/type_check.cpp new file mode 100644 index 0000000000..f25a3b3627 --- /dev/null +++ b/test/asio/type_check.cpp @@ -0,0 +1,9 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail 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) +// + +// Test that header file is self-contained. +#include