diff options
Diffstat (limited to 'boost/beast/core')
101 files changed, 13555 insertions, 4766 deletions
diff --git a/boost/beast/core/async_base.hpp b/boost/beast/core/async_base.hpp new file mode 100644 index 0000000000..ee6a7d7a39 --- /dev/null +++ b/boost/beast/core/async_base.hpp @@ -0,0 +1,711 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_ASYNC_BASE_HPP +#define BOOST_BEAST_CORE_ASYNC_BASE_HPP + +#include <boost/beast/core/detail/config.hpp> +#include <boost/beast/core/bind_handler.hpp> +#include <boost/beast/core/detail/allocator.hpp> +#include <boost/beast/core/detail/async_base.hpp> +#include <boost/asio/associated_allocator.hpp> +#include <boost/asio/associated_executor.hpp> +#include <boost/asio/bind_executor.hpp> +#include <boost/asio/executor_work_guard.hpp> +#include <boost/asio/handler_alloc_hook.hpp> +#include <boost/asio/handler_continuation_hook.hpp> +#include <boost/asio/handler_invoke_hook.hpp> +#include <boost/asio/post.hpp> +#include <boost/core/exchange.hpp> +#include <boost/core/empty_value.hpp> +#include <utility> + +namespace boost { +namespace beast { + +/** Base class to assist writing composed operations. + + A function object submitted to intermediate initiating functions during + a composed operation may derive from this type to inherit all of the + boilerplate to forward the executor, allocator, and legacy customization + points associated with the completion handler invoked at the end of the + composed operation. + + The composed operation must be typical; that is, associated with one + executor of an I/O object, and invoking a caller-provided completion + handler when the operation is finished. Classes derived from + @ref async_base will acquire these properties: + + @li Ownership of the final completion handler provided upon construction. + + @li If the final handler has an associated allocator, this allocator will + be propagated to the composed operation subclass. Otherwise, the + associated allocator will be the type specified in the allocator + template parameter, or the default of `std::allocator<void>` if the + parameter is omitted. + + @li If the final handler has an associated executor, then it will be used + as the executor associated with the composed operation. Otherwise, + the specified `Executor1` will be the type of executor associated + with the composed operation. + + @li An instance of `net::executor_work_guard` for the instance of `Executor1` + shall be maintained until either the final handler is invoked, or the + operation base is destroyed, whichever comes first. + + @li Calls to the legacy customization points + `asio_handler_invoke`, + `asio_handler_allocate`, + `asio_handler_deallocate`, and + `asio_handler_is_continuation`, + which use argument-dependent lookup, will be forwarded to the + legacy customization points associated with the handler. + + @par Example + + The following code demonstrates how @ref async_base may be be used to + assist authoring an asynchronous initiating function, by providing all of + the boilerplate to manage the final completion handler in a way that + maintains the allocator and executor associations: + + @code + + // Asynchronously read into a buffer until the buffer is full, or an error occurs + template<class AsyncReadStream, class ReadHandler> + typename net::async_result<ReadHandler, void(error_code, std::size_t)>::return_type + async_read(AsyncReadStream& stream, net::mutable_buffer buffer, ReadHandler&& handler) + { + using handler_type = BOOST_ASIO_HANDLER_TYPE(ReadHandler, void(error_code, std::size_t)); + using base_type = async_base<handler_type, typename AsyncReadStream::executor_type>; + + struct op : base_type + { + AsyncReadStream& stream_; + net::mutable_buffer buffer_; + std::size_t total_bytes_transferred_; + + op( + AsyncReadStream& stream, + net::mutable_buffer buffer, + handler_type& handler) + : base_type(std::move(handler), stream.get_executor()) + , stream_(stream) + , buffer_(buffer) + , total_bytes_transferred_(0) + { + (*this)({}, 0, false); // start the operation + } + + void operator()(error_code ec, std::size_t bytes_transferred, bool is_continuation = true) + { + // Adjust the count of bytes and advance our buffer + total_bytes_transferred_ += bytes_transferred; + buffer_ = buffer_ + bytes_transferred; + + // Keep reading until buffer is full or an error occurs + if(! ec && buffer_.size() > 0) + return stream_.async_read_some(buffer_, std::move(*this)); + + // Call the completion handler with the result. If `is_continuation` is + // false, which happens on the first time through this function, then + // `net::post` will be used to call the completion handler, otherwise + // the completion handler will be invoked directly. + + this->invoke(is_continuation, ec, total_bytes_transferred_); + } + }; + + net::async_completion<ReadHandler, void(error_code, std::size_t)> init{handler}; + op(stream, buffer, init.completion_handler); + return init.result.get(); + } + + @endcode + + Data members of composed operations implemented as completion handlers + do not have stable addresses, as the composed operation object is move + constructed upon each call to an initiating function. For most operations + this is not a problem. For complex operations requiring stable temporary + storage, the class @ref stable_async_base is provided which offers + additional functionality: + + @li The free function @ref allocate_stable may be used to allocate + one or more temporary objects associated with the composed operation. + + @li Memory for stable temporary objects is allocated using the allocator + associated with the composed operation. + + @li Stable temporary objects are automatically destroyed, and the memory + freed using the associated allocator, either before the final completion + handler is invoked (a Networking requirement) or when the composed operation + is destroyed, whichever occurs first. + + @par Temporary Storage Example + + The following example demonstrates how a composed operation may store a + temporary object. + + @code + + @endcode + + @tparam Handler The type of the completion handler to store. + This type must meet the requirements of <em>CompletionHandler</em>. + + @tparam Executor1 The type of the executor used when the handler has no + associated executor. An instance of this type must be provided upon + construction. The implementation will maintain an executor work guard + and a copy of this instance. + + @tparam Allocator The allocator type to use if the handler does not + have an associated allocator. If this parameter is omitted, then + `std::allocator<void>` will be used. If the specified allocator is + not default constructible, an instance of the type must be provided + upon construction. + + @see stable_async_base +*/ +template< + class Handler, + class Executor1, + class Allocator = std::allocator<void> +> +class async_base +#if ! BOOST_BEAST_DOXYGEN + : private boost::empty_value<Allocator> +#endif +{ + static_assert( + net::is_executor<Executor1>::value, + "Executor type requirements not met"); + + Handler h_; + net::executor_work_guard<Executor1> wg1_; + + virtual + void + before_invoke_hook() + { + } + +public: + /** Constructor + + @param handler The final completion handler. + The type of this object must meet the requirements of <em>CompletionHandler</em>. + The implementation takes ownership of the handler by performing a decay-copy. + + @param ex1 The executor associated with the implied I/O object + target of the operation. The implementation shall maintain an + executor work guard for the lifetime of the operation, or until + the final completion handler is invoked, whichever is shorter. + + @param alloc The allocator to be associated with objects + derived from this class. If `Allocator` is default-constructible, + this parameter is optional and may be omitted. + */ +#if BOOST_BEAST_DOXYGEN + template<class Handler_> + async_base( + Handler&& handler, + Executor1 const& ex1, + Allocator const& alloc = Allocator()); +#else + template< + class Handler_, + class = typename std::enable_if< + ! std::is_same<typename + std::decay<Handler_>::type, + async_base + >::value>::type + > + async_base( + Handler_&& handler, + Executor1 const& ex1) + : h_(std::forward<Handler_>(handler)) + , wg1_(ex1) + { + } + + template<class Handler_> + async_base( + Handler_&& handler, + Executor1 const& ex1, + Allocator const& alloc) + : boost::empty_value<Allocator>( + boost::empty_init_t{}, alloc) + , h_(std::forward<Handler_>(handler)) + , wg1_(ex1) + { + } +#endif + + /// Move Constructor + async_base(async_base&& other) = default; + + /** The type of allocator associated with this object. + + If a class derived from @ref async_base is a completion + handler, then the associated allocator of the derived class will + be this type. + */ + using allocator_type = + net::associated_allocator_t<Handler, Allocator>; + + /** The type of executor associated with this object. + + If a class derived from @ref async_base is a completion + handler, then the associated executor of the derived class will + be this type. + */ + using executor_type = + net::associated_executor_t<Handler, Executor1>; + + /** Returns the allocator associated with this object. + + If a class derived from @ref async_base is a completion + handler, then the object returned from this function will be used + as the associated allocator of the derived class. + */ + allocator_type + get_allocator() const noexcept + { + return net::get_associated_allocator(h_, + boost::empty_value<Allocator>::get()); + } + + /** Returns the executor associated with this object. + + If a class derived from @ref async_base is a completion + handler, then the object returned from this function will be used + as the associated executor of the derived class. + */ + executor_type + get_executor() const noexcept + { + return net::get_associated_executor( + h_, wg1_.get_executor()); + } + + /// Returns the handler associated with this object + Handler const& + handler() const noexcept + { + return h_; + } + + /** Returns ownership of the handler associated with this object + + This function is used to transfer ownership of the handler to + the caller, by move-construction. After the move, the only + valid operations on the base object are move construction and + destruction. + */ + Handler + release_handler() + { + return std::move(h_); + } + + /** Invoke the final completion handler, maybe using post. + + This invokes the final completion handler with the specified + arguments forwarded. It is undefined to call either of + @ref complete or @ref complete_now more than once. + + Any temporary objects allocated with @ref beast::allocate_stable will + be automatically destroyed before the final completion handler + is invoked. + + @param is_continuation If this value is `false`, then the + handler will be submitted to the executor using `net::post`. + Otherwise the handler will be invoked as if by calling + @ref complete_now. + + @param args A list of optional parameters to invoke the handler + with. The completion handler must be invocable with the parameter + list, or else a compilation error will result. + */ + template<class... Args> + void + complete(bool is_continuation, Args&&... args) + { + this->before_invoke_hook(); + if(! is_continuation) + { + auto const ex = get_executor(); + net::post(net::bind_executor( + ex, + beast::bind_front_handler( + std::move(h_), + std::forward<Args>(args)...))); + wg1_.reset(); + } + else + { + wg1_.reset(); + h_(std::forward<Args>(args)...); + } + } + + /** Invoke the final completion handler. + + This invokes the final completion handler with the specified + arguments forwarded. It is undefined to call either of + @ref complete or @ref complete_now more than once. + + Any temporary objects allocated with @ref beast::allocate_stable will + be automatically destroyed before the final completion handler + is invoked. + + @param args A list of optional parameters to invoke the handler + with. The completion handler must be invocable with the parameter + list, or else a compilation error will result. + */ + template<class... Args> + void + complete_now(Args&&... args) + { + this->before_invoke_hook(); + wg1_.reset(); + h_(std::forward<Args>(args)...); + } + +#if ! BOOST_BEAST_DOXYGEN + Handler* + get_legacy_handler_pointer() noexcept + { + return std::addressof(h_); + } +#endif +}; + +//------------------------------------------------------------------------------ + +/** Base class to provide completion handler boilerplate for composed operations. + + A function object submitted to intermediate initiating functions during + a composed operation may derive from this type to inherit all of the + boilerplate to forward the executor, allocator, and legacy customization + points associated with the completion handler invoked at the end of the + composed operation. + + The composed operation must be typical; that is, associated with one + executor of an I/O object, and invoking a caller-provided completion + handler when the operation is finished. Classes derived from + @ref async_base will acquire these properties: + + @li Ownership of the final completion handler provided upon construction. + + @li If the final handler has an associated allocator, this allocator will + be propagated to the composed operation subclass. Otherwise, the + associated allocator will be the type specified in the allocator + template parameter, or the default of `std::allocator<void>` if the + parameter is omitted. + + @li If the final handler has an associated executor, then it will be used + as the executor associated with the composed operation. Otherwise, + the specified `Executor1` will be the type of executor associated + with the composed operation. + + @li An instance of `net::executor_work_guard` for the instance of `Executor1` + shall be maintained until either the final handler is invoked, or the + operation base is destroyed, whichever comes first. + + @li Calls to the legacy customization points + `asio_handler_invoke`, + `asio_handler_allocate`, + `asio_handler_deallocate`, and + `asio_handler_is_continuation`, + which use argument-dependent lookup, will be forwarded to the + legacy customization points associated with the handler. + + Data members of composed operations implemented as completion handlers + do not have stable addresses, as the composed operation object is move + constructed upon each call to an initiating function. For most operations + this is not a problem. For complex operations requiring stable temporary + storage, the class @ref stable_async_base is provided which offers + additional functionality: + + @li The free function @ref beast::allocate_stable may be used to allocate + one or more temporary objects associated with the composed operation. + + @li Memory for stable temporary objects is allocated using the allocator + associated with the composed operation. + + @li Stable temporary objects are automatically destroyed, and the memory + freed using the associated allocator, either before the final completion + handler is invoked (a Networking requirement) or when the composed operation + is destroyed, whichever occurs first. + + @par Example + + The following code demonstrates how @ref stable_async_base may be be used to + assist authoring an asynchronous initiating function, by providing all of + the boilerplate to manage the final completion handler in a way that maintains + the allocator and executor associations. Furthermore, the operation shown + allocates temporary memory using @ref beast::allocate_stable for the timer and + message, whose addresses must not change between intermediate operations: + + @code + + // Asynchronously send a message multiple times, once per second + template <class AsyncWriteStream, class T, class WriteHandler> + auto async_write_messages( + AsyncWriteStream& stream, + T const& message, + std::size_t repeat_count, + WriteHandler&& handler) -> + typename net::async_result< + typename std::decay<WriteHandler>::type, + void(error_code)>::return_type + { + using handler_type = typename net::async_completion<WriteHandler, void(error_code)>::completion_handler_type; + using base_type = stable_async_base<handler_type, typename AsyncWriteStream::executor_type>; + + struct op : base_type, boost::asio::coroutine + { + // This object must have a stable address + struct temporary_data + { + // Although std::string is in theory movable, most implementations + // use a "small buffer optimization" which means that we might + // be submitting a buffer to the write operation and then + // moving the string, invalidating the buffer. To prevent + // undefined behavior we store the string object itself at + // a stable location. + std::string const message; + + net::steady_timer timer; + + temporary_data(std::string message_, net::io_context& ctx) + : message(std::move(message_)) + , timer(ctx) + { + } + }; + + AsyncWriteStream& stream_; + std::size_t repeats_; + temporary_data& data_; + + op(AsyncWriteStream& stream, std::size_t repeats, std::string message, handler_type& handler) + : base_type(std::move(handler), stream.get_executor()) + , stream_(stream) + , repeats_(repeats) + , data_(allocate_stable<temporary_data>(*this, std::move(message), stream.get_executor().context())) + { + (*this)(); // start the operation + } + + // Including this file provides the keywords for macro-based coroutines + #include <boost/asio/yield.hpp> + + void operator()(error_code ec = {}, std::size_t = 0) + { + reenter(*this) + { + // If repeats starts at 0 then we must complete immediately. But + // we can't call the final handler from inside the initiating + // function, so we post our intermediate handler first. We use + // net::async_write with an empty buffer instead of calling + // net::post to avoid an extra function template instantiation, to + // keep compile times lower and make the resulting executable smaller. + yield net::async_write(stream_, net::const_buffer{}, std::move(*this)); + while(! ec && repeats_-- > 0) + { + // Send the string. We construct a `const_buffer` here to guarantee + // that we do not create an additional function template instantation + // of net::async_write, since we already instantiated it above for + // net::const_buffer. + + yield net::async_write(stream_, + net::const_buffer(net::buffer(data_.message)), std::move(*this)); + if(ec) + break; + + // Set the timer and wait + data_.timer.expires_after(std::chrono::seconds(1)); + yield data_.timer.async_wait(std::move(*this)); + } + } + + // The base class destroys the temporary data automatically, + // before invoking the final completion handler + this->complete_now(ec); + } + + // Including this file undefines the macros for the coroutines + #include <boost/asio/unyield.hpp> + }; + + net::async_completion<WriteHandler, void(error_code)> completion(handler); + std::ostringstream os; + os << message; + op(stream, repeat_count, os.str(), completion.completion_handler); + return completion.result.get(); + } + + @endcode + + @tparam Handler The type of the completion handler to store. + This type must meet the requirements of <em>CompletionHandler</em>. + + @tparam Executor1 The type of the executor used when the handler has no + associated executor. An instance of this type must be provided upon + construction. The implementation will maintain an executor work guard + and a copy of this instance. + + @tparam Allocator The allocator type to use if the handler does not + have an associated allocator. If this parameter is omitted, then + `std::allocator<void>` will be used. If the specified allocator is + not default constructible, an instance of the type must be provided + upon construction. + + @see allocate_stable, async_base +*/ +template< + class Handler, + class Executor1, + class Allocator = std::allocator<void> +> +class stable_async_base + : public async_base< + Handler, Executor1, Allocator> +{ + detail::stable_base* list_ = nullptr; + + void + before_invoke_hook() override + { + detail::stable_base::destroy_list(list_); + } + +public: + /** Constructor + + @param handler The final completion handler. + The type of this object must meet the requirements of <em>CompletionHandler</em>. + The implementation takes ownership of the handler by performing a decay-copy. + + @param ex1 The executor associated with the implied I/O object + target of the operation. The implementation shall maintain an + executor work guard for the lifetime of the operation, or until + the final completion handler is invoked, whichever is shorter. + + @param alloc The allocator to be associated with objects + derived from this class. If `Allocator` is default-constructible, + this parameter is optional and may be omitted. + */ +#if BOOST_BEAST_DOXYGEN + template<class Handler> + stable_async_base( + Handler&& handler, + Executor1 const& ex1, + Allocator const& alloc = Allocator()); +#else + template< + class Handler_, + class = typename std::enable_if< + ! std::is_same<typename + std::decay<Handler_>::type, + stable_async_base + >::value>::type + > + stable_async_base( + Handler_&& handler, + Executor1 const& ex1) + : async_base< + Handler, Executor1, Allocator>( + std::forward<Handler_>(handler), ex1) + { + } + + template<class Handler_> + stable_async_base( + Handler_&& handler, + Executor1 const& ex1, + Allocator const& alloc) + : async_base< + Handler, Executor1, Allocator>( + std::forward<Handler_>(handler), ex1, alloc) + { + } +#endif + + /// Move Constructor + stable_async_base(stable_async_base&& other) + : async_base<Handler, Executor1, Allocator>( + std::move(other)) + , list_(boost::exchange(other.list_, nullptr)) + { + } + + /** Destructor + + If the completion handler was not invoked, then any + state objects allocated with @ref allocate_stable will + be destroyed here. + */ + ~stable_async_base() + { + detail::stable_base::destroy_list(list_); + } + + /** Allocate a temporary object to hold operation state. + + The object will be destroyed just before the completion + handler is invoked, or when the operation base is destroyed. + */ + template< + class State, + class Handler_, + class Executor1_, + class Allocator_, + class... Args> + friend + State& + allocate_stable( + stable_async_base< + Handler_, Executor1_, Allocator_>& base, + Args&&... args); +}; + +/** Allocate a temporary object to hold stable asynchronous operation state. + + The object will be destroyed just before the completion + handler is invoked, or when the base is destroyed. + + @tparam State The type of object to allocate. + + @param base The helper to allocate from. + + @param args An optional list of parameters to forward to the + constructor of the object being allocated. + + @see stable_async_base +*/ +template< + class State, + class Handler, + class Executor1, + class Allocator, + class... Args> +State& +allocate_stable( + stable_async_base< + Handler, Executor1, Allocator>& base, + Args&&... args); + +} // beast +} // boost + +#include <boost/beast/core/impl/async_base.hpp> + +#endif diff --git a/boost/beast/core/basic_stream.hpp b/boost/beast/core/basic_stream.hpp new file mode 100644 index 0000000000..057ab07ffd --- /dev/null +++ b/boost/beast/core/basic_stream.hpp @@ -0,0 +1,1400 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_BASIC_STREAM_HPP +#define BOOST_BEAST_CORE_BASIC_STREAM_HPP + +#include <boost/beast/core/detail/config.hpp> +#include <boost/beast/core/detail/stream_base.hpp> +#include <boost/beast/core/error.hpp> +#include <boost/beast/core/rate_policy.hpp> +#include <boost/beast/core/role.hpp> +#include <boost/beast/core/stream_traits.hpp> +#include <boost/asio/async_result.hpp> +#include <boost/asio/basic_stream_socket.hpp> +#include <boost/asio/connect.hpp> +#include <boost/asio/executor.hpp> +#include <boost/asio/is_executor.hpp> +#include <boost/core/empty_value.hpp> +#include <boost/config/workaround.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/shared_ptr.hpp> +#include <chrono> +#include <limits> +#include <memory> + +#if ! BOOST_BEAST_DOXYGEN +namespace boost { +namespace asio { +namespace ssl { +template<typename> class stream; +} // ssl +} // asio +} // boost +#endif + +namespace boost { +namespace beast { + +/** A stream socket wrapper with timeouts, an executor, and a rate limit policy. + + This stream wraps a `net::basic_stream_socket` to provide + the following features: + + @li An <em>Executor</em> may be associated with the stream, which will + be used to invoke any completion handlers which do not already have + an associated executor. This achieves support for + <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html">[P1322R0] Networking TS enhancement to enable custom I/O executors</a>. + + @li Timeouts may be specified for each logical asynchronous operation + performing any reading, writing, or connecting. + + @li A <em>RatePolicy</em> may be associated with the stream, to implement + rate limiting through the policy's interface. + + Although the stream supports multiple concurrent outstanding asynchronous + operations, the stream object is not thread-safe. The caller is responsible + for ensuring that the stream is accessed from only one thread at a time. + This includes the times when the stream, and its underlying socket, are + accessed by the networking implementation. To meet this thread safety + requirement, all asynchronous operations must be performed by the stream + within the same implicit strand (only one thread `net::io_context::run`) + or within the same explicit strand, such as an instance of `net::strand`. + + Completion handlers with explicit associated executors (such as those + arising from use of `net::bind_executor`) will be invoked by the stream + using the associated executor. Otherwise, the completion handler will + be invoked by the executor associated with the stream upon construction. + The type of executor used with this stream must meet the following + requirements: + + @li Function objects submitted to the executor shall never run + concurrently with each other. + + The executor type `net::strand` meets these requirements. Use of a + strand as the executor in the stream class template offers an additional + notational convenience: the strand does not need to be specified in + each individual initiating function call. + + Unlike other stream wrappers, the underlying socket is accessed + through the @ref socket member function instead of `next_layer`. + This causes the @ref basic_stream to be returned in calls + to @ref get_lowest_layer. + + @par Usage + + To use this stream declare an instance of the class. Then, before + each logical operation for which a timeout is desired, call + @ref expires_after with a duration, or call @ref expires_at with a + time point. Alternatively, call @ref expires_never to disable the + timeout for subsequent logical operations. A logical operation + is any series of one or more direct or indirect calls to the timeout + stream's asynchronous read, asynchronous write, or asynchronous connect + functions. + + When a timeout is set and a mixed operation is performed (one that + includes both reads and writes, for example) the timeout applies + to all of the intermediate asynchronous operations used in the + enclosing operation. This allows timeouts to be applied to stream + algorithms which were not written specifically to allow for timeouts, + when those algorithms are passed a timeout stream with a timeout set. + + When a timeout occurs the socket will be closed, canceling any + pending I/O operations. The completion handlers for these canceled + operations will be invoked with the error @ref beast::error::timeout. + + @par Examples + + This function reads an HTTP request with a timeout, then sends the + HTTP response with a different timeout. + + @code + void process_http_1 (tcp_stream& stream, net::yield_context yield) + { + flat_buffer buffer; + http::request<http::empty_body> req; + + // Read the request, with a 15 second timeout + stream.expires_after(std::chrono::seconds(15)); + http::async_read(stream, buffer, req, yield); + + // Calculate the response + http::response<http::string_body> res = make_response(req); + + // Send the response, with a 30 second timeout. + stream.expires_after (std::chrono::seconds(30)); + http::async_write (stream, res, yield); + } + @endcode + + The example above could be expressed using a single timeout with a + simple modification. The function that follows first reads an HTTP + request then sends the HTTP response, with a single timeout that + applies to the entire combined operation of reading and writing: + + @code + void process_http_2 (tcp_stream& stream, net::yield_context yield) + { + flat_buffer buffer; + http::request<http::empty_body> req; + + // Require that the read and write combined take no longer than 30 seconds + stream.expires_after(std::chrono::seconds(30)); + + http::async_read(stream, buffer, req, yield); + + http::response<http::string_body> res = make_response(req); + http::async_write (stream, res, yield); + } + @endcode + + Some stream algorithms, such as `ssl::stream::async_handshake` perform + both reads and writes. A timeout set before calling the initiating function + of such composite stream algorithms will apply to the entire composite + operation. For example, a timeout may be set on performing the SSL handshake + thusly: + + @code + void do_ssl_handshake (net::ssl::stream<tcp_stream>& stream, net::yield_context yield) + { + // Require that the SSL handshake take no longer than 10 seconds + stream.expires_after(std::chrono::seconds(10)); + + stream.async_handshake(net::ssl::stream_base::client, yield); + } + @endcode + + @par Blocking I/O + + Synchronous functions behave identically as that of the wrapped + `net::basic_stream_socket`. Timeouts are not available when performing + blocking calls. + + @tparam Protocol A type meeting the requirements of <em>Protocol</em> + representing the protocol the protocol to use for the basic stream socket. + A common choice is `net::ip::tcp`. + + @tparam Executor A type meeting the requirements of <em>Executor</em> to + be used for submitting all completion handlers which do not already have an + associated executor. If this type is omitted, the default of `net::executor` + will be used. + + @par Thread Safety + <em>Distinct objects</em>: Safe.@n + <em>Shared objects</em>: Unsafe. The application must also ensure + that all asynchronous operations are performed within the same + implicit or explicit strand. + + @see + + @li <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html">[P1322R0] Networking TS enhancement to enable custom I/O executors</a>. +*/ +template< + class Protocol, + class Executor = net::executor, + class RatePolicy = unlimited_rate_policy +> +class basic_stream +#if ! BOOST_BEAST_DOXYGEN + : private detail::stream_base +#endif +{ +public: + /// The type of the underlying socket. + using socket_type = + net::basic_stream_socket<Protocol, Executor>; + + /** The type of the executor associated with the stream. + + This will be the type of executor used to invoke completion + handlers which do not have an explicit associated executor. + */ + using executor_type = beast::executor_type<socket_type>; + + /// The protocol type. + using protocol_type = Protocol; + + /// The endpoint type. + using endpoint_type = typename Protocol::endpoint; + +private: + static_assert(net::is_executor<Executor>::value, + "Executor type requirements not met"); + + struct impl_type + : boost::enable_shared_from_this<impl_type> + , boost::empty_value<RatePolicy> + { + // must come first + net::basic_stream_socket< + Protocol, Executor> socket; + + op_state read; + op_state write; + net::steady_timer timer; // rate timer + int waiting = 0; + + impl_type(impl_type&&) = default; + + template<class... Args> + explicit + impl_type(std::false_type, Args&&...); + + template<class RatePolicy_, class... Args> + explicit + impl_type(std::true_type, + RatePolicy_&& policy, Args&&...); + + impl_type& operator=(impl_type&&) = delete; + + beast::executor_type<socket_type> + ex() noexcept + { + return this->socket.get_executor(); + } + + RatePolicy& + policy() noexcept + { + return this->boost::empty_value<RatePolicy>::get(); + } + + RatePolicy const& + policy() const noexcept + { + return this->boost::empty_value<RatePolicy>::get(); + } + + template<class Executor2> + void on_timer(Executor2 const& ex2); + + void reset(); // set timeouts to never + void close(); // cancel everything + }; + + // We use shared ownership for the state so it can + // outlive the destruction of the stream_socket object, + // in the case where there is no outstanding read or write + // but the implementation is still waiting on a timer. + boost::shared_ptr<impl_type> impl_; + + struct timeout_handler; + + struct ops; + +#if ! BOOST_BEAST_DOXYGEN + // boost::asio::ssl::stream needs these + // DEPRECATED + template<class> + friend class boost::asio::ssl::stream; + // DEPRECATED + using lowest_layer_type = socket_type; + // DEPRECATED + lowest_layer_type& + lowest_layer() noexcept + { + return impl_->socket; + } + // DEPRECATED + lowest_layer_type const& + lowest_layer() const noexcept + { + return impl_->socket; + } +#endif + +public: + /** Destructor + + This function destroys the stream, cancelling any outstanding + asynchronous operations associated with the socket as if by + calling cancel. + */ + ~basic_stream(); + + /** Constructor + + This constructor creates the stream by forwarding all arguments + to the underlying socket. The socket then needs to be open and + connected or accepted before data can be sent or received on it. + + @param args A list of parameters forwarded to the constructor of + the underlying socket. + */ +#if BOOST_BEAST_DOXYGEN + template<class... Args> + explicit + basic_stream(Args&&... args); +#else + template<class Arg0, class... Args, + class = typename std::enable_if< + ! std::is_constructible<RatePolicy, Arg0>::value>::type> + explicit + basic_stream(Arg0&& argo, Args&&... args); +#endif + + /** Constructor + + This constructor creates the stream with the specified rate + policy, and forwards all remaining arguments to the underlying + socket. The socket then needs to be open and connected or + accepted before data can be sent or received on it. + + @param policy The rate policy object to use. The stream will + take ownership of this object by decay-copy. + + @param args A list of parameters forwarded to the constructor of + the underlying socket. + */ +#if BOOST_BEAST_DOXYGEN + template<class RatePolicy_, class... Args> + explicit + basic_stream(RatePolicy_&& policy, Args&&... args); +#else + template<class RatePolicy_, class Arg0, class... Args, + class = typename std::enable_if< + std::is_constructible< + RatePolicy, RatePolicy_>::value>::type> + basic_stream( + RatePolicy_&& policy, Arg0&& arg, Args&&... args); +#endif + + /** Move constructor + + @param other The other object from which the move will occur. + + @note Following the move, the moved-from object is in the + same state as if newly constructed. + */ + basic_stream(basic_stream&& other); + + /// Move assignment (deleted). + basic_stream& operator=(basic_stream&&) = delete; + + /// Return a reference to the underlying socket + socket_type& + socket() noexcept + { + return impl_->socket; + } + + /// Return a reference to the underlying socket + socket_type const& + socket() const noexcept + { + return impl_->socket; + } + + /** Release ownership of the underlying socket. + + This function causes all outstanding asynchronous connect, + read, and write operations to be canceled as if by a call + to @ref cancel. Ownership of the underlying socket is then + transferred to the caller. + */ + socket_type + release_socket(); + + //-------------------------------------------------------------------------- + + /// Returns the rate policy associated with the object + RatePolicy& + rate_policy() noexcept + { + return impl_->policy(); + } + + /// Returns the rate policy associated with the object + RatePolicy const& + rate_policy() const noexcept + { + return impl_->policy(); + } + + /** Set the timeout for the next logical operation. + + This sets either the read timer, the write timer, or + both timers to expire after the specified amount of time + has elapsed. If a timer expires when the corresponding + asynchronous operation is outstanding, the stream will be + closed and any outstanding operations will complete with the + error @ref beast::error::timeout. Otherwise, if the timer + expires while no operations are outstanding, and the expiraton + is not set again, the next operation will time out immediately. + + The timer applies collectively to any asynchronous reads + or writes initiated after the expiration is set, until the + expiration is set again. A call to @ref async_connect + counts as both a read and a write. + + @param expiry_time The amount of time after which a logical + operation should be considered timed out. + */ + void + expires_after( + std::chrono::nanoseconds expiry_time); + + /** Set the timeout for the next logical operation. + + This sets either the read timer, the write timer, or both + timers to expire at the specified time point. If a timer + expires when the corresponding asynchronous operation is + outstanding, the stream will be closed and any outstanding + operations will complete with the error @ref beast::error::timeout. + Otherwise, if the timer expires while no operations are outstanding, + and the expiraton is not set again, the next operation will time out + immediately. + + The timer applies collectively to any asynchronous reads + or writes initiated after the expiration is set, until the + expiration is set again. A call to @ref async_connect + counts as both a read and a write. + + @param expiry_time The time point after which a logical + operation should be considered timed out. + */ + void + expires_at(net::steady_timer::time_point expiry_time); + + /// Disable the timeout for the next logical operation. + void + expires_never(); + + /** Cancel all asynchronous operations associated with the socket. + + This function causes all outstanding asynchronous connect, + read, and write operations to finish immediately. Completion + handlers for cancelled operations will receive the error + `net::error::operation_aborted`. Completion handlers not + yet invoked whose operations have completed, will receive + the error corresponding to the result of the operation (which + may indicate success). + */ + void + cancel(); + + /** Close the timed stream. + + This cancels all of the outstanding asynchronous operations + as if by calling @ref cancel, and closes the underlying socket. + */ + void + close(); + + //-------------------------------------------------------------------------- + + /** Get the executor associated with the object. + + This function may be used to obtain the executor object that the + stream uses to dispatch completion handlers without an assocaited + executor. + + @return A copy of the executor that stream will use to dispatch handlers. + */ + executor_type + get_executor() const noexcept + { + return impl_->ex(); + } + + /** Connect the stream to the specified endpoint. + + This function is used to connect the underlying socket to the + specified remote endpoint. The function call will block until + the connection is successfully made or an error occurs. + The underlying socket is automatically opened if needed. + An automatically opened socket is not returned to the + closed state upon failure. + + @param ep The remote endpoint to connect to. + + @throws system_error Thrown on failure. + + @see connect + */ + void + connect(endpoint_type const& ep) + { + socket().connect(ep); + } + + /** Connect the stream to the specified endpoint. + + This function is used to connect the underlying socket to the + specified remote endpoint. The function call will block until + the connection is successfully made or an error occurs. + The underlying socket is automatically opened if needed. + An automatically opened socket is not returned to the + closed state upon failure. + + @param ep The remote endpoint to connect to. + + @param ec Set to indicate what error occurred, if any. + + @see connect + */ + void + connect(endpoint_type const& ep, error_code& ec) + { + socket().connect(ep, ec); + } + + /** Establishes a connection by trying each endpoint in a sequence. + + This function attempts to connect the stream to one of a sequence of + endpoints by trying each endpoint until a connection is successfully + established. + The underlying socket is automatically opened if needed. + An automatically opened socket is not returned to the + closed state upon failure. + + The algorithm, known as a <em>composed operation</em>, is implemented + in terms of calls to the underlying socket's `connect` function. + + @param endpoints A sequence of endpoints. + + @returns The successfully connected endpoint. + + @throws system_error Thrown on failure. If the sequence is + empty, the associated error code is `net::error::not_found`. + Otherwise, contains the error from the last connection attempt. + */ + template<class EndpointSequence + #if ! BOOST_BEAST_DOXYGEN + ,class = typename std::enable_if< + net::is_endpoint_sequence< + EndpointSequence>::value>::type + #endif + > + typename Protocol::endpoint + connect(EndpointSequence const& endpoints) + { + return net::connect(socket(), endpoints); + } + + /** Establishes a connection by trying each endpoint in a sequence. + + This function attempts to connect the stream to one of a sequence of + endpoints by trying each endpoint until a connection is successfully + established. + The underlying socket is automatically opened if needed. + An automatically opened socket is not returned to the + closed state upon failure. + + The algorithm, known as a <em>composed operation</em>, is implemented + in terms of calls to the underlying socket's `connect` function. + + @param endpoints A sequence of endpoints. + + @param ec Set to indicate what error occurred, if any. If the sequence is + empty, set to `net::error::not_found`. Otherwise, contains the error + from the last connection attempt. + + @returns On success, the successfully connected endpoint. Otherwise, a + default-constructed endpoint. + */ + template<class EndpointSequence + #if ! BOOST_BEAST_DOXYGEN + ,class = typename std::enable_if< + net::is_endpoint_sequence< + EndpointSequence>::value>::type + #endif + > + typename Protocol::endpoint + connect( + EndpointSequence const& endpoints, + error_code& ec + ) + { + return net::connect(socket(), endpoints, ec); + } + + /** Establishes a connection by trying each endpoint in a sequence. + + This function attempts to connect the stream to one of a sequence of + endpoints by trying each endpoint until a connection is successfully + established. + The underlying socket is automatically opened if needed. + An automatically opened socket is not returned to the + closed state upon failure. + + The algorithm, known as a <em>composed operation</em>, is implemented + in terms of calls to the underlying socket's `connect` function. + + @param begin An iterator pointing to the start of a sequence of endpoints. + + @param end An iterator pointing to the end of a sequence of endpoints. + + @returns An iterator denoting the successfully connected endpoint. + + @throws system_error Thrown on failure. If the sequence is + empty, the associated error code is `net::error::not_found`. + Otherwise, contains the error from the last connection attempt. + */ + template<class Iterator> + Iterator + connect( + Iterator begin, Iterator end) + { + return net::connect(socket(), begin, end); + } + + /** Establishes a connection by trying each endpoint in a sequence. + + This function attempts to connect the stream to one of a sequence of + endpoints by trying each endpoint until a connection is successfully + established. + The underlying socket is automatically opened if needed. + An automatically opened socket is not returned to the + closed state upon failure. + + The algorithm, known as a <em>composed operation</em>, is implemented + in terms of calls to the underlying socket's `connect` function. + + @param begin An iterator pointing to the start of a sequence of endpoints. + + @param end An iterator pointing to the end of a sequence of endpoints. + + @param ec Set to indicate what error occurred, if any. If the sequence is + empty, set to boost::asio::error::not_found. Otherwise, contains the error + from the last connection attempt. + + @returns On success, an iterator denoting the successfully connected + endpoint. Otherwise, the end iterator. + */ + template<class Iterator> + Iterator + connect( + Iterator begin, Iterator end, + error_code& ec) + { + return net::connect(socket(), begin, end, ec); + } + + /** Establishes a connection by trying each endpoint in a sequence. + + This function attempts to connect the stream to one of a sequence of + endpoints by trying each endpoint until a connection is successfully + established. + The underlying socket is automatically opened if needed. + An automatically opened socket is not returned to the + closed state upon failure. + + The algorithm, known as a <em>composed operation</em>, is implemented + in terms of calls to the underlying socket's `connect` function. + + @param endpoints A sequence of endpoints. + + @param connect_condition A function object that is called prior to each + connection attempt. The signature of the function object must be: + @code + bool connect_condition( + error_code const& ec, + typename Protocol::endpoint const& next); + @endcode + The @c ec parameter contains the result from the most recent connect + operation. Before the first connection attempt, @c ec is always set to + indicate success. The @c next parameter is the next endpoint to be tried. + The function object should return true if the next endpoint should be tried, + and false if it should be skipped. + + @returns The successfully connected endpoint. + + @throws boost::system::system_error Thrown on failure. If the sequence is + empty, the associated error code is `net::error::not_found`. + Otherwise, contains the error from the last connection attempt. + */ + template< + class EndpointSequence, class ConnectCondition + #if ! BOOST_BEAST_DOXYGEN + ,class = typename std::enable_if< + net::is_endpoint_sequence< + EndpointSequence>::value>::type + #endif + > + typename Protocol::endpoint + connect( + EndpointSequence const& endpoints, + ConnectCondition connect_condition + ) + { + return net::connect(socket(), endpoints, connect_condition); + } + + /** Establishes a connection by trying each endpoint in a sequence. + + This function attempts to connect the stream to one of a sequence of + endpoints by trying each endpoint until a connection is successfully + established. + The underlying socket is automatically opened if needed. + An automatically opened socket is not returned to the + closed state upon failure. + + The algorithm, known as a <em>composed operation</em>, is implemented + in terms of calls to the underlying socket's `connect` function. + + @param endpoints A sequence of endpoints. + + @param connect_condition A function object that is called prior to each + connection attempt. The signature of the function object must be: + @code + bool connect_condition( + error_code const& ec, + typename Protocol::endpoint const& next); + @endcode + The @c ec parameter contains the result from the most recent connect + operation. Before the first connection attempt, @c ec is always set to + indicate success. The @c next parameter is the next endpoint to be tried. + The function object should return true if the next endpoint should be tried, + and false if it should be skipped. + + @param ec Set to indicate what error occurred, if any. If the sequence is + empty, set to `net::error::not_found`. Otherwise, contains the error + from the last connection attempt. + + @returns On success, the successfully connected endpoint. Otherwise, a + default-constructed endpoint. + */ + template< + class EndpointSequence, class ConnectCondition + #if ! BOOST_BEAST_DOXYGEN + ,class = typename std::enable_if< + net::is_endpoint_sequence< + EndpointSequence>::value>::type + #endif + > + typename Protocol::endpoint + connect( + EndpointSequence const& endpoints, + ConnectCondition connect_condition, + error_code& ec) + { + return net::connect(socket(), endpoints, connect_condition, ec); + } + + /** Establishes a connection by trying each endpoint in a sequence. + + This function attempts to connect the stream to one of a sequence of + endpoints by trying each endpoint until a connection is successfully + established. + The underlying socket is automatically opened if needed. + An automatically opened socket is not returned to the + closed state upon failure. + + The algorithm, known as a <em>composed operation</em>, is implemented + in terms of calls to the underlying socket's `connect` function. + + @param begin An iterator pointing to the start of a sequence of endpoints. + + @param end An iterator pointing to the end of a sequence of endpoints. + + @param connect_condition A function object that is called prior to each + connection attempt. The signature of the function object must be: + @code + bool connect_condition( + error_code const& ec, + typename Protocol::endpoint const& next); + @endcode + The @c ec parameter contains the result from the most recent connect + operation. Before the first connection attempt, @c ec is always set to + indicate success. The @c next parameter is the next endpoint to be tried. + The function object should return true if the next endpoint should be tried, + and false if it should be skipped. + + @returns An iterator denoting the successfully connected endpoint. + + @throws boost::system::system_error Thrown on failure. If the sequence is + empty, the associated @c error_code is `net::error::not_found`. + Otherwise, contains the error from the last connection attempt. + */ + template< + class Iterator, class ConnectCondition> + Iterator + connect( + Iterator begin, Iterator end, + ConnectCondition connect_condition) + { + return net::connect(socket(), begin, end, connect_condition); + } + + /** Establishes a connection by trying each endpoint in a sequence. + + This function attempts to connect the stream to one of a sequence of + endpoints by trying each endpoint until a connection is successfully + established. + The underlying socket is automatically opened if needed. + An automatically opened socket is not returned to the + closed state upon failure. + + The algorithm, known as a <em>composed operation</em>, is implemented + in terms of calls to the underlying socket's `connect` function. + + @param begin An iterator pointing to the start of a sequence of endpoints. + + @param end An iterator pointing to the end of a sequence of endpoints. + + @param connect_condition A function object that is called prior to each + connection attempt. The signature of the function object must be: + @code + bool connect_condition( + error_code const& ec, + typename Protocol::endpoint const& next); + @endcode + The @c ec parameter contains the result from the most recent connect + operation. Before the first connection attempt, @c ec is always set to + indicate success. The @c next parameter is the next endpoint to be tried. + The function object should return true if the next endpoint should be tried, + and false if it should be skipped. + + @param ec Set to indicate what error occurred, if any. If the sequence is + empty, set to `net::error::not_found`. Otherwise, contains the error + from the last connection attempt. + + @returns On success, an iterator denoting the successfully connected + endpoint. Otherwise, the end iterator. + */ + template< + class Iterator, class ConnectCondition> + Iterator + connect( + Iterator begin, Iterator end, + ConnectCondition connect_condition, + error_code& ec) + { + return net::connect(socket(), begin, end, connect_condition, ec); + } + + /** Connect the stream to the specified endpoint asynchronously. + + This function is used to asynchronously connect the underlying + socket to the specified remote endpoint. The function call always + returns immediately. + The underlying socket is automatically opened if needed. + An automatically opened socket is not returned to the + closed state upon failure. + + If the timeout timer expires while the operation is outstanding, + the operation will be canceled and the completion handler will be + invoked with the error @ref error::timeout. + + @param ep The remote endpoint to which the underlying socket will be + connected. Copies will be made of the endpoint object as required. + + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( + error_code ec // Result of operation + ); + @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `net::post`. + + @see async_connect + */ + template<class ConnectHandler> + BOOST_BEAST_ASYNC_RESULT1(ConnectHandler) + async_connect( + endpoint_type const& ep, + ConnectHandler&& handler); + + /** Establishes a connection by trying each endpoint in a sequence asynchronously. + + This function attempts to connect the stream to one of a sequence of + endpoints by trying each endpoint until a connection is successfully + established. + The underlying socket is automatically opened if needed. + An automatically opened socket is not returned to the + closed state upon failure. + + The algorithm, known as a <em>composed asynchronous operation</em>, is + implemented in terms of calls to the underlying socket's `async_connect` + function. + + If the timeout timer expires while the operation is outstanding, + the current connection attempt will be canceled and the completion + handler will be invoked with the error @ref error::timeout. + + @param endpoints A sequence of endpoints. This this object must meet + the requirements of <em>EndpointSequence</em>. + + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( + // Result of operation. if the sequence is empty, set to + // net::error::not_found. Otherwise, contains the + // error from the last connection attempt. + error_code const& error, + + // On success, the successfully connected endpoint. + // Otherwise, a default-constructed endpoint. + typename Protocol::endpoint const& endpoint + ); + @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `net::post`. + */ + template< + class EndpointSequence, + class RangeConnectHandler + #if ! BOOST_BEAST_DOXYGEN + ,class = typename std::enable_if< + net::is_endpoint_sequence< + EndpointSequence>::value>::type + #endif + > + BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,void (error_code, typename Protocol::endpoint)) + async_connect( + EndpointSequence const& endpoints, + RangeConnectHandler&& handler); + + /** Establishes a connection by trying each endpoint in a sequence asynchronously. + + This function attempts to connect the stream to one of a sequence of + endpoints by trying each endpoint until a connection is successfully + established. + The underlying socket is automatically opened if needed. + An automatically opened socket is not returned to the + closed state upon failure. + + The algorithm, known as a <em>composed asynchronous operation</em>, is + implemented in terms of calls to the underlying socket's `async_connect` + function. + + If the timeout timer expires while the operation is outstanding, + the current connection attempt will be canceled and the completion + handler will be invoked with the error @ref error::timeout. + + @param endpoints A sequence of endpoints. This this object must meet + the requirements of <em>EndpointSequence</em>. + + @param connect_condition A function object that is called prior to each + connection attempt. The signature of the function object must be: + @code + bool connect_condition( + error_code const& ec, + typename Protocol::endpoint const& next); + @endcode + The @c ec parameter contains the result from the most recent connect + operation. Before the first connection attempt, @c ec is always set to + indicate success. The @c next parameter is the next endpoint to be tried. + The function object should return true if the next endpoint should be tried, + and false if it should be skipped. + + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( + // Result of operation. if the sequence is empty, set to + // net::error::not_found. Otherwise, contains the + // error from the last connection attempt. + error_code const& error, + + // On success, the successfully connected endpoint. + // Otherwise, a default-constructed endpoint. + typename Protocol::endpoint const& endpoint + ); + @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `net::post`. + + @par Example + The following connect condition function object can be used to output + information about the individual connection attempts: + @code + struct my_connect_condition + { + bool operator()( + error_code const& ec, + net::ip::tcp::endpoint const& next) + { + if (ec) + std::cout << "Error: " << ec.message() << std::endl; + std::cout << "Trying: " << next << std::endl; + return true; + } + }; + @endcode + */ + template< + class EndpointSequence, + class ConnectCondition, + class RangeConnectHandler + #if ! BOOST_BEAST_DOXYGEN + ,class = typename std::enable_if< + net::is_endpoint_sequence< + EndpointSequence>::value>::type + #endif + > + BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,void (error_code, typename Protocol::endpoint)) + async_connect( + EndpointSequence const& endpoints, + ConnectCondition connect_condition, + RangeConnectHandler&& handler); + + /** Establishes a connection by trying each endpoint in a sequence asynchronously. + + This function attempts to connect the stream to one of a sequence of + endpoints by trying each endpoint until a connection is successfully + established. + The underlying socket is automatically opened if needed. + An automatically opened socket is not returned to the + closed state upon failure. + + The algorithm, known as a <em>composed asynchronous operation</em>, is + implemented in terms of calls to the underlying socket's `async_connect` + function. + + If the timeout timer expires while the operation is outstanding, + the current connection attempt will be canceled and the completion + handler will be invoked with the error @ref error::timeout. + + @param begin An iterator pointing to the start of a sequence of endpoints. + + @param end An iterator pointing to the end of a sequence of endpoints. + + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( + // Result of operation. if the sequence is empty, set to + // net::error::not_found. Otherwise, contains the + // error from the last connection attempt. + error_code const& error, + + // On success, an iterator denoting the successfully + // connected endpoint. Otherwise, the end iterator. + Iterator iterator + ); + @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `net::post`. + */ + template< + class Iterator, + class IteratorConnectHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,void (error_code, Iterator)) + async_connect( + Iterator begin, Iterator end, + IteratorConnectHandler&& handler); + + /** Establishes a connection by trying each endpoint in a sequence asynchronously. + + This function attempts to connect the stream to one of a sequence of + endpoints by trying each endpoint until a connection is successfully + established. + The algorithm, known as a <em>composed asynchronous operation</em>, is + implemented in terms of calls to the underlying socket's `async_connect` + function. + + If the timeout timer expires while the operation is outstanding, + the current connection attempt will be canceled and the completion + handler will be invoked with the error @ref error::timeout. + + @param begin An iterator pointing to the start of a sequence of endpoints. + + @param end An iterator pointing to the end of a sequence of endpoints. + + @param connect_condition A function object that is called prior to each + connection attempt. The signature of the function object must be: + @code + bool connect_condition( + error_code const& ec, + Iterator next); + @endcode + + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( + // Result of operation. if the sequence is empty, set to + // net::error::not_found. Otherwise, contains the + // error from the last connection attempt. + error_code const& error, + + // On success, an iterator denoting the successfully + // connected endpoint. Otherwise, the end iterator. + Iterator iterator + ); + @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `net::post`. + */ + template< + class Iterator, + class ConnectCondition, + class IteratorConnectHandler> + BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,void (error_code, Iterator)) + async_connect( + Iterator begin, Iterator end, + ConnectCondition connect_condition, + IteratorConnectHandler&& handler); + + //-------------------------------------------------------------------------- + + /** Read some data. + + This function is used to read some data from the stream. + + The call blocks until one of the following is true: + + @li One or more bytes are read from the stream. + + @li An error occurs. + + @param buffers The buffers into which the data will be read. If the + size of the buffers is zero bytes, the call always returns + immediately with no error. + + @returns The number of bytes read. + + @throws system_error Thrown on failure. + + @note The `read_some` operation may not receive all of the requested + number of bytes. Consider using the function `net::read` if you need + to ensure that the requested amount of data is read before the + blocking operation completes. + */ + template<class MutableBufferSequence> + std::size_t + read_some(MutableBufferSequence const& buffers) + { + return impl_->socket.read_some(buffers); + } + + /** Read some data. + + This function is used to read some data from the underlying socket. + + The call blocks until one of the following is true: + + @li One or more bytes are read from the stream. + + @li An error occurs. + + @param buffers The buffers into which the data will be read. If the + size of the buffers is zero bytes, the call always returns + immediately with no error. + + @param ec Set to indicate what error occurred, if any. + + @returns The number of bytes read. + + @note The `read_some` operation may not receive all of the requested + number of bytes. Consider using the function `net::read` if you need + to ensure that the requested amount of data is read before the + blocking operation completes. + */ + template<class MutableBufferSequence> + std::size_t + read_some( + MutableBufferSequence const& buffers, + error_code& ec) + { + return impl_->socket.read_some(buffers, ec); + } + + /** Read some data asynchronously. + + This function is used to asynchronously read data from the stream. + + This call always returns immediately. The asynchronous operation + will continue until one of the following conditions is true: + + @li One or more bytes are read from the stream. + + @li An error occurs. + + The algorithm, known as a <em>composed asynchronous operation</em>, + is implemented in terms of calls to the next layer's `async_read_some` + function. The program must ensure that no other calls to @ref read_some + or @ref async_read_some are performed until this operation completes. + + If the timeout timer expires while the operation is outstanding, + the operation will be canceled and the completion handler will be + invoked with the error @ref error::timeout. + + @param buffers The buffers into which the data will be read. If the size + of the buffers is zero bytes, the operation always completes immediately + with no error. + Although the buffers object may be copied as necessary, ownership of the + underlying memory blocks is retained by the caller, which must guarantee + that they remain valid until the handler is called. + + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( + error_code error, // Result of operation. + std::size_t bytes_transferred // Number of bytes read. + ); + @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `net::post`. + + @note The `async_read_some` operation may not receive all of the requested + number of bytes. Consider using the function `net::async_read` if you need + to ensure that the requested amount of data is read before the asynchronous + operation completes. + */ + template<class MutableBufferSequence, class ReadHandler> + BOOST_BEAST_ASYNC_RESULT2(ReadHandler) + async_read_some( + MutableBufferSequence const& buffers, + ReadHandler&& handler); + + /** Write some data. + + This function is used to write some data to the stream. + + The call blocks until one of the following is true: + + @li One or more bytes are written to the stream. + + @li An error occurs. + + @param buffers The buffers from which the data will be written. If the + size of the buffers is zero bytes, the call always returns immediately + with no error. + + @returns The number of bytes written. + + @throws system_error Thrown on failure. + + @note The `write_some` operation may not transmit all of the requested + number of bytes. Consider using the function `net::write` if you need + to ensure that the requested amount of data is written before the + blocking operation completes. + */ + template<class ConstBufferSequence> + std::size_t + write_some(ConstBufferSequence const& buffers) + { + return impl_->socket.write_some(buffers); + } + + /** Write some data. + + This function is used to write some data to the stream. + + The call blocks until one of the following is true: + + @li One or more bytes are written to the stream. + + @li An error occurs. + + @param buffers The buffers from which the data will be written. If the + size of the buffers is zero bytes, the call always returns immediately + with no error. + + @param ec Set to indicate what error occurred, if any. + + @returns The number of bytes written. + + @throws system_error Thrown on failure. + + @note The `write_some` operation may not transmit all of the requested + number of bytes. Consider using the function `net::write` if you need + to ensure that the requested amount of data is written before the + blocking operation completes. + */ + template<class ConstBufferSequence> + std::size_t + write_some( + ConstBufferSequence const& buffers, + error_code& ec) + { + return impl_->socket.write_some(buffers, ec); + } + + /** Write some data asynchronously. + + This function is used to asynchronously write data to the underlying socket. + + This call always returns immediately. The asynchronous operation + will continue until one of the following conditions is true: + + @li One or more bytes are written to the stream. + + @li An error occurs. + + The algorithm, known as a <em>composed asynchronous operation</em>, + is implemented in terms of calls to the next layer's `async_write_some` + function. The program must ensure that no other calls to @ref async_write_some + are performed until this operation completes. + + If the timeout timer expires while the operation is outstanding, + the operation will be canceled and the completion handler will be + invoked with the error @ref error::timeout. + + @param buffers The buffers from which the data will be written. If the + size of the buffers is zero bytes, the operation always completes + immediately with no error. + Although the buffers object may be copied as necessary, ownership of the + underlying memory blocks is retained by the caller, which must guarantee + that they remain valid until the handler is called. + + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( + error_code error, // Result of operation. + std::size_t bytes_transferred // Number of bytes written. + ); + @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `net::post`. + + @note The `async_write_some` operation may not transmit all of the requested + number of bytes. Consider using the function `net::async_write` if you need + to ensure that the requested amount of data is sent before the asynchronous + operation completes. + */ + template<class ConstBufferSequence, class WriteHandler> + BOOST_BEAST_ASYNC_RESULT2(WriteHandler) + async_write_some( + ConstBufferSequence const& buffers, + WriteHandler&& handler); +}; + +} // beast +} // boost + +#include <boost/beast/core/impl/basic_stream.hpp> + +#endif diff --git a/boost/beast/core/bind_handler.hpp b/boost/beast/core/bind_handler.hpp index 0fb7b8233b..cfa6a74bf5 100644 --- a/boost/beast/core/bind_handler.hpp +++ b/boost/beast/core/bind_handler.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -11,7 +11,6 @@ #define BOOST_BEAST_BIND_HANDLER_HPP #include <boost/beast/core/detail/config.hpp> -#include <boost/beast/core/type_traits.hpp> #include <boost/beast/core/detail/bind_handler.hpp> #include <type_traits> #include <utility> @@ -31,21 +30,25 @@ namespace beast { handler, whose associated allocator and associated executor will will be the same as those of the original handler. - Example: + @par Example + + This function posts the invocation of the specified completion + handler with bound arguments: @code - template<class AsyncReadStream, class ReadHandler> + template <class AsyncReadStream, class ReadHandler> void - signal_aborted(AsyncReadStream& stream, ReadHandler&& handler) + signal_aborted (AsyncReadStream& stream, ReadHandler&& handler) { - boost::asio::post( + net::post( stream.get_executor(), - bind_handler(std::forward<ReadHandler>(handler), - boost::asio::error::operation_aborted, 0)); + bind_handler (std::forward <ReadHandler> (handler), + net::error::operation_aborted, 0)); } @endcode @param handler The handler to wrap. + The implementation takes ownership of the handler by performing a decay-copy. @param args A list of arguments to bind to the handler. The arguments are forwarded into the returned object. These @@ -54,22 +57,73 @@ namespace beast { */ template<class Handler, class... Args> #if BOOST_BEAST_DOXYGEN -implementation_defined +__implementation_defined__ #else -detail::bound_handler< - typename std::decay<Handler>::type, Args...> +detail::bind_wrapper< + typename std::decay<Handler>::type, + typename std::decay<Args>::type...> #endif bind_handler(Handler&& handler, Args&&... args) { -#if 0 - // Can't do this because of placeholders - static_assert(is_completion_handler< - Handler, void(Args...)>::value, - "Handler requirements not met"); + return detail::bind_wrapper< + typename std::decay<Handler>::type, + typename std::decay<Args>::type...>( + std::forward<Handler>(handler), + std::forward<Args>(args)...); +} + +/** Bind parameters to a completion handler, creating a new handler. + + This function creates a new handler which, when invoked, calls + the original handler with the list of bound arguments. Any + parameters passed in the invocation will be forwarded in + the parameter list after the bound arguments. + + The passed handler and arguments are forwarded into the returned + handler, whose associated allocator and associated executor will + will be the same as those of the original handler. + + @par Example + + This function posts the invocation of the specified completion + handler with bound arguments: + + @code + template <class AsyncReadStream, class ReadHandler> + void + signal_eof (AsyncReadStream& stream, ReadHandler&& handler) + { + net::post( + stream.get_executor(), + bind_front_handler (std::forward<ReadHandler> (handler), + net::error::eof, 0)); + } + @endcode + + @param handler The handler to wrap. + The implementation takes ownership of the handler by performing a decay-copy. + + @param args A list of arguments to bind to the handler. + The arguments are forwarded into the returned object. +*/ +template<class Handler, class... Args> +#if BOOST_BEAST_DOXYGEN +__implementation_defined__ +#else +auto #endif - return detail::bound_handler<typename std::decay< - Handler>::type, Args...>(std::forward< - Handler>(handler), std::forward<Args>(args)...); +bind_front_handler( + Handler&& handler, + Args&&... args) -> + detail::bind_front_wrapper< + typename std::decay<Handler>::type, + typename std::decay<Args>::type...> +{ + return detail::bind_front_wrapper< + typename std::decay<Handler>::type, + typename std::decay<Args>::type...>( + std::forward<Handler>(handler), + std::forward<Args>(args)...); } } // beast diff --git a/boost/beast/core/buffer_traits.hpp b/boost/beast/core/buffer_traits.hpp new file mode 100644 index 0000000000..f7b9adae7a --- /dev/null +++ b/boost/beast/core/buffer_traits.hpp @@ -0,0 +1,164 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_BUFFER_TRAITS_HPP +#define BOOST_BEAST_BUFFER_TRAITS_HPP + +#include <boost/beast/core/detail/config.hpp> +#include <boost/beast/core/detail/buffer_traits.hpp> +#include <boost/beast/core/detail/static_const.hpp> +#include <boost/asio/buffer.hpp> +#include <boost/config/workaround.hpp> +#include <boost/mp11/function.hpp> +#include <type_traits> + +namespace boost { +namespace beast { + +/** Determine if a list of types satisfy the <em>ConstBufferSequence</em> requirements. + + This metafunction is used to determine if all of the specified types + meet the requirements for constant buffer sequences. This type alias + will be `std::true_type` if each specified type meets the requirements, + otherwise, this type alias will be `std::false_type`. + + @tparam BufferSequence A list of zero or more types to check. If this + list is empty, the resulting type alias will be `std::true_type`. +*/ +template<class... BufferSequence> +#if BOOST_BEAST_DOXYGEN +using is_const_buffer_sequence = __see_below__; +#else +using is_const_buffer_sequence = mp11::mp_all< + net::is_const_buffer_sequence< + typename std::decay<BufferSequence>::type>...>; +#endif + +/** Determine if a list of types satisfy the <em>MutableBufferSequence</em> requirements. + + This metafunction is used to determine if all of the specified types + meet the requirements for mutable buffer sequences. This type alias + will be `std::true_type` if each specified type meets the requirements, + otherwise, this type alias will be `std::false_type`. + + @tparam BufferSequence A list of zero or more types to check. If this + list is empty, the resulting type alias will be `std::true_type`. +*/ +template<class... BufferSequence> +#if BOOST_BEAST_DOXYGEN +using is_mutable_buffer_sequence = __see_below__; +#else +using is_mutable_buffer_sequence = mp11::mp_all< + net::is_mutable_buffer_sequence< + typename std::decay<BufferSequence>::type>...>; +#endif + +/** Type alias for the underlying buffer type of a list of buffer sequence types. + + This metafunction is used to determine the underlying buffer type for + a list of buffer sequence. The equivalent type of the alias will vary + depending on the template type argument: + + @li If every type in the list is a <em>MutableBufferSequence</em>, + the resulting type alias will be `net::mutable_buffer`, otherwise + + @li The resulting type alias will be `net::const_buffer`. + + @par Example + The following code returns the first buffer in a buffer sequence, + or generates a compilation error if the argument is not a buffer + sequence: + @code + template <class BufferSequence> + buffers_type <BufferSequence> + buffers_front (BufferSequence const& buffers) + { + static_assert( + net::is_const_buffer_sequence<BufferSequence>::value, + "BufferSequence type requirements not met"); + auto const first = net::buffer_sequence_begin (buffers); + if (first == net::buffer_sequence_end (buffers)) + return {}; + return *first; + } + @endcode + + @tparam BufferSequence A list of zero or more types to check. If this + list is empty, the resulting type alias will be `net::mutable_buffer`. +*/ +template<class... BufferSequence> +#if BOOST_BEAST_DOXYGEN +using buffers_type = __see_below__; +#else +using buffers_type = typename std::conditional< + is_mutable_buffer_sequence<BufferSequence...>::value, + net::mutable_buffer, net::const_buffer>::type; +#endif + +/** Type alias for the iterator type of a buffer sequence type. + + This metafunction is used to determine the type of iterator + used by a particular buffer sequence. + + @tparam T The buffer sequence type to use. The resulting + type alias will be equal to the iterator type used by + the buffer sequence. +*/ +template <class BufferSequence> +#if BOOST_BEAST_DOXYGEN +using buffers_iterator_type = __see_below__; +#elif BOOST_WORKAROUND(BOOST_MSVC, < 1910) +using buffers_iterator_type = typename + detail::buffers_iterator_type_helper< + typename std::decay<BufferSequence>::type>::type; +#else +using buffers_iterator_type = + decltype(net::buffer_sequence_begin( + std::declval<BufferSequence const&>())); +#endif + +/** Return the total number of bytes in a buffer or buffer sequence + + This function returns the total number of bytes in a buffer, + buffer sequence, or object convertible to a buffer. Specifically + it may be passed: + + @li A <em>ConstBufferSequence</em> or <em>MutableBufferSequence</em> + + @li A `net::const_buffer` or `net::mutable_buffer` + + @li An object convertible to `net::const_buffer` + + This function is designed as an easier-to-use replacement for + `net::buffer_size`. It recognizes customization points found through + argument-dependent lookup. The call `beast::buffer_bytes(b)` is + equivalent to performing: + @code + using namespace net; + buffer_bytes(b); + @endcode + In addition this handles types which are convertible to + `net::const_buffer`; these are not handled by `net::buffer_size`. + + @param buffers The buffer or buffer sequence to calculate the size of. + + @return The total number of bytes in the buffer or sequence. +*/ +#if BOOST_BEAST_DOXYGEN +template<class BufferSequence> +std::size_t +buffer_bytes(BufferSequence const& buffers); +#else +BOOST_BEAST_INLINE_VARIABLE(buffer_bytes, detail::buffer_bytes_impl) +#endif + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/buffered_read_stream.hpp b/boost/beast/core/buffered_read_stream.hpp index 974062309f..66cd561e98 100644 --- a/boost/beast/core/buffered_read_stream.hpp +++ b/boost/beast/core/buffered_read_stream.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -13,7 +13,7 @@ #include <boost/beast/core/detail/config.hpp> #include <boost/beast/core/error.hpp> #include <boost/beast/core/multi_buffer.hpp> -#include <boost/beast/core/type_traits.hpp> +#include <boost/beast/core/stream_traits.hpp> #include <boost/asio/async_result.hpp> #include <boost/asio/buffer.hpp> #include <boost/asio/io_context.hpp> @@ -23,25 +23,25 @@ namespace boost { namespace beast { -/** A @b Stream with attached @b DynamicBuffer to buffer reads. +/** A <em>Stream</em> with attached <em>DynamicBuffer</em> to buffer reads. - This wraps a @b Stream implementation so that calls to write are + This wraps a <em>Stream</em> 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 @b DynamicBuffer which + first consume the input sequence stored in a <em>DynamicBuffer</em> 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 + `net::buffered_read_stream`. It is designed to facilitate + the use of `net::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` + buffered version of `net::ssl::stream::async_handshake` could make use of this wrapper. Uses: @li Transparently leave untouched input acquired in calls - to `boost::asio::read_until` behind for subsequent callers. + to `net::read_until` behind for subsequent callers. @li "Preload" a stream with handshake input data acquired from other sources. @@ -51,9 +51,9 @@ namespace beast { // Process the next HTTP header on the stream, // leaving excess bytes behind for the next call. // - template<class DynamicBuffer> + template<class Stream, class DynamicBuffer> void process_http_message( - buffered_read_stream<DynamicBuffer>& stream) + buffered_read_stream<Stream, DynamicBuffer>& stream) { // Read up to and including the end of the HTTP // header, leaving the sequence in the stream's @@ -62,7 +62,7 @@ namespace beast { // part up to the end of the delimiter. // std::size_t bytes_transferred = - boost::asio::read_until( + net::read_until( stream.next_layer(), stream.buffer(), "\r\n\r\n"); // Use buffers_prefix() to limit the input @@ -92,11 +92,10 @@ template<class Stream, class DynamicBuffer> class buffered_read_stream { static_assert( - boost::asio::is_dynamic_buffer<DynamicBuffer>::value, - "DynamicBuffer requirements not met"); + net::is_dynamic_buffer<DynamicBuffer>::value, + "DynamicBuffer type requirements not met"); - template<class Buffers, class Handler> - class read_some_op; + struct ops; DynamicBuffer buffer_; std::size_t capacity_ = 0; @@ -110,9 +109,6 @@ public: using next_layer_type = typename std::remove_reference<Stream>::type; - /// The type of the lowest layer. - using lowest_layer_type = get_lowest_layer<next_layer_type>; - /** Move constructor. @note The behavior of move assignment on or from streams @@ -137,31 +133,20 @@ public: /// Get a reference to the next layer. next_layer_type& - next_layer() + next_layer() noexcept { return next_layer_; } /// Get a const reference to the next layer. next_layer_type const& - next_layer() const + next_layer() const noexcept { 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(); - } + using executor_type = + beast::executor_type<next_layer_type>; /** Get the executor associated with the object. @@ -169,21 +154,9 @@ public: uses to dispatch handlers for asynchronous operations. @return A copy of the executor that stream will use to dispatch handlers. - - @note This function participates in overload resolution only if - `NextLayer` has a member function named `get_executor`. */ -#if BOOST_BEAST_DOXYGEN - implementation_defined -#else - template< - class T = next_layer_type, - class = typename std::enable_if< - has_get_executor<next_layer_type>::value>::type> - auto -#endif - get_executor() noexcept -> - decltype(std::declval<T&>().get_executor()) + executor_type + get_executor() noexcept { return next_layer_.get_executor(); } @@ -196,14 +169,14 @@ public: the caller defined maximum. */ DynamicBuffer& - buffer() + buffer() noexcept { return buffer_; } /// Access the internal buffer DynamicBuffer const& - buffer() const + buffer() const noexcept { return buffer_; } @@ -224,7 +197,7 @@ public: than the amount of data in the buffer, no bytes are discarded. */ void - capacity(std::size_t size) + capacity(std::size_t size) noexcept { capacity_ = size; } @@ -273,21 +246,23 @@ public: is retained by the caller, which must guarantee that they remain valid until the handler is called. - @param handler Invoked when the operation completes. - The handler may be moved or copied as needed. - The equivalent function signature of the handler must be: - @code void handler( + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( error_code const& error, // result of operation std::size_t bytes_transferred // number of bytes transferred - ); @endcode + ); + @endcode Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a - manner equivalent to using `boost::asio::io_context::post`. + manner equivalent to using `net::post`. */ template<class MutableBufferSequence, class ReadHandler> - BOOST_ASIO_INITFN_RESULT_TYPE( - ReadHandler, void(error_code, std::size_t)) + BOOST_BEAST_ASYNC_RESULT2(ReadHandler) async_read_some(MutableBufferSequence const& buffers, ReadHandler&& handler); @@ -308,7 +283,7 @@ public: write_some(ConstBufferSequence const& buffers) { static_assert(is_sync_write_stream<next_layer_type>::value, - "SyncWriteStream requirements not met"); + "SyncWriteStream type requirements not met"); return next_layer_.write_some(buffers); } @@ -330,7 +305,7 @@ public: error_code& ec) { static_assert(is_sync_write_stream<next_layer_type>::value, - "SyncWriteStream requirements not met"); + "SyncWriteStream type requirements not met"); return next_layer_.write_some(buffers, ec); } @@ -345,21 +320,23 @@ public: retained by the caller, which must guarantee that they remain valid until the handler is called. - @param handler Invoked when the operation completes. - The handler may be moved or copied as needed. - The equivalent function signature of the handler must be: - @code void handler( + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( error_code const& error, // result of operation std::size_t bytes_transferred // number of bytes transferred - ); @endcode + ); + @endcode Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a - manner equivalent to using `boost::asio::io_context::post`. + manner equivalent to using `net::post`. */ template<class ConstBufferSequence, class WriteHandler> - BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t)) + BOOST_BEAST_ASYNC_RESULT2(WriteHandler) async_write_some(ConstBufferSequence const& buffers, WriteHandler&& handler); }; @@ -367,6 +344,6 @@ public: } // beast } // boost -#include <boost/beast/core/impl/buffered_read_stream.ipp> +#include <boost/beast/core/impl/buffered_read_stream.hpp> #endif diff --git a/boost/beast/core/buffers_adapter.hpp b/boost/beast/core/buffers_adapter.hpp index 2000ce24bb..6c584bb295 100644 --- a/boost/beast/core/buffers_adapter.hpp +++ b/boost/beast/core/buffers_adapter.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -11,178 +11,25 @@ #define BOOST_BEAST_BUFFERS_ADAPTER_HPP #include <boost/beast/core/detail/config.hpp> -#include <boost/beast/core/type_traits.hpp> -#include <boost/asio/buffer.hpp> -#include <boost/optional.hpp> -#include <type_traits> -namespace boost { -namespace beast { - -/** Adapts a @b MutableBufferSequence into a @b DynamicBuffer. +#ifdef BOOST_BEAST_ALLOW_DEPRECATED - This class wraps a @b MutableBufferSequence to meet the requirements - of @b DynamicBuffer. 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. +#include <boost/beast/core/buffers_adaptor.hpp> - The size of the mutable buffer sequence determines the maximum - number of bytes which may be prepared and committed. +namespace boost { +namespace beast { - @tparam MutableBufferSequence The type of mutable buffer sequence to wrap. -*/ template<class MutableBufferSequence> -class buffers_adapter -{ - static_assert(boost::asio::is_mutable_buffer_sequence<MutableBufferSequence>::value, - "MutableBufferSequence requirements not met"); - - using iter_type = typename - detail::buffer_sequence_iterator< - MutableBufferSequence>::type; - - MutableBufferSequence 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<class Deduced> - buffers_adapter(Deduced&& other, - std::size_t nbegin, std::size_t nout, - std::size_t nend) - : bs_(std::forward<Deduced>(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_) - { - } - - iter_type end_impl() const; - -public: - /// The type of the underlying mutable buffer sequence - using value_type = MutableBufferSequence; +using buffers_adapter = buffers_adaptor<MutableBufferSequence>; -#if BOOST_BEAST_DOXYGEN - /// The type used to represent the input sequence as a list of buffers. - using const_buffers_type = implementation_defined; - - /// The type used to represent the output sequence as a list of buffers. - using mutable_buffers_type = implementation_defined; +} // beast +} // boost #else - class const_buffers_type; - class mutable_buffers_type; +// The new filename is <boost/beast/core/buffers_adaptor.hpp> +#error The file <boost/beast/core/buffers_adapter.hpp> is deprecated, define BOOST_BEAST_ALLOW_DEPRECATED to disable this error #endif - /// 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(MutableBufferSequence const& buffers); - - /** Constructor - - This constructs the buffer adapter in-place from - a list of arguments. - - @param args Arguments forwarded to the buffers constructor. - */ - template<class... Args> - explicit - buffers_adapter(boost::in_place_init_t, Args&&... args); - - /// 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_; - } - - /// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation. - std::size_t - capacity() const - { - return max_size_; - } - - /// Returns the original mutable buffer sequence - value_type const& - value() const - { - return bs_; - } - - /** 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. - - @note Buffers representing the input sequence acquired prior to - this call remain valid. - */ - mutable_buffers_type - prepare(std::size_t n); - - /** Move bytes from the output sequence to the input sequence. - - @note Buffers representing the input sequence acquired prior to - this call remain valid. - */ - void - commit(std::size_t n); - - /** Get a list of buffers that represents the input sequence. - - @note These buffers remain valid across subsequent calls to `prepare`. - */ - const_buffers_type - data() const; - - /// Remove bytes from the input sequence. - void - consume(std::size_t n); -}; - -} // beast -} // boost - -#include <boost/beast/core/impl/buffers_adapter.ipp> - #endif diff --git a/boost/beast/core/buffers_adaptor.hpp b/boost/beast/core/buffers_adaptor.hpp new file mode 100644 index 0000000000..d6400481ce --- /dev/null +++ b/boost/beast/core/buffers_adaptor.hpp @@ -0,0 +1,226 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_BUFFERS_ADAPTOR_HPP +#define BOOST_BEAST_BUFFERS_ADAPTOR_HPP + +#include <boost/beast/core/detail/config.hpp> +#include <boost/beast/core/buffer_traits.hpp> +#include <boost/optional.hpp> +#include <type_traits> + +namespace boost { +namespace beast { + +/** Adapts a <em>MutableBufferSequence</em> into a <em>DynamicBuffer</em>. + + This class wraps a <em>MutableBufferSequence</em> to meet the requirements + of <em>DynamicBuffer</em>. 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 MutableBufferSequence The type of mutable buffer sequence to adapt. +*/ +template<class MutableBufferSequence> +class buffers_adaptor +{ + static_assert(net::is_mutable_buffer_sequence< + MutableBufferSequence>::value, + "MutableBufferSequence type requirements not met"); + + using iter_type = + buffers_iterator_type<MutableBufferSequence>; + + template<bool> + class readable_bytes; + + MutableBufferSequence 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 + + iter_type end_impl() const; + + buffers_adaptor( + buffers_adaptor const& other, + std::size_t nbegin, + std::size_t nout, + std::size_t nend); + +public: + /// The type of the underlying mutable buffer sequence + using value_type = MutableBufferSequence; + + /** Construct a buffers adaptor. + + @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_adaptor(MutableBufferSequence const& buffers); + + /** Constructor + + This constructs the buffer adaptor in-place from + a list of arguments. + + @param args Arguments forwarded to the buffers constructor. + */ + template<class... Args> + explicit + buffers_adaptor(boost::in_place_init_t, Args&&... args); + + /// Copy Constructor + buffers_adaptor(buffers_adaptor const& other); + + /// Copy Assignment + buffers_adaptor& operator=(buffers_adaptor const&); + + /// Returns the original mutable buffer sequence + value_type const& + value() const + { + return bs_; + } + + //-------------------------------------------------------------------------- + +#if BOOST_BEAST_DOXYGEN + /// The ConstBufferSequence used to represent the readable bytes. + using const_buffers_type = __implementation_defined__; + + /// The MutableBufferSequence used to represent the readable bytes. + using mutable_data_type = __implementation_defined__; + + /// The MutableBufferSequence used to represent the writable bytes. + using mutable_buffers_type = __implementation_defined__; + +#else + using const_buffers_type = readable_bytes<false>; + using mutable_data_type = readable_bytes<true>; + class mutable_buffers_type; +#endif + + /// Returns the number of readable bytes. + std::size_t + size() const noexcept + { + return in_size_; + } + + /// Return the maximum number of bytes, both readable and writable, that can ever be held. + std::size_t + max_size() const noexcept + { + return max_size_; + } + + /// Return the maximum number of bytes, both readable and writable, that can be held without requiring an allocation. + std::size_t + capacity() const noexcept + { + return max_size_; + } + + /// Returns a constant buffer sequence representing the readable bytes + const_buffers_type + data() const noexcept; + + /// Returns a constant buffer sequence representing the readable bytes + const_buffers_type + cdata() const noexcept + { + return data(); + } + + /// Returns a mutable buffer sequence representing the readable bytes. + mutable_data_type + data() noexcept; + + /** Returns a mutable buffer sequence representing writable bytes. + + Returns a mutable buffer sequence representing the writable + bytes containing exactly `n` bytes of storage. This function + does not allocate memory. Instead, the storage comes from + the underlying mutable buffer sequence. + + All buffer sequences previously obtained using @ref prepare are + invalidated. Buffer sequences previously obtained using @ref data + remain valid. + + @param n The desired number of bytes in the returned buffer + sequence. + + @throws std::length_error if `size() + n` exceeds `max_size()`. + + @esafe + + Strong guarantee. + */ + mutable_buffers_type + prepare(std::size_t n); + + /** Append writable bytes to the readable bytes. + + Appends n bytes from the start of the writable bytes to the + end of the readable bytes. The remainder of the writable bytes + are discarded. If n is greater than the number of writable + bytes, all writable bytes are appended to the readable bytes. + + All buffer sequences previously obtained using @ref prepare are + invalidated. Buffer sequences previously obtained using @ref data + remain valid. + + @param n The number of bytes to append. If this number + is greater than the number of writable bytes, all + writable bytes are appended. + + @esafe + + No-throw guarantee. + */ + void + commit(std::size_t n) noexcept; + + /** Remove bytes from beginning of the readable bytes. + + Removes n bytes from the beginning of the readable bytes. + + All buffers sequences previously obtained using + @ref data or @ref prepare are invalidated. + + @param n The number of bytes to remove. If this number + is greater than the number of readable bytes, all + readable bytes are removed. + + @esafe + + No-throw guarantee. + */ + void + consume(std::size_t n) noexcept; +}; + +} // beast +} // boost + +#include <boost/beast/core/impl/buffers_adaptor.hpp> + +#endif diff --git a/boost/beast/core/buffers_cat.hpp b/boost/beast/core/buffers_cat.hpp index 1d711b3b30..532ea32eef 100644 --- a/boost/beast/core/buffers_cat.hpp +++ b/boost/beast/core/buffers_cat.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -11,66 +11,59 @@ #define BOOST_BEAST_BUFFERS_CAT_HPP #include <boost/beast/core/detail/config.hpp> +#include <boost/beast/core/buffer_traits.hpp> +#include <boost/beast/core/detail/tuple.hpp> #include <boost/beast/core/detail/type_traits.hpp> -#include <tuple> namespace boost { namespace beast { /** A buffer sequence representing a concatenation of buffer sequences. - @see @ref buffers_cat + @see buffers_cat */ template<class... Buffers> class buffers_cat_view { - std::tuple<Buffers...> bn_; + detail::tuple<Buffers...> bn_; public: /** The type of buffer returned when dereferencing an iterator. - If every buffer sequence in the view is a @b MutableBufferSequence, - then `value_type` will be `boost::asio::mutable_buffer`. - Otherwise, `value_type` will be `boost::asio::const_buffer`. + If every buffer sequence in the view is a <em>MutableBufferSequence</em>, + then `value_type` will be `net::mutable_buffer`. + Otherwise, `value_type` will be `net::const_buffer`. */ #if BOOST_BEAST_DOXYGEN - using value_type = implementation_defined; + using value_type = __see_below__; #else - using value_type = typename - detail::common_buffers_type<Buffers...>::type; + using value_type = buffers_type<Buffers...>; #endif /// The type of iterator used by the concatenated sequence class const_iterator; - /// Constructor - buffers_cat_view(buffers_cat_view&&) = default; - - /// Assignment - buffers_cat_view& operator=(buffers_cat_view&&) = default; + /// Copy Constructor + buffers_cat_view(buffers_cat_view const&) = default; - /// Assignment + /// Copy Assignment buffers_cat_view& operator=(buffers_cat_view const&) = default; /** Constructor @param buffers The list of buffer sequences to concatenate. - Copies of the arguments will be made; however, the ownership - of memory is not transferred. + Copies of the arguments will be maintained for the lifetime + of the concatenated sequence; however, the ownership of the + memory buffers themselves is not transferred. */ explicit buffers_cat_view(Buffers const&... buffers); - //----- - - /// Required for @b BufferSequence - buffers_cat_view(buffers_cat_view const&) = default; - - /// Required for @b BufferSequence + /// Returns an iterator to the first buffer in the sequence const_iterator begin() const; - /// Required for @b BufferSequence + /// Returns an iterator to one past the last buffer in the sequence const_iterator end() const; }; @@ -88,11 +81,11 @@ public: @return A new buffer sequence that represents the concatenation of the input buffer sequences. This buffer sequence will be a - @b MutableBufferSequence if each of the passed buffer sequences is - also a @b MutableBufferSequence; otherwise the returned buffer - sequence will be a @b ConstBufferSequence. + <em>MutableBufferSequence</em> if each of the passed buffer sequences is + also a <em>MutableBufferSequence</em>; otherwise the returned buffer + sequence will be a <em>ConstBufferSequence</em>. - @see @ref buffers_cat_view + @see buffers_cat_view */ #if BOOST_BEAST_DOXYGEN template<class... BufferSequence> @@ -100,20 +93,19 @@ buffers_cat_view<BufferSequence...> buffers_cat(BufferSequence const&... buffers) #else template<class B1, class B2, class... Bn> -inline buffers_cat_view<B1, B2, Bn...> buffers_cat(B1 const& b1, B2 const& b2, Bn const&... bn) #endif { static_assert( - detail::is_all_const_buffer_sequence<B1, B2, Bn...>::value, - "BufferSequence requirements not met"); + is_const_buffer_sequence<B1, B2, Bn...>::value, + "BufferSequence type requirements not met"); return buffers_cat_view<B1, B2, Bn...>{b1, b2, bn...}; } } // beast } // boost -#include <boost/beast/core/impl/buffers_cat.ipp> +#include <boost/beast/core/impl/buffers_cat.hpp> #endif diff --git a/boost/beast/core/buffers_prefix.hpp b/boost/beast/core/buffers_prefix.hpp index 175610f171..5468de55e9 100644 --- a/boost/beast/core/buffers_prefix.hpp +++ b/boost/beast/core/buffers_prefix.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -11,16 +11,20 @@ #define BOOST_BEAST_BUFFERS_PREFIX_HPP #include <boost/beast/core/detail/config.hpp> -#include <boost/beast/core/type_traits.hpp> -#include <boost/asio/buffer.hpp> -#include <boost/optional/optional.hpp> +#include <boost/beast/core/buffer_traits.hpp> +#include <boost/optional/optional.hpp> // for in_place_init_t +#include <algorithm> #include <cstdint> #include <type_traits> +#if BOOST_WORKAROUND(BOOST_MSVC, < 1910) +#include <boost/type_traits.hpp> +#endif + namespace boost { namespace beast { -/** A buffer sequence adapter that shortens the sequence size. +/** A buffer sequence adaptor that shortens the sequence size. The class adapts a buffer sequence to efficiently represent a shorter subset of the original list of buffers starting @@ -31,70 +35,70 @@ namespace beast { template<class BufferSequence> class buffers_prefix_view { - using buffers_type = typename - std::decay<BufferSequence>::type; - - using iter_type = typename - detail::buffer_sequence_iterator<buffers_type>::type; + using iter_type = + buffers_iterator_type<BufferSequence>; BufferSequence bs_; - std::size_t size_; - std::size_t remain_; - iter_type end_; - - template<class Deduced> - buffers_prefix_view( - Deduced&& other, std::size_t dist) - : bs_(std::forward<Deduced>(other).bs_) - , size_(other.size_) - , remain_(other.remain_) - , end_(std::next(bs_.begin(), dist)) - { - } + std::size_t size_ = 0; + std::size_t remain_ = 0; + iter_type end_{}; void setup(std::size_t size); + buffers_prefix_view( + buffers_prefix_view const& other, + std::size_t dist); + public: - /// The type for each element in the list of buffers. + /** The type for each element in the list of buffers. + + If the type `BufferSequence` meets the requirements of + <em>MutableBufferSequence</em>, then `value_type` is + `net::mutable_buffer`. Otherwise, `value_type` is + `net::const_buffer`. + + @see buffers_type + */ +#if BOOST_BEAST_DOXYGEN + using value_type = __see_below__; +#elif BOOST_WORKAROUND(BOOST_MSVC, < 1910) using value_type = typename std::conditional< - std::is_convertible<typename + boost::is_convertible<typename std::iterator_traits<iter_type>::value_type, - boost::asio::mutable_buffer>::value, - boost::asio::mutable_buffer, - boost::asio::const_buffer>::type; + net::mutable_buffer>::value, + net::mutable_buffer, + net::const_buffer>::type; +#else + using value_type = buffers_type<BufferSequence>; +#endif #if BOOST_BEAST_DOXYGEN /// A bidirectional iterator type that may be used to read elements. - using const_iterator = implementation_defined; + using const_iterator = __implementation_defined__; #else class const_iterator; #endif - /// Move constructor. - buffers_prefix_view(buffers_prefix_view&&); - - /// Copy constructor. + /// Copy Constructor buffers_prefix_view(buffers_prefix_view const&); - /// Move assignment. - buffers_prefix_view& operator=(buffers_prefix_view&&); - - /// Copy assignment. + /// Copy Assignment buffers_prefix_view& operator=(buffers_prefix_view const&); /** Construct a buffer sequence prefix. @param size The maximum number of bytes in the prefix. - If this is larger than the size of passed, buffers, + If this is larger than the size of passed buffers, the resulting sequence will represent the entire input sequence. @param buffers The buffer sequence to adapt. A copy of the sequence will be made, but ownership of the underlying - memory is not transferred. + memory is not transferred. The copy is maintained for + the lifetime of the view. */ buffers_prefix_view( std::size_t size, @@ -103,11 +107,11 @@ public: /** Construct a buffer sequence prefix in-place. @param size The maximum number of bytes in the prefix. - If this is larger than the size of passed, buffers, + If this is larger than the size of passed buffers, the resulting sequence will represent the entire input sequence. - @param args Arguments forwarded to the contained buffers constructor. + @param args Arguments forwarded to the contained buffer's constructor. */ template<class... Args> buffers_prefix_view( @@ -115,95 +119,56 @@ public: boost::in_place_init_t, Args&&... args); - /// Get a bidirectional iterator to the first element. + /// Returns an iterator to the first buffer in the sequence const_iterator begin() const; - /// Get a bidirectional iterator to one past the last element. + /// Returns an iterator to one past the last buffer in the sequence const_iterator end() const; -}; - -/** Returns a prefix of a constant buffer. - - The returned buffer points to the same memory as the - passed buffer, but with a size that is equal to or less - than the size of the original buffer. - @param size The size of the returned buffer. - - @param buffer The buffer to shorten. The underlying - memory is not modified. - - @return A new buffer that points to the first `size` - bytes of the original buffer. -*/ -inline -boost::asio::const_buffer -buffers_prefix(std::size_t size, - boost::asio::const_buffer buffer) -{ - return {buffer.data(), - (std::min)(size, buffer.size())}; -} - -/** Returns a prefix of a mutable buffer. - - The returned buffer points to the same memory as the - passed buffer, but with a size that is equal to or less - than the size of the original buffer. - - @param size The size of the returned buffer. +#if ! BOOST_BEAST_DOXYGEN + std::size_t + buffer_bytes_impl() const noexcept + { + return size_; + } +#endif +}; - @param buffer The buffer to shorten. The underlying - memory is not modified. +//------------------------------------------------------------------------------ - @return A new buffer that points to the first `size` bytes - of the original buffer. -*/ -inline -boost::asio::mutable_buffer -buffers_prefix(std::size_t size, - boost::asio::mutable_buffer buffer) -{ - return {buffer.data(), - (std::min)(size, buffer.size())}; -} +/** Returns a prefix of a constant or mutable buffer sequence. -/** Returns a prefix of a buffer sequence. + The returned buffer sequence points to the same memory as the + passed buffer sequence, but with a size that is equal to or + smaller. No memory allocations are performed; the resulting + sequence is calculated as a lazy range. - This function returns a new buffer sequence which when iterated, - presents a shorter subset of the original list of buffers starting - with the first byte of the original sequence. + @param size The maximum size of the returned buffer sequence + in bytes. If this is greater than or equal to the size of + the passed buffer sequence, the result will have the same + size as the original buffer sequence. - @param size The maximum number of bytes in the wrapped - sequence. If this is larger than the size of passed, - buffers, the resulting sequence will represent the - entire input sequence. + @param buffers An object whose type meets the requirements + of <em>BufferSequence</em>. The returned value will + maintain a copy of the passed buffers for its lifetime; + however, ownership of the underlying memory is not + transferred. - @param buffers An instance of @b ConstBufferSequence or - @b MutableBufferSequence to adapt. A copy of the sequence - will be made, but ownership of the underlying memory is - not transferred. + @return A constant buffer sequence that represents the prefix + of the original buffer sequence. If the original buffer sequence + also meets the requirements of <em>MutableBufferSequence</em>, + then the returned value will also be a mutable buffer sequence. */ template<class BufferSequence> -#if BOOST_BEAST_DOXYGEN buffers_prefix_view<BufferSequence> -#else -inline -typename std::enable_if< - ! std::is_same<BufferSequence, - boost::asio::const_buffer>::value && - ! std::is_same<BufferSequence, - boost::asio::mutable_buffer>::value, - buffers_prefix_view<BufferSequence>>::type -#endif -buffers_prefix(std::size_t size, BufferSequence const& buffers) +buffers_prefix( + std::size_t size, BufferSequence const& buffers) { static_assert( - boost::asio::is_const_buffer_sequence<BufferSequence>::value || - boost::asio::is_mutable_buffer_sequence<BufferSequence>::value, - "BufferSequence requirements not met"); + net::is_const_buffer_sequence<BufferSequence>::value, + "BufferSequence type requirements not met"); return buffers_prefix_view<BufferSequence>(size, buffers); } @@ -218,15 +183,12 @@ buffers_prefix(std::size_t size, BufferSequence const& buffers) Otherwise, the returned buffer sequence will be constant. */ template<class BufferSequence> -typename std::conditional< - boost::asio::is_mutable_buffer_sequence<BufferSequence>::value, - boost::asio::mutable_buffer, - boost::asio::const_buffer>::type +buffers_type<BufferSequence> buffers_front(BufferSequence const& buffers) { auto const first = - boost::asio::buffer_sequence_begin(buffers); - if(first == boost::asio::buffer_sequence_end(buffers)) + net::buffer_sequence_begin(buffers); + if(first == net::buffer_sequence_end(buffers)) return {}; return *first; } @@ -234,6 +196,6 @@ buffers_front(BufferSequence const& buffers) } // beast } // boost -#include <boost/beast/core/impl/buffers_prefix.ipp> +#include <boost/beast/core/impl/buffers_prefix.hpp> #endif diff --git a/boost/beast/core/buffers_range.hpp b/boost/beast/core/buffers_range.hpp new file mode 100644 index 0000000000..c7659e2fad --- /dev/null +++ b/boost/beast/core/buffers_range.hpp @@ -0,0 +1,128 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_BUFFERS_RANGE_HPP +#define BOOST_BEAST_BUFFERS_RANGE_HPP + +#include <boost/beast/core/detail/config.hpp> +#include <boost/beast/core/buffer_traits.hpp> +#include <boost/beast/core/detail/buffers_range_adaptor.hpp> + +namespace boost { +namespace beast { + +/** Returns an iterable range representing a buffer sequence. + + This function returns an iterable range representing the + passed buffer sequence. The values obtained when iterating + the range will be `net::const_buffer`, unless the underlying + buffer sequence is a <em>MutableBufferSequence</em>, in which case + the value obtained when iterating will be a `net::mutable_buffer`. + + @par Example + + The following function returns the total number of bytes in + the specified buffer sequence. A copy of the buffer sequence + is maintained for the lifetime of the range object: + + @code + template <class BufferSequence> + std::size_t buffer_sequence_size (BufferSequence const& buffers) + { + std::size_t size = 0; + for (auto const buffer : buffers_range (buffers)) + size += buffer.size(); + return size; + } + @endcode + + @param buffers The buffer sequence to adapt into a range. The + range object returned from this function will contain a copy + of the passed buffer sequence. + + @return An object of unspecified type which meets the requirements + of <em>ConstBufferSequence</em>. If `buffers` is a mutable buffer + sequence, the returned object will also meet the requirements of + <em>MutableBufferSequence</em>. + + @see buffers_range_ref +*/ +template<class BufferSequence> +#if BOOST_BEAST_DOXYGEN +__implementation_defined__ +#else +detail::buffers_range_adaptor<BufferSequence> +#endif +buffers_range(BufferSequence const& buffers) +{ + static_assert( + is_const_buffer_sequence<BufferSequence>::value, + "BufferSequence type requirements not met"); + return detail::buffers_range_adaptor< + BufferSequence>(buffers); +} + +/** Returns an iterable range representing a buffer sequence. + + This function returns an iterable range representing the + passed buffer sequence. The values obtained when iterating + the range will be `net::const_buffer`, unless the underlying + buffer sequence is a <em>MutableBufferSequence</em>, in which case + the value obtained when iterating will be a `net::mutable_buffer`. + + @par Example + + The following function returns the total number of bytes in + the specified buffer sequence. A reference to the original + buffers is maintained for the lifetime of the range object: + + @code + template <class BufferSequence> + std::size_t buffer_sequence_size_ref (BufferSequence const& buffers) + { + std::size_t size = 0; + for (auto const buffer : buffers_range_ref (buffers)) + size += buffer.size(); + return size; + } + @endcode + + @param buffers The buffer sequence to adapt into a range. The + range returned from this function will maintain a reference to + these buffers. The application is responsible for ensuring that + the lifetime of the referenced buffers extends until the range + object is destroyed. + + @return An object of unspecified type which meets the requirements + of <em>ConstBufferSequence</em>. If `buffers` is a mutable buffer + sequence, the returned object will also meet the requirements of + <em>MutableBufferSequence</em>. + + @see buffers_range +*/ +template<class BufferSequence> +#if BOOST_BEAST_DOXYGEN +__implementation_defined__ +#else +detail::buffers_range_adaptor<BufferSequence const&> +#endif +buffers_range_ref(BufferSequence const& buffers) +{ + static_assert( + is_const_buffer_sequence<BufferSequence>::value, + "BufferSequence type requirements not met"); + return detail::buffers_range_adaptor< + BufferSequence const&>(buffers); +} +/** @} */ + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/buffers_suffix.hpp b/boost/beast/core/buffers_suffix.hpp index 3959694577..adf8fd5977 100644 --- a/boost/beast/core/buffers_suffix.hpp +++ b/boost/beast/core/buffers_suffix.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -11,8 +11,7 @@ #define BOOST_BEAST_BUFFERS_SUFFIX_HPP #include <boost/beast/core/detail/config.hpp> -#include <boost/beast/core/detail/type_traits.hpp> -#include <boost/asio/buffer.hpp> +#include <boost/beast/core/buffer_traits.hpp> #include <boost/optional.hpp> #include <cstdint> #include <iterator> @@ -21,12 +20,12 @@ namespace boost { namespace beast { -/** Adapter to trim the front of a `BufferSequence`. +/** Adaptor to progressively trim the front of a <em>BufferSequence</em>. - This adapter wraps a buffer sequence to create a new sequence + This adaptor 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 + instead the adaptor efficiently iterates through a subset of the buffers wrapped. The wrapped buffer is not modified, a copy is made instead. @@ -45,7 +44,7 @@ namespace beast { void send(SyncWriteStream& stream, ConstBufferSequence const& buffers) { buffers_suffix<ConstBufferSequence> bs{buffers}; - while(boost::asio::buffer_size(bs) > 0) + while(buffer_bytes(bs) > 0) bs.consume(stream.write_some(bs)); } @endcode @@ -53,21 +52,18 @@ namespace beast { template<class BufferSequence> class buffers_suffix { - using buffers_type = - typename std::decay<BufferSequence>::type; - - using iter_type = typename - detail::buffer_sequence_iterator<buffers_type>::type; + using iter_type = + buffers_iterator_type<BufferSequence>; BufferSequence bs_; - iter_type begin_; + iter_type begin_{}; std::size_t skip_ = 0; template<class Deduced> buffers_suffix(Deduced&& other, std::size_t dist) : bs_(std::forward<Deduced>(other).bs_) , begin_(std::next( - boost::asio::buffer_sequence_begin(bs_), + net::buffer_sequence_begin(bs_), dist)) , skip_(other.skip_) { @@ -76,25 +72,20 @@ class buffers_suffix public: /** The type for each element in the list of buffers. - If the buffers in the underlying sequence are convertible to - `boost::asio::mutable_buffer`, then this type will be - `boost::asio::mutable_buffer`, else this type will be - `boost::asio::const_buffer`. + If <em>BufferSequence</em> meets the requirements of + <em>MutableBufferSequence</em>, then this type will be + `net::mutable_buffer`, otherwise this type will be + `net::const_buffer`. */ #if BOOST_BEAST_DOXYGEN - using value_type = implementation_defined; + using value_type = __see_below__; #else - using value_type = typename std::conditional< - std::is_convertible<typename - std::iterator_traits<iter_type>::value_type, - boost::asio::mutable_buffer>::value, - boost::asio::mutable_buffer, - boost::asio::const_buffer>::type; + using value_type = buffers_type<BufferSequence>; #endif #if BOOST_BEAST_DOXYGEN /// A bidirectional iterator type that may be used to read elements. - using const_iterator = implementation_defined; + using const_iterator = __implementation_defined__; #else class const_iterator; @@ -104,10 +95,7 @@ public: /// Constructor buffers_suffix(); - /// Constructor - buffers_suffix(buffers_suffix&&); - - /// Constructor + /// Copy Constructor buffers_suffix(buffers_suffix const&); /** Constructor @@ -126,12 +114,10 @@ public: @param args Arguments forwarded to the buffers constructor. */ template<class... Args> + explicit buffers_suffix(boost::in_place_init_t, Args&&... args); - /// Assignment - buffers_suffix& operator=(buffers_suffix&&); - - /// Assignment + /// Copy Assignment buffers_suffix& operator=(buffers_suffix const&); /// Get a bidirectional iterator to the first element. @@ -155,6 +141,6 @@ public: } // beast } // boost -#include <boost/beast/core/impl/buffers_suffix.ipp> +#include <boost/beast/core/impl/buffers_suffix.hpp> #endif diff --git a/boost/beast/core/buffers_to_string.hpp b/boost/beast/core/buffers_to_string.hpp index 1c70768ebc..86efa3d3cc 100644 --- a/boost/beast/core/buffers_to_string.hpp +++ b/boost/beast/core/buffers_to_string.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -11,7 +11,8 @@ #define BOOST_BEAST_BUFFERS_TO_STRING_HPP #include <boost/beast/core/detail/config.hpp> -#include <boost/beast/core/detail/type_traits.hpp> +#include <boost/beast/core/buffer_traits.hpp> +#include <boost/beast/core/buffers_range.hpp> #include <boost/asio/buffer.hpp> #include <string> @@ -41,16 +42,17 @@ namespace beast { @endcode */ template<class ConstBufferSequence> -inline std::string buffers_to_string(ConstBufferSequence const& buffers) { + static_assert( + net::is_const_buffer_sequence<ConstBufferSequence>::value, + "ConstBufferSequence type requirements not met"); std::string result; - result.reserve(boost::asio::buffer_size(buffers)); - for(boost::asio::const_buffer buffer : - detail::buffers_range(buffers)) - result.append(static_cast< - char const*>(buffer.data()), buffer.size()); + result.reserve(buffer_bytes(buffers)); + for(auto const buffer : buffers_range_ref(buffers)) + result.append(static_cast<char const*>( + buffer.data()), buffer.size()); return result; } diff --git a/boost/beast/core/detail/allocator.hpp b/boost/beast/core/detail/allocator.hpp index e893f5c274..d6b24ccb09 100644 --- a/boost/beast/core/detail/allocator.hpp +++ b/boost/beast/core/detail/allocator.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) diff --git a/boost/beast/core/detail/async_base.hpp b/boost/beast/core/detail/async_base.hpp new file mode 100644 index 0000000000..0224daf703 --- /dev/null +++ b/boost/beast/core/detail/async_base.hpp @@ -0,0 +1,46 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_DETAIL_ASYNC_BASE_HPP +#define BOOST_BEAST_CORE_DETAIL_ASYNC_BASE_HPP + +#include <boost/core/exchange.hpp> + +namespace boost { +namespace beast { +namespace detail { + +struct stable_base +{ + static + void + destroy_list(stable_base*& list) + { + while(list) + { + auto next = list->next_; + list->destroy(); + list = next; + } + } + + stable_base* next_ = nullptr; + +protected: + stable_base() = default; + virtual ~stable_base() = default; + + virtual void destroy() = 0; +}; + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/base64.hpp b/boost/beast/core/detail/base64.hpp index 6cb3f67a77..7f91c04777 100644 --- a/boost/beast/core/detail/base64.hpp +++ b/boost/beast/core/detail/base64.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -7,39 +7,10 @@ // Official repository: https://github.com/boostorg/beast // -/* - Portions from http://www.adp-gmbh.ch/cpp/common/base64.html - Copyright notice: - - base64.cpp and base64.h - - Copyright (C) 2004-2008 Rene Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - Rene Nyffenegger rene.nyffenegger@adp-gmbh.ch - -*/ - #ifndef BOOST_BEAST_DETAIL_BASE64_HPP #define BOOST_BEAST_DETAIL_BASE64_HPP +#include <boost/beast/core/string.hpp> #include <cctype> #include <string> #include <utility> @@ -50,44 +21,16 @@ namespace detail { namespace base64 { -inline +BOOST_BEAST_DECL char const* -get_alphabet() -{ - static char constexpr tab[] = { - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - }; - return &tab[0]; -} +get_alphabet(); -inline +BOOST_BEAST_DECL signed char const* -get_inverse() -{ - static signed char constexpr tab[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47 - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48-63 - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79 - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95 - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111 - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255 - }; - return &tab[0]; -} - +get_inverse(); /// Returns max chars needed to encode a base64 string -inline +BOOST_BEAST_DECL std::size_t constexpr encoded_size(std::size_t n) { @@ -100,7 +43,6 @@ std::size_t constexpr decoded_size(std::size_t n) { return n / 4 * 3; // requires n&3==0, smaller - //return 3 * n / 4; } /** Encode a series of octets as a padded, base64 string. @@ -115,45 +57,9 @@ decoded_size(std::size_t n) @return The number of characters written to `out`. This will exclude any null termination. */ -template<class = void> +BOOST_BEAST_DECL std::size_t -encode(void* dest, void const* src, std::size_t len) -{ - char* out = static_cast<char*>(dest); - char const* in = static_cast<char const*>(src); - auto const tab = base64::get_alphabet(); - - for(auto n = len / 3; n--;) - { - *out++ = tab[ (in[0] & 0xfc) >> 2]; - *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)]; - *out++ = tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)]; - *out++ = tab[ in[2] & 0x3f]; - in += 3; - } - - switch(len % 3) - { - case 2: - *out++ = tab[ (in[0] & 0xfc) >> 2]; - *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)]; - *out++ = tab[ (in[1] & 0x0f) << 2]; - *out++ = '='; - break; - - case 1: - *out++ = tab[ (in[0] & 0xfc) >> 2]; - *out++ = tab[((in[0] & 0x03) << 4)]; - *out++ = '='; - *out++ = '='; - break; - - case 0: - break; - } - - return out - static_cast<char*>(dest); -} +encode(void* dest, void const* src, std::size_t len); /** Decode a padded base64 string into a series of octets. @@ -166,75 +72,23 @@ encode(void* dest, void const* src, std::size_t len) the number of characters read from the input string, expressed as a pair. */ -template<class = void> +BOOST_BEAST_DECL std::pair<std::size_t, std::size_t> -decode(void* dest, char const* src, std::size_t len) -{ - char* out = static_cast<char*>(dest); - auto in = reinterpret_cast<unsigned char const*>(src); - unsigned char c3[3], c4[4]; - int i = 0; - int j = 0; - - auto const inverse = base64::get_inverse(); - - while(len-- && *in != '=') - { - auto const v = inverse[*in]; - if(v == -1) - break; - ++in; - c4[i] = v; - if(++i == 4) - { - c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4); - c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); - c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; - - for(i = 0; i < 3; i++) - *out++ = c3[i]; - i = 0; - } - } - - if(i) - { - c3[0] = ( c4[0] << 2) + ((c4[1] & 0x30) >> 4); - c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); - c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; - - for(j = 0; j < i - 1; j++) - *out++ = c3[j]; - } - - return {out - static_cast<char*>(dest), - in - reinterpret_cast<unsigned char const*>(src)}; -} +decode(void* dest, char const* src, std::size_t len); } // base64 -template<class = void> +BOOST_BEAST_DECL std::string -base64_encode (std::uint8_t const* data, - std::size_t len) -{ - std::string dest; - dest.resize(base64::encoded_size(len)); - dest.resize(base64::encode(&dest[0], data, len)); - return dest; -} +base64_encode(std::uint8_t const* data, std::size_t len); -inline +BOOST_BEAST_DECL std::string -base64_encode(std::string const& s) -{ - return base64_encode (reinterpret_cast < - std::uint8_t const*> (s.data()), s.size()); -} +base64_encode(string_view s); template<class = void> std::string -base64_decode(std::string const& data) +base64_decode(string_view data) { std::string dest; dest.resize(base64::decoded_size(data.size())); @@ -248,4 +102,8 @@ base64_decode(std::string const& data) } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY +#include <boost/beast/core/detail/base64.ipp> +#endif + #endif diff --git a/boost/beast/core/detail/base64.ipp b/boost/beast/core/detail/base64.ipp new file mode 100644 index 0000000000..7e11d188b6 --- /dev/null +++ b/boost/beast/core/detail/base64.ipp @@ -0,0 +1,220 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +/* + Portions from http://www.adp-gmbh.ch/cpp/common/base64.html + Copyright notice: + + base64.cpp and base64.h + + Copyright (C) 2004-2008 Rene Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + Rene Nyffenegger rene.nyffenegger@adp-gmbh.ch +*/ + +#ifndef BOOST_BEAST_DETAIL_BASE64_IPP +#define BOOST_BEAST_DETAIL_BASE64_IPP + +#include <boost/beast/core/detail/base64.hpp> +#include <boost/beast/core/string.hpp> +#include <cctype> +#include <string> +#include <utility> + +namespace boost { +namespace beast { +namespace detail { + +namespace base64 { + +char const* +get_alphabet() +{ + static char constexpr tab[] = { + "ABCDEFGHIJKLMNOP" + "QRSTUVWXYZabcdef" + "ghijklmnopqrstuv" + "wxyz0123456789+/" + }; + return &tab[0]; +} + +signed char const* +get_inverse() +{ + static signed char constexpr tab[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47 + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48-63 + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79 + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95 + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111 + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255 + }; + return &tab[0]; +} + +/** Encode a series of octets as a padded, base64 string. + + The resulting string will not be null terminated. + + @par Requires + + The memory pointed to by `out` points to valid memory + of at least `encoded_size(len)` bytes. + + @return The number of characters written to `out`. This + will exclude any null termination. +*/ +std::size_t +encode(void* dest, void const* src, std::size_t len) +{ + char* out = static_cast<char*>(dest); + char const* in = static_cast<char const*>(src); + auto const tab = base64::get_alphabet(); + + for(auto n = len / 3; n--;) + { + *out++ = tab[ (in[0] & 0xfc) >> 2]; + *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)]; + *out++ = tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)]; + *out++ = tab[ in[2] & 0x3f]; + in += 3; + } + + switch(len % 3) + { + case 2: + *out++ = tab[ (in[0] & 0xfc) >> 2]; + *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)]; + *out++ = tab[ (in[1] & 0x0f) << 2]; + *out++ = '='; + break; + + case 1: + *out++ = tab[ (in[0] & 0xfc) >> 2]; + *out++ = tab[((in[0] & 0x03) << 4)]; + *out++ = '='; + *out++ = '='; + break; + + case 0: + break; + } + + return out - static_cast<char*>(dest); +} + +/** Decode a padded base64 string into a series of octets. + + @par Requires + + The memory pointed to by `out` points to valid memory + of at least `decoded_size(len)` bytes. + + @return The number of octets written to `out`, and + the number of characters read from the input string, + expressed as a pair. +*/ +std::pair<std::size_t, std::size_t> +decode(void* dest, char const* src, std::size_t len) +{ + char* out = static_cast<char*>(dest); + auto in = reinterpret_cast<unsigned char const*>(src); + unsigned char c3[3], c4[4]; + int i = 0; + int j = 0; + + auto const inverse = base64::get_inverse(); + + while(len-- && *in != '=') + { + auto const v = inverse[*in]; + if(v == -1) + break; + ++in; + c4[i] = v; + if(++i == 4) + { + c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4); + c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); + c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; + + for(i = 0; i < 3; i++) + *out++ = c3[i]; + i = 0; + } + } + + if(i) + { + c3[0] = ( c4[0] << 2) + ((c4[1] & 0x30) >> 4); + c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); + c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; + + for(j = 0; j < i - 1; j++) + *out++ = c3[j]; + } + + return {out - static_cast<char*>(dest), + in - reinterpret_cast<unsigned char const*>(src)}; +} + +} // base64 + +std::string +base64_encode( + std::uint8_t const* data, + std::size_t len) +{ + std::string dest; + dest.resize(base64::encoded_size(len)); + dest.resize(base64::encode(&dest[0], data, len)); + return dest; +} + +std::string +base64_encode(string_view s) +{ + return base64_encode (reinterpret_cast < + std::uint8_t const*> (s.data()), s.size()); +} + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/bind_continuation.hpp b/boost/beast/core/detail/bind_continuation.hpp new file mode 100644 index 0000000000..2c4b6e5265 --- /dev/null +++ b/boost/beast/core/detail/bind_continuation.hpp @@ -0,0 +1,110 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_BIND_CONTINUATION_HPP +#define BOOST_BEAST_DETAIL_BIND_CONTINUATION_HPP + +#include <boost/beast/core/detail/config.hpp> +#include <boost/beast/core/detail/remap_post_to_defer.hpp> +#include <boost/asio/bind_executor.hpp> +#include <boost/core/empty_value.hpp> +#include <type_traits> +#include <utility> + +namespace boost { +namespace beast { +namespace detail { + +#if 0 +/** Mark a completion handler as a continuation. + + This function wraps a completion handler to associate it with an + executor whose `post` operation is remapped to the `defer` operation. + It is used by composed asynchronous operation implementations to + indicate that a completion handler submitted to an initiating + function represents a continuation of the current asynchronous + flow of control. + + @param handler The handler to wrap. + The implementation takes ownership of the handler by performing a decay-copy. + + @see + + @li <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4242.html">[N4242] Executors and Asynchronous Operations, Revision 1</a> +*/ +template<class CompletionHandler> +#if BOOST_BEAST_DOXYGEN +__implementation_defined__ +#else +net::executor_binder< + typename std::decay<CompletionHandler>::type, + detail::remap_post_to_defer< + net::associated_executor_t<CompletionHandler>>> +#endif +bind_continuation(CompletionHandler&& handler) +{ + return net::bind_executor( + detail::remap_post_to_defer< + net::associated_executor_t<CompletionHandler>>( + net::get_associated_executor(handler)), + std::forward<CompletionHandler>(handler)); +} + +/** Mark a completion handler as a continuation. + + This function wraps a completion handler to associate it with an + executor whose `post` operation is remapped to the `defer` operation. + It is used by composed asynchronous operation implementations to + indicate that a completion handler submitted to an initiating + function represents a continuation of the current asynchronous + flow of control. + + @param ex The executor to use + + @param handler The handler to wrap + The implementation takes ownership of the handler by performing a decay-copy. + + @see + + @li <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4242.html">[N4242] Executors and Asynchronous Operations, Revision 1</a> +*/ +template<class Executor, class CompletionHandler> +#if BOOST_BEAST_DOXYGEN +__implementation_defined__ +#else +net::executor_binder<typename + std::decay<CompletionHandler>::type, + detail::remap_post_to_defer<Executor>> +#endif +bind_continuation( + Executor const& ex, CompletionHandler&& handler) +{ + return net::bind_executor( + detail::remap_post_to_defer<Executor>(ex), + std::forward<CompletionHandler>(handler)); +} +#else +// VFALCO I turned these off at the last minute because they cause +// the completion handler to be moved before the initiating +// function is invoked rather than after, which is a foot-gun. +// +// REMINDER: Uncomment the tests when this is put back +template<class F> +F&& +bind_continuation(F&& f) +{ + return std::forward<F>(f); +} +#endif + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/bind_default_executor.hpp b/boost/beast/core/detail/bind_default_executor.hpp new file mode 100644 index 0000000000..6fa006e160 --- /dev/null +++ b/boost/beast/core/detail/bind_default_executor.hpp @@ -0,0 +1,123 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_DETAIL_BIND_DEFAULT_EXECUTOR_HPP +#define BOOST_BEAST_CORE_DETAIL_BIND_DEFAULT_EXECUTOR_HPP + +#include <boost/asio/associated_allocator.hpp> +#include <boost/asio/associated_executor.hpp> +#include <boost/asio/dispatch.hpp> +#include <boost/asio/executor.hpp> +#include <boost/asio/handler_alloc_hook.hpp> +#include <boost/asio/handler_continuation_hook.hpp> +#include <boost/asio/handler_invoke_hook.hpp> +#include <boost/core/empty_value.hpp> +#include <utility> + +namespace boost { +namespace beast { +namespace detail { + +template<class Handler, class Executor> +class bind_default_executor_wrapper + : private boost::empty_value<Executor> +{ + Handler h_; + +public: + template<class Handler_> + bind_default_executor_wrapper( + Handler_&& h, + Executor const& ex) + : boost::empty_value<Executor>( + boost::empty_init_t{}, ex) + , h_(std::forward<Handler_>(h)) + { + } + + template<class... Args> + void + operator()(Args&&... args) + { + h_(std::forward<Args>(args)...); + } + + using allocator_type = + net::associated_allocator_t<Handler>; + + allocator_type + get_allocator() const noexcept + { + return net::get_associated_allocator(h_); + } + + using executor_type = + net::associated_executor_t<Handler, Executor>; + + executor_type + get_executor() const noexcept + { + return net::get_associated_executor( + h_, this->get()); + } + + template<class Function> + void + asio_handler_invoke(Function&& f, + bind_default_executor_wrapper* p) + { + net::dispatch(p->get_executor(), std::move(f)); + } + + friend + void* asio_handler_allocate( + std::size_t size, bind_default_executor_wrapper* p) + { + using net::asio_handler_allocate; + return asio_handler_allocate( + size, std::addressof(p->h_)); + } + + friend + void asio_handler_deallocate( + void* mem, std::size_t size, + bind_default_executor_wrapper* p) + { + using net::asio_handler_deallocate; + asio_handler_deallocate(mem, size, + std::addressof(p->h_)); + } + + friend + bool asio_handler_is_continuation( + bind_default_executor_wrapper* p) + { + using net::asio_handler_is_continuation; + return asio_handler_is_continuation( + std::addressof(p->h_)); + } +}; + +template<class Executor, class Handler> +auto +bind_default_executor(Executor const& ex, Handler&& h) -> + bind_default_executor_wrapper< + typename std::decay<Handler>::type, + Executor> +{ + return bind_default_executor_wrapper< + typename std::decay<Handler>::type, + Executor>(std::forward<Handler>(h), ex); +} + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/bind_handler.hpp b/boost/beast/core/detail/bind_handler.hpp index 8bacc921f7..db481757f9 100644 --- a/boost/beast/core/detail/bind_handler.hpp +++ b/boost/beast/core/detail/bind_handler.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -10,39 +10,44 @@ #ifndef BOOST_BEAST_DETAIL_BIND_HANDLER_HPP #define BOOST_BEAST_DETAIL_BIND_HANDLER_HPP -#include <boost/beast/core/detail/integer_sequence.hpp> +#include <boost/beast/core/error.hpp> +#include <boost/beast/core/detail/tuple.hpp> #include <boost/asio/associated_allocator.hpp> #include <boost/asio/associated_executor.hpp> +#include <boost/asio/handler_alloc_hook.hpp> #include <boost/asio/handler_continuation_hook.hpp> #include <boost/asio/handler_invoke_hook.hpp> #include <boost/core/ignore_unused.hpp> +#include <boost/mp11/integer_sequence.hpp> #include <boost/is_placeholder.hpp> #include <functional> +#include <type_traits> #include <utility> namespace boost { namespace beast { namespace detail { -/* Nullary handler that calls Handler with bound arguments. +//------------------------------------------------------------------------------ +// +// bind_handler +// +//------------------------------------------------------------------------------ - The bound handler provides the same io_context execution - guarantees as the original handler. -*/ template<class Handler, class... Args> -class bound_handler +class bind_wrapper { - // Can't friend partial specializations, - // so we just friend the whole thing. - template<class T, class Executor> - friend struct boost::asio::associated_executor; - - using args_type = std::tuple< - typename std::decay<Args>::type...>; + using args_type = detail::tuple<Args...>; Handler h_; args_type args_; + template<class T, class Executor> + friend struct net::associated_executor; + + template<class T, class Allocator> + friend struct net::associated_allocator; + template<class Arg, class Vals> static typename std::enable_if< @@ -62,13 +67,12 @@ class bound_handler typename std::enable_if< std::is_placeholder<typename std::decay<Arg>::type>::value != 0, - typename std::tuple_element< - std::is_placeholder< - typename std::decay<Arg>::type>::value - 1, - Vals>>::type::type&& + tuple_element<std::is_placeholder< + typename std::decay<Arg>::type>::value - 1, + Vals>>::type&& extract(Arg&&, Vals&& vals) { - return std::get<std::is_placeholder< + return detail::get<std::is_placeholder< typename std::decay<Arg>::type>::value - 1>( std::forward<Vals>(vals)); } @@ -78,30 +82,27 @@ class bound_handler typename std::enable_if< boost::is_placeholder<typename std::decay<Arg>::type>::value != 0, - typename std::tuple_element< - boost::is_placeholder< - typename std::decay<Arg>::type>::value - 1, - Vals>>::type::type&& + tuple_element<boost::is_placeholder< + typename std::decay<Arg>::type>::value - 1, + Vals>>::type&& extract(Arg&&, Vals&& vals) { - return std::get<boost::is_placeholder< + return detail::get<boost::is_placeholder< typename std::decay<Arg>::type>::value - 1>( std::forward<Vals>(vals)); } - template< - class ArgsTuple, - std::size_t... S> + template<class ArgsTuple, std::size_t... S> static void invoke( Handler& h, ArgsTuple& args, - std::tuple<>&&, - index_sequence<S...>) + tuple<>&&, + mp11::index_sequence<S...>) { boost::ignore_unused(args); - h(std::get<S>(std::move(args))...); + h(detail::get<S>(std::move(args))...); } template< @@ -114,104 +115,296 @@ class bound_handler Handler& h, ArgsTuple& args, ValsTuple&& vals, - index_sequence<S...>) + mp11::index_sequence<S...>) { boost::ignore_unused(args); boost::ignore_unused(vals); - h(extract(std::get<S>(std::move(args)), + h(extract(detail::get<S>(std::move(args)), std::forward<ValsTuple>(vals))...); } public: - using result_type = void; + using result_type = void; // asio needs this - using allocator_type = - boost::asio::associated_allocator_t<Handler>; + bind_wrapper(bind_wrapper&&) = default; + bind_wrapper(bind_wrapper const&) = default; - bound_handler(bound_handler&&) = default; - bound_handler(bound_handler const&) = delete; - - template<class DeducedHandler> + template< + class DeducedHandler, + class... Args_> explicit - bound_handler( - DeducedHandler&& handler, Args&&... args) + bind_wrapper( + DeducedHandler&& handler, + Args_&&... args) : h_(std::forward<DeducedHandler>(handler)) - , args_(std::forward<Args>(args)...) + , args_(std::forward<Args_>(args)...) + { + } + + template<class... Values> + void + operator()(Values&&... values) { + invoke(h_, args_, + tuple<Values&&...>( + std::forward<Values>(values)...), + mp11::index_sequence_for<Args...>()); } - allocator_type - get_allocator() const noexcept + // + + template<class Function> + friend + void asio_handler_invoke( + Function&& f, bind_wrapper* op) { - return (boost::asio::get_associated_allocator)(h_); + using net::asio_handler_invoke; + asio_handler_invoke(f, std::addressof(op->h_)); } friend - bool - asio_handler_is_continuation(bound_handler* h) + bool asio_handler_is_continuation( + bind_wrapper* op) { - using boost::asio::asio_handler_is_continuation; - return asio_handler_is_continuation(std::addressof(h->h_)); + using net::asio_handler_is_continuation; + return asio_handler_is_continuation( + std::addressof(op->h_)); } - template<class Function> friend - void asio_handler_invoke(Function&& f, bound_handler* h) + void* asio_handler_allocate( + std::size_t size, bind_wrapper* op) { - using boost::asio::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(h->h_)); + using net::asio_handler_allocate; + return asio_handler_allocate( + size, std::addressof(op->h_)); } - template<class... Values> + friend + void asio_handler_deallocate( + void* p, std::size_t size, bind_wrapper* op) + { + using net::asio_handler_deallocate; + asio_handler_deallocate( + p, size, std::addressof(op->h_)); + } +}; + +template<class Handler, class... Args> +class bind_back_wrapper; + +template<class Handler, class... Args> +class bind_front_wrapper; + +//------------------------------------------------------------------------------ +// +// bind_front +// +//------------------------------------------------------------------------------ + +template<class Handler, class... Args> +class bind_front_wrapper +{ + Handler h_; + detail::tuple<Args...> args_; + + template<class T, class Executor> + friend struct net::associated_executor; + + template<class T, class Allocator> + friend struct net::associated_allocator; + + template<std::size_t... I, class... Ts> void - operator()(Values&&... values) + invoke( + std::false_type, + mp11::index_sequence<I...>, + Ts&&... ts) { - invoke(h_, args_, - std::forward_as_tuple( - std::forward<Values>(values)...), - index_sequence_for<Args...>()); + h_( detail::get<I>(std::move(args_))..., + std::forward<Ts>(ts)...); } - template<class... Values> + template<std::size_t... I, class... Ts> void - operator()(Values&&... values) const + invoke( + std::true_type, + mp11::index_sequence<I...>, + Ts&&... ts) { - invoke(h_, args_, - std::forward_as_tuple( - std::forward<Values>(values)...), - index_sequence_for<Args...>()); + std::mem_fn(h_)( + detail::get<I>(std::move(args_))..., + std::forward<Ts>(ts)...); + } + +public: + using result_type = void; // asio needs this + + bind_front_wrapper(bind_front_wrapper&&) = default; + bind_front_wrapper(bind_front_wrapper const&) = default; + + template<class Handler_, class... Args_> + bind_front_wrapper( + Handler_&& handler, + Args_&&... args) + : h_(std::forward<Handler_>(handler)) + , args_(std::forward<Args_>(args)...) + { + } + + template<class... Ts> + void operator()(Ts&&... ts) + { + invoke( + std::is_member_function_pointer<Handler>{}, + mp11::index_sequence_for<Args...>{}, + std::forward<Ts>(ts)...); + } + + // + + template<class Function> + friend + void asio_handler_invoke( + Function&& f, bind_front_wrapper* op) + { + using net::asio_handler_invoke; + asio_handler_invoke(f, std::addressof(op->h_)); + } + + friend + bool asio_handler_is_continuation( + bind_front_wrapper* op) + { + using net::asio_handler_is_continuation; + return asio_handler_is_continuation( + std::addressof(op->h_)); + } + + friend + void* asio_handler_allocate( + std::size_t size, bind_front_wrapper* op) + { + using net::asio_handler_allocate; + return asio_handler_allocate( + size, std::addressof(op->h_)); + } + + friend + void asio_handler_deallocate( + void* p, std::size_t size, bind_front_wrapper* op) + { + using net::asio_handler_deallocate; + asio_handler_deallocate( + p, size, std::addressof(op->h_)); } }; } // detail } // beast +} // boost +//------------------------------------------------------------------------------ + +namespace boost { namespace asio { + template<class Handler, class... Args, class Executor> struct associated_executor< - beast::detail::bound_handler<Handler, Args...>, Executor> + beast::detail::bind_wrapper<Handler, Args...>, Executor> { using type = typename associated_executor<Handler, Executor>::type; static type - get(beast::detail::bound_handler<Handler, Args...> const& h, - Executor const& ex = Executor()) noexcept + get(beast::detail::bind_wrapper<Handler, Args...> const& op, + Executor const& ex = Executor{}) noexcept { return associated_executor< - Handler, Executor>::get(h.h_, ex); + Handler, Executor>::get(op.h_, ex); } }; -} // asio +template<class Handler, class... Args, class Executor> +struct associated_executor< + beast::detail::bind_front_wrapper<Handler, Args...>, Executor> +{ + using type = typename + associated_executor<Handler, Executor>::type; + + static + type + get(beast::detail::bind_front_wrapper<Handler, Args...> const& op, + Executor const& ex = Executor{}) noexcept + { + return associated_executor< + Handler, Executor>::get(op.h_, ex); + } +}; + +// + +template<class Handler, class... Args, class Allocator> +struct associated_allocator< + beast::detail::bind_wrapper<Handler, Args...>, Allocator> +{ + using type = typename + associated_allocator<Handler, Allocator>::type; + + static + type + get(beast::detail::bind_wrapper<Handler, Args...> const& op, + Allocator const& alloc = Allocator{}) noexcept + { + return associated_allocator< + Handler, Allocator>::get(op.h_, alloc); + } +}; + +template<class Handler, class... Args, class Allocator> +struct associated_allocator< + beast::detail::bind_front_wrapper<Handler, Args...>, Allocator> +{ + using type = typename + associated_allocator<Handler, Allocator>::type; + + static + type + get(beast::detail::bind_front_wrapper<Handler, Args...> const& op, + Allocator const& alloc = Allocator{}) noexcept + { + return associated_allocator< + Handler, Allocator>::get(op.h_, alloc); + } +}; + +} // asio } // boost +//------------------------------------------------------------------------------ + namespace std { + +// VFALCO Using std::bind on a completion handler will +// cause undefined behavior later, because the executor +// associated with the handler is not propagated to the +// wrapper returned by std::bind; these overloads are +// deleted to prevent mistakes. If this creates a problem +// please contact me. + template<class Handler, class... Args> void -bind(boost::beast::detail::bound_handler< +bind(boost::beast::detail::bind_wrapper< Handler, Args...>, ...) = delete; + +template<class Handler, class... Args> +void +bind(boost::beast::detail::bind_front_wrapper< + Handler, Args...>, ...) = delete; + } // std +//------------------------------------------------------------------------------ + #endif diff --git a/boost/beast/core/detail/buffer.hpp b/boost/beast/core/detail/buffer.hpp index d359d10059..c898d95c61 100644 --- a/boost/beast/core/detail/buffer.hpp +++ b/boost/beast/core/detail/buffer.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -39,7 +39,7 @@ dynamic_buffer_prepare_noexcept( boost::optional<typename DynamicBuffer::mutable_buffers_type> result; result.emplace(buffer.prepare(size)); - ec.assign(0, ec.category()); + ec = {}; return result; } @@ -61,7 +61,7 @@ dynamic_buffer_prepare( boost::optional<typename DynamicBuffer::mutable_buffers_type> result; result.emplace(buffer.prepare(size)); - ec.assign(0, ec.category()); + ec = {}; return result; } catch(std::length_error const&) diff --git a/boost/beast/core/detail/buffer_traits.hpp b/boost/beast/core/detail/buffer_traits.hpp new file mode 100644 index 0000000000..a50aa5d80d --- /dev/null +++ b/boost/beast/core/detail/buffer_traits.hpp @@ -0,0 +1,98 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_BUFFER_TRAITS_HPP +#define BOOST_BEAST_DETAIL_BUFFER_TRAITS_HPP + +#include <boost/asio/buffer.hpp> +#include <boost/config/workaround.hpp> +#include <boost/type_traits/make_void.hpp> +#include <cstdint> +#include <type_traits> + +namespace boost { +namespace beast { +namespace detail { + +#if BOOST_WORKAROUND(BOOST_MSVC, < 1910) + +template<class T> +struct buffers_iterator_type_helper +{ + using type = decltype( + net::buffer_sequence_begin( + std::declval<T const&>())); +}; + +template<> +struct buffers_iterator_type_helper< + net::const_buffer> +{ + using type = net::const_buffer const*; +}; + +template<> +struct buffers_iterator_type_helper< + net::mutable_buffer> +{ + using type = net::mutable_buffer const*; +}; + +#endif + +struct buffer_bytes_impl +{ + std::size_t + operator()(net::const_buffer b) const noexcept + { + return net::const_buffer(b).size(); + } + + std::size_t + operator()(net::mutable_buffer b) const noexcept + { + return net::mutable_buffer(b).size(); + } + + template< + class B, + class = typename std::enable_if< + net::is_const_buffer_sequence<B>::value>::type> + std::size_t + operator()(B const& b) const noexcept + { + using net::buffer_size; + return buffer_size(b); + } +}; + +/** Return `true` if a buffer sequence is empty + + This is sometimes faster than using @ref buffer_bytes +*/ +template<class ConstBufferSequence> +bool +buffers_empty(ConstBufferSequence const& buffers) +{ + auto it = net::buffer_sequence_begin(buffers); + auto end = net::buffer_sequence_end(buffers); + while(it != end) + { + if(net::const_buffer(*it).size() > 0) + return false; + ++it; + } + return true; +} + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/buffers_pair.hpp b/boost/beast/core/detail/buffers_pair.hpp new file mode 100644 index 0000000000..d8e6505d36 --- /dev/null +++ b/boost/beast/core/detail/buffers_pair.hpp @@ -0,0 +1,115 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_BUFFERS_PAIR_HPP +#define BOOST_BEAST_DETAIL_BUFFERS_PAIR_HPP + +#include <boost/asio/buffer.hpp> +#include <boost/assert.hpp> +#include <boost/config/workaround.hpp> +#include <type_traits> + +namespace boost { +namespace beast { +namespace detail { + +#if BOOST_WORKAROUND(BOOST_MSVC, < 1910) +# pragma warning (push) +# pragma warning (disable: 4521) // multiple copy constructors specified +# pragma warning (disable: 4522) // multiple assignment operators specified +#endif + +template<bool isMutable> +class buffers_pair +{ +public: + // VFALCO: This type is public otherwise + // asio::buffers_iterator won't compile. + using value_type = typename + std::conditional<isMutable, + net::mutable_buffer, + net::const_buffer>::type; + + using const_iterator = value_type const*; + + buffers_pair() = default; + +#if BOOST_WORKAROUND(BOOST_MSVC, < 1910) + buffers_pair(buffers_pair const& other) + : buffers_pair( + *other.begin(), *(other.begin() + 1)) + { + } + + buffers_pair& + operator=(buffers_pair const& other) + { + b_[0] = *other.begin(); + b_[1] = *(other.begin() + 1); + return *this; + } +#else + buffers_pair(buffers_pair const& other) = default; + buffers_pair& operator=(buffers_pair const& other) = default; +#endif + + template< + bool isMutable_ = isMutable, + class = typename std::enable_if< + ! isMutable_>::type> + buffers_pair(buffers_pair<true> const& other) + : buffers_pair( + *other.begin(), *(other.begin() + 1)) + { + } + + template< + bool isMutable_ = isMutable, + class = typename std::enable_if< + ! isMutable_>::type> + buffers_pair& + operator=(buffers_pair<true> const& other) + { + b_[0] = *other.begin(); + b_[1] = *(other.begin() + 1); + return *this; + } + + buffers_pair(value_type b0, value_type b1) + : b_{b0, b1} + { + } + + const_iterator + begin() const noexcept + { + return &b_[0]; + } + + const_iterator + end() const noexcept + { + if(b_[1].size() > 0) + return &b_[2]; + return &b_[1]; + } + +private: + value_type b_[2]; +}; + +#if BOOST_WORKAROUND(BOOST_MSVC, < 1910) +# pragma warning (pop) +#endif + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/buffers_range_adaptor.hpp b/boost/beast/core/detail/buffers_range_adaptor.hpp new file mode 100644 index 0000000000..2e04d14f6d --- /dev/null +++ b/boost/beast/core/detail/buffers_range_adaptor.hpp @@ -0,0 +1,142 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_BUFFERS_RANGE_ADAPTOR_HPP +#define BOOST_BEAST_DETAIL_BUFFERS_RANGE_ADAPTOR_HPP + +#include <boost/beast/core/buffer_traits.hpp> +#include <iterator> +#include <type_traits> + +namespace boost { +namespace beast { +namespace detail { + +template<class BufferSequence> +class buffers_range_adaptor +{ + BufferSequence b_; + +public: +#if BOOST_BEAST_DOXYGEN + using value_type = __see_below__; +#else + using value_type = buffers_type<BufferSequence>; +#endif + + class const_iterator + { + friend class buffers_range_adaptor; + + using iter_type = + buffers_iterator_type<BufferSequence>; + + iter_type it_{}; + buffers_range_adaptor const* b_ = nullptr; + + const_iterator( + buffers_range_adaptor const& b, + iter_type const& it) + : it_(it) + , b_(&b) + { + } + + public: + using value_type = typename + buffers_range_adaptor::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; + + 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 + { + 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; + } + }; + + buffers_range_adaptor( + buffers_range_adaptor const&) = default; + buffers_range_adaptor& operator=( + buffers_range_adaptor const&) = default; + + explicit + buffers_range_adaptor(BufferSequence const& b) + : b_(b) + { + } + + const_iterator + begin() const noexcept + { + return {*this, net::buffer_sequence_begin(b_)}; + } + + const_iterator + end() const noexcept + { + return {*this, net::buffer_sequence_end(b_)}; + } +}; + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/buffers_ref.hpp b/boost/beast/core/detail/buffers_ref.hpp index e56a9764fd..a4f60248b4 100644 --- a/boost/beast/core/detail/buffers_ref.hpp +++ b/boost/beast/core/detail/buffers_ref.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -10,8 +10,9 @@ #ifndef BOOST_BEAST_DETAIL_BUFFERS_REF_HPP #define BOOST_BEAST_DETAIL_BUFFERS_REF_HPP -#include <boost/beast/core/type_traits.hpp> +#include <boost/beast/core/buffer_traits.hpp> #include <iterator> +#include <memory> namespace boost { namespace beast { @@ -24,11 +25,11 @@ class buffers_ref BufferSequence const* buffers_; public: - using const_iterator = typename - buffer_sequence_iterator<BufferSequence>::type; + using const_iterator = + buffers_iterator_type<BufferSequence>; - using value_type = typename std::iterator_traits< - const_iterator>::value_type; + using value_type = typename + std::iterator_traits<const_iterator>::value_type; buffers_ref(buffers_ref const&) = default; buffers_ref& operator=(buffers_ref const&) = default; @@ -42,13 +43,13 @@ public: const_iterator begin() const { - return boost::asio::buffer_sequence_begin(*buffers_); + return net::buffer_sequence_begin(*buffers_); } const_iterator end() const { - return boost::asio::buffer_sequence_end(*buffers_); + return net::buffer_sequence_end(*buffers_); } }; @@ -57,6 +58,9 @@ template<class BufferSequence> buffers_ref<BufferSequence> make_buffers_ref(BufferSequence const& buffers) { + static_assert( + is_const_buffer_sequence<BufferSequence>::value, + "BufferSequence type requirements not met"); return buffers_ref<BufferSequence>(buffers); } diff --git a/boost/beast/core/detail/chacha.hpp b/boost/beast/core/detail/chacha.hpp index 976ade5607..d13e323309 100644 --- a/boost/beast/core/detail/chacha.hpp +++ b/boost/beast/core/detail/chacha.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -35,7 +35,6 @@ #include <cstdint> #include <limits> -#include <iosfwd> namespace boost { namespace beast { @@ -44,150 +43,81 @@ namespace detail { template<std::size_t R> class chacha { - void generate_block(); - void chacha_core(); - alignas(16) std::uint32_t block_[16]; std::uint32_t keysetup_[8]; std::uint64_t ctr_ = 0; int idx_ = 16; -public: - static constexpr std::size_t state_size = sizeof(chacha::keysetup_); - - using result_type = std::uint32_t; - - chacha(std::uint32_t const* v, std::uint64_t stream); - - std::uint32_t - operator()(); - -#if 0 - template<std::size_t R_> - friend - bool - operator==(chacha<R_> const& lhs, chacha<R_> const& rhs); - - template<std::size_t R_> - friend - bool - operator!=(chacha<R_> const& lhs, chacha<R_> const& rhs); - - static - constexpr - std::uint32_t - min() + void generate_block() { - return (std::numeric_limits<std::uint32_t>::min)(); + std::uint32_t constexpr constants[4] = { + 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 }; + std::uint32_t input[16]; + for (int i = 0; i < 4; ++i) + input[i] = constants[i]; + for (int i = 0; i < 8; ++i) + input[4 + i] = keysetup_[i]; + input[12] = (ctr_ / 16) & 0xffffffffu; + input[13] = (ctr_ / 16) >> 32; + input[14] = input[15] = 0xdeadbeef; // Could use 128-bit counter. + for (int i = 0; i < 16; ++i) + block_[i] = input[i]; + chacha_core(); + for (int i = 0; i < 16; ++i) + block_[i] += input[i]; } - static - constexpr - std::uint32_t - max() + void chacha_core() { - return (std::numeric_limits<std::uint32_t>::max)(); + #define BOOST_BEAST_CHACHA_ROTL32(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + #define BOOST_BEAST_CHACHA_QUARTERROUND(x, a, b, c, d) \ + x[a] = x[a] + x[b]; x[d] ^= x[a]; x[d] = BOOST_BEAST_CHACHA_ROTL32(x[d], 16); \ + x[c] = x[c] + x[d]; x[b] ^= x[c]; x[b] = BOOST_BEAST_CHACHA_ROTL32(x[b], 12); \ + x[a] = x[a] + x[b]; x[d] ^= x[a]; x[d] = BOOST_BEAST_CHACHA_ROTL32(x[d], 8); \ + x[c] = x[c] + x[d]; x[b] ^= x[c]; x[b] = BOOST_BEAST_CHACHA_ROTL32(x[b], 7) + + for (unsigned i = 0; i < R; i += 2) + { + BOOST_BEAST_CHACHA_QUARTERROUND(block_, 0, 4, 8, 12); + BOOST_BEAST_CHACHA_QUARTERROUND(block_, 1, 5, 9, 13); + BOOST_BEAST_CHACHA_QUARTERROUND(block_, 2, 6, 10, 14); + BOOST_BEAST_CHACHA_QUARTERROUND(block_, 3, 7, 11, 15); + BOOST_BEAST_CHACHA_QUARTERROUND(block_, 0, 5, 10, 15); + BOOST_BEAST_CHACHA_QUARTERROUND(block_, 1, 6, 11, 12); + BOOST_BEAST_CHACHA_QUARTERROUND(block_, 2, 7, 8, 13); + BOOST_BEAST_CHACHA_QUARTERROUND(block_, 3, 4, 9, 14); + } + + #undef BOOST_BEAST_CHACHA_QUARTERROUND + #undef BOOST_BEAST_CHACHA_ROTL32 } -#endif -}; -template<std::size_t R> -chacha<R>:: -chacha(std::uint32_t const* v, std::uint64_t stream) -{ - for (int i = 0; i < 6; ++i) - keysetup_[i] = v[i]; - keysetup_[6] = v[6] + (stream & 0xffffffff); - keysetup_[7] = v[7] + ((stream >> 32) & 0xffffffff); -} +public: + static constexpr std::size_t state_size = sizeof(chacha::keysetup_); -template<std::size_t R> -std::uint32_t -chacha<R>:: -operator()() -{ - if(idx_ == 16) + using result_type = std::uint32_t; + + chacha(std::uint32_t const* v, std::uint64_t stream) { - idx_ = 0; - ++ctr_; - generate_block(); + for (int i = 0; i < 6; ++i) + keysetup_[i] = v[i]; + keysetup_[6] = v[6] + (stream & 0xffffffff); + keysetup_[7] = v[7] + ((stream >> 32) & 0xffffffff); } - return block_[idx_++]; -} -template<std::size_t R> -void -chacha<R>:: -generate_block() -{ - std::uint32_t constexpr constants[4] = { - 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 }; - std::uint32_t input[16]; - for (int i = 0; i < 4; ++i) - input[i] = constants[i]; - for (int i = 0; i < 8; ++i) - input[4 + i] = keysetup_[i]; - input[12] = (ctr_ / 16) & 0xffffffffu; - input[13] = (ctr_ / 16) >> 32; - input[14] = input[15] = 0xdeadbeef; // Could use 128-bit counter. - for (int i = 0; i < 16; ++i) - block_[i] = input[i]; - chacha_core(); - for (int i = 0; i < 16; ++i) - block_[i] += input[i]; -} - -template<std::size_t R> -void -chacha<R>:: -chacha_core() -{ - #define BOOST_BEAST_CHACHA_ROTL32(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) - - #define BOOST_BEAST_CHACHA_QUARTERROUND(x, a, b, c, d) \ - x[a] = x[a] + x[b]; x[d] ^= x[a]; x[d] = BOOST_BEAST_CHACHA_ROTL32(x[d], 16); \ - x[c] = x[c] + x[d]; x[b] ^= x[c]; x[b] = BOOST_BEAST_CHACHA_ROTL32(x[b], 12); \ - x[a] = x[a] + x[b]; x[d] ^= x[a]; x[d] = BOOST_BEAST_CHACHA_ROTL32(x[d], 8); \ - x[c] = x[c] + x[d]; x[b] ^= x[c]; x[b] = BOOST_BEAST_CHACHA_ROTL32(x[b], 7) - - for (unsigned i = 0; i < R; i += 2) + std::uint32_t + operator()() { - BOOST_BEAST_CHACHA_QUARTERROUND(block_, 0, 4, 8, 12); - BOOST_BEAST_CHACHA_QUARTERROUND(block_, 1, 5, 9, 13); - BOOST_BEAST_CHACHA_QUARTERROUND(block_, 2, 6, 10, 14); - BOOST_BEAST_CHACHA_QUARTERROUND(block_, 3, 7, 11, 15); - BOOST_BEAST_CHACHA_QUARTERROUND(block_, 0, 5, 10, 15); - BOOST_BEAST_CHACHA_QUARTERROUND(block_, 1, 6, 11, 12); - BOOST_BEAST_CHACHA_QUARTERROUND(block_, 2, 7, 8, 13); - BOOST_BEAST_CHACHA_QUARTERROUND(block_, 3, 4, 9, 14); + if(idx_ == 16) + { + idx_ = 0; + ++ctr_; + generate_block(); + } + return block_[idx_++]; } - - #undef BOOST_BEAST_CHACHA_QUARTERROUND - #undef BOOST_BEAST_CHACHA_ROTL32 -} - -//#endif - -#if 0 -// Implement <random> interface. - -template<std::size_t R> -bool -operator==(chacha<R> const& lhs, chacha<R> const& rhs) -{ - for (int i = 0; i < 8; ++i) - if (lhs.keysetup_[i] != rhs.keysetup_[i]) - return false; - return lhs.ctr_ == rhs.ctr_; -} - -template<std::size_t R> -bool -operator!=(chacha<R> const& lhs, chacha<R> const& rhs) -{ - return !(lhs == rhs); -} -#endif +}; } // detail } // beast diff --git a/boost/beast/core/detail/clamp.hpp b/boost/beast/core/detail/clamp.hpp index 95a8fab425..0ec2768caa 100644 --- a/boost/beast/core/detail/clamp.hpp +++ b/boost/beast/core/detail/clamp.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) diff --git a/boost/beast/core/detail/config.hpp b/boost/beast/core/detail/config.hpp index 68e3dda8ee..bebe8c79ef 100644 --- a/boost/beast/core/detail/config.hpp +++ b/boost/beast/core/detail/config.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -10,14 +10,21 @@ #ifndef BOOST_BEAST_CORE_DETAIL_CONFIG_HPP #define BOOST_BEAST_CORE_DETAIL_CONFIG_HPP -#include <boost/config.hpp> -#include <boost/version.hpp> - // Available to every header #include <boost/config.hpp> +#include <boost/version.hpp> #include <boost/core/ignore_unused.hpp> #include <boost/static_assert.hpp> +namespace boost { +namespace asio +{ +} // asio +namespace beast { +namespace net = boost::asio; +} // beast +} // boost + /* _MSC_VER and _MSC_FULL_VER by version: @@ -51,4 +58,39 @@ #define BOOST_BEAST_DEPRECATION_STRING \ "This is a deprecated interface, #define BOOST_BEAST_ALLOW_DEPRECATED to allow it" +#ifndef BOOST_BEAST_ASSUME +# ifdef BOOST_GCC +# define BOOST_BEAST_ASSUME(cond) \ + do { if (!(cond)) __builtin_unreachable(); } while (0) +# else +# define BOOST_BEAST_ASSUME(cond) do { } while(0) +# endif +#endif + +// Default to a header-only implementation. The user must specifically +// request separate compilation by defining BOOST_BEAST_SEPARATE_COMPILATION +#ifndef BOOST_BEAST_HEADER_ONLY +# ifndef BOOST_BEAST_SEPARATE_COMPILATION +# define BOOST_BEAST_HEADER_ONLY 1 +# endif +#endif + +#if BOOST_BEAST_DOXYGEN +# define BOOST_BEAST_DECL +#elif defined(BOOST_BEAST_HEADER_ONLY) +# define BOOST_BEAST_DECL inline +#else +# define BOOST_BEAST_DECL +#endif + +#ifndef BOOST_BEAST_ASYNC_RESULT1 +#define BOOST_BEAST_ASYNC_RESULT1(type) \ + BOOST_ASIO_INITFN_RESULT_TYPE(type, void(::boost::beast::error_code)) +#endif + +#ifndef BOOST_BEAST_ASYNC_RESULT2 +#define BOOST_BEAST_ASYNC_RESULT2(type) \ + BOOST_ASIO_INITFN_RESULT_TYPE(type, void(::boost::beast::error_code, std::size_t)) +#endif + #endif diff --git a/boost/beast/core/detail/flat_stream.hpp b/boost/beast/core/detail/flat_stream.hpp new file mode 100644 index 0000000000..59f00e970c --- /dev/null +++ b/boost/beast/core/detail/flat_stream.hpp @@ -0,0 +1,73 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_DETAIL_FLAT_STREAM_HPP +#define BOOST_BEAST_CORE_DETAIL_FLAT_STREAM_HPP + +#include <boost/beast/core/buffer_traits.hpp> +#include <boost/asio/buffer.hpp> +#include <cstdlib> + +namespace boost { +namespace beast { +namespace detail { + +class flat_stream_base +{ +public: + // Largest buffer size we will flatten. + // 16KB is the upper limit on reasonably sized HTTP messages. + static std::size_t constexpr max_size = 16 * 1024; + + // Largest stack we will use to flatten + static std::size_t constexpr max_stack = 8 * 1024; + + struct flatten_result + { + std::size_t size; + bool flatten; + }; + + // calculates the flatten settings for a buffer sequence + template<class BufferSequence> + static + flatten_result + flatten( + BufferSequence const& buffers, std::size_t limit) + { + flatten_result result{0, false}; + auto first = net::buffer_sequence_begin(buffers); + auto last = net::buffer_sequence_end(buffers); + if(first != last) + { + result.size = buffer_bytes(*first); + if(result.size < limit) + { + auto it = first; + auto prev = first; + while(++it != last) + { + auto const n = buffer_bytes(*it); + if(result.size + n > limit) + break; + result.size += n; + prev = it; + } + result.flatten = prev != first; + } + } + return result; + } +}; + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/get_io_context.hpp b/boost/beast/core/detail/get_io_context.hpp new file mode 100644 index 0000000000..27c816ea43 --- /dev/null +++ b/boost/beast/core/detail/get_io_context.hpp @@ -0,0 +1,107 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_GET_IO_CONTEXT_HPP +#define BOOST_BEAST_DETAIL_GET_IO_CONTEXT_HPP + +#include <boost/beast/core/stream_traits.hpp> +#include <boost/asio/executor.hpp> +#include <boost/asio/io_context.hpp> +#include <boost/asio/strand.hpp> +#include <memory> +#include <type_traits> + +namespace boost { +namespace beast { +namespace detail { + +//------------------------------------------------------------------------------ + +inline +net::io_context* +get_io_context(net::io_context& ioc) +{ + return std::addressof(ioc); +} + +inline +net::io_context* +get_io_context(net::io_context::executor_type const& ex) +{ + return std::addressof(ex.context()); +} + +inline +net::io_context* +get_io_context(net::strand< + net::io_context::executor_type> const& ex) +{ + return std::addressof( + ex.get_inner_executor().context()); +} + +template<class Executor> +net::io_context* +get_io_context(net::strand<Executor> const& ex) +{ + return get_io_context(ex.get_inner_executor()); +} + +template< + class T, + class = typename std::enable_if< + std::is_same<T, net::executor>::value>::type> +net::io_context* +get_io_context(T const& ex) +{ + auto p = ex.template target<typename + net::io_context::executor_type>(); + if(! p) + return nullptr; + return std::addressof(p->context()); +} + +inline +net::io_context* +get_io_context(...) +{ + return nullptr; +} + +//------------------------------------------------------------------------------ + +template<class T> +net::io_context* +get_io_context_impl(T& t, std::true_type) +{ + return get_io_context( + t.get_executor()); +} + +template<class T> +net::io_context* +get_io_context_impl(T const&, std::false_type) +{ + return nullptr; +} + +// Returns the io_context*, or nullptr, for any object. +template<class T> +net::io_context* +get_io_context(T& t) +{ + return get_io_context_impl(t, + has_get_executor<T>{}); +} + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/impl/read.hpp b/boost/beast/core/detail/impl/read.hpp new file mode 100644 index 0000000000..137384aa40 --- /dev/null +++ b/boost/beast/core/detail/impl/read.hpp @@ -0,0 +1,260 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_IMPL_READ_HPP +#define BOOST_BEAST_DETAIL_IMPL_READ_HPP + +#include <boost/beast/core/bind_handler.hpp> +#include <boost/beast/core/async_base.hpp> +#include <boost/beast/core/flat_static_buffer.hpp> +#include <boost/asio/basic_stream_socket.hpp> +#include <boost/asio/coroutine.hpp> +#include <boost/throw_exception.hpp> + +namespace boost { +namespace beast { +namespace detail { + +// The number of bytes in the stack buffer when using non-blocking. +static std::size_t constexpr default_max_stack_buffer = 16384; + +//------------------------------------------------------------------------------ + +struct dynamic_read_ops +{ + +// read into a dynamic buffer until the +// condition is met or an error occurs +template< + class Stream, + class DynamicBuffer, + class Condition, + class Handler> +class read_op + : public net::coroutine + , public async_base< + Handler, beast::executor_type<Stream>> +{ + Stream& s_; + DynamicBuffer& b_; + Condition cond_; + error_code ec_; + std::size_t total_ = 0; + +public: + read_op(read_op&&) = default; + + template<class Handler_, class Condition_> + read_op( + Handler_&& h, + Stream& s, + DynamicBuffer& b, + Condition_&& cond) + : async_base<Handler, + beast::executor_type<Stream>>( + std::forward<Handler_>(h), + s.get_executor()) + , s_(s) + , b_(b) + , cond_(std::forward<Condition_>(cond)) + { + (*this)({}, 0, false); + } + + void + operator()( + error_code ec, + std::size_t bytes_transferred, + bool cont = true) + { + std::size_t max_size; + std::size_t max_prepare; + BOOST_ASIO_CORO_REENTER(*this) + { + for(;;) + { + max_size = cond_(ec, total_, b_); + max_prepare = std::min<std::size_t>( + std::max<std::size_t>( + 512, b_.capacity() - b_.size()), + std::min<std::size_t>( + max_size, b_.max_size() - b_.size())); + if(max_prepare == 0) + break; + BOOST_ASIO_CORO_YIELD + s_.async_read_some( + b_.prepare(max_prepare), std::move(*this)); + b_.commit(bytes_transferred); + total_ += bytes_transferred; + } + if(! cont) + { + // run this handler "as-if" using net::post + // to reduce template instantiations + ec_ = ec; + BOOST_ASIO_CORO_YIELD + s_.async_read_some( + b_.prepare(0), std::move(*this)); + ec = ec_; + } + this->complete_now(ec, total_); + } + } +}; + +//------------------------------------------------------------------------------ + +struct run_read_op +{ + template< + class AsyncReadStream, + class DynamicBuffer, + class Condition, + class ReadHandler> + void + operator()( + ReadHandler&& h, + AsyncReadStream* s, + DynamicBuffer* b, + Condition&& c) + { + // If you get an error on the following line it means + // that your handler does not meet the documented type + // requirements for the handler. + + static_assert( + beast::detail::is_invocable<ReadHandler, + void(error_code, std::size_t)>::value, + "ReadHandler type requirements not met"); + + read_op< + AsyncReadStream, + DynamicBuffer, + typename std::decay<Condition>::type, + typename std::decay<ReadHandler>::type>( + std::forward<ReadHandler>(h), + *s, + *b, + std::forward<Condition>(c)); + } + +}; + +}; + +//------------------------------------------------------------------------------ + +template< + class SyncReadStream, + class DynamicBuffer, + class CompletionCondition, + class> +std::size_t +read( + SyncReadStream& stream, + DynamicBuffer& buffer, + CompletionCondition cond) +{ + static_assert(is_sync_read_stream<SyncReadStream>::value, + "SyncReadStream type requirements not met"); + static_assert( + net::is_dynamic_buffer<DynamicBuffer>::value, + "DynamicBuffer type requirements not met"); + static_assert( + detail::is_invocable<CompletionCondition, + void(error_code&, std::size_t, DynamicBuffer&)>::value, + "CompletionCondition type requirements not met"); + error_code ec; + auto const bytes_transferred = detail::read( + stream, buffer, std::move(cond), ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); + return bytes_transferred; +} + +template< + class SyncReadStream, + class DynamicBuffer, + class CompletionCondition, + class> +std::size_t +read( + SyncReadStream& stream, + DynamicBuffer& buffer, + CompletionCondition cond, + error_code& ec) +{ + static_assert(is_sync_read_stream<SyncReadStream>::value, + "SyncReadStream type requirements not met"); + static_assert( + net::is_dynamic_buffer<DynamicBuffer>::value, + "DynamicBuffer type requirements not met"); + static_assert( + detail::is_invocable<CompletionCondition, + void(error_code&, std::size_t, DynamicBuffer&)>::value, + "CompletionCondition type requirements not met"); + ec = {}; + std::size_t total = 0; + std::size_t max_size; + std::size_t max_prepare; + for(;;) + { + max_size = cond(ec, total, buffer); + max_prepare = std::min<std::size_t>( + std::max<std::size_t>( + 512, buffer.capacity() - buffer.size()), + std::min<std::size_t>( + max_size, buffer.max_size() - buffer.size())); + if(max_prepare == 0) + break; + std::size_t const bytes_transferred = + stream.read_some(buffer.prepare(max_prepare), ec); + buffer.commit(bytes_transferred); + total += bytes_transferred; + } + return total; +} + +template< + class AsyncReadStream, + class DynamicBuffer, + class CompletionCondition, + class ReadHandler, + class> +BOOST_BEAST_ASYNC_RESULT2(ReadHandler) +async_read( + AsyncReadStream& stream, + DynamicBuffer& buffer, + CompletionCondition&& cond, + ReadHandler&& handler) +{ + static_assert(is_async_read_stream<AsyncReadStream>::value, + "AsyncReadStream type requirements not met"); + static_assert( + net::is_dynamic_buffer<DynamicBuffer>::value, + "DynamicBuffer type requirements not met"); + static_assert( + detail::is_invocable<CompletionCondition, + void(error_code&, std::size_t, DynamicBuffer&)>::value, + "CompletionCondition type requirements not met"); + return net::async_initiate< + ReadHandler, + void(error_code, std::size_t)>( + typename dynamic_read_ops::run_read_op{}, + handler, + &stream, + &buffer, + std::forward<CompletionCondition>(cond)); +} + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/integer_sequence.hpp b/boost/beast/core/detail/integer_sequence.hpp deleted file mode 100644 index 71664229af..0000000000 --- a/boost/beast/core/detail/integer_sequence.hpp +++ /dev/null @@ -1,143 +0,0 @@ -// -// Copyright (c) 2016-2017 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) -// -// Official repository: https://github.com/boostorg/beast -// - -#ifndef BOOST_BEAST_DETAIL_INTEGER_SEQUENCE_HPP -#define BOOST_BEAST_DETAIL_INTEGER_SEQUENCE_HPP - -#include <boost/config.hpp> -#include <cstddef> -#include <type_traits> -#include <utility> - -namespace boost { -namespace beast { -namespace detail { - -template<class T, T... Ints> -struct integer_sequence -{ - using value_type = T; - BOOST_STATIC_ASSERT(std::is_integral<T>::value); - - static std::size_t constexpr static_size = sizeof...(Ints); - - static std::size_t constexpr size() - { - return sizeof...(Ints); - } -}; - -template<std::size_t... Ints> -using index_sequence = integer_sequence<std::size_t, Ints...>; - -// This workaround is needed for broken sizeof... -template<class... Args> -struct sizeof_workaround -{ - static std::size_t constexpr size = sizeof... (Args); -}; - -#ifdef BOOST_MSVC - -// This implementation compiles on real MSVC and clang but not gcc - -template<class T, unsigned long long N, class Seq> -struct make_integer_sequence_unchecked; - -template<class T, unsigned long long N, unsigned long long ...Indices> -struct make_integer_sequence_unchecked< - T, N, integer_sequence<T, Indices...>> -{ - using type = typename make_integer_sequence_unchecked< - T, N-1, integer_sequence<T, N-1, Indices...>>::type; -}; - -template<class T, unsigned long long ...Indices> -struct make_integer_sequence_unchecked< - T, 0, integer_sequence<T, Indices...>> -{ - using type = integer_sequence<T, Indices...>; -}; - -template<class T, T N> -struct make_integer_sequence_checked -{ - BOOST_STATIC_ASSERT(std::is_integral<T>::value); - BOOST_STATIC_ASSERT(N >= 0); - - using type = typename make_integer_sequence_unchecked< - T, N, integer_sequence<T>>::type; -}; - -template<class T, T N> -using make_integer_sequence = - typename make_integer_sequence_checked<T, N>::type; - -template<std::size_t N> -using make_index_sequence = make_integer_sequence<std::size_t, N>; - -template<class... Args> -using index_sequence_for = - make_index_sequence<sizeof_workaround<Args...>::size>; - -#else - -// This implementation compiles on gcc but not MSVC - -template<std::size_t... Ints> -struct index_tuple -{ - using next = index_tuple<Ints..., sizeof... (Ints)>; - -}; - -template<std::size_t N> -struct build_index_tuple -{ - using type = typename build_index_tuple<N-1>::type::next; -}; - -template<> -struct build_index_tuple<0> -{ - using type = index_tuple<>; -}; - -template<class T, T N, - class Seq = typename build_index_tuple<N>::type -> -struct integer_sequence_helper; - -template<class T, T N, std::size_t... Ints> -struct integer_sequence_helper<T, N, index_tuple<Ints...>> -{ - BOOST_STATIC_ASSERT(std::is_integral<T>::value); - BOOST_STATIC_ASSERT(N >= 0); - - using type = integer_sequence<T, static_cast<T> (Ints)...>; -}; - -template<class T, T N> -using make_integer_sequence = - typename integer_sequence_helper<T, N>::type; - -template<std::size_t N> -using make_index_sequence = make_integer_sequence<std::size_t, N>; - -template<class... Args> -using index_sequence_for = - make_index_sequence<sizeof_workaround<Args...>::size>; - -#endif - -} // detail -} // beast -} // boost - -#endif diff --git a/boost/beast/core/detail/is_invocable.hpp b/boost/beast/core/detail/is_invocable.hpp new file mode 100644 index 0000000000..65f1b6c842 --- /dev/null +++ b/boost/beast/core/detail/is_invocable.hpp @@ -0,0 +1,58 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_IS_INVOCABLE_HPP +#define BOOST_BEAST_DETAIL_IS_INVOCABLE_HPP + +#include <type_traits> +#include <utility> + +namespace boost { +namespace beast { +namespace detail { + +template<class R, class C, class ...A> +auto +is_invocable_test(C&& c, int, A&& ...a) + -> decltype(std::is_convertible< + decltype(c(std::forward<A>(a)...)), R>::value || + std::is_same<R, void>::value, + std::true_type()); + +template<class R, class C, class ...A> +std::false_type +is_invocable_test(C&& c, long, A&& ...a); + +/** Metafunction returns `true` if F callable as R(A...) + + Example: + + @code + is_invocable<T, void(std::string)>::value + @endcode +*/ +/** @{ */ +template<class C, class F> +struct is_invocable : std::false_type +{ +}; + +template<class C, class R, class ...A> +struct is_invocable<C, R(A...)> + : decltype(is_invocable_test<R>( + std::declval<C>(), 1, std::declval<A>()...)) +{ +}; +/** @} */ + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/ostream.hpp b/boost/beast/core/detail/ostream.hpp index 6b921367d8..65da560179 100644 --- a/boost/beast/core/detail/ostream.hpp +++ b/boost/beast/core/detail/ostream.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -11,11 +11,12 @@ #define BOOST_BEAST_DETAIL_OSTREAM_HPP #include <boost/beast/core/buffers_prefix.hpp> -#include <boost/beast/core/read_size.hpp> +#include <boost/beast/core/buffers_range.hpp> #include <boost/beast/core/detail/type_traits.hpp> +#include <boost/throw_exception.hpp> #include <boost/asio/buffer.hpp> #include <memory> -#include <iosfwd> +#include <ostream> #include <streambuf> #include <type_traits> #include <utility> @@ -24,37 +25,6 @@ namespace boost { namespace beast { namespace detail { -template<class Buffers> -class buffers_helper -{ - Buffers b_; - -public: - explicit - buffers_helper(Buffers const& b) - : b_(b) - { - } - - template<class B> - friend - std::ostream& - operator<<(std::ostream& os, - buffers_helper<B> const& v); -}; - -template<class Buffers> -std::ostream& -operator<<(std::ostream& os, - buffers_helper<Buffers> const& v) -{ - for(auto b : buffers_range(v.b_)) - os.write(static_cast<char const*>(b.data()), b.size()); - return os; -} - -//------------------------------------------------------------------------------ - struct basic_streambuf_movable_helper : std::basic_streambuf<char, std::char_traits<char>> { @@ -65,12 +35,12 @@ struct basic_streambuf_movable_helper : using basic_streambuf_movable = std::is_move_constructible<basic_streambuf_movable_helper>; -//------------------------------------------------------------------------------ - template<class DynamicBuffer, class CharT, class Traits, bool isMovable> class ostream_buffer; +//------------------------------------------------------------------------------ + template<class DynamicBuffer, class CharT, class Traits> class ostream_buffer <DynamicBuffer, CharT, Traits, true> @@ -82,9 +52,7 @@ class ostream_buffer using traits_type = typename std::basic_streambuf<CharT, Traits>::traits_type; - static std::size_t constexpr max_size = 512; - - DynamicBuffer& buf_; + DynamicBuffer& b_; public: ostream_buffer(ostream_buffer&&) = default; @@ -96,56 +64,48 @@ public: } explicit - ostream_buffer(DynamicBuffer& buf) - : buf_(buf) + ostream_buffer(DynamicBuffer& b) + : b_(b) { - prepare(); } int_type overflow(int_type ch) override { - if(! Traits::eq_int_type(ch, Traits::eof())) - { - Traits::assign(*this->pptr(), - static_cast<CharT>(ch)); - flush(1); - prepare(); - return ch; - } - flush(); - return traits_type::eof(); - } - - int - sync() override - { - flush(); - prepare(); - return 0; - } + BOOST_ASSERT(! Traits::eq_int_type( + ch, Traits::eof())); + sync(); -private: - void - prepare() - { - auto bs = buf_.prepare( - read_size_or_throw(buf_, max_size)); + static std::size_t constexpr max_size = 65536; + auto const max_prepare = std::min<std::size_t>( + std::max<std::size_t>( + 512, b_.capacity() - b_.size()), + std::min<std::size_t>( + max_size, b_.max_size() - b_.size())); + if(max_prepare == 0) + return Traits::eof(); + auto const bs = b_.prepare(max_prepare); auto const b = buffers_front(bs); auto const p = static_cast<CharT*>(b.data()); - this->setp(p, - p + b.size() / sizeof(CharT) - 1); + this->setp(p, p + b.size() / sizeof(CharT)); + + BOOST_ASSERT(b_.capacity() > b_.size()); + return this->sputc( + Traits::to_char_type(ch)); } - void - flush(int extra = 0) + int + sync() override { - buf_.commit( - (this->pptr() - this->pbase() + extra) * - sizeof(CharT)); + b_.commit( + (this->pptr() - this->pbase()) * + sizeof(CharT)); + return 0; } }; +//------------------------------------------------------------------------------ + // This nonsense is all to work around a glitch in libstdc++ // where std::basic_streambuf copy constructor is private: // https://github.com/gcc-mirror/gcc/blob/gcc-4_8-branch/libstdc%2B%2B-v3/include/std/streambuf#L799 @@ -161,9 +121,7 @@ class ostream_buffer using traits_type = typename std::basic_streambuf<CharT, Traits>::traits_type; - static std::size_t constexpr max_size = 512; - - DynamicBuffer& buf_; + DynamicBuffer& b_; public: ostream_buffer(ostream_buffer&&) = delete; @@ -175,53 +133,43 @@ public: } explicit - ostream_buffer(DynamicBuffer& buf) - : buf_(buf) + ostream_buffer(DynamicBuffer& b) + : b_(b) { - prepare(); } int_type overflow(int_type ch) override { - if(! Traits::eq_int_type(ch, Traits::eof())) - { - Traits::assign(*this->pptr(), - static_cast<CharT>(ch)); - flush(1); - prepare(); - return ch; - } - flush(); - return traits_type::eof(); - } - - int - sync() override - { - flush(); - prepare(); - return 0; - } + BOOST_ASSERT(! Traits::eq_int_type( + ch, Traits::eof())); + sync(); -private: - void - prepare() - { - auto bs = buf_.prepare( - read_size_or_throw(buf_, max_size)); + static std::size_t constexpr max_size = 65536; + auto const max_prepare = std::min<std::size_t>( + std::max<std::size_t>( + 512, b_.capacity() - b_.size()), + std::min<std::size_t>( + max_size, b_.max_size() - b_.size())); + if(max_prepare == 0) + return Traits::eof(); + auto const bs = b_.prepare(max_prepare); auto const b = buffers_front(bs); auto const p = static_cast<CharT*>(b.data()); - this->setp(p, - p + b.size() / sizeof(CharT) - 1); + this->setp(p, p + b.size() / sizeof(CharT)); + + BOOST_ASSERT(b_.capacity() > b_.size()); + return this->sputc( + Traits::to_char_type(ch)); } - void - flush(int extra = 0) + int + sync() override { - buf_.commit( - (this->pptr() - this->pbase() + extra) * - sizeof(CharT)); + b_.commit( + (this->pptr() - this->pbase()) * + sizeof(CharT)); + return 0; } }; @@ -241,24 +189,22 @@ class ostream_helper< public: explicit - ostream_helper(DynamicBuffer& buf); + ostream_helper(DynamicBuffer& b); ostream_helper(ostream_helper&& other); }; template<class DynamicBuffer, class CharT, class Traits> ostream_helper<DynamicBuffer, CharT, Traits, true>:: -ostream_helper(DynamicBuffer& buf) - : std::basic_ostream<CharT, Traits>( - &this->osb_) - , osb_(buf) +ostream_helper(DynamicBuffer& b) + : std::basic_ostream<CharT, Traits>(&this->osb_) + , osb_(b) { } template<class DynamicBuffer, class CharT, class Traits> ostream_helper<DynamicBuffer, CharT, Traits, true>:: -ostream_helper( - ostream_helper&& other) +ostream_helper(ostream_helper&& other) : std::basic_ostream<CharT, Traits>(&osb_) , osb_(std::move(other.osb_)) { @@ -292,12 +238,13 @@ class ostream_helper< { public: explicit - ostream_helper(DynamicBuffer& buf) + ostream_helper(DynamicBuffer& b) : ostream_helper_base<ostream_buffer< DynamicBuffer, CharT, Traits, false>>( new ostream_buffer<DynamicBuffer, - CharT, Traits, false>(buf)) - , std::basic_ostream<CharT, Traits>(this->member.get()) + CharT, Traits, false>(b)) + , std::basic_ostream<CharT, Traits>( + this->member.get()) { } @@ -305,7 +252,8 @@ public: : ostream_helper_base<ostream_buffer< DynamicBuffer, CharT, Traits, false>>( std::move(other)) - , std::basic_ostream<CharT, Traits>(this->member.get()) + , std::basic_ostream<CharT, Traits>( + this->member.get()) { } }; diff --git a/boost/beast/core/detail/pcg.hpp b/boost/beast/core/detail/pcg.hpp new file mode 100644 index 0000000000..2815882afa --- /dev/null +++ b/boost/beast/core/detail/pcg.hpp @@ -0,0 +1,65 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_DETAIL_PCG_HPP +#define BOOST_BEAST_CORE_DETAIL_PCG_HPP + +#include <boost/core/ignore_unused.hpp> +#include <cstdint> +#include <random> + +namespace boost { +namespace beast { +namespace detail { + +class pcg +{ + std::uint64_t state_ = 0; + std::uint64_t increment_; + +public: + using result_type = std::uint32_t; + + // Initialize the generator. + // There are no restrictions on the input values. + pcg( + std::uint64_t seed, + std::uint64_t stream) + { + // increment must be odd + increment_ = 2 * stream + 1; + boost::ignore_unused((*this)()); + state_ += seed; + boost::ignore_unused((*this)()); + } + + std::uint32_t + operator()() + { + std::uint64_t const p = state_; + state_ = p * + 6364136223846793005ULL + + increment_; + std::uint32_t const x = + static_cast<std::uint32_t>( + ((p >> 18) ^ p) >> 27); + std::uint32_t const r = p >> 59; + #ifdef BOOST_MSVC + return _rotr(x, r); + #else + return (x >> r) | (x << ((1 + ~r) & 31)); + #endif + } +}; + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/read.hpp b/boost/beast/core/detail/read.hpp new file mode 100644 index 0000000000..3f7c17f27f --- /dev/null +++ b/boost/beast/core/detail/read.hpp @@ -0,0 +1,245 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_READ_HPP +#define BOOST_BEAST_DETAIL_READ_HPP + +#include <boost/beast/core/detail/config.hpp> +#include <boost/beast/core/error.hpp> +#include <boost/beast/core/stream_traits.hpp> +#include <boost/beast/core/detail/is_invocable.hpp> +#include <boost/asio/async_result.hpp> +#include <cstdlib> + +namespace boost { +namespace beast { +namespace detail { + +//------------------------------------------------------------------------------ + +/** Read data into a dynamic buffer from a stream until a condition is met. + + This function is used to read from a stream into a dynamic buffer until + a condition is met. The call will block until one of the following is true: + + @li The specified dynamic buffer sequence is full (that is, it has + reached its currently configured maximum size). + + @li The `completion_condition` function object returns 0. + + This operation is implemented in terms of zero or more calls to the + stream's `read_some` function. + + @param stream The stream from which the data is to be read. The type + must support the <em>SyncReadStream</em> requirements. + + @param buffer The dynamic buffer sequence into which the data will be read. + + @param completion_condition The function object to be called to determine + whether the read operation is complete. The function object must be invocable + with this signature: + @code + std::size_t + completion_condition( + // Modifiable result of latest read_some operation. + error_code& ec, + + // Number of bytes transferred so far. + std::size_t bytes_transferred + + // The dynamic buffer used to store the bytes read + DynamicBuffer& buffer + ); + @endcode + A non-zero return value indicates the maximum number of bytes to be read on + the next call to the stream's `read_some` function. A return value of 0 + from the completion condition indicates that the read operation is complete; + in this case the optionally modifiable error passed to the completion + condition will be delivered to the caller as an exception. + + @returns The number of bytes transferred from the stream. + + @throws net::system_error Thrown on failure. +*/ +template< + class SyncReadStream, + class DynamicBuffer, + class CompletionCondition +#if ! BOOST_BEAST_DOXYGEN + , class = typename std::enable_if< + is_sync_read_stream<SyncReadStream>::value && + net::is_dynamic_buffer<DynamicBuffer>::value && + detail::is_invocable<CompletionCondition, + void(error_code&, std::size_t, DynamicBuffer&)>::value + >::type +#endif +> +std::size_t +read( + SyncReadStream& stream, + DynamicBuffer& buffer, + CompletionCondition completion_condition); + +/** Read data into a dynamic buffer from a stream until a condition is met. + + This function is used to read from a stream into a dynamic buffer until + a condition is met. The call will block until one of the following is true: + + @li The specified dynamic buffer sequence is full (that is, it has + reached its currently configured maximum size). + + @li The `completion_condition` function object returns 0. + + This operation is implemented in terms of zero or more calls to the + stream's `read_some` function. + + @param stream The stream from which the data is to be read. The type + must support the <em>SyncReadStream</em> requirements. + + @param buffer The dynamic buffer sequence into which the data will be read. + + @param completion_condition The function object to be called to determine + whether the read operation is complete. The function object must be invocable + with this signature: + @code + std::size_t + completion_condition( + // Modifiable result of latest read_some operation. + error_code& ec, + + // Number of bytes transferred so far. + std::size_t bytes_transferred + + // The dynamic buffer used to store the bytes read + DynamicBuffer& buffer + ); + @endcode + A non-zero return value indicates the maximum number of bytes to be read on + the next call to the stream's `read_some` function. A return value of 0 + from the completion condition indicates that the read operation is complete; + in this case the optionally modifiable error passed to the completion + condition will be delivered to the caller. + + @returns The number of bytes transferred from the stream. +*/ +template< + class SyncReadStream, + class DynamicBuffer, + class CompletionCondition +#if ! BOOST_BEAST_DOXYGEN + , class = typename std::enable_if< + is_sync_read_stream<SyncReadStream>::value && + net::is_dynamic_buffer<DynamicBuffer>::value && + detail::is_invocable<CompletionCondition, + void(error_code&, std::size_t, DynamicBuffer&)>::value + >::type +#endif +> +std::size_t +read( + SyncReadStream& stream, + DynamicBuffer& buffer, + CompletionCondition completion_condition, + error_code& ec); + +/** Asynchronously read data into a dynamic buffer from a stream until a condition is met. + + This function is used to asynchronously read from a stream into a dynamic + buffer until a condition is met. The function call always returns immediately. + The asynchronous operation will continue until one of the following is true: + + @li The specified dynamic buffer sequence is full (that is, it has + reached its currently configured maximum size). + + @li The `completion_condition` function object returns 0. + + This operation is implemented in terms of zero or more calls to the stream's + `async_read_some` function, and is known as a <em>composed operation</em>. The + program must ensure that the stream performs no other read operations (such + as `async_read`, the stream's `async_read_some` function, or any other composed + operations that perform reads) until this operation completes. + + @param stream The stream from which the data is to be read. The type must + support the <em>AsyncReadStream</em> requirements. + + @param buffer The dynamic buffer sequence into which the data will be read. + Ownership of the object is retained by the caller, which must guarantee + that it remains valid until the handler is called. + + @param completion_condition The function object to be called to determine + whether the read operation is complete. The function object must be invocable + with this signature: + @code + std::size_t + completion_condition( + // Modifiable result of latest async_read_some operation. + error_code& ec, + + // Number of bytes transferred so far. + std::size_t bytes_transferred, + + // The dynamic buffer used to store the bytes read + DynamicBuffer& buffer + ); + @endcode + A non-zero return value indicates the maximum number of bytes to be read on + the next call to the stream's `async_read_some` function. A return value of 0 + from the completion condition indicates that the read operation is complete; + in this case the optionally modifiable error passed to the completion + condition will be delivered to the completion handler. + + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void + handler( + error_code const& ec, // Result of operation. + + std::size_t bytes_transferred // Number of bytes copied into + // the dynamic buffer. If an error + // occurred, this will be the number + // of bytes successfully transferred + // prior to the error. + ); + @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `net::post`. +*/ +template< + class AsyncReadStream, + class DynamicBuffer, + class CompletionCondition, + class ReadHandler +#if ! BOOST_BEAST_DOXYGEN + , class = typename std::enable_if< + is_async_read_stream<AsyncReadStream>::value && + net::is_dynamic_buffer<DynamicBuffer>::value && + detail::is_invocable<CompletionCondition, + void(error_code&, std::size_t, DynamicBuffer&)>::value + >::type +#endif +> +BOOST_BEAST_ASYNC_RESULT2(ReadHandler) +async_read( + AsyncReadStream& stream, + DynamicBuffer& buffer, + CompletionCondition&& completion_condition, + ReadHandler&& handler); + +} // detail +} // beast +} // boost + +#include <boost/beast/core/detail/impl/read.hpp> + +#endif diff --git a/boost/beast/core/detail/remap_post_to_defer.hpp b/boost/beast/core/detail/remap_post_to_defer.hpp new file mode 100644 index 0000000000..77f7d8ab20 --- /dev/null +++ b/boost/beast/core/detail/remap_post_to_defer.hpp @@ -0,0 +1,109 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_REMAP_POST_TO_DEFER_HPP +#define BOOST_BEAST_DETAIL_REMAP_POST_TO_DEFER_HPP + +#include <boost/asio/bind_executor.hpp> +#include <boost/asio/is_executor.hpp> +#include <boost/core/empty_value.hpp> +#include <type_traits> +#include <utility> + +namespace boost { +namespace beast { +namespace detail { + +template<class Executor> +class remap_post_to_defer + : private boost::empty_value<Executor> +{ + BOOST_STATIC_ASSERT( + net::is_executor<Executor>::value); + + Executor const& + ex() const noexcept + { + return this->get(); + } + +public: + remap_post_to_defer( + remap_post_to_defer&&) = default; + + remap_post_to_defer( + remap_post_to_defer const&) = default; + + explicit + remap_post_to_defer( + Executor const& ex) + : boost::empty_value<Executor>( + boost::empty_init_t{}, ex) + { + } + + bool + operator==( + remap_post_to_defer const& other) const noexcept + { + return ex() == other.ex(); + } + + bool + operator!=( + remap_post_to_defer const& other) const noexcept + { + return ex() != other.ex(); + } + + decltype(std::declval<Executor const&>().context()) + context() const noexcept + { + return ex().context(); + } + + void + on_work_started() const noexcept + { + ex().on_work_started(); + } + + void + on_work_finished() const noexcept + { + ex().on_work_finished(); + } + + template<class F, class A> + void + dispatch(F&& f, A const& a) const + { + ex().dispatch(std::forward<F>(f), a); + } + + template<class F, class A> + void + post(F&& f, A const& a) const + { + ex().defer(std::forward<F>(f), a); + } + + template<class F, class A> + void + defer(F&& f, A const& a) const + { + ex().defer(std::forward<F>(f), a); + } +}; + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/service_base.hpp b/boost/beast/core/detail/service_base.hpp new file mode 100644 index 0000000000..adec09bbf1 --- /dev/null +++ b/boost/beast/core/detail/service_base.hpp @@ -0,0 +1,43 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_SERVICE_BASE_HPP +#define BOOST_BEAST_DETAIL_SERVICE_BASE_HPP + +#include <boost/asio/execution_context.hpp> + +namespace boost { +namespace beast { +namespace detail { + +template<class T> +struct service_id : net::execution_context::id +{ +}; + +template<class T> +struct service_base : net::execution_context::service +{ + static service_id<T> id; + + explicit + service_base(net::execution_context& ctx) + : net::execution_context::service(ctx) + { + } +}; + +template<class T> +service_id<T> service_base<T>::id; + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/sha1.hpp b/boost/beast/core/detail/sha1.hpp index 034fb9befe..abcc307407 100644 --- a/boost/beast/core/detail/sha1.hpp +++ b/boost/beast/core/detail/sha1.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -10,6 +10,8 @@ #ifndef BOOST_BEAST_DETAIL_SHA1_HPP #define BOOST_BEAST_DETAIL_SHA1_HPP +#include <boost/beast/core/detail/config.hpp> + #include <algorithm> #include <cstdint> #include <cstring> @@ -34,189 +36,6 @@ static std::size_t constexpr BLOCK_INTS = 16; static std::size_t constexpr BLOCK_BYTES = 64; static std::size_t constexpr DIGEST_BYTES = 20; -inline -std::uint32_t -rol(std::uint32_t value, std::size_t bits) -{ - return (value << bits) | (value >> (32 - bits)); -} - -inline -std::uint32_t -blk(std::uint32_t block[BLOCK_INTS], std::size_t i) -{ - return rol( - block[(i+13)&15] ^ block[(i+8)&15] ^ - block[(i+2)&15] ^ block[i], 1); -} - -inline -void -R0(std::uint32_t block[BLOCK_INTS], std::uint32_t v, - std::uint32_t &w, std::uint32_t x, std::uint32_t y, - std::uint32_t &z, std::size_t i) -{ - z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5); - w = rol(w, 30); -} - - -inline -void -R1(std::uint32_t block[BLOCK_INTS], std::uint32_t v, - std::uint32_t &w, std::uint32_t x, std::uint32_t y, - std::uint32_t &z, std::size_t i) -{ - block[i] = blk(block, i); - z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5); - w = rol(w, 30); -} - -inline -void -R2(std::uint32_t block[BLOCK_INTS], std::uint32_t v, - std::uint32_t &w, std::uint32_t x, std::uint32_t y, - std::uint32_t &z, std::size_t i) -{ - block[i] = blk(block, i); - z += (w^x^y) + block[i] + 0x6ed9eba1 + rol(v, 5); - w = rol(w, 30); -} - -inline -void -R3(std::uint32_t block[BLOCK_INTS], std::uint32_t v, - std::uint32_t &w, std::uint32_t x, std::uint32_t y, - std::uint32_t &z, std::size_t i) -{ - block[i] = blk(block, i); - z += (((w|x)&y)|(w&x)) + block[i] + 0x8f1bbcdc + rol(v, 5); - w = rol(w, 30); -} - -inline -void -R4(std::uint32_t block[BLOCK_INTS], std::uint32_t v, - std::uint32_t &w, std::uint32_t x, std::uint32_t y, - std::uint32_t &z, std::size_t i) -{ - block[i] = blk(block, i); - z += (w^x^y) + block[i] + 0xca62c1d6 + rol(v, 5); - w = rol(w, 30); -} - -inline -void -make_block(std::uint8_t const* p, - std::uint32_t block[BLOCK_INTS]) -{ - for(std::size_t i = 0; i < BLOCK_INTS; i++) - block[i] = - (static_cast<std::uint32_t>(p[4*i+3])) | - (static_cast<std::uint32_t>(p[4*i+2]))<< 8 | - (static_cast<std::uint32_t>(p[4*i+1]))<<16 | - (static_cast<std::uint32_t>(p[4*i+0]))<<24; -} - -template<class = void> -void -transform( - std::uint32_t digest[], std::uint32_t block[BLOCK_INTS]) -{ - std::uint32_t a = digest[0]; - std::uint32_t b = digest[1]; - std::uint32_t c = digest[2]; - std::uint32_t d = digest[3]; - std::uint32_t e = digest[4]; - - R0(block, a, b, c, d, e, 0); - R0(block, e, a, b, c, d, 1); - R0(block, d, e, a, b, c, 2); - R0(block, c, d, e, a, b, 3); - R0(block, b, c, d, e, a, 4); - R0(block, a, b, c, d, e, 5); - R0(block, e, a, b, c, d, 6); - R0(block, d, e, a, b, c, 7); - R0(block, c, d, e, a, b, 8); - R0(block, b, c, d, e, a, 9); - R0(block, a, b, c, d, e, 10); - R0(block, e, a, b, c, d, 11); - R0(block, d, e, a, b, c, 12); - R0(block, c, d, e, a, b, 13); - R0(block, b, c, d, e, a, 14); - R0(block, a, b, c, d, e, 15); - R1(block, e, a, b, c, d, 0); - R1(block, d, e, a, b, c, 1); - R1(block, c, d, e, a, b, 2); - R1(block, b, c, d, e, a, 3); - R2(block, a, b, c, d, e, 4); - R2(block, e, a, b, c, d, 5); - R2(block, d, e, a, b, c, 6); - R2(block, c, d, e, a, b, 7); - R2(block, b, c, d, e, a, 8); - R2(block, a, b, c, d, e, 9); - R2(block, e, a, b, c, d, 10); - R2(block, d, e, a, b, c, 11); - R2(block, c, d, e, a, b, 12); - R2(block, b, c, d, e, a, 13); - R2(block, a, b, c, d, e, 14); - R2(block, e, a, b, c, d, 15); - R2(block, d, e, a, b, c, 0); - R2(block, c, d, e, a, b, 1); - R2(block, b, c, d, e, a, 2); - R2(block, a, b, c, d, e, 3); - R2(block, e, a, b, c, d, 4); - R2(block, d, e, a, b, c, 5); - R2(block, c, d, e, a, b, 6); - R2(block, b, c, d, e, a, 7); - R3(block, a, b, c, d, e, 8); - R3(block, e, a, b, c, d, 9); - R3(block, d, e, a, b, c, 10); - R3(block, c, d, e, a, b, 11); - R3(block, b, c, d, e, a, 12); - R3(block, a, b, c, d, e, 13); - R3(block, e, a, b, c, d, 14); - R3(block, d, e, a, b, c, 15); - R3(block, c, d, e, a, b, 0); - R3(block, b, c, d, e, a, 1); - R3(block, a, b, c, d, e, 2); - R3(block, e, a, b, c, d, 3); - R3(block, d, e, a, b, c, 4); - R3(block, c, d, e, a, b, 5); - R3(block, b, c, d, e, a, 6); - R3(block, a, b, c, d, e, 7); - R3(block, e, a, b, c, d, 8); - R3(block, d, e, a, b, c, 9); - R3(block, c, d, e, a, b, 10); - R3(block, b, c, d, e, a, 11); - R4(block, a, b, c, d, e, 12); - R4(block, e, a, b, c, d, 13); - R4(block, d, e, a, b, c, 14); - R4(block, c, d, e, a, b, 15); - R4(block, b, c, d, e, a, 0); - R4(block, a, b, c, d, e, 1); - R4(block, e, a, b, c, d, 2); - R4(block, d, e, a, b, c, 3); - R4(block, c, d, e, a, b, 4); - R4(block, b, c, d, e, a, 5); - R4(block, a, b, c, d, e, 6); - R4(block, e, a, b, c, d, 7); - R4(block, d, e, a, b, c, 8); - R4(block, c, d, e, a, b, 9); - R4(block, b, c, d, e, a, 10); - R4(block, a, b, c, d, e, 11); - R4(block, e, a, b, c, d, 12); - R4(block, d, e, a, b, c, 13); - R4(block, c, d, e, a, b, 14); - R4(block, b, c, d, e, a, 15); - - digest[0] += a; - digest[1] += b; - digest[2] += c; - digest[3] += d; - digest[4] += e; -} - } // sha1 struct sha1_context @@ -230,84 +49,29 @@ struct sha1_context std::uint8_t buf[block_size]; }; -template<class = void> +BOOST_BEAST_DECL void -init(sha1_context& ctx) noexcept -{ - ctx.buflen = 0; - ctx.blocks = 0; - ctx.digest[0] = 0x67452301; - ctx.digest[1] = 0xefcdab89; - ctx.digest[2] = 0x98badcfe; - ctx.digest[3] = 0x10325476; - ctx.digest[4] = 0xc3d2e1f0; -} +init(sha1_context& ctx) noexcept; -template<class = void> +BOOST_BEAST_DECL void -update(sha1_context& ctx, - void const* message, std::size_t size) noexcept -{ - auto p = static_cast< - std::uint8_t const*>(message); - for(;;) - { - auto const n = (std::min)( - size, sizeof(ctx.buf) - ctx.buflen); - std::memcpy(ctx.buf + ctx.buflen, p, n); - ctx.buflen += n; - if(ctx.buflen != 64) - return; - p += n; - size -= n; - ctx.buflen = 0; - std::uint32_t block[sha1::BLOCK_INTS]; - sha1::make_block(ctx.buf, block); - sha1::transform(ctx.digest, block); - ++ctx.blocks; - } -} +update( + sha1_context& ctx, + void const* message, + std::size_t size) noexcept; -template<class = void> +BOOST_BEAST_DECL void -finish(sha1_context& ctx, void* digest) noexcept -{ - using sha1::BLOCK_INTS; - using sha1::BLOCK_BYTES; - - std::uint64_t total_bits = - (ctx.blocks*64 + ctx.buflen) * 8; - // pad - ctx.buf[ctx.buflen++] = 0x80; - auto const buflen = ctx.buflen; - while(ctx.buflen < 64) - ctx.buf[ctx.buflen++] = 0x00; - std::uint32_t block[BLOCK_INTS]; - sha1::make_block(ctx.buf, block); - if(buflen > BLOCK_BYTES - 8) - { - sha1::transform(ctx.digest, block); - for(size_t i = 0; i < BLOCK_INTS - 2; i++) - block[i] = 0; - } - - /* Append total_bits, split this uint64_t into two uint32_t */ - block[BLOCK_INTS - 1] = total_bits & 0xffffffff; - block[BLOCK_INTS - 2] = (total_bits >> 32); - sha1::transform(ctx.digest, block); - for(std::size_t i = 0; i < sha1::DIGEST_BYTES/4; i++) - { - std::uint8_t* d = - static_cast<std::uint8_t*>(digest) + 4 * i; - d[3] = ctx.digest[i] & 0xff; - d[2] = (ctx.digest[i] >> 8) & 0xff; - d[1] = (ctx.digest[i] >> 16) & 0xff; - d[0] = (ctx.digest[i] >> 24) & 0xff; - } -} +finish( + sha1_context& ctx, + void* digest) noexcept; } // detail } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY +#include <boost/beast/core/detail/sha1.ipp> +#endif + #endif diff --git a/boost/beast/core/detail/sha1.ipp b/boost/beast/core/detail/sha1.ipp new file mode 100644 index 0000000000..8d739f3449 --- /dev/null +++ b/boost/beast/core/detail/sha1.ipp @@ -0,0 +1,301 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_SHA1_IPP +#define BOOST_BEAST_DETAIL_SHA1_IPP + +#include <boost/beast/core/detail/sha1.hpp> + +#include <algorithm> +#include <cstdint> +#include <cstring> + +// Based on https://github.com/vog/sha1 +/* + Original authors: + Steve Reid (Original C Code) + Bruce Guenter (Small changes to fit into bglibs) + Volker Grabsch (Translation to simpler C++ Code) + Eugene Hopkinson (Safety improvements) + Vincent Falco (beast adaptation) +*/ + +namespace boost { +namespace beast { +namespace detail { + +namespace sha1 { + +inline +std::uint32_t +rol(std::uint32_t value, std::size_t bits) +{ + return (value << bits) | (value >> (32 - bits)); +} + +inline +std::uint32_t +blk(std::uint32_t block[BLOCK_INTS], std::size_t i) +{ + return rol( + block[(i+13)&15] ^ block[(i+8)&15] ^ + block[(i+2)&15] ^ block[i], 1); +} + +inline +void +R0(std::uint32_t block[BLOCK_INTS], std::uint32_t v, + std::uint32_t &w, std::uint32_t x, std::uint32_t y, + std::uint32_t &z, std::size_t i) +{ + z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5); + w = rol(w, 30); +} + + +inline +void +R1(std::uint32_t block[BLOCK_INTS], std::uint32_t v, + std::uint32_t &w, std::uint32_t x, std::uint32_t y, + std::uint32_t &z, std::size_t i) +{ + block[i] = blk(block, i); + z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5); + w = rol(w, 30); +} + +inline +void +R2(std::uint32_t block[BLOCK_INTS], std::uint32_t v, + std::uint32_t &w, std::uint32_t x, std::uint32_t y, + std::uint32_t &z, std::size_t i) +{ + block[i] = blk(block, i); + z += (w^x^y) + block[i] + 0x6ed9eba1 + rol(v, 5); + w = rol(w, 30); +} + +inline +void +R3(std::uint32_t block[BLOCK_INTS], std::uint32_t v, + std::uint32_t &w, std::uint32_t x, std::uint32_t y, + std::uint32_t &z, std::size_t i) +{ + block[i] = blk(block, i); + z += (((w|x)&y)|(w&x)) + block[i] + 0x8f1bbcdc + rol(v, 5); + w = rol(w, 30); +} + +inline +void +R4(std::uint32_t block[BLOCK_INTS], std::uint32_t v, + std::uint32_t &w, std::uint32_t x, std::uint32_t y, + std::uint32_t &z, std::size_t i) +{ + block[i] = blk(block, i); + z += (w^x^y) + block[i] + 0xca62c1d6 + rol(v, 5); + w = rol(w, 30); +} + +inline +void +make_block(std::uint8_t const* p, + std::uint32_t block[BLOCK_INTS]) +{ + for(std::size_t i = 0; i < BLOCK_INTS; i++) + block[i] = + (static_cast<std::uint32_t>(p[4*i+3])) | + (static_cast<std::uint32_t>(p[4*i+2]))<< 8 | + (static_cast<std::uint32_t>(p[4*i+1]))<<16 | + (static_cast<std::uint32_t>(p[4*i+0]))<<24; +} + +inline +void +transform( + std::uint32_t digest[], std::uint32_t block[BLOCK_INTS]) +{ + std::uint32_t a = digest[0]; + std::uint32_t b = digest[1]; + std::uint32_t c = digest[2]; + std::uint32_t d = digest[3]; + std::uint32_t e = digest[4]; + + R0(block, a, b, c, d, e, 0); + R0(block, e, a, b, c, d, 1); + R0(block, d, e, a, b, c, 2); + R0(block, c, d, e, a, b, 3); + R0(block, b, c, d, e, a, 4); + R0(block, a, b, c, d, e, 5); + R0(block, e, a, b, c, d, 6); + R0(block, d, e, a, b, c, 7); + R0(block, c, d, e, a, b, 8); + R0(block, b, c, d, e, a, 9); + R0(block, a, b, c, d, e, 10); + R0(block, e, a, b, c, d, 11); + R0(block, d, e, a, b, c, 12); + R0(block, c, d, e, a, b, 13); + R0(block, b, c, d, e, a, 14); + R0(block, a, b, c, d, e, 15); + R1(block, e, a, b, c, d, 0); + R1(block, d, e, a, b, c, 1); + R1(block, c, d, e, a, b, 2); + R1(block, b, c, d, e, a, 3); + R2(block, a, b, c, d, e, 4); + R2(block, e, a, b, c, d, 5); + R2(block, d, e, a, b, c, 6); + R2(block, c, d, e, a, b, 7); + R2(block, b, c, d, e, a, 8); + R2(block, a, b, c, d, e, 9); + R2(block, e, a, b, c, d, 10); + R2(block, d, e, a, b, c, 11); + R2(block, c, d, e, a, b, 12); + R2(block, b, c, d, e, a, 13); + R2(block, a, b, c, d, e, 14); + R2(block, e, a, b, c, d, 15); + R2(block, d, e, a, b, c, 0); + R2(block, c, d, e, a, b, 1); + R2(block, b, c, d, e, a, 2); + R2(block, a, b, c, d, e, 3); + R2(block, e, a, b, c, d, 4); + R2(block, d, e, a, b, c, 5); + R2(block, c, d, e, a, b, 6); + R2(block, b, c, d, e, a, 7); + R3(block, a, b, c, d, e, 8); + R3(block, e, a, b, c, d, 9); + R3(block, d, e, a, b, c, 10); + R3(block, c, d, e, a, b, 11); + R3(block, b, c, d, e, a, 12); + R3(block, a, b, c, d, e, 13); + R3(block, e, a, b, c, d, 14); + R3(block, d, e, a, b, c, 15); + R3(block, c, d, e, a, b, 0); + R3(block, b, c, d, e, a, 1); + R3(block, a, b, c, d, e, 2); + R3(block, e, a, b, c, d, 3); + R3(block, d, e, a, b, c, 4); + R3(block, c, d, e, a, b, 5); + R3(block, b, c, d, e, a, 6); + R3(block, a, b, c, d, e, 7); + R3(block, e, a, b, c, d, 8); + R3(block, d, e, a, b, c, 9); + R3(block, c, d, e, a, b, 10); + R3(block, b, c, d, e, a, 11); + R4(block, a, b, c, d, e, 12); + R4(block, e, a, b, c, d, 13); + R4(block, d, e, a, b, c, 14); + R4(block, c, d, e, a, b, 15); + R4(block, b, c, d, e, a, 0); + R4(block, a, b, c, d, e, 1); + R4(block, e, a, b, c, d, 2); + R4(block, d, e, a, b, c, 3); + R4(block, c, d, e, a, b, 4); + R4(block, b, c, d, e, a, 5); + R4(block, a, b, c, d, e, 6); + R4(block, e, a, b, c, d, 7); + R4(block, d, e, a, b, c, 8); + R4(block, c, d, e, a, b, 9); + R4(block, b, c, d, e, a, 10); + R4(block, a, b, c, d, e, 11); + R4(block, e, a, b, c, d, 12); + R4(block, d, e, a, b, c, 13); + R4(block, c, d, e, a, b, 14); + R4(block, b, c, d, e, a, 15); + + digest[0] += a; + digest[1] += b; + digest[2] += c; + digest[3] += d; + digest[4] += e; +} + +} // sha1 + +void +init(sha1_context& ctx) noexcept +{ + ctx.buflen = 0; + ctx.blocks = 0; + ctx.digest[0] = 0x67452301; + ctx.digest[1] = 0xefcdab89; + ctx.digest[2] = 0x98badcfe; + ctx.digest[3] = 0x10325476; + ctx.digest[4] = 0xc3d2e1f0; +} + +void +update( + sha1_context& ctx, + void const* message, + std::size_t size) noexcept +{ + auto p = static_cast< + std::uint8_t const*>(message); + for(;;) + { + auto const n = (std::min)( + size, sizeof(ctx.buf) - ctx.buflen); + std::memcpy(ctx.buf + ctx.buflen, p, n); + ctx.buflen += n; + if(ctx.buflen != 64) + return; + p += n; + size -= n; + ctx.buflen = 0; + std::uint32_t block[sha1::BLOCK_INTS]; + sha1::make_block(ctx.buf, block); + sha1::transform(ctx.digest, block); + ++ctx.blocks; + } +} + +void +finish( + sha1_context& ctx, + void* digest) noexcept +{ + using sha1::BLOCK_INTS; + using sha1::BLOCK_BYTES; + + std::uint64_t total_bits = + (ctx.blocks*64 + ctx.buflen) * 8; + // pad + ctx.buf[ctx.buflen++] = 0x80; + auto const buflen = ctx.buflen; + while(ctx.buflen < 64) + ctx.buf[ctx.buflen++] = 0x00; + std::uint32_t block[BLOCK_INTS]; + sha1::make_block(ctx.buf, block); + if(buflen > BLOCK_BYTES - 8) + { + sha1::transform(ctx.digest, block); + for(size_t i = 0; i < BLOCK_INTS - 2; i++) + block[i] = 0; + } + + /* Append total_bits, split this uint64_t into two uint32_t */ + block[BLOCK_INTS - 1] = total_bits & 0xffffffff; + block[BLOCK_INTS - 2] = (total_bits >> 32); + sha1::transform(ctx.digest, block); + for(std::size_t i = 0; i < sha1::DIGEST_BYTES/4; i++) + { + std::uint8_t* d = + static_cast<std::uint8_t*>(digest) + 4 * i; + d[3] = ctx.digest[i] & 0xff; + d[2] = (ctx.digest[i] >> 8) & 0xff; + d[1] = (ctx.digest[i] >> 16) & 0xff; + d[0] = (ctx.digest[i] >> 24) & 0xff; + } +} + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/static_const.hpp b/boost/beast/core/detail/static_const.hpp new file mode 100644 index 0000000000..f8582326c8 --- /dev/null +++ b/boost/beast/core/detail/static_const.hpp @@ -0,0 +1,49 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_STATIC_CONST_HPP +#define BOOST_BEAST_DETAIL_STATIC_CONST_HPP + +/* This is a derivative work, original copyright: + + Copyright Eric Niebler 2013-present + + Use, modification and distribution is subject to 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) + + Project home: https://github.com/ericniebler/range-v3 +*/ + +namespace boost { +namespace beast { +namespace detail { + +template<typename T> +struct static_const +{ + static constexpr T value {}; +}; + +template<typename T> +constexpr T static_const<T>::value; + +#define BOOST_BEAST_INLINE_VARIABLE(name, type) \ + namespace \ + { \ + constexpr auto& name = \ + ::boost::beast::detail::static_const<type>::value; \ + } + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/static_ostream.hpp b/boost/beast/core/detail/static_ostream.hpp index cb5a00b3da..5eaa413b30 100644 --- a/boost/beast/core/detail/static_ostream.hpp +++ b/boost/beast/core/detail/static_ostream.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) diff --git a/boost/beast/core/detail/static_string.hpp b/boost/beast/core/detail/static_string.hpp index a5384591ff..3b080e916e 100644 --- a/boost/beast/core/detail/static_string.hpp +++ b/boost/beast/core/detail/static_string.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -41,24 +41,22 @@ lexicographical_compare( } template<class CharT, class Traits> -inline int lexicographical_compare( basic_string_view<CharT, Traits> s1, CharT const* s2, std::size_t n2) { - return lexicographical_compare<CharT, Traits>( - s1.data(), s1.size(), s2, n2); + return detail::lexicographical_compare< + CharT, Traits>(s1.data(), s1.size(), s2, n2); } template<class CharT, class Traits> -inline int lexicographical_compare( basic_string_view<CharT, Traits> s1, basic_string_view<CharT, Traits> s2) { - return lexicographical_compare<CharT, Traits>( + return detail::lexicographical_compare<CharT, Traits>( s1.data(), s1.size(), s2.data(), s2.size()); } diff --git a/boost/beast/core/detail/stream_base.hpp b/boost/beast/core/detail/stream_base.hpp new file mode 100644 index 0000000000..00591216de --- /dev/null +++ b/boost/beast/core/detail/stream_base.hpp @@ -0,0 +1,107 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_DETAIL_STREAM_BASE_HPP +#define BOOST_BEAST_CORE_DETAIL_STREAM_BASE_HPP + +#include <boost/asio/steady_timer.hpp> +#include <boost/assert.hpp> +#include <boost/core/exchange.hpp> +#include <chrono> +#include <cstdint> +#include <utility> + +namespace boost { +namespace beast { +namespace detail { + +struct any_endpoint +{ + template<class Error, class Endpoint> + bool + operator()( + Error const&, Endpoint const&) const noexcept + { + return true; + } +}; + +struct stream_base +{ + using clock_type = std::chrono::steady_clock; + using time_point = typename + std::chrono::steady_clock::time_point; + using tick_type = std::uint64_t; + + struct op_state + { + net::steady_timer timer; // for timing out + tick_type tick = 0; // counts waits + bool pending = false; // if op is pending + bool timeout = false; // if timed out + + template<class... Args> + explicit + op_state(Args&&... args) + : timer(std::forward<Args>(args)...) + { + } + }; + + class pending_guard + { + bool& b_; + bool clear_ = true; + + public: + ~pending_guard() + { + if(clear_) + b_ = false; + } + + explicit + pending_guard(bool& b) + : b_(b) + { + BOOST_ASSERT(! b_); + b_ = true; + } + + pending_guard( + pending_guard&& other) noexcept + : b_(other.b_) + , clear_(boost::exchange( + other.clear_, false)) + { + } + + void + reset() + { + BOOST_ASSERT(clear_); + b_ = false; + clear_ = false; + } + }; + + static time_point never() noexcept + { + return (time_point::max)(); + } + + static std::size_t constexpr no_limit = + (std::numeric_limits<std::size_t>::max)(); +}; + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/stream_traits.hpp b/boost/beast/core/detail/stream_traits.hpp new file mode 100644 index 0000000000..bb1e29c2d3 --- /dev/null +++ b/boost/beast/core/detail/stream_traits.hpp @@ -0,0 +1,111 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_STREAM_TRAITS_HPP +#define BOOST_BEAST_DETAIL_STREAM_TRAITS_HPP + +#include <boost/beast/core/error.hpp> +#include <boost/asio/buffer.hpp> +#include <boost/type_traits/make_void.hpp> +#include <type_traits> + +namespace boost { +namespace beast { +namespace detail { + +//------------------------------------------------------------------------------ +// +// get_lowest_layer +// lowest_layer_type +// detail::has_next_layer +// + +template <class T> +std::false_type has_next_layer_impl(void*); + +template <class T> +auto has_next_layer_impl(decltype(nullptr)) -> + decltype(std::declval<T&>().next_layer(), std::true_type{}); + +template <class T> +using has_next_layer = decltype(has_next_layer_impl<T>(nullptr)); + +template<class T, bool = has_next_layer<T>::value> +struct lowest_layer_type_impl +{ + using type = typename std::remove_reference<T>::type; +}; + +template<class T> +struct lowest_layer_type_impl<T, true> +{ + using type = typename lowest_layer_type_impl< + decltype(std::declval<T&>().next_layer())>::type; +}; + +template<class T> +using lowest_layer_type = typename + lowest_layer_type_impl<T>::type; + +template<class T> +T& +get_lowest_layer_impl( + T& t, std::false_type) noexcept +{ + return t; +} + +template<class T> +lowest_layer_type<T>& +get_lowest_layer_impl( + T& t, std::true_type) noexcept +{ + return get_lowest_layer_impl(t.next_layer(), + has_next_layer<typename std::decay< + decltype(t.next_layer())>::type>{}); +} + +//------------------------------------------------------------------------------ + +// Types that meet the requirements, +// for use with std::declval only. +template<class BufferType> +struct BufferSequence +{ + using value_type = BufferType; + using const_iterator = BufferType const*; + ~BufferSequence() = default; + BufferSequence(BufferSequence const&) = default; + const_iterator begin() const noexcept { return {}; } + const_iterator end() const noexcept { return {}; } +}; +using ConstBufferSequence = + BufferSequence<net::const_buffer>; +using MutableBufferSequence = + BufferSequence<net::mutable_buffer>; + +// + +// Types that meet the requirements, +// for use with std::declval only. +struct StreamHandler +{ + StreamHandler(StreamHandler const&) = default; + void operator()(error_code, std::size_t) {} +}; +using ReadHandler = StreamHandler; +using WriteHandler = StreamHandler; + +//------------------------------------------------------------------------------ + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/tuple.hpp b/boost/beast/core/detail/tuple.hpp new file mode 100644 index 0000000000..5d65e9de77 --- /dev/null +++ b/boost/beast/core/detail/tuple.hpp @@ -0,0 +1,116 @@ +// +// Copyright (c) 2016-2019Damian Jarek (damian dot jarek93 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_DETAIL_TUPLE_HPP +#define BOOST_BEAST_DETAIL_TUPLE_HPP + +#include <boost/mp11/integer_sequence.hpp> +#include <boost/mp11/algorithm.hpp> +#include <boost/type_traits/remove_cv.hpp> +#include <boost/type_traits/copy_cv.hpp> +#include <cstdlib> +#include <utility> + +namespace boost { +namespace beast { +namespace detail { + +template<std::size_t I, class T> +struct tuple_element_impl +{ + T t; + + tuple_element_impl(T const& t_) + : t(t_) + { + } + + tuple_element_impl(T&& t_) + : t(std::move(t_)) + { + } +}; + +template<std::size_t I, class T> +struct tuple_element_impl<I, T&> +{ + T& t; + + tuple_element_impl(T& t_) + : t(t_) + { + } +}; + +template<class... Ts> +struct tuple_impl; + +template<class... Ts, std::size_t... Is> +struct tuple_impl< + boost::mp11::index_sequence<Is...>, Ts...> + : tuple_element_impl<Is, Ts>... +{ + template<class... Us> + explicit tuple_impl(Us&&... us) + : tuple_element_impl<Is, Ts>( + std::forward<Us>(us))... + { + } +}; + +template<class... Ts> +struct tuple : tuple_impl< + boost::mp11::index_sequence_for<Ts...>, Ts...> +{ + template<class... Us> + explicit tuple(Us&&... us) + : tuple_impl< + boost::mp11::index_sequence_for<Ts...>, Ts...>{ + std::forward<Us>(us)...} + { + } +}; + +template<std::size_t I, class T> +T& +get(tuple_element_impl<I, T>& te) +{ + return te.t; +} + +template<std::size_t I, class T> +T const& +get(tuple_element_impl<I, T> const& te) +{ + return te.t; +} + +template<std::size_t I, class T> +T&& +get(tuple_element_impl<I, T>&& te) +{ + return std::move(te.t); +} + +template<std::size_t I, class T> +T& +get(tuple_element_impl<I, T&>&& te) +{ + return te.t; +} + +template <std::size_t I, class T> +using tuple_element = typename boost::copy_cv< + mp11::mp_at_c<typename remove_cv<T>::type, I>, T>::type; + +} // detail +} // beast +} // boost + +#endif diff --git a/boost/beast/core/detail/type_traits.hpp b/boost/beast/core/detail/type_traits.hpp index 3d379846b9..3d895403b3 100644 --- a/boost/beast/core/detail/type_traits.hpp +++ b/boost/beast/core/detail/type_traits.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -11,8 +11,10 @@ #define BOOST_BEAST_DETAIL_TYPE_TRAITS_HPP #include <boost/beast/core/error.hpp> +#include <boost/beast/core/detail/is_invocable.hpp> #include <boost/asio/buffer.hpp> -#include <boost/type_traits.hpp> +#include <boost/mp11/function.hpp> +#include <boost/type_traits/make_void.hpp> #include <iterator> #include <tuple> #include <type_traits> @@ -23,8 +25,22 @@ namespace boost { namespace beast { namespace detail { +// variadic min +template<class T> +T constexpr min(T t) +{ + return t; +} + +template<class T, class...Tn> +T constexpr min(T t0, T t1, Tn... tn) +{ + return (t0 < t1) ? + (detail::min)(t0, tn...) : + (detail::min)(t1, tn...); +} + template<class U> -inline std::size_t constexpr max_sizeof() { @@ -32,7 +48,6 @@ max_sizeof() } template<class U0, class U1, class... Us> -inline std::size_t constexpr max_sizeof() { @@ -42,7 +57,6 @@ max_sizeof() } template<class U> -inline std::size_t constexpr max_alignof() { @@ -84,76 +98,11 @@ using aligned_union_t = //------------------------------------------------------------------------------ template<class T> -inline void accept_rv(T){} //------------------------------------------------------------------------------ -template<unsigned N, class T, class... Tn> -struct repeat_tuple_impl -{ - using type = typename repeat_tuple_impl< - N - 1, T, T, Tn...>::type; -}; - -template<class T, class... Tn> -struct repeat_tuple_impl<0, T, Tn...> -{ - using type = std::tuple<T, Tn...>; -}; - -template<unsigned N, class T> -struct repeat_tuple -{ - using type = - typename repeat_tuple_impl<N-1, T>::type; -}; - -template<class T> -struct repeat_tuple<0, T> -{ - using type = std::tuple<>; -}; - -//------------------------------------------------------------------------------ - -template<class R, class C, class ...A> -auto -is_invocable_test(C&& c, int, A&& ...a) - -> decltype(std::is_convertible< - decltype(c(std::forward<A>(a)...)), R>::value || - std::is_same<R, void>::value, - std::true_type()); - -template<class R, class C, class ...A> -std::false_type -is_invocable_test(C&& c, long, A&& ...a); - -/** Metafunction returns `true` if F callable as R(A...) - - Example: - - @code - is_invocable<T, void(std::string)> - @endcode -*/ -/** @{ */ -template<class C, class F> -struct is_invocable : std::false_type -{ -}; - -template<class C, class R, class ...A> -struct is_invocable<C, R(A...)> - : decltype(is_invocable_test<R>( - std::declval<C>(), 1, std::declval<A>()...)) -{ -}; -/** @} */ - -//------------------------------------------------------------------------------ - // for span template<class T, class E, class = void> struct is_contiguous_container: std::false_type {}; @@ -175,297 +124,6 @@ struct is_contiguous_container<T, E, void_t< >::type>>: std::true_type {}; -//------------------------------------------------------------------------------ - -template<class...> -struct unwidest_unsigned; - -template<class U0> -struct unwidest_unsigned<U0> -{ - using type = U0; -}; - -template<class U0, class... UN> -struct unwidest_unsigned<U0, UN...> -{ - BOOST_STATIC_ASSERT(std::is_unsigned<U0>::value); - using type = typename std::conditional< - (sizeof(U0) < sizeof(typename unwidest_unsigned<UN...>::type)), - U0, typename unwidest_unsigned<UN...>::type>::type; -}; - -template<class...> -struct widest_unsigned; - -template<class U0> -struct widest_unsigned<U0> -{ - using type = U0; -}; - -template<class U0, class... UN> -struct widest_unsigned<U0, UN...> -{ - BOOST_STATIC_ASSERT(std::is_unsigned<U0>::value); - using type = typename std::conditional< - (sizeof(U0) > sizeof(typename widest_unsigned<UN...>::type)), - U0, typename widest_unsigned<UN...>::type>::type; -}; - -template<class U> -inline -constexpr -U -min_all(U u) -{ - BOOST_STATIC_ASSERT(std::is_unsigned<U>::value); - return u; -} - -template<class U0, class U1, class... UN> -inline -constexpr -typename unwidest_unsigned<U0, U1, UN...>::type -min_all(U0 u0, U1 u1, UN... un) -{ - using type = - typename unwidest_unsigned<U0, U1, UN...>::type; - return u0 < u1 ? - static_cast<type>(min_all(u0, un...)) : - static_cast<type>(min_all(u1, un...)); -} - -template<class U> -inline -constexpr -U -max_all(U u) -{ - BOOST_STATIC_ASSERT(std::is_unsigned<U>::value); - return u; -} - -template<class U0, class U1, class... UN> -inline -constexpr -typename widest_unsigned<U0, U1, UN...>::type -max_all(U0 u0, U1 u1, UN... un) -{ - return u0 > u1? max_all(u0, un...) : max_all(u1, un...); -} - -//------------------------------------------------------------------------------ - -template<class T, class = void> -struct get_lowest_layer_helper -{ - using type = T; -}; - -template<class T> -struct get_lowest_layer_helper<T, - void_t<typename T::lowest_layer_type>> -{ - using type = typename T::lowest_layer_type; -}; - -//------------------------------------------------------------------------------ - -// -// buffer concepts -// - -// Types that meet the requirements, -// for use with std::declval only. -template<class BufferType> -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<boost::asio::const_buffer>; -using MutableBufferSequence = - BufferSequence<boost::asio::mutable_buffer>; - -template<class B1, class... Bn> -struct is_all_const_buffer_sequence - : std::integral_constant<bool, - boost::asio::is_const_buffer_sequence<B1>::value && - is_all_const_buffer_sequence<Bn...>::value> -{ -}; - -template<class B> -struct is_all_const_buffer_sequence<B> - : boost::asio::is_const_buffer_sequence<B> -{ -}; - -template<class... Bn> -struct common_buffers_type -{ - using type = typename std::conditional< - boost::is_convertible<std::tuple<Bn...>, - typename repeat_tuple<sizeof...(Bn), - boost::asio::mutable_buffer>::type>::value, - boost::asio::mutable_buffer, - boost::asio::const_buffer>::type; -}; - -template<class B> -struct buffer_sequence_iterator -{ - using type = decltype( - boost::asio::buffer_sequence_begin( - std::declval<B const&>())); -}; - -// Types that meet the requirements, -// for use with std::declval only. -struct StreamHandler -{ - StreamHandler(StreamHandler const&) = default; - void operator()(error_code ec, std::size_t); -}; -using ReadHandler = StreamHandler; -using WriteHandler = StreamHandler; - -template<class Buffers> -class buffers_range_adaptor -{ - Buffers const& b_; - -public: - using value_type = typename std::conditional< - boost::is_convertible< - typename std::iterator_traits< - typename buffer_sequence_iterator< - Buffers>::type>::value_type, - boost::asio::mutable_buffer>::value, - boost::asio::mutable_buffer, - boost::asio::const_buffer>::type; - - class const_iterator - { - friend class buffers_range_adaptor; - - using iter_type = typename - buffer_sequence_iterator<Buffers>::type; - - iter_type it_; - - const_iterator(iter_type const& it) - : it_(it) - { - } - - public: - using value_type = typename - buffers_range_adaptor::value_type; - using pointer = value_type const*; - using reference = value_type; - using difference_type = std::ptrdiff_t; - using iterator_category = - std::bidirectional_iterator_tag; - - bool - operator==(const_iterator const& other) const - { - return it_ == other.it_; - } - - bool - operator!=(const_iterator const& other) const - { - return ! (*this == other); - } - - reference - operator*() const - { - return *it_; - } - - pointer - operator->() const = delete; - - const_iterator& - operator++() - { - ++it_; - return *this; - } - - const_iterator - operator++(int) - { - auto temp = *this; - ++(*this); - return temp; - } - - // deprecated - const_iterator& - operator--() - { - --it_; - return *this; - } - - // deprecated - const_iterator - operator--(int) - { - auto temp = *this; - --(*this); - return temp; - } - }; - - explicit - buffers_range_adaptor(Buffers const& b) - : b_(b) - { - } - - const_iterator - begin() const noexcept - { - return boost::asio::buffer_sequence_begin(b_); - } - - const_iterator - end() const noexcept - { - return boost::asio::buffer_sequence_end(b_); - } -}; - -template<class Buffers> -buffers_range_adaptor<Buffers> -buffers_range(Buffers const& buffers) -{ - return buffers_range_adaptor<Buffers>{buffers}; -} - -/* If this static assert goes off, it means that the completion - handler you provided to an asynchronous initiating function did - not have the right signature. Check the parameter types for your - completion handler and make sure they match the list of types - expected by the initiating function, -*/ -#define BOOST_BEAST_HANDLER_INIT(type, sig) \ - static_assert(boost::beast::is_completion_handler< \ - BOOST_ASIO_HANDLER_TYPE(type, sig), sig>::value, \ - "CompletionHandler signature requirements not met"); \ - boost::asio::async_completion<type, sig> init{handler} - } // detail } // beast } // boost diff --git a/boost/beast/core/detail/variant.hpp b/boost/beast/core/detail/variant.hpp index 536cb7593f..94bd4c040c 100644 --- a/boost/beast/core/detail/variant.hpp +++ b/boost/beast/core/detail/variant.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -12,9 +12,7 @@ #include <boost/beast/core/detail/type_traits.hpp> #include <boost/assert.hpp> -#include <cstddef> -#include <tuple> -#include <type_traits> +#include <boost/mp11/algorithm.hpp> namespace boost { namespace beast { @@ -34,19 +32,115 @@ class variant detail::aligned_union_t<1, TN...> buf_; unsigned char i_ = 0; - template<std::size_t I> - using type = typename std::tuple_element< - I, std::tuple<TN...>>::type; + struct destroy + { + variant& self; - template<std::size_t I> - using C = std::integral_constant<std::size_t, I>; + void operator()(mp11::mp_size_t<0>) + { + } + + template<class I> + void operator()(I) noexcept + { + using T = + mp11::mp_at_c<variant, I::value - 1>; + reinterpret_cast<T&>(self.buf_).~T(); + } + }; + + struct copy + { + variant& self; + variant const& other; + + void operator()(mp11::mp_size_t<0>) + { + } + + template<class I> + void operator()(I) + { + using T = + mp11::mp_at_c<variant, I::value - 1>; + ::new(&self.buf_) T( + reinterpret_cast<T const&>(other.buf_)); + self.i_ = I::value; + } + }; + + struct move + { + variant& self; + variant& other; + + void operator()(mp11::mp_size_t<0>) + { + } + + template<class I> + void operator()(I) + { + using T = + mp11::mp_at_c<variant, I::value - 1>; + ::new(&self.buf_) T( + reinterpret_cast<T&&>(other.buf_)); + reinterpret_cast<T&>(other.buf_).~T(); + self.i_ = I::value; + } + }; + + struct equals + { + variant const& self; + variant const& other; + + bool operator()(mp11::mp_size_t<0>) + { + return true; + } + + template<class I> + bool operator()(I) + { + using T = + mp11::mp_at_c<variant, I::value - 1>; + return + reinterpret_cast<T const&>(self.buf_) == + reinterpret_cast<T const&>(other.buf_); + } + }; + + + void destruct() + { + mp11::mp_with_index< + sizeof...(TN) + 1>( + i_, destroy{*this}); + i_ = 0; + } + + void copy_construct(variant const& other) + { + mp11::mp_with_index< + sizeof...(TN) + 1>( + other.i_, copy{*this, other}); + } + + void move_construct(variant& other) + { + mp11::mp_with_index< + sizeof...(TN) + 1>( + other.i_, move{*this, other}); + other.i_ = 0; + } public: variant() = default; ~variant() { - destroy(C<0>{}); + destruct(); } bool @@ -54,7 +148,9 @@ public: { if(i_ != other.i_) return false; - return equal(other, C<0>{}); + return mp11::mp_with_index< + sizeof...(TN) + 1>( + i_, equals{*this, other}); } // 0 = empty @@ -65,15 +161,14 @@ public: } // moved-from object becomes empty - variant(variant&& other) + variant(variant&& other) noexcept { - i_ = other.move(&buf_, C<0>{}); - other.i_ = 0; + move_construct(other); } variant(variant const& other) { - i_ = other.copy(&buf_, C<0>{}); + copy_construct(other); } // moved-from object becomes empty @@ -81,182 +176,55 @@ public: { if(this != &other) { - destroy(C<0>{}); - i_ = other.move(&buf_, C<0>{}); - other.i_ = 0; + destruct(); + move_construct(other); } return *this; } - + variant& operator=(variant const& other) { if(this != &other) { - destroy(C<0>{}); - i_ = other.copy(&buf_, C<0>{}); + destruct(); + copy_construct(other); + } return *this; } template<std::size_t I, class... Args> void - emplace(Args&&... args) + emplace(Args&&... args) noexcept { - destroy(C<0>{}); - new(&buf_) type<I-1>( + destruct(); + ::new(&buf_) mp11::mp_at_c<variant, I - 1>( std::forward<Args>(args)...); i_ = I; } template<std::size_t I> - type<I-1>& + mp11::mp_at_c<variant, I - 1>& get() { BOOST_ASSERT(i_ == I); return *reinterpret_cast< - type<I-1>*>(&buf_); + mp11::mp_at_c<variant, I - 1>*>(&buf_); } template<std::size_t I> - type<I-1> const& + mp11::mp_at_c<variant, I - 1> const& get() const { BOOST_ASSERT(i_ == I); return *reinterpret_cast< - type<I-1> const*>(&buf_); + mp11::mp_at_c<variant, I - 1> const*>(&buf_); } void reset() { - destroy(C<0>{}); - } - -private: - void - destroy(C<0>) - { - auto const I = 0; - if(i_ == I) - return; - destroy(C<I+1>{}); - i_ = 0; - } - - template<std::size_t I> - void - destroy(C<I>) - { - if(i_ == I) - { - using T = type<I-1>; - get<I>().~T(); - return; - } - destroy(C<I+1>{}); - } - - void - destroy(C<sizeof...(TN)>) - { - auto const I = sizeof...(TN); - BOOST_ASSERT(i_ == I); - using T = type<I-1>; - get<I>().~T(); - } - - unsigned char - move(void* dest, C<0>) - { - auto const I = 0; - if(i_ == I) - return I; - return move(dest, C<I+1>{}); - } - - template<std::size_t I> - unsigned char - move(void* dest, C<I>) - { - if(i_ == I) - { - using T = type<I-1>; - new(dest) T(std::move(get<I>())); - get<I>().~T(); - return I; - } - return move(dest, C<I+1>{}); - } - - unsigned char - move(void* dest, C<sizeof...(TN)>) - { - auto const I = sizeof...(TN); - BOOST_ASSERT(i_ == I); - using T = type<I-1>; - new(dest) T(std::move(get<I>())); - get<I>().~T(); - return I; - } - - unsigned char - copy(void* dest, C<0>) const - { - auto const I = 0; - if(i_ == I) - return I; - return copy(dest, C<I+1>{}); - } - - template<std::size_t I> - unsigned char - copy(void* dest, C<I>) const - { - if(i_ == I) - { - using T = type<I-1>; - auto const& t = get<I>(); - new(dest) T(t); - return I; - } - return copy(dest, C<I+1>{}); - } - - unsigned char - copy(void* dest, C<sizeof...(TN)>) const - { - auto const I = sizeof...(TN); - BOOST_ASSERT(i_ == I); - using T = type<I-1>; - auto const& t = get<I>(); - new(dest) T(t); - return I; - } - - bool - equal(variant const& other, C<0>) const - { - auto constexpr I = 0; - if(i_ == I) - return true; - return equal(other, C<I+1>{}); - } - - template<std::size_t I> - bool - equal(variant const& other, C<I>) const - { - if(i_ == I) - return get<I>() == other.get<I>(); - return equal(other, C<I+1>{}); - } - - bool - equal(variant const& other, C<sizeof...(TN)>) const - { - auto constexpr I = sizeof...(TN); - BOOST_ASSERT(i_ == I); - return get<I>() == other.get<I>(); + destruct(); } }; diff --git a/boost/beast/core/detect_ssl.hpp b/boost/beast/core/detect_ssl.hpp new file mode 100644 index 0000000000..b7b0550175 --- /dev/null +++ b/boost/beast/core/detect_ssl.hpp @@ -0,0 +1,651 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_DETECT_SSL_HPP +#define BOOST_BEAST_CORE_DETECT_SSL_HPP + +#include <boost/beast/core/detail/config.hpp> +#include <boost/beast/core/async_base.hpp> +#include <boost/beast/core/error.hpp> +#include <boost/beast/core/read_size.hpp> +#include <boost/beast/core/stream_traits.hpp> +#include <boost/logic/tribool.hpp> +#include <boost/asio/async_result.hpp> +#include <boost/asio/coroutine.hpp> +#include <type_traits> + +namespace boost { +namespace beast { + +//------------------------------------------------------------------------------ +// +// Example: Detect TLS client_hello +// +// This is an example and also a public interface. It implements +// an algorithm for determining if a "TLS client_hello" message +// is received. It can be used to implement a listening port that +// can handle both plain and TLS encrypted connections. +// +//------------------------------------------------------------------------------ + +//[example_core_detect_ssl_1 + +// By convention, the "detail" namespace means "not-public." +// Identifiers in a detail namespace are not visible in the documentation, +// and users should not directly use those identifiers in programs, otherwise +// their program may break in the future. +// +// Using a detail namespace gives the library writer the freedom to change +// the interface or behavior later, and maintain backward-compatibility. + +namespace detail { + +/** Return `true` if the buffer contains a TLS Protocol client_hello message. + + This function analyzes the bytes at the beginning of the buffer + and compares it to a valid client_hello message. This is the + message required to be sent by a client at the beginning of + any TLS (encrypted communication) session, including when + resuming a session. + + The return value will be: + + @li `true` if the contents of the buffer unambiguously define + contain a client_hello message, + + @li `false` if the contents of the buffer cannot possibly + be a valid client_hello message, or + + @li `boost::indeterminate` if the buffer contains an + insufficient number of bytes to determine the result. In + this case the caller should read more data from the relevant + stream, append it to the buffers, and call this function again. + + @param buffers The buffer sequence to inspect. + This type must meet the requirements of <em>ConstBufferSequence</em>. + + @return `boost::tribool` indicating whether the buffer contains + a TLS client handshake, does not contain a handshake, or needs + additional bytes to determine an outcome. + + @see + + <a href="https://tools.ietf.org/html/rfc2246#section-7.4">7.4. Handshake protocol</a> + (RFC2246: The TLS Protocol) +*/ +template <class ConstBufferSequence> +boost::tribool +is_tls_client_hello (ConstBufferSequence const& buffers); + +} // detail + +//] + +//[example_core_detect_ssl_2 + +namespace detail { + +template <class ConstBufferSequence> +boost::tribool +is_tls_client_hello (ConstBufferSequence const& buffers) +{ + // Make sure buffers meets the requirements + static_assert( + net::is_const_buffer_sequence<ConstBufferSequence>::value, + "ConstBufferSequence type requirements not met"); + +/* + The first message on a TLS connection must be the client_hello, + which is a type of handshake record, and it cannot be compressed + or encrypted. A plaintext record has this format: + + 0 byte record_type // 0x16 = handshake + 1 byte major // major protocol version + 2 byte minor // minor protocol version + 3-4 uint16 length // size of the payload + 5 byte handshake_type // 0x01 = client_hello + 6 uint24 length // size of the ClientHello + 9 byte major // major protocol version + 10 byte minor // minor protocol version + 11 uint32 gmt_unix_time + 15 byte random_bytes[28] + ... +*/ + + // Flatten the input buffers into a single contiguous range + // of bytes on the stack to make it easier to work with the data. + unsigned char buf[9]; + auto const n = net::buffer_copy( + net::mutable_buffer(buf, sizeof(buf)), buffers); + + // Can't do much without any bytes + if(n < 1) + return boost::indeterminate; + + // Require the first byte to be 0x16, indicating a TLS handshake record + if(buf[0] != 0x16) + return false; + + // We need at least 5 bytes to know the record payload size + if(n < 5) + return boost::indeterminate; + + // Calculate the record payload size + std::uint32_t const length = (buf[3] << 8) + buf[4]; + + // A ClientHello message payload is at least 34 bytes. + // There can be multiple handshake messages in the same record. + if(length < 34) + return false; + + // We need at least 6 bytes to know the handshake type + if(n < 6) + return boost::indeterminate; + + // The handshake_type must be 0x01 == client_hello + if(buf[5] != 0x01) + return false; + + // We need at least 9 bytes to know the payload size + if(n < 9) + return boost::indeterminate; + + // Calculate the message payload size + std::uint32_t const size = + (buf[6] << 16) + (buf[7] << 8) + buf[8]; + + // The message payload can't be bigger than the enclosing record + if(size + 4 > length) + return false; + + // This can only be a TLS client_hello message + return true; +} + +} // detail + +//] + +//[example_core_detect_ssl_3 + +/** Detect a TLS client handshake on a stream. + + This function reads from a stream to determine if a client + handshake message is being received. + + The call blocks until one of the following is true: + + @li A TLS client opening handshake is detected, + + @li The received data is invalid for a TLS client handshake, or + + @li An error occurs. + + The algorithm, known as a <em>composed operation</em>, is implemented + in terms of calls to the next layer's `read_some` function. + + Bytes read from the stream will be stored in the passed dynamic + buffer, which may be used to perform the TLS handshake if the + detector returns true, or be otherwise consumed by the caller based + on the expected protocol. + + @param stream The stream to read from. This type must meet the + requirements of <em>SyncReadStream</em>. + + @param buffer The dynamic buffer to use. This type must meet the + requirements of <em>DynamicBuffer</em>. + + @param ec Set to the error if any occurred. + + @return `true` if the buffer contains a TLS client handshake and + no error occurred, otherwise `false`. +*/ +template< + class SyncReadStream, + class DynamicBuffer> +bool +detect_ssl( + SyncReadStream& stream, + DynamicBuffer& buffer, + error_code& ec) +{ + namespace beast = boost::beast; + + // Make sure arguments meet the requirements + + static_assert( + is_sync_read_stream<SyncReadStream>::value, + "SyncReadStream type requirements not met"); + + static_assert( + net::is_dynamic_buffer<DynamicBuffer>::value, + "DynamicBuffer type requirements not met"); + + // Loop until an error occurs or we get a definitive answer + for(;;) + { + // There could already be data in the buffer + // so we do this first, before reading from the stream. + auto const result = detail::is_tls_client_hello(buffer.data()); + + // If we got an answer, return it + if(! boost::indeterminate(result)) + { + // A definite answer is a success + ec = {}; + return static_cast<bool>(result); + } + + // Try to fill our buffer by reading from the stream. + // The function read_size calculates a reasonable size for the + // amount to read next, using existing capacity if possible to + // avoid allocating memory, up to the limit of 1536 bytes which + // is the size of a normal TCP frame. + + std::size_t const bytes_transferred = stream.read_some( + buffer.prepare(beast::read_size(buffer, 1536)), ec); + + // Commit what we read into the buffer's input area. + buffer.commit(bytes_transferred); + + // Check for an error + if(ec) + break; + } + + // error + return false; +} + +//] + +//[example_core_detect_ssl_4 + +/** Detect a TLS/SSL handshake asynchronously on a stream. + + This function reads asynchronously from a stream to determine + if a client handshake message is being received. + + This call always returns immediately. The asynchronous operation + will continue until one of the following conditions is true: + + @li A TLS client opening handshake is detected, + + @li The received data is invalid for a TLS client handshake, or + + @li An error occurs. + + The algorithm, known as a <em>composed asynchronous operation</em>, + is implemented in terms of calls to the next layer's `async_read_some` + function. The program must ensure that no other calls to + `async_read_some` are performed until this operation completes. + + Bytes read from the stream will be stored in the passed dynamic + buffer, which may be used to perform the TLS handshake if the + detector returns true, or be otherwise consumed by the caller based + on the expected protocol. + + @param stream The stream to read from. This type must meet the + requirements of <em>AsyncReadStream</em>. + + @param buffer The dynamic buffer to use. This type must meet the + requirements of <em>DynamicBuffer</em>. + + @param token The completion token used to determine the method + used to provide the result of the asynchronous operation. If + this is a completion handler, the implementation takes ownership + of the handler by performing a decay-copy, and the equivalent + function signature of the handler must be: + @code + void handler( + error_code const& error, // Set to the error, if any + bool result // The result of the detector + ); + @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `net::post`. +*/ +template< + class AsyncReadStream, + class DynamicBuffer, + class CompletionToken> +#if BOOST_BEAST_DOXYGEN +BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, bool)) +#else +auto +#endif +async_detect_ssl( + AsyncReadStream& stream, + DynamicBuffer& buffer, + CompletionToken&& token) -> + typename net::async_result< + typename std::decay<CompletionToken>::type, /*< `async_result` customizes the return value based on the completion token >*/ + void(error_code, bool)>::return_type; /*< This is the signature for the completion handler >*/ +//] + +//[example_core_detect_ssl_5 + +// These implementation details don't need to be public + +namespace detail { + +// The composed operation object +template< + class DetectHandler, + class AsyncReadStream, + class DynamicBuffer> +class detect_ssl_op; + +// This is a function object which `net::async_initiate` can use to launch +// our composed operation. This is a relatively new feature in networking +// which allows the asynchronous operation to be "lazily" executed (meaning +// that it is launched later). Users don't need to worry about this, but +// authors of composed operations need to write it this way to get the +// very best performance, for example when using Coroutines TS (`co_await`). + +struct run_detect_ssl_op +{ + // The implementation of `net::async_initiate` captures the + // arguments of the initiating function, and then calls this + // function object later with the captured arguments in order + // to launch the composed operation. All we need to do here + // is take those arguments and construct our composed operation + // object. + // + // `async_initiate` takes care of transforming the completion + // token into the "real handler" which must have the correct + // signature, in this case `void(error_code, boost::tri_bool)`. + + template< + class DetectHandler, + class AsyncReadStream, + class DynamicBuffer> + void operator()( + DetectHandler&& h, + AsyncReadStream* s, // references are passed as pointers + DynamicBuffer& b) + { + detect_ssl_op< + typename std::decay<DetectHandler>::type, + AsyncReadStream, + DynamicBuffer>( + std::forward<DetectHandler>(h), *s, b); + } +}; + +} // detail + +//] + +//[example_core_detect_ssl_6 + +// Here is the implementation of the asynchronous initiation function +template< + class AsyncReadStream, + class DynamicBuffer, + class CompletionToken> +#if BOOST_BEAST_DOXYGEN +BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, bool)) +#else +auto +#endif +async_detect_ssl( + AsyncReadStream& stream, + DynamicBuffer& buffer, + CompletionToken&& token) + -> typename net::async_result< + typename std::decay<CompletionToken>::type, + void(error_code, bool)>::return_type +{ + // Make sure arguments meet the type requirements + + static_assert( + is_async_read_stream<AsyncReadStream>::value, + "SyncReadStream type requirements not met"); + + static_assert( + net::is_dynamic_buffer<DynamicBuffer>::value, + "DynamicBuffer type requirements not met"); + + // The function `net::async_initate` uses customization points + // to allow one asynchronous initiating function to work with + // all sorts of notification systems, such as callbacks but also + // fibers, futures, coroutines, and user-defined types. + // + // It works by capturing all of the arguments using perfect + // forwarding, and then depending on the specialization of + // `net::async_result` for the type of `CompletionToken`, + // the `initiation` object will be invoked with the saved + // parameters and the actual completion handler. Our + // initiating object is `run_detect_ssl_op`. + // + // Non-const references need to be passed as pointers, + // since we don't want a decay-copy. + + + return net::async_initiate< + CompletionToken, + void(error_code, bool)>( + detail::run_detect_ssl_op{}, + token, + &stream, // pass the reference by pointer + buffer); +} + +//] + +//[example_core_detect_ssl_7 + +namespace detail { + +// Read from a stream, calling is_tls_client_hello on the data +// data to determine if the TLS client handshake is present. +// +// This will be implemented using Asio's "stackless coroutines" +// which are based on macros forming a switch statement. The +// operation is derived from `coroutine` for this reason. +// +// The library type `async_base` takes care of all of the +// boilerplate for writing composed operations, including: +// +// * Storing the user's completion handler +// * Maintaining the work guard for the handler's associated executor +// * Propagating the associated allocator of the handler +// * Propagating the associated executor of the handler +// * Deallocating temporary storage before invoking the handler +// * Posting the handler to the executor on an immediate completion +// +// `async_base` needs to know the type of the handler, as well +// as the executor of the I/O object being used. The metafunction +// `executor_type` returns the type of executor used by an +// I/O object. +// +template< + class DetectHandler, + class AsyncReadStream, + class DynamicBuffer> +class detect_ssl_op + : public boost::asio::coroutine + , public async_base< + DetectHandler, executor_type<AsyncReadStream>> +{ + // This composed operation has trivial state, + // so it is just kept inside the class and can + // be cheaply copied as needed by the implementation. + + AsyncReadStream& stream_; + + // The callers buffer is used to hold all received data + DynamicBuffer& buffer_; + + // We're going to need this in case we have to post the handler + error_code ec_; + + boost::tribool result_ = false; + +public: + // Completion handlers must be MoveConstructible. + detect_ssl_op(detect_ssl_op&&) = default; + + // Construct the operation. The handler is deduced through + // the template type `DetectHandler_`, this lets the same constructor + // work properly for both lvalues and rvalues. + // + template<class DetectHandler_> + detect_ssl_op( + DetectHandler_&& handler, + AsyncReadStream& stream, + DynamicBuffer& buffer) + : beast::async_base< + DetectHandler_, + beast::executor_type<AsyncReadStream>>( + std::forward<DetectHandler_>(handler), + stream.get_executor()) + , stream_(stream) + , buffer_(buffer) + { + // This starts the operation. We pass `false` to tell the + // algorithm that it needs to use net::post if it wants to + // complete immediately. This is required by Networking, + // as initiating functions are not allowed to invoke the + // completion handler on the caller's thread before + // returning. + (*this)({}, 0, false); + } + + // Our main entry point. This will get called as our + // intermediate operations complete. Definition below. + // + // The parameter `cont` indicates if we are being called subsequently + // from the original invocation + // + void operator()( + error_code ec, + std::size_t bytes_transferred, + bool cont = true); +}; + +} // detail + +//] + +//[example_core_detect_ssl_8 + +namespace detail { + +// This example uses the Asio's stackless "fauxroutines", implemented +// using a macro-based solution. It makes the code easier to write and +// easier to read. This include file defines the necessary macros and types. +#include <boost/asio/yield.hpp> + +// detect_ssl_op is callable with the signature void(error_code, bytes_transferred), +// allowing `*this` to be used as a ReadHandler +// +template< + class AsyncStream, + class DynamicBuffer, + class Handler> +void +detect_ssl_op<AsyncStream, DynamicBuffer, Handler>:: +operator()(error_code ec, std::size_t bytes_transferred, bool cont) +{ + namespace beast = boost::beast; + + // This introduces the scope of the stackless coroutine + reenter(*this) + { + // Loop until an error occurs or we get a definitive answer + for(;;) + { + // There could already be a hello in the buffer so check first + result_ = is_tls_client_hello(buffer_.data()); + + // If we got an answer, then the operation is complete + if(! boost::indeterminate(result_)) + break; + + // Try to fill our buffer by reading from the stream. + // The function read_size calculates a reasonable size for the + // amount to read next, using existing capacity if possible to + // avoid allocating memory, up to the limit of 1536 bytes which + // is the size of a normal TCP frame. + // + // `async_read_some` expects a ReadHandler as the completion + // handler. The signature of a read handler is void(error_code, size_t), + // and this function matches that signature (the `cont` parameter has + // a default of true). We pass `std::move(*this)` as the completion + // handler for the read operation. This transfers ownership of this + // entire state machine back into the `async_read_some` operation. + // Care must be taken with this idiom, to ensure that parameters + // passed to the initiating function which could be invalidated + // by the move, are first moved to the stack before calling the + // initiating function. + + yield stream_.async_read_some(buffer_.prepare( + read_size(buffer_, 1536)), std::move(*this)); + + // Commit what we read into the buffer's input area. + buffer_.commit(bytes_transferred); + + // Check for an error + if(ec) + break; + } + + // If `cont` is true, the handler will be invoked directly. + // + // Otherwise, the handler cannot be invoked directly, because + // initiating functions are not allowed to call the handler + // before returning. Instead, the handler must be posted to + // the I/O context. We issue a zero-byte read using the same + // type of buffers used in the ordinary read above, to prevent + // the compiler from creating an extra instantiation of the + // function template. This reduces compile times and the size + // of the program executable. + + if(! cont) + { + // Save the error, otherwise it will be overwritten with + // a successful error code when this read completes + // immediately. + ec_ = ec; + + // Zero-byte reads and writes are guaranteed to complete + // immediately with succcess. The type of buffers and the + // type of handler passed here need to exactly match the types + // used in the call to async_read_some above, to avoid + // instantiating another version of the function template. + + yield stream_.async_read_some(buffer_.prepare(0), std::move(*this)); + + // Restore the saved error code + ec = ec_; + } + + // Invoke the final handler. + // At this point, we are guaranteed that the original initiating + // function is no longer on our stack frame. + + this->complete_now(ec, static_cast<bool>(result_)); + } +} + +// Including this file undefines the macros used by the stackless fauxroutines. +#include <boost/asio/unyield.hpp> + +} // detail + +//] + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/error.hpp b/boost/beast/core/error.hpp index f034f7a404..29a8648a3d 100644 --- a/boost/beast/core/error.hpp +++ b/boost/beast/core/error.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -52,7 +52,37 @@ enum errc{}; namespace errc = boost::system::errc; #endif +//------------------------------------------------------------------------------ + +/// Error codes returned from library operations +enum class error +{ + /** The socket was closed due to a timeout + + This error indicates that a socket was closed due to a + a timeout detected during an operation. + + Error codes with this value will compare equal to @ref condition::timeout. + */ + timeout = 1 +}; + +/// Error conditions corresponding to sets of library error codes. +enum class condition +{ + /** The operation timed out + + This error indicates that an operation took took too long. + */ + timeout = 1 +}; + } // beast } // boost +#include <boost/beast/core/impl/error.hpp> +#ifdef BOOST_BEAST_HEADER_ONLY +#include <boost/beast/core/impl/error.ipp> +#endif + #endif diff --git a/boost/beast/core/file.hpp b/boost/beast/core/file.hpp index 09c8e82081..207f8ef26c 100644 --- a/boost/beast/core/file.hpp +++ b/boost/beast/core/file.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2015-2019 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) @@ -15,7 +15,6 @@ #include <boost/beast/core/file_posix.hpp> #include <boost/beast/core/file_stdio.hpp> #include <boost/beast/core/file_win32.hpp> -#include <boost/config.hpp> namespace boost { namespace beast { @@ -23,7 +22,7 @@ namespace beast { /** An implementation of File. This alias is set to the best available implementation - of @b File given the platform and build settings. + of <em>File</em> given the platform and build settings. */ #if BOOST_BEAST_DOXYGEN struct file : file_stdio diff --git a/boost/beast/core/file_base.hpp b/boost/beast/core/file_base.hpp index c2b3c53a53..cf75e78579 100644 --- a/boost/beast/core/file_base.hpp +++ b/boost/beast/core/file_base.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2015-2019 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) @@ -11,42 +11,63 @@ #define BOOST_BEAST_CORE_FILE_BASE_HPP #include <boost/beast/core/detail/config.hpp> -#include <boost/beast/core/string.hpp> +#include <boost/beast/core/error.hpp> +#include <boost/type_traits/make_void.hpp> +#include <type_traits> namespace boost { namespace beast { +/* + +file_mode acesss sharing seeking file std mode +-------------------------------------------------------------------------------------- +read read-only shared random must exist "rb" +scan read-only shared sequential must exist "rbS" +write read/write exclusive random create/truncate "wb+" +write_new read/write exclusive random must not exist "wbx" +write_existing read/write exclusive random must exist "rb+" +append write-only exclusive sequential create/truncate "ab" +append_existing write-only exclusive sequential must exist "ab" + +*/ + /** File open modes These modes are used when opening files using - instances of the @b File concept. + instances of the <em>File</em> concept. @see file_stdio */ enum class file_mode { - /// Random reading + /// Random read-only access to an existing file read, - /// Sequential reading + /// Sequential read-only access to an existing file scan, - /** Random writing to a new or truncated file - - @li If the file does not exist, it is created. + /** Random reading and writing to a new or truncated file - @li If the file exists, it is truncated to - zero size upon opening. + This mode permits random-access reading and writing + for the specified file. If the file does not exist + prior to the function call, it is created with an + initial size of zero bytes. Otherwise if the file + already exists, the size is truncated to zero bytes. */ write, - /** Random writing to new file only + /** Random reading and writing to a new file only - If the file exists, an error is generated. + This mode permits random-access reading and writing + for the specified file. The file will be created with + an initial size of zero bytes. If the file already exists + prior to the function call, an error is returned and + no file is opened. */ write_new, - /** Random writing to existing file + /** Random write-only access to existing file If the file does not exist, an error is generated. */ @@ -64,15 +85,6 @@ enum class file_mode */ append, - /** Appending to a new file only - - The current file position shall be set to the end of - the file prior to each write. - - If the file exists, an error is generated. - */ - append_new, - /** Appending to an existing file The current file position shall be set to the end of @@ -83,6 +95,69 @@ enum class file_mode append_existing }; +/** Determine if `T` meets the requirements of <em>File</em>. + + Metafunctions are used to perform compile time checking of template + types. This type will be `std::true_type` if `T` meets the requirements, + else the type will be `std::false_type`. + + @par Example + + Use with `static_assert`: + + @code + template<class File> + void f(File& file) + { + static_assert(is_file<File>::value, + "File type requirements not met"); + ... + @endcode + + Use with `std::enable_if` (SFINAE): + + @code + template<class File> + typename std::enable_if<is_file<File>::value>::type + f(File& file); + @endcode +*/ +#if BOOST_BEAST_DOXYGEN +template<class T> +struct is_file : std::integral_constant<bool, ...>{}; +#else +template<class T, class = void> +struct is_file : std::false_type {}; + +template<class T> +struct is_file<T, boost::void_t<decltype( + std::declval<bool&>() = std::declval<T const&>().is_open(), + std::declval<T&>().close(std::declval<error_code&>()), + std::declval<T&>().open( + std::declval<char const*>(), + std::declval<file_mode>(), + std::declval<error_code&>()), + std::declval<std::uint64_t&>() = std::declval<T&>().size( + std::declval<error_code&>()), + std::declval<std::uint64_t&>() = std::declval<T&>().pos( + std::declval<error_code&>()), + std::declval<T&>().seek( + std::declval<std::uint64_t>(), + std::declval<error_code&>()), + std::declval<std::size_t&>() = std::declval<T&>().read( + std::declval<void*>(), + std::declval<std::size_t>(), + std::declval<error_code&>()), + std::declval<std::size_t&>() = std::declval<T&>().write( + std::declval<void const*>(), + std::declval<std::size_t>(), + std::declval<error_code&>()) + )>> : std::integral_constant<bool, + std::is_default_constructible<T>::value && + std::is_destructible<T>::value + > {}; +#endif + } // beast } // boost diff --git a/boost/beast/core/file_posix.hpp b/boost/beast/core/file_posix.hpp index 65bac2065d..b7a70a54ae 100644 --- a/boost/beast/core/file_posix.hpp +++ b/boost/beast/core/file_posix.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2015-2019 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) @@ -10,7 +10,7 @@ #ifndef BOOST_BEAST_CORE_FILE_POSIX_HPP #define BOOST_BEAST_CORE_FILE_POSIX_HPP -#include <boost/config.hpp> +#include <boost/beast/core/detail/config.hpp> #if ! defined(BOOST_BEAST_NO_POSIX_FILE) # if ! defined(__APPLE__) && ! defined(__linux__) @@ -37,12 +37,17 @@ namespace beast { /** An implementation of File for POSIX systems. - This class implements a @b File using POSIX interfaces. + This class implements a <em>File</em> using POSIX interfaces. */ class file_posix { int fd_ = -1; + BOOST_BEAST_DECL + static + int + native_close(int& fd); + public: /** The type of the underlying file handle. @@ -54,6 +59,7 @@ public: If the file is open it is first closed. */ + BOOST_BEAST_DECL ~file_posix(); /** Constructor @@ -66,12 +72,14 @@ public: The moved-from object behaves as if default constructed. */ + BOOST_BEAST_DECL file_posix(file_posix&& other); /** Assignment The moved-from object behaves as if default constructed. */ + BOOST_BEAST_DECL file_posix& operator=(file_posix&& other); /// Returns the native handle associated with the file. @@ -87,6 +95,7 @@ public: @param fd The native file handle to assign. */ + BOOST_BEAST_DECL void native_handle(native_handle_type fd); @@ -101,6 +110,7 @@ public: @param ec Set to the error, if any occurred. */ + BOOST_BEAST_DECL void close(error_code& ec); @@ -112,6 +122,7 @@ public: @param ec Set to the error, if any occurred */ + BOOST_BEAST_DECL void open(char const* path, file_mode mode, error_code& ec); @@ -121,6 +132,7 @@ public: @return The size in bytes */ + BOOST_BEAST_DECL std::uint64_t size(error_code& ec) const; @@ -130,6 +142,7 @@ public: @return The offset in bytes from the beginning of the file */ + BOOST_BEAST_DECL std::uint64_t pos(error_code& ec) const; @@ -139,6 +152,7 @@ public: @param ec Set to the error, if any occurred */ + BOOST_BEAST_DECL void seek(std::uint64_t offset, error_code& ec); @@ -150,6 +164,7 @@ public: @param ec Set to the error, if any occurred */ + BOOST_BEAST_DECL std::size_t read(void* buffer, std::size_t n, error_code& ec) const; @@ -161,6 +176,7 @@ public: @param ec Set to the error, if any occurred */ + BOOST_BEAST_DECL std::size_t write(void const* buffer, std::size_t n, error_code& ec); }; @@ -168,7 +184,9 @@ public: } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY #include <boost/beast/core/impl/file_posix.ipp> +#endif #endif diff --git a/boost/beast/core/file_stdio.hpp b/boost/beast/core/file_stdio.hpp index 09ca0c42de..d56632b899 100644 --- a/boost/beast/core/file_stdio.hpp +++ b/boost/beast/core/file_stdio.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2015-2019 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) @@ -39,6 +39,7 @@ public: If the file is open it is first closed. */ + BOOST_BEAST_DECL ~file_stdio(); /** Constructor @@ -51,12 +52,14 @@ public: The moved-from object behaves as if default constructed. */ + BOOST_BEAST_DECL file_stdio(file_stdio&& other); /** Assignment The moved-from object behaves as if default constructed. */ + BOOST_BEAST_DECL file_stdio& operator=(file_stdio&& other); /// Returns the native handle associated with the file. @@ -72,6 +75,7 @@ public: @param f The native file handle to assign. */ + BOOST_BEAST_DECL void native_handle(FILE* f); @@ -86,6 +90,7 @@ public: @param ec Set to the error, if any occurred. */ + BOOST_BEAST_DECL void close(error_code& ec); @@ -97,6 +102,7 @@ public: @param ec Set to the error, if any occurred */ + BOOST_BEAST_DECL void open(char const* path, file_mode mode, error_code& ec); @@ -106,6 +112,7 @@ public: @return The size in bytes */ + BOOST_BEAST_DECL std::uint64_t size(error_code& ec) const; @@ -115,6 +122,7 @@ public: @return The offset in bytes from the beginning of the file */ + BOOST_BEAST_DECL std::uint64_t pos(error_code& ec) const; @@ -124,6 +132,7 @@ public: @param ec Set to the error, if any occurred */ + BOOST_BEAST_DECL void seek(std::uint64_t offset, error_code& ec); @@ -135,6 +144,7 @@ public: @param ec Set to the error, if any occurred */ + BOOST_BEAST_DECL std::size_t read(void* buffer, std::size_t n, error_code& ec) const; @@ -146,6 +156,7 @@ public: @param ec Set to the error, if any occurred */ + BOOST_BEAST_DECL std::size_t write(void const* buffer, std::size_t n, error_code& ec); }; @@ -153,6 +164,8 @@ public: } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY #include <boost/beast/core/impl/file_stdio.ipp> +#endif #endif diff --git a/boost/beast/core/file_win32.hpp b/boost/beast/core/file_win32.hpp index 3e9a18abaa..908076d7fe 100644 --- a/boost/beast/core/file_win32.hpp +++ b/boost/beast/core/file_win32.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2015-2019 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) @@ -10,7 +10,7 @@ #ifndef BOOST_BEAST_CORE_FILE_WIN32_HPP #define BOOST_BEAST_CORE_FILE_WIN32_HPP -#include <boost/config.hpp> +#include <boost/beast/core/detail/config.hpp> #if ! defined(BOOST_BEAST_USE_WIN32_FILE) # ifdef BOOST_MSVC @@ -34,7 +34,7 @@ namespace beast { /** An implementation of File for Win32. - This class implements a @b File using Win32 native interfaces. + This class implements a <em>File</em> using Win32 native interfaces. */ class file_win32 { @@ -56,6 +56,7 @@ public: If the file is open it is first closed. */ + BOOST_BEAST_DECL ~file_win32(); /** Constructor @@ -68,12 +69,14 @@ public: The moved-from object behaves as if default constructed. */ + BOOST_BEAST_DECL file_win32(file_win32&& other); /** Assignment The moved-from object behaves as if default constructed. */ + BOOST_BEAST_DECL file_win32& operator=(file_win32&& other); /// Returns the native handle associated with the file. @@ -89,6 +92,7 @@ public: @param h The native file handle to assign. */ + BOOST_BEAST_DECL void native_handle(native_handle_type h); @@ -103,6 +107,7 @@ public: @param ec Set to the error, if any occurred. */ + BOOST_BEAST_DECL void close(error_code& ec); @@ -114,6 +119,7 @@ public: @param ec Set to the error, if any occurred */ + BOOST_BEAST_DECL void open(char const* path, file_mode mode, error_code& ec); @@ -123,6 +129,7 @@ public: @return The size in bytes */ + BOOST_BEAST_DECL std::uint64_t size(error_code& ec) const; @@ -132,6 +139,7 @@ public: @return The offset in bytes from the beginning of the file */ + BOOST_BEAST_DECL std::uint64_t pos(error_code& ec); @@ -141,6 +149,7 @@ public: @param ec Set to the error, if any occurred */ + BOOST_BEAST_DECL void seek(std::uint64_t offset, error_code& ec); @@ -152,6 +161,7 @@ public: @param ec Set to the error, if any occurred */ + BOOST_BEAST_DECL std::size_t read(void* buffer, std::size_t n, error_code& ec); @@ -163,6 +173,7 @@ public: @param ec Set to the error, if any occurred */ + BOOST_BEAST_DECL std::size_t write(void const* buffer, std::size_t n, error_code& ec); }; @@ -170,7 +181,9 @@ public: } // beast } // boost +#ifdef BOOST_BEAST_HEADER_ONLY #include <boost/beast/core/impl/file_win32.ipp> +#endif #endif diff --git a/boost/beast/core/flat_buffer.hpp b/boost/beast/core/flat_buffer.hpp index 1e0f0ef519..9e8d639c4d 100644 --- a/boost/beast/core/flat_buffer.hpp +++ b/boost/beast/core/flat_buffer.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -16,22 +16,35 @@ #include <boost/core/empty_value.hpp> #include <limits> #include <memory> +#include <type_traits> namespace boost { namespace beast { -/** A linear dynamic buffer. +/** A dynamic buffer providing buffer sequences of length one. - Objects of this type meet the requirements of @b DynamicBuffer - and offer additional invariants: - - @li Buffer sequences returned by @ref data and @ref prepare - will always be of length one. + A dynamic buffer encapsulates memory storage that may be + automatically resized as required, where the memory is + divided into two regions: readable bytes followed by + writable bytes. These memory regions are internal to + the dynamic buffer, but direct access to the elements + is provided to permit them to be efficiently used with + I/O operations. + + Objects of this type meet the requirements of <em>DynamicBuffer</em> + and have the following additional properties: + + @li A mutable buffer sequence representing the readable + bytes is returned by @ref data when `this` is non-const. @li A configurable maximum buffer size may be set upon construction. Attempts to exceed the buffer size will throw `std::length_error`. + @li Buffer sequences representing the readable and writable + bytes, returned by @ref data and @ref prepare, will have + length one. + Upon construction, a maximum size for the buffer may be specified. If this limit is exceeded, the `std::length_error` exception will be thrown. @@ -49,11 +62,6 @@ class basic_flat_buffer template rebind_alloc<char>> #endif { - enum - { - min_size = 512 - }; - template<class OtherAlloc> friend class basic_flat_buffer; @@ -61,13 +69,21 @@ class basic_flat_buffer detail::allocator_traits<Allocator>:: template rebind_alloc<char>; + static bool constexpr default_nothrow = + std::is_nothrow_default_constructible<Allocator>::value; + using alloc_traits = - detail::allocator_traits<base_alloc_type>; + beast::detail::allocator_traits<base_alloc_type>; + + using pocma = typename + alloc_traits::propagate_on_container_move_assignment; + + using pocca = typename + alloc_traits::propagate_on_container_copy_assignment; static - inline std::size_t - dist(char const* first, char const* last) + dist(char const* first, char const* last) noexcept { return static_cast<std::size_t>(last - first); } @@ -83,260 +99,435 @@ public: /// The type of allocator used. using allocator_type = Allocator; - /// The type used to represent the input sequence as a list of buffers. - using const_buffers_type = boost::asio::const_buffer; - - /// The type used to represent the output sequence as a list of buffers. - using mutable_buffers_type = boost::asio::mutable_buffer; - /// Destructor ~basic_flat_buffer(); /** Constructor - Upon construction, capacity will be zero. + After construction, @ref capacity will return zero, and + @ref max_size will return the largest value which may + be passed to the allocator's `allocate` function. */ - basic_flat_buffer(); + basic_flat_buffer() noexcept(default_nothrow); /** Constructor - Upon construction, capacity will be zero. + After construction, @ref capacity will return zero, and + @ref max_size will return the specified value of `limit`. - @param limit The setting for @ref max_size. + @param limit The desired maximum size. */ explicit - basic_flat_buffer(std::size_t limit); + basic_flat_buffer( + std::size_t limit) noexcept(default_nothrow); /** Constructor - Upon construction, capacity will be zero. + After construction, @ref capacity will return zero, and + @ref max_size will return the largest value which may + be passed to the allocator's `allocate` function. - @param alloc The allocator to construct with. + @param alloc The allocator to use for the object. + + @esafe + + No-throw guarantee. */ explicit - basic_flat_buffer(Allocator const& alloc); + basic_flat_buffer(Allocator const& alloc) noexcept; /** Constructor - Upon construction, capacity will be zero. + After construction, @ref capacity will return zero, and + @ref max_size will return the specified value of `limit`. + + @param limit The desired maximum size. + + @param alloc The allocator to use for the object. - @param limit The setting for @ref max_size. + @esafe - @param alloc The allocator to use. + No-throw guarantee. */ basic_flat_buffer( - std::size_t limit, Allocator const& alloc); + std::size_t limit, + Allocator const& alloc) noexcept; - /** Constructor + /** Move Constructor - After the move, `*this` will have an empty output sequence. + The container is constructed with the contents of `other` + using move semantics. The maximum size will be the same + as the moved-from object. - @param other The object to move from. After the move, - The object's state will be as if constructed using - its current allocator and limit. + Buffer sequences previously obtained from `other` using + @ref data or @ref prepare remain valid after the move. + + @param other The object to move from. After the move, the + moved-from object will have zero capacity, zero readable + bytes, and zero writable bytes. + + @esafe + + No-throw guarantee. */ - basic_flat_buffer(basic_flat_buffer&& other); + basic_flat_buffer(basic_flat_buffer&& other) noexcept; - /** Constructor + /** Move Constructor - After the move, `*this` will have an empty output sequence. + Using `alloc` as the allocator for the new container, the + contents of `other` are moved. If `alloc != other.get_allocator()`, + this results in a copy. The maximum size will be the same + as the moved-from object. + + Buffer sequences previously obtained from `other` using + @ref data or @ref prepare become invalid after the move. @param other The object to move from. After the move, - The object's state will be as if constructed using - its current allocator and limit. + the moved-from object will have zero capacity, zero readable + bytes, and zero writable bytes. + + @param alloc The allocator to use for the object. - @param alloc The allocator to use. + @throws std::length_error if `other.size()` exceeds the + maximum allocation size of `alloc`. */ basic_flat_buffer( - basic_flat_buffer&& other, Allocator const& alloc); + basic_flat_buffer&& other, + Allocator const& alloc); - /** Constructor + /** Copy Constructor + + This container is constructed with the contents of `other` + using copy semantics. The maximum size will be the same + as the copied object. @param other The object to copy from. + + @throws std::length_error if `other.size()` exceeds the + maximum allocation size of the allocator. */ basic_flat_buffer(basic_flat_buffer const& other); - /** Constructor + /** Copy Constructor + + This container is constructed with the contents of `other` + using copy semantics and the specified allocator. The maximum + size will be the same as the copied object. @param other The object to copy from. - @param alloc The allocator to use. + @param alloc The allocator to use for the object. + + @throws std::length_error if `other.size()` exceeds the + maximum allocation size of `alloc`. */ - basic_flat_buffer(basic_flat_buffer const& other, + basic_flat_buffer( + basic_flat_buffer const& other, Allocator const& alloc); - /** Constructor + /** Copy Constructor + + This container is constructed with the contents of `other` + using copy semantics. The maximum size will be the same + as the copied object. @param other The object to copy from. + + @throws std::length_error if `other.size()` exceeds the + maximum allocation size of the allocator. */ template<class OtherAlloc> basic_flat_buffer( - basic_flat_buffer<OtherAlloc> const& other); + basic_flat_buffer<OtherAlloc> const& other) + noexcept(default_nothrow); - /** Constructor + /** Copy Constructor + + This container is constructed with the contents of `other` + using copy semantics. The maximum size will be the same + as the copied object. @param other The object to copy from. - @param alloc The allocator to use. + @param alloc The allocator to use for the object. + + @throws std::length_error if `other.size()` exceeds the + maximum allocation size of `alloc`. */ template<class OtherAlloc> basic_flat_buffer( basic_flat_buffer<OtherAlloc> const& other, - Allocator const& alloc); + Allocator const& alloc); + + /** Move Assignment - /** Assignment + The container is assigned with the contents of `other` + using move semantics. The maximum size will be the same + as the moved-from object. - After the move, `*this` will have an empty output sequence. + Buffer sequences previously obtained from `other` using + @ref data or @ref prepare remain valid after the move. @param other The object to move from. After the move, - the object's state will be as if constructed using - its current allocator and limit. + the moved-from object will have zero capacity, zero readable + bytes, and zero writable bytes. + + @esafe + + No-throw guarantee. */ basic_flat_buffer& - operator=(basic_flat_buffer&& other); + operator=(basic_flat_buffer&& other) noexcept; - /** Assignment + /** Copy Assignment - After the copy, `*this` will have an empty output sequence. + The container is assigned with the contents of `other` + using copy semantics. The maximum size will be the same + as the copied object. + + After the copy, `this` will have zero writable bytes. @param other The object to copy from. + + @throws std::length_error if `other.size()` exceeds the + maximum allocation size of the allocator. */ basic_flat_buffer& operator=(basic_flat_buffer const& other); /** Copy assignment - After the copy, `*this` will have an empty output sequence. + The container is assigned with the contents of `other` + using copy semantics. The maximum size will be the same + as the copied object. + + After the copy, `this` will have zero writable bytes. @param other The object to copy from. + + @throws std::length_error if `other.size()` exceeds the + maximum allocation size of the allocator. */ template<class OtherAlloc> basic_flat_buffer& operator=(basic_flat_buffer<OtherAlloc> const& other); - /// Returns a copy of the associated allocator. + /// Returns a copy of the allocator used. allocator_type get_allocator() const { return this->get(); } - /// Returns the size of the input sequence. + /** Set the maximum allowed capacity + + This function changes the currently configured upper limit + on capacity to the specified value. + + @param n The maximum number of bytes ever allowed for capacity. + + @esafe + + No-throw guarantee. + */ + void + max_size(std::size_t n) noexcept + { + max_ = n; + } + + /** Guarantee a minimum capacity + + This function adjusts the internal storage (if necessary) + to guarantee space for at least `n` bytes. + + Buffer sequences previously obtained using @ref data or + @ref prepare become invalid. + + @param n The minimum number of byte for the new capacity. + If this value is greater than the maximum size, then the + maximum size will be adjusted upwards to this value. + + @esafe + + Basic guarantee. + + @throws std::length_error if n is larger than the maximum + allocation size of the allocator. + */ + void + reserve(std::size_t n); + + /** Reallocate the buffer to fit the readable bytes exactly. + + Buffer sequences previously obtained using @ref data or + @ref prepare become invalid. + + @esafe + + Strong guarantee. + */ + void + shrink_to_fit(); + + /** Set the size of the readable and writable bytes to zero. + + This clears the buffer without changing capacity. + Buffer sequences previously obtained using @ref data or + @ref prepare become invalid. + + @esafe + + No-throw guarantee. + */ + void + clear() noexcept; + + /// Exchange two dynamic buffers + template<class Alloc> + friend + void + swap( + basic_flat_buffer<Alloc>&, + basic_flat_buffer<Alloc>&); + + //-------------------------------------------------------------------------- + + /// The ConstBufferSequence used to represent the readable bytes. + using const_buffers_type = net::const_buffer; + + /// The MutableBufferSequence used to represent the readable bytes. + using mutable_data_type = net::mutable_buffer; + + /// The MutableBufferSequence used to represent the writable bytes. + using mutable_buffers_type = net::mutable_buffer; + + /// Returns the number of readable bytes. std::size_t - size() const + size() const noexcept { return dist(in_, out_); } - /// Return the maximum sum of the input and output sequence sizes. + /// Return the maximum number of bytes, both readable and writable, that can ever be held. std::size_t - max_size() const + max_size() const noexcept { return max_; } - /// Return the maximum sum of input and output sizes that can be held without an allocation. + /// Return the maximum number of bytes, both readable and writable, that can be held without requiring an allocation. std::size_t - capacity() const + capacity() const noexcept { return dist(begin_, end_); } - /// Get a list of buffers that represent the input sequence. + /// Returns a constant buffer sequence representing the readable bytes const_buffers_type - data() const + data() const noexcept { return {in_, dist(in_, out_)}; } - /** Get a list of buffers that represent the output sequence, with the given size. - - @throws std::length_error if `size() + n` exceeds `max_size()`. + /// Returns a constant buffer sequence representing the readable bytes + const_buffers_type + cdata() const noexcept + { + return data(); + } - @note All previous buffers sequences obtained from - calls to @ref data or @ref prepare are invalidated. - */ - mutable_buffers_type - prepare(std::size_t n); + /// Returns a mutable buffer sequence representing the readable bytes + mutable_data_type + data() noexcept + { + return {in_, dist(in_, out_)}; + } - /** Move bytes from the output sequence to the input sequence. + /** Returns a mutable buffer sequence representing writable bytes. + + Returns a mutable buffer sequence representing the writable + bytes containing exactly `n` bytes of storage. Memory may be + reallocated as needed. - @param n The number of bytes to move. If this is larger than - the number of bytes in the output sequences, then the entire - output sequences is moved. + All buffers sequences previously obtained using + @ref data or @ref prepare become invalid. - @note All previous buffers sequences obtained from - calls to @ref data or @ref prepare are invalidated. - */ - void - commit(std::size_t n) - { - out_ += (std::min)(n, dist(out_, last_)); - } + @param n The desired number of bytes in the returned buffer + sequence. - /** Remove bytes from the input sequence. + @throws std::length_error if `size() + n` exceeds either + `max_size()` or the allocator's maximum allocation size. - If `n` is greater than the number of bytes in the input - sequence, all bytes in the input sequence are removed. + @esafe - @note All previous buffers sequences obtained from - calls to @ref data or @ref prepare are invalidated. + Strong guarantee. */ - void - consume(std::size_t n); + mutable_buffers_type + prepare(std::size_t n); - /** Reallocate the buffer to fit the input sequence. + /** Append writable bytes to the readable bytes. - @note All previous buffers sequences obtained from - calls to @ref data or @ref prepare are invalidated. - */ - void - shrink_to_fit(); + Appends n bytes from the start of the writable bytes to the + end of the readable bytes. The remainder of the writable bytes + are discarded. If n is greater than the number of writable + bytes, all writable bytes are appended to the readable bytes. - /// Exchange two flat buffers - template<class Alloc> - friend - void - swap( - basic_flat_buffer<Alloc>& lhs, - basic_flat_buffer<Alloc>& rhs); + All buffers sequences previously obtained using + @ref data or @ref prepare become invalid. -private: - void - reset(); + @param n The number of bytes to append. If this number + is greater than the number of writable bytes, all + writable bytes are appended. - template<class DynamicBuffer> - void - copy_from(DynamicBuffer const& other); + @esafe + No-throw guarantee. + */ void - move_assign(basic_flat_buffer&, std::true_type); + commit(std::size_t n) noexcept + { + out_ += (std::min)(n, dist(out_, last_)); + } - void - move_assign(basic_flat_buffer&, std::false_type); + /** Remove bytes from beginning of the readable bytes. - void - copy_assign(basic_flat_buffer const&, std::true_type); + Removes n bytes from the beginning of the readable bytes. - void - copy_assign(basic_flat_buffer const&, std::false_type); + All buffers sequences previously obtained using + @ref data or @ref prepare become invalid. - void - swap(basic_flat_buffer&); + @param n The number of bytes to remove. If this number + is greater than the number of readable bytes, all + readable bytes are removed. - void - swap(basic_flat_buffer&, std::true_type); + @esafe + No-throw guarantee. + */ void - swap(basic_flat_buffer&, std::false_type); + consume(std::size_t n) noexcept; + +private: + template<class OtherAlloc> + void copy_from(basic_flat_buffer<OtherAlloc> const& other); + void move_assign(basic_flat_buffer&, std::true_type); + void move_assign(basic_flat_buffer&, std::false_type); + void copy_assign(basic_flat_buffer const&, std::true_type); + void copy_assign(basic_flat_buffer const&, std::false_type); + void swap(basic_flat_buffer&); + void swap(basic_flat_buffer&, std::true_type); + void swap(basic_flat_buffer&, std::false_type); + char* alloc(std::size_t n); }; +/// A flat buffer which uses the default allocator. using flat_buffer = basic_flat_buffer<std::allocator<char>>; } // beast } // boost -#include <boost/beast/core/impl/flat_buffer.ipp> +#include <boost/beast/core/impl/flat_buffer.hpp> #endif diff --git a/boost/beast/core/flat_static_buffer.hpp b/boost/beast/core/flat_static_buffer.hpp index c8eae90945..f951fe2c62 100644 --- a/boost/beast/core/flat_static_buffer.hpp +++ b/boost/beast/core/flat_static_buffer.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -19,22 +19,34 @@ namespace boost { namespace beast { -/** A flat @b DynamicBuffer with a fixed size internal buffer. +/** A dynamic buffer using a fixed size internal buffer. - Buffer sequences returned by @ref data and @ref prepare - will always be of length one. - Ownership of the underlying storage belongs to the derived class. + A dynamic buffer encapsulates memory storage that may be + automatically resized as required, where the memory is + divided into two regions: readable bytes followed by + writable bytes. These memory regions are internal to + the dynamic buffer, but direct access to the elements + is provided to permit them to be efficiently used with + I/O operations. - @note Variables are usually declared using the template class - @ref flat_static_buffer; 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 - @ref flat_static_buffer_base. + Objects of this type meet the requirements of <em>DynamicBuffer</em> + and have the following additional properties: + + @li A mutable buffer sequence representing the readable + bytes is returned by @ref data when `this` is non-const. + + @li Buffer sequences representing the readable and writable + bytes, returned by @ref data and @ref prepare, will have + length one. - When used with @ref flat_static_buffer this implements a dynamic - buffer using no memory allocations. + @li Ownership of the underlying storage belongs to the + derived class. - @see @ref flat_static_buffer + @note Variables are usually declared using the template class + @ref flat_static_buffer; however, to reduce the number of template + instantiations, objects should be passed `flat_static_buffer_base&`. + + @see flat_static_buffer */ class flat_static_buffer_base { @@ -44,95 +56,175 @@ class flat_static_buffer_base char* last_; char* end_; - flat_static_buffer_base(flat_static_buffer_base const& other) = delete; - flat_static_buffer_base& operator=(flat_static_buffer_base const&) = delete; + flat_static_buffer_base( + flat_static_buffer_base const& other) = delete; + flat_static_buffer_base& operator=( + flat_static_buffer_base const&) = delete; public: - /** The type used to represent the input sequence as a list of buffers. + /** Constructor - This buffer sequence is guaranteed to have length 1. - */ - using const_buffers_type = boost::asio::const_buffer; + This creates a dynamic buffer using the provided storage area. - /** The type used to represent the output sequence as a list of buffers. + @param p A pointer to valid storage of at least `n` bytes. - This buffer sequence is guaranteed to have length 1. + @param n The number of valid bytes pointed to by `p`. */ - using mutable_buffers_type = boost::asio::mutable_buffer; + flat_static_buffer_base( + void* p, std::size_t n) noexcept + { + reset(p, n); + } - /** Constructor + /** Clear the readable and writable bytes to zero. - This creates a dynamic buffer using the provided storage area. + This function causes the readable and writable bytes + to become empty. The capacity is not changed. - @param p A pointer to valid storage of at least `n` bytes. + Buffer sequences previously obtained using @ref data or + @ref prepare become invalid. - @param n The number of valid bytes pointed to by `p`. + @esafe + + No-throw guarantee. */ - flat_static_buffer_base(void* p, std::size_t n) + BOOST_BEAST_DECL + void + clear() noexcept; + +#ifdef BOOST_BEAST_ALLOW_DEPRECATED + /// Change the number of readable and writable bytes to zero. + void + reset() noexcept { - reset_impl(p, n); + clear(); } +#elif ! BOOST_BEAST_DOXYGEN + template<std::size_t I = 0> + void + reset() noexcept + { + static_assert(I != 0, + BOOST_BEAST_DEPRECATION_STRING); + } +#endif + + //-------------------------------------------------------------------------- - /// Return the size of the input sequence. + /// The ConstBufferSequence used to represent the readable bytes. + using const_buffers_type = net::const_buffer; + + /// The MutableBufferSequence used to represent the readable bytes. + using mutable_data_type = net::mutable_buffer; + + /// The MutableBufferSequence used to represent the writable bytes. + using mutable_buffers_type = net::mutable_buffer; + + /// Returns the number of readable bytes. std::size_t - size() const + size() const noexcept { return out_ - in_; } - /// Return the maximum sum of the input and output sequence sizes. + /// Return the maximum number of bytes, both readable and writable, that can ever be held. std::size_t - max_size() const + max_size() const noexcept { return dist(begin_, end_); } - /// Return the maximum sum of input and output sizes that can be held without an allocation. + /// Return the maximum number of bytes, both readable and writable, that can be held without requiring an allocation. std::size_t - capacity() const + capacity() const noexcept { return max_size(); } - /** Get a list of buffers that represent the input sequence. + /// Returns a constant buffer sequence representing the readable bytes + const_buffers_type + data() const noexcept + { + return {in_, dist(in_, out_)}; + } - @note These buffers remain valid across subsequent calls to `prepare`. - */ + /// Returns a constant buffer sequence representing the readable bytes const_buffers_type - data() const; + cdata() const noexcept + { + return data(); + } - /// Set the input and output sequences to size 0 - void - reset(); + /// Returns a mutable buffer sequence representing the readable bytes + mutable_data_type + data() noexcept + { + return {in_, dist(in_, out_)}; + } + + /** Returns a mutable buffer sequence representing writable bytes. + + Returns a mutable buffer sequence representing the writable + bytes containing exactly `n` bytes of storage. - /** Get a list of buffers that represent the output sequence, with the given size. + All buffers sequences previously obtained using + @ref data or @ref prepare are invalidated. - @throws std::length_error if the size would exceed the limit - imposed by the underlying mutable buffer sequence. + @param n The desired number of bytes in the returned buffer + sequence. - @note Buffers representing the input sequence acquired prior to - this call remain valid. + @throws std::length_error if `size() + n` exceeds `max_size()`. + + @esafe + + Strong guarantee. */ + BOOST_BEAST_DECL mutable_buffers_type prepare(std::size_t n); - /** Move bytes from the output sequence to the input sequence. + /** Append writable bytes to the readable bytes. + + Appends n bytes from the start of the writable bytes to the + end of the readable bytes. The remainder of the writable bytes + are discarded. If n is greater than the number of writable + bytes, all writable bytes are appended to the readable bytes. + + All buffers sequences previously obtained using + @ref data or @ref prepare are invalidated. + + @param n The number of bytes to append. If this number + is greater than the number of writable bytes, all + writable bytes are appended. - @note Buffers representing the input sequence acquired prior to - this call remain valid. + @esafe + + No-throw guarantee. */ void - commit(std::size_t n) + commit(std::size_t n) noexcept { out_ += (std::min<std::size_t>)(n, last_ - out_); } - /// Remove bytes from the input sequence. + /** Remove bytes from beginning of the readable bytes. + + Removes n bytes from the beginning of the readable bytes. + + All buffers sequences previously obtained using + @ref data or @ref prepare are invalidated. + + @param n The number of bytes to remove. If this number + is greater than the number of readable bytes, all + readable bytes are removed. + + @esafe + + No-throw guarantee. + */ + BOOST_BEAST_DECL void - consume(std::size_t n) - { - consume_impl(n); - } + consume(std::size_t n) noexcept; protected: /** Constructor @@ -153,39 +245,27 @@ protected: @param p A pointer to valid storage of at least `n` bytes. @param n The number of valid bytes pointed to by `p`. + + @esafe + + No-throw guarantee. */ + BOOST_BEAST_DECL void - reset(void* p, std::size_t n); + reset(void* p, std::size_t n) noexcept; private: static - inline std::size_t - dist(char const* first, char const* last) + dist(char const* first, char const* last) noexcept { return static_cast<std::size_t>(last - first); } - - template<class = void> - void - reset_impl(); - - template<class = void> - void - reset_impl(void* p, std::size_t n); - - template<class = void> - mutable_buffers_type - prepare_impl(std::size_t n); - - template<class = void> - void - consume_impl(std::size_t n); }; //------------------------------------------------------------------------------ -/** A @b DynamicBuffer with a fixed size internal buffer. +/** A <em>DynamicBuffer</em> with a fixed size internal buffer. Buffer sequences returned by @ref data and @ref prepare will always be of length one. @@ -197,7 +277,7 @@ private: objects of this type in a deduced context, the signature of the receiving function should use @ref flat_static_buffer_base instead. - @see @ref flat_static_buffer_base + @see flat_static_buffer_base */ template<std::size_t N> class flat_static_buffer : public flat_static_buffer_base @@ -249,6 +329,9 @@ public: } // beast } // boost +#include <boost/beast/core/impl/flat_static_buffer.hpp> +#ifdef BOOST_BEAST_HEADER_ONLY #include <boost/beast/core/impl/flat_static_buffer.ipp> - #endif + +#endif
\ No newline at end of file diff --git a/boost/beast/core/flat_stream.hpp b/boost/beast/core/flat_stream.hpp new file mode 100644 index 0000000000..fb8d9c19c9 --- /dev/null +++ b/boost/beast/core/flat_stream.hpp @@ -0,0 +1,347 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_FLAT_STREAM_HPP +#define BOOST_BEAST_CORE_FLAT_STREAM_HPP + +#include <boost/beast/core/detail/config.hpp> +#include <boost/beast/core/error.hpp> +#include <boost/beast/core/flat_buffer.hpp> +#include <boost/beast/core/stream_traits.hpp> +#include <boost/beast/core/detail/flat_stream.hpp> +#include <boost/asio/async_result.hpp> +#include <cstdlib> +#include <utility> + +namespace boost { +namespace beast { + +/** Stream wrapper to improve write performance. + + This wrapper flattens writes for buffer sequences having length + greater than 1 and total size below a predefined amount, using + a dynamic memory allocation. It is primarily designed to overcome + a performance limitation of the current version of `net::ssl::stream`, + which does not use OpenSSL's scatter/gather interface for its + low-level read some and write some operations. + + It is normally not necessary to use this class directly if you + are already using @ref ssl_stream. The following examples shows + how to use this class with the ssl stream that comes with + networking: + + @par Example + + To use the @ref flat_stream template with SSL streams, declare + a variable of the correct type. Parameters passed to the constructor + will be forwarded to the next layer's constructor: + + @code + flat_stream<net::ssl::stream<ip::tcp::socket>> fs{ioc, ctx}; + @endcode + Alternatively you can write + @code + ssl::stream<ip::tcp::socket> ss{ioc, ctx}; + flat_stream<net::ssl::stream<ip::tcp::socket>&> fs{ss}; + @endcode + + The resulting stream may be passed to any stream algorithms which + operate on synchronous or asynchronous read or write streams, + examples include: + + @li `net::read`, `net::async_read` + + @li `net::write`, `net::async_write` + + @li `net::read_until`, `net::async_read_until` + + The stream may also be used as a template parameter in other + stream wrappers, such as for websocket: + @code + websocket::stream<flat_stream<net::ssl::stream<ip::tcp::socket>>> ws{ioc, ctx}; + @endcode + + @tparam NextLayer The type representing the next layer, to which + data will be read and written during operations. For synchronous + operations, the type must support the @b SyncStream concept. For + asynchronous operations, the type must support the @b AsyncStream + concept. This type will usually be some variation of + `net::ssl::stream`. + + @par Concepts + @li SyncStream + @li AsyncStream + + @see + @li https://github.com/boostorg/asio/issues/100 + @li https://github.com/boostorg/beast/issues/1108 + @li https://stackoverflow.com/questions/38198638/openssl-ssl-write-from-multiple-buffers-ssl-writev + @li https://stackoverflow.com/questions/50026167/performance-drop-on-port-from-beast-1-0-0-b66-to-boost-1-67-0-beast +*/ +template<class NextLayer> +class flat_stream +#if ! BOOST_BEAST_DOXYGEN + : private detail::flat_stream_base +#endif +{ + NextLayer stream_; + flat_buffer buffer_; + + BOOST_STATIC_ASSERT(has_get_executor<NextLayer>::value); + + struct ops; + + template<class ConstBufferSequence> + std::size_t + stack_write_some( + std::size_t size, + ConstBufferSequence const& buffers, + error_code& ec); + +public: + /// The type of the next layer. + using next_layer_type = + typename std::remove_reference<NextLayer>::type; + + /// The type of the executor associated with the object. + using executor_type = beast::executor_type<next_layer_type>; + + flat_stream(flat_stream&&) = default; + flat_stream(flat_stream const&) = default; + flat_stream& operator=(flat_stream&&) = default; + flat_stream& operator=(flat_stream const&) = default; + + /** Destructor + + The treatment of pending operations will be the same as that + of the next layer. + */ + ~flat_stream() = default; + + /** Constructor + + Arguments, if any, are forwarded to the next layer's constructor. + */ + template<class... Args> + explicit + flat_stream(Args&&... args); + + //-------------------------------------------------------------------------- + + /** Get the executor associated with the object. + + This function may be used to obtain the executor object that the + stream uses to dispatch handlers for asynchronous operations. + + @return A copy of the executor that stream will use to dispatch handlers. + */ + executor_type + get_executor() noexcept + { + return stream_.get_executor(); + } + + /** Get a reference to the next layer + + This function returns a reference to the next layer + in a stack of stream layers. + + @return A reference to the next layer in the stack of + stream layers. + */ + next_layer_type& + next_layer() noexcept + { + return stream_; + } + + /** Get a reference to the next layer + + This function returns a reference to the next layer in a + stack of stream layers. + + @return A reference to the next layer in the stack of + stream layers. + */ + next_layer_type const& + next_layer() const noexcept + { + return stream_; + } + + //-------------------------------------------------------------------------- + + /** Read some data from the stream. + + This function is used to read data from the stream. The function call will + block until one or more bytes of data has been read successfully, or until + an error occurs. + + @param buffers The buffers into which the data will be read. + + @returns The number of bytes read. + + @throws boost::system::system_error Thrown on failure. + + @note The `read_some` operation may not read all of the requested number of + bytes. Consider using the function `net::read` if you need to ensure + that the requested amount of data is read before the blocking operation + completes. + */ + template<class MutableBufferSequence> + std::size_t + read_some(MutableBufferSequence const& buffers); + + /** Read some data from the stream. + + This function is used to read data from the stream. The function call will + block until one or more bytes of data has been read successfully, or until + an error occurs. + + @param buffers The buffers into which the data will be read. + + @param ec Set to indicate what error occurred, if any. + + @returns The number of bytes read. + + @note The `read_some` operation may not read all of the requested number of + bytes. Consider using the function `net::read` if you need to ensure + that the requested amount of data is read before the blocking operation + completes. + */ + template<class MutableBufferSequence> + std::size_t + read_some( + MutableBufferSequence const& buffers, + error_code& ec); + + /** Start an asynchronous read. + + This function is used to asynchronously read one or more bytes of data from + the stream. The function call always returns immediately. + + @param buffers The buffers into which the data will be read. Although the + buffers object may be copied as necessary, ownership of the underlying + buffers is retained by the caller, which must guarantee that they remain + valid until the handler is called. + + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( + error_code const& error, // Result of operation. + std::size_t bytes_transferred // Number of bytes read. + ); + @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `net::post`. + + @note The `read_some` operation may not read all of the requested number of + bytes. Consider using the function `net::async_read` if you need + to ensure that the requested amount of data is read before the asynchronous + operation completes. + */ + template< + class MutableBufferSequence, + class ReadHandler> + BOOST_BEAST_ASYNC_RESULT2(ReadHandler) + async_read_some( + MutableBufferSequence const& buffers, + ReadHandler&& handler); + + /** Write some data to the stream. + + This function is used to write data on the stream. The function call will + block until one or more bytes of data has been written successfully, or + until an error occurs. + + @param buffers The data to be written. + + @returns The number of bytes written. + + @throws boost::system::system_error Thrown on failure. + + @note The `write_some` operation may not transmit all of the data to the + peer. Consider using the function `net::write` if you need to + ensure that all data is written before the blocking operation completes. + */ + template<class ConstBufferSequence> + std::size_t + write_some(ConstBufferSequence const& buffers); + + /** Write some data to the stream. + + This function is used to write data on the stream. The function call will + block until one or more bytes of data has been written successfully, or + until an error occurs. + + @param buffers The data to be written. + + @param ec Set to indicate what error occurred, if any. + + @returns The number of bytes written. + + @note The `write_some` operation may not transmit all of the data to the + peer. Consider using the function `net::write` if you need to + ensure that all data is written before the blocking operation completes. + */ + template<class ConstBufferSequence> + std::size_t + write_some( + ConstBufferSequence const& buffers, + error_code& ec); + + /** Start an asynchronous write. + + This function is used to asynchronously write one or more bytes of data to + the stream. The function call always returns immediately. + + @param buffers The data to be written to the stream. Although the buffers + object may be copied as necessary, ownership of the underlying buffers is + retained by the caller, which must guarantee that they remain valid until + the handler is called. + + @param handler The completion handler to invoke when the operation + completes. The implementation takes ownership of the handler by + performing a decay-copy. The equivalent function signature of + the handler must be: + @code + void handler( + error_code const& ec, // Result of operation. + std::size_t bytes_transferred // Number of bytes written. + ); + @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `net::post`. + + @note The `async_write_some` operation may not transmit all of the data to + the peer. Consider using the function `net::async_write` if you need + to ensure that all data is written before the asynchronous operation completes. + */ + template< + class ConstBufferSequence, + class WriteHandler> + BOOST_BEAST_ASYNC_RESULT2(WriteHandler) + async_write_some( + ConstBufferSequence const& buffers, + WriteHandler&& handler); +}; + +} // beast +} // boost + +#include <boost/beast/core/impl/flat_stream.hpp> + +#endif diff --git a/boost/beast/core/handler_ptr.hpp b/boost/beast/core/handler_ptr.hpp index 64de1b7dab..24fd244fd9 100644 --- a/boost/beast/core/handler_ptr.hpp +++ b/boost/beast/core/handler_ptr.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -10,13 +10,17 @@ #ifndef BOOST_BEAST_HANDLER_PTR_HPP #define BOOST_BEAST_HANDLER_PTR_HPP -#include <boost/beast/core/detail/allocator.hpp> #include <boost/beast/core/detail/config.hpp> -#include <boost/beast/core/detail/type_traits.hpp> +#include <boost/beast/core/detail/allocator.hpp> #include <boost/assert.hpp> +#include <boost/config/pragma_message.hpp> #include <type_traits> #include <utility> +#ifndef BOOST_BEAST_DOXYGEN + +BOOST_PRAGMA_MESSAGE("<boost/beast/core/handler_ptr.hpp> is DEPRECATED and will be removed in a future release.") + namespace boost { namespace beast { @@ -50,17 +54,20 @@ namespace beast { template<class T, class Handler> class handler_ptr { - using handler_storage_t = typename detail::aligned_union<1, Handler>::type; +#ifndef BOOST_BEAST_ALLOW_DEPRECATED + static_assert(sizeof(T) == 0, + BOOST_BEAST_DEPRECATION_STRING); +#endif T* t_ = nullptr; - handler_storage_t h_; + union + { + Handler h_; + }; void clear(); public: - static_assert(std::is_nothrow_destructible<T>::value, - "T must be nothrow destructible"); - /// The type of element stored using element_type = T; @@ -106,30 +113,31 @@ public: T::T(Handler const&, Args&&...) @endcode - @par Exception Safety + @esafe Strong guarantee. - @param handler The handler to associate with the owned - object. The argument will be moved if it is an xvalue. + @param handler The handler to associate with the owned object. + The implementation takes ownership of the handler by performing a decay-copy. @param args Optional arguments forwarded to the owned object's constructor. */ template<class DeducedHandler, class... Args> - explicit handler_ptr(DeducedHandler&& handler, Args&&... args); + explicit + handler_ptr(DeducedHandler&& handler, Args&&... args); /// Return a reference to the handler handler_type const& - handler() const + handler() const noexcept { - return *reinterpret_cast<Handler const*>(&h_); + return h_; } /// Return a reference to the handler handler_type& - handler() + handler() noexcept { - return *reinterpret_cast<Handler*>(&h_); + return h_; } /// Return `true` if `*this` owns an object @@ -167,10 +175,11 @@ public: T* operator->() const { + BOOST_ASSERT(t_); return t_; } - /** Return ownership of the handler + /** Returns ownership of the handler Before this function returns, the owned object is destroyed, satisfying the deallocation-before-invocation @@ -213,6 +222,8 @@ public: } // beast } // boost -#include <boost/beast/core/impl/handler_ptr.ipp> +#include <boost/beast/core/impl/handler_ptr.hpp> + +#endif #endif diff --git a/boost/beast/core/impl/async_base.hpp b/boost/beast/core/impl/async_base.hpp new file mode 100644 index 0000000000..ec17b1cc1e --- /dev/null +++ b/boost/beast/core/impl/async_base.hpp @@ -0,0 +1,156 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_IMPL_ASYNC_BASE_HPP +#define BOOST_BEAST_CORE_IMPL_ASYNC_BASE_HPP + +#include <boost/core/exchange.hpp> + +namespace boost { +namespace beast { + +namespace detail { + +template<class State, class Allocator> +struct allocate_stable_state final + : stable_base + , boost::empty_value<Allocator> +{ + State value; + + template<class... Args> + explicit + allocate_stable_state( + Allocator const& alloc, + Args&&... args) + : boost::empty_value<Allocator>( + boost::empty_init_t{}, alloc) + , value{std::forward<Args>(args)...} + { + } + + void destroy() override + { + using A = typename allocator_traits< + Allocator>::template rebind_alloc< + allocate_stable_state>; + + A a(this->get()); + detail::allocator_traits<A>::destroy(a, this); + detail::allocator_traits<A>::deallocate(a, this, 1); + } +}; + +} // detail + +template< + class Handler, + class Executor1, + class Allocator, + class Function> +void asio_handler_invoke( + Function&& f, + async_base<Handler, Executor1, Allocator>* p) +{ + using net::asio_handler_invoke; + asio_handler_invoke(f, + p->get_legacy_handler_pointer()); +} + +template< + class Handler, + class Executor1, + class Allocator> +void* +asio_handler_allocate( + std::size_t size, + async_base<Handler, Executor1, Allocator>* p) +{ + using net::asio_handler_allocate; + return asio_handler_allocate(size, + p->get_legacy_handler_pointer()); +} + +template< + class Handler, + class Executor1, + class Allocator> +void +asio_handler_deallocate( + void* mem, std::size_t size, + async_base<Handler, Executor1, Allocator>* p) +{ + using net::asio_handler_deallocate; + asio_handler_deallocate(mem, size, + p->get_legacy_handler_pointer()); +} + +template< + class Handler, + class Executor1, + class Allocator> +bool +asio_handler_is_continuation( + async_base<Handler, Executor1, Allocator>* p) +{ + using net::asio_handler_is_continuation; + return asio_handler_is_continuation( + p->get_legacy_handler_pointer()); +} + +template< + class State, + class Handler, + class Executor1, + class Allocator, + class... Args> +State& +allocate_stable( + stable_async_base< + Handler, Executor1, Allocator>& base, + Args&&... args) +{ + using allocator_type = typename stable_async_base< + Handler, Executor1, Allocator>::allocator_type; + + using A = typename detail::allocator_traits< + allocator_type>::template rebind_alloc< + detail::allocate_stable_state< + State, allocator_type>>; + + struct deleter + { + allocator_type alloc; + detail::allocate_stable_state< + State, allocator_type>* ptr; + + ~deleter() + { + if(ptr) + { + A a(alloc); + detail::allocator_traits<A>::deallocate(a, ptr, 1); + } + } + }; + + A a(base.get_allocator()); + deleter d{base.get_allocator(), nullptr}; + d.ptr = detail::allocator_traits<A>::allocate(a, 1); + detail::allocator_traits<A>::construct(a, d.ptr, + d.alloc, std::forward<Args>(args)...); + d.ptr->next_ = base.list_; + base.list_ = d.ptr; + return boost::exchange(d.ptr, nullptr)->value; +} + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/impl/basic_stream.hpp b/boost/beast/core/impl/basic_stream.hpp new file mode 100644 index 0000000000..c871944552 --- /dev/null +++ b/boost/beast/core/impl/basic_stream.hpp @@ -0,0 +1,995 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_IMPL_BASIC_STREAM_HPP +#define BOOST_BEAST_CORE_IMPL_BASIC_STREAM_HPP + +#include <boost/beast/core/async_base.hpp> +#include <boost/beast/core/buffer_traits.hpp> +#include <boost/beast/core/buffers_prefix.hpp> +#include <boost/beast/core/detail/type_traits.hpp> +#include <boost/beast/websocket/teardown.hpp> +#include <boost/asio/bind_executor.hpp> +#include <boost/asio/coroutine.hpp> +#include <boost/assert.hpp> +#include <boost/make_shared.hpp> +#include <boost/core/exchange.hpp> +#include <cstdlib> +#include <type_traits> +#include <utility> + +namespace boost { +namespace beast { + +//------------------------------------------------------------------------------ + +template<class Protocol, class Executor, class RatePolicy> +template<class... Args> +basic_stream<Protocol, Executor, RatePolicy>:: +impl_type:: +impl_type(std::false_type, Args&&... args) + : socket(std::forward<Args>(args)...) + , read(ex()) + , write(ex()) + , timer(ex()) +{ + reset(); +} + +template<class Protocol, class Executor, class RatePolicy> +template<class RatePolicy_, class... Args> +basic_stream<Protocol, Executor, RatePolicy>:: +impl_type:: +impl_type(std::true_type, + RatePolicy_&& policy, Args&&... args) + : boost::empty_value<RatePolicy>( + boost::empty_init_t{}, + std::forward<RatePolicy_>(policy)) + , socket(std::forward<Args>(args)...) + , read(ex()) + , write(ex()) + , timer(ex()) +{ + reset(); +} + +template<class Protocol, class Executor, class RatePolicy> +template<class Executor2> +void +basic_stream<Protocol, Executor, RatePolicy>:: +impl_type:: +on_timer(Executor2 const& ex2) +{ + BOOST_ASSERT(waiting > 0); + + // the last waiter starts the new slice + if(--waiting > 0) + return; + + // update the expiration time + BOOST_VERIFY(timer.expires_after( + std::chrono::seconds(1)) == 0); + + rate_policy_access::on_timer(policy()); + + struct handler : boost::empty_value<Executor2> + { + boost::weak_ptr<impl_type> wp; + + using executor_type = Executor2; + + executor_type + get_executor() const noexcept + { + return this->get(); + } + + handler( + Executor2 const& ex2, + boost::shared_ptr<impl_type> const& sp) + : boost::empty_value<Executor2>( + boost::empty_init_t{}, ex2) + , wp(sp) + { + } + + void + operator()(error_code ec) + { + auto sp = wp.lock(); + if(! sp) + return; + if(ec == net::error::operation_aborted) + return; + BOOST_ASSERT(! ec); + if(ec) + return; + sp->on_timer(this->get()); + } + }; + + // wait on the timer again + ++waiting; + timer.async_wait(handler(ex2, this->shared_from_this())); +} + +template<class Protocol, class Executor, class RatePolicy> +void +basic_stream<Protocol, Executor, RatePolicy>:: +impl_type:: +reset() +{ + // If assert goes off, it means that there are + // already read or write (or connect) operations + // outstanding, so there is nothing to apply + // the expiration time to! + // + BOOST_ASSERT(! read.pending || ! write.pending); + + if(! read.pending) + BOOST_VERIFY( + read.timer.expires_at(never()) == 0); + + if(! write.pending) + BOOST_VERIFY( + write.timer.expires_at(never()) == 0); +} + +template<class Protocol, class Executor, class RatePolicy> +void +basic_stream<Protocol, Executor, RatePolicy>:: +impl_type:: +close() +{ + socket.close(); + timer.cancel(); + + // have to let the read/write ops cancel the timer, + // otherwise we will get error::timeout on close when + // we actually want net::error::operation_aborted. + // + //read.timer.cancel(); + //write.timer.cancel(); +} + +//------------------------------------------------------------------------------ + +template<class Protocol, class Executor, class RatePolicy> +struct basic_stream<Protocol, Executor, RatePolicy>:: + timeout_handler +{ + op_state& state; + boost::weak_ptr<impl_type> wp; + tick_type tick; + + void + operator()(error_code ec) + { + // timer canceled + if(ec == net::error::operation_aborted) + return; + BOOST_ASSERT(! ec); + + auto sp = wp.lock(); + + // stream destroyed + if(! sp) + return; + + // stale timer + if(tick < state.tick) + return; + BOOST_ASSERT(tick == state.tick); + + // timeout + BOOST_ASSERT(! state.timeout); + sp->close(); + state.timeout = true; + } +}; + +//------------------------------------------------------------------------------ + +template<class Protocol, class Executor, class RatePolicy> +struct basic_stream<Protocol, Executor, RatePolicy>::ops +{ + +template<bool isRead, class Buffers, class Handler> +class transfer_op + : public async_base<Handler, Executor> + , public boost::asio::coroutine +{ + boost::shared_ptr<impl_type> impl_; + pending_guard pg_; + Buffers b_; + + using is_read = std::integral_constant<bool, isRead>; + + op_state& + state(std::true_type) + { + return impl_->read; + } + + op_state& + state(std::false_type) + { + return impl_->write; + } + + op_state& + state() + { + return state( + std::integral_constant<bool, isRead>{}); + } + + std::size_t + available_bytes(std::true_type) + { + return rate_policy_access:: + available_read_bytes(impl_->policy()); + } + + std::size_t + available_bytes(std::false_type) + { + return rate_policy_access:: + available_write_bytes(impl_->policy()); + } + + std::size_t + available_bytes() + { + return available_bytes(is_read{}); + } + + void + transfer_bytes(std::size_t n, std::true_type) + { + rate_policy_access:: + transfer_read_bytes(impl_->policy(), n); + } + + void + transfer_bytes(std::size_t n, std::false_type) + { + rate_policy_access:: + transfer_write_bytes(impl_->policy(), n); + } + + void + transfer_bytes(std::size_t n) + { + transfer_bytes(n, is_read{}); + } + + void + async_perform( + std::size_t amount, std::true_type) + { + impl_->socket.async_read_some( + beast::buffers_prefix(amount, b_), + std::move(*this)); + } + + void + async_perform( + std::size_t amount, std::false_type) + { + impl_->socket.async_write_some( + beast::buffers_prefix(amount, b_), + std::move(*this)); + } + +public: + template<class Handler_> + transfer_op( + Handler_&& h, + basic_stream& s, + Buffers const& b) + : async_base<Handler, Executor>( + std::forward<Handler_>(h), s.get_executor()) + , impl_(s.impl_) + , pg_(state().pending) + , b_(b) + { + (*this)({}); + } + + void + operator()( + error_code ec, + std::size_t bytes_transferred = 0) + { + BOOST_ASIO_CORO_REENTER(*this) + { + // handle empty buffers + if(detail::buffers_empty(b_)) + { + // make sure we perform the no-op + BOOST_ASIO_CORO_YIELD + async_perform(0, is_read{}); + // apply the timeout manually, otherwise + // behavior varies across platforms. + if(state().timer.expiry() <= clock_type::now()) + { + impl_->close(); + ec = beast::error::timeout; + } + goto upcall; + } + + // if a timeout is active, wait on the timer + if(state().timer.expiry() != never()) + state().timer.async_wait( + net::bind_executor( + this->get_executor(), + timeout_handler{ + state(), + impl_, + state().tick + })); + + // check rate limit, maybe wait + std::size_t amount; + amount = available_bytes(); + if(amount == 0) + { + ++impl_->waiting; + BOOST_ASIO_CORO_YIELD + impl_->timer.async_wait(std::move(*this)); + if(ec) + { + // socket was closed, or a timeout + BOOST_ASSERT(ec == + net::error::operation_aborted); + // timeout handler invoked? + if(state().timeout) + { + // yes, socket already closed + ec = beast::error::timeout; + state().timeout = false; + } + goto upcall; + } + impl_->on_timer(this->get_executor()); + + // Allow at least one byte, otherwise + // bytes_transferred could be 0. + amount = std::max<std::size_t>( + available_bytes(), 1); + } + + BOOST_ASIO_CORO_YIELD + async_perform(amount, is_read{}); + + if(state().timer.expiry() != never()) + { + ++state().tick; + + // try cancelling timer + auto const n = + state().timer.cancel(); + if(n == 0) + { + // timeout handler invoked? + if(state().timeout) + { + // yes, socket already closed + ec = beast::error::timeout; + state().timeout = false; + } + } + else + { + BOOST_ASSERT(n == 1); + BOOST_ASSERT(! state().timeout); + } + } + + upcall: + pg_.reset(); + transfer_bytes(bytes_transferred); + this->complete_now(ec, bytes_transferred); + } + } +}; + +template<class Handler> +class connect_op + : public async_base<Handler, Executor> +{ + boost::shared_ptr<impl_type> impl_; + pending_guard pg0_; + pending_guard pg1_; + + op_state& + state() noexcept + { + return impl_->write; + } + +public: + template<class Handler_> + connect_op( + Handler_&& h, + basic_stream& s, + endpoint_type ep) + : async_base<Handler, Executor>( + std::forward<Handler_>(h), s.get_executor()) + , impl_(s.impl_) + , pg0_(impl_->read.pending) + , pg1_(impl_->write.pending) + { + if(state().timer.expiry() != stream_base::never()) + impl_->write.timer.async_wait( + net::bind_executor( + this->get_executor(), + timeout_handler{ + state(), + impl_, + state().tick})); + + impl_->socket.async_connect( + ep, std::move(*this)); + // *this is now moved-from + } + + template< + class Endpoints, class Condition, + class Handler_> + connect_op( + Handler_&& h, + basic_stream& s, + Endpoints const& eps, + Condition const& cond) + : async_base<Handler, Executor>( + std::forward<Handler_>(h), s.get_executor()) + , impl_(s.impl_) + , pg0_(impl_->read.pending) + , pg1_(impl_->write.pending) + { + if(state().timer.expiry() != stream_base::never()) + impl_->write.timer.async_wait( + net::bind_executor( + this->get_executor(), + timeout_handler{ + state(), + impl_, + state().tick})); + + net::async_connect(impl_->socket, + eps, cond, std::move(*this)); + // *this is now moved-from + } + + template< + class Iterator, class Condition, + class Handler_> + connect_op( + Handler_&& h, + basic_stream& s, + Iterator begin, Iterator end, + Condition const& cond) + : async_base<Handler, Executor>( + std::forward<Handler_>(h), s.get_executor()) + , impl_(s.impl_) + , pg0_(impl_->read.pending) + , pg1_(impl_->write.pending) + { + if(state().timer.expiry() != stream_base::never()) + impl_->write.timer.async_wait( + net::bind_executor( + this->get_executor(), + timeout_handler{ + state(), + impl_, + state().tick})); + + net::async_connect(impl_->socket, + begin, end, cond, std::move(*this)); + // *this is now moved-from + } + + template<class... Args> + void + operator()(error_code ec, Args&&... args) + { + if(state().timer.expiry() != stream_base::never()) + { + ++state().tick; + + // try cancelling timer + auto const n = + impl_->write.timer.cancel(); + if(n == 0) + { + // timeout handler invoked? + if(state().timeout) + { + // yes, socket already closed + ec = beast::error::timeout; + state().timeout = false; + } + } + else + { + BOOST_ASSERT(n == 1); + BOOST_ASSERT(! state().timeout); + } + } + + pg0_.reset(); + pg1_.reset(); + this->complete_now(ec, std::forward<Args>(args)...); + } +}; + +struct run_read_op +{ + template<class ReadHandler, class Buffers> + void + operator()( + ReadHandler&& h, + basic_stream* s, + Buffers const& b) + { + // If you get an error on the following line it means + // that your handler does not meet the documented type + // requirements for the handler. + + static_assert( + detail::is_invocable<ReadHandler, + void(error_code, std::size_t)>::value, + "ReadHandler type requirements not met"); + + transfer_op< + true, + Buffers, + typename std::decay<ReadHandler>::type>( + std::forward<ReadHandler>(h), *s, b); + } +}; + +struct run_write_op +{ + template<class WriteHandler, class Buffers> + void + operator()( + WriteHandler&& h, + basic_stream* s, + Buffers const& b) + { + // If you get an error on the following line it means + // that your handler does not meet the documented type + // requirements for the handler. + + static_assert( + detail::is_invocable<WriteHandler, + void(error_code, std::size_t)>::value, + "WriteHandler type requirements not met"); + + transfer_op< + false, + Buffers, + typename std::decay<WriteHandler>::type>( + std::forward<WriteHandler>(h), *s, b); + } +}; + +struct run_connect_op +{ + template<class ConnectHandler> + void + operator()( + ConnectHandler&& h, + basic_stream* s, + endpoint_type const& ep) + { + // If you get an error on the following line it means + // that your handler does not meet the documented type + // requirements for the handler. + + static_assert( + detail::is_invocable<ConnectHandler, + void(error_code)>::value, + "ConnectHandler type requirements not met"); + + connect_op<typename std::decay<ConnectHandler>::type>( + std::forward<ConnectHandler>(h), *s, ep); + } +}; + +struct run_connect_range_op +{ + template< + class RangeConnectHandler, + class EndpointSequence, + class Condition> + void + operator()( + RangeConnectHandler&& h, + basic_stream* s, + EndpointSequence const& eps, + Condition const& cond) + { + // If you get an error on the following line it means + // that your handler does not meet the documented type + // requirements for the handler. + + static_assert( + detail::is_invocable<RangeConnectHandler, + void(error_code, typename Protocol::endpoint)>::value, + "RangeConnectHandler type requirements not met"); + + connect_op<typename std::decay<RangeConnectHandler>::type>( + std::forward<RangeConnectHandler>(h), *s, eps, cond); + } +}; + +struct run_connect_iter_op +{ + template< + class IteratorConnectHandler, + class Iterator, + class Condition> + void + operator()( + IteratorConnectHandler&& h, + basic_stream* s, + Iterator begin, Iterator end, + Condition const& cond) + { + // If you get an error on the following line it means + // that your handler does not meet the documented type + // requirements for the handler. + + static_assert( + detail::is_invocable<IteratorConnectHandler, + void(error_code, Iterator)>::value, + "IteratorConnectHandler type requirements not met"); + + connect_op<typename std::decay<IteratorConnectHandler>::type>( + std::forward<IteratorConnectHandler>(h), *s, begin, end, cond); + } +}; + +}; + +//------------------------------------------------------------------------------ + +template<class Protocol, class Executor, class RatePolicy> +basic_stream<Protocol, Executor, RatePolicy>:: +~basic_stream() +{ + // the shared object can outlive *this, + // cancel any operations so the shared + // object is destroyed as soon as possible. + impl_->close(); +} + +template<class Protocol, class Executor, class RatePolicy> +template<class Arg0, class... Args, class> +basic_stream<Protocol, Executor, RatePolicy>:: +basic_stream(Arg0&& arg0, Args&&... args) + : impl_(boost::make_shared<impl_type>( + std::false_type{}, + std::forward<Arg0>(arg0), + std::forward<Args>(args)...)) +{ +} + +template<class Protocol, class Executor, class RatePolicy> +template<class RatePolicy_, class Arg0, class... Args, class> +basic_stream<Protocol, Executor, RatePolicy>:: +basic_stream( + RatePolicy_&& policy, Arg0&& arg0, Args&&... args) + : impl_(boost::make_shared<impl_type>( + std::true_type{}, + std::forward<RatePolicy_>(policy), + std::forward<Arg0>(arg0), + std::forward<Args>(args)...)) +{ +} + +template<class Protocol, class Executor, class RatePolicy> +basic_stream<Protocol, Executor, RatePolicy>:: +basic_stream(basic_stream&& other) + : impl_(boost::make_shared<impl_type>( + std::move(*other.impl_))) +{ + // VFALCO I'm not sure this implementation is correct... +} + +//------------------------------------------------------------------------------ + +template<class Protocol, class Executor, class RatePolicy> +auto +basic_stream<Protocol, Executor, RatePolicy>:: +release_socket() -> + socket_type +{ + this->cancel(); + return std::move(impl_->socket); +} + +template<class Protocol, class Executor, class RatePolicy> +void +basic_stream<Protocol, Executor, RatePolicy>:: +expires_after(std::chrono::nanoseconds expiry_time) +{ + // If assert goes off, it means that there are + // already read or write (or connect) operations + // outstanding, so there is nothing to apply + // the expiration time to! + // + BOOST_ASSERT( + ! impl_->read.pending || + ! impl_->write.pending); + + if(! impl_->read.pending) + BOOST_VERIFY( + impl_->read.timer.expires_after( + expiry_time) == 0); + + if(! impl_->write.pending) + BOOST_VERIFY( + impl_->write.timer.expires_after( + expiry_time) == 0); +} + +template<class Protocol, class Executor, class RatePolicy> +void +basic_stream<Protocol, Executor, RatePolicy>:: +expires_at( + net::steady_timer::time_point expiry_time) +{ + // If assert goes off, it means that there are + // already read or write (or connect) operations + // outstanding, so there is nothing to apply + // the expiration time to! + // + BOOST_ASSERT( + ! impl_->read.pending || + ! impl_->write.pending); + + if(! impl_->read.pending) + BOOST_VERIFY( + impl_->read.timer.expires_at( + expiry_time) == 0); + + if(! impl_->write.pending) + BOOST_VERIFY( + impl_->write.timer.expires_at( + expiry_time) == 0); +} + +template<class Protocol, class Executor, class RatePolicy> +void +basic_stream<Protocol, Executor, RatePolicy>:: +expires_never() +{ + impl_->reset(); +} + +template<class Protocol, class Executor, class RatePolicy> +void +basic_stream<Protocol, Executor, RatePolicy>:: +cancel() +{ + error_code ec; + impl_->socket.cancel(ec); + impl_->timer.cancel(); +} + +template<class Protocol, class Executor, class RatePolicy> +void +basic_stream<Protocol, Executor, RatePolicy>:: +close() +{ + impl_->close(); +} + +//------------------------------------------------------------------------------ + +template<class Protocol, class Executor, class RatePolicy> +template<class ConnectHandler> +BOOST_BEAST_ASYNC_RESULT1(ConnectHandler) +basic_stream<Protocol, Executor, RatePolicy>:: +async_connect( + endpoint_type const& ep, + ConnectHandler&& handler) +{ + return net::async_initiate< + ConnectHandler, + void(error_code)>( + typename ops::run_connect_op{}, + handler, + this, + ep); +} + +template<class Protocol, class Executor, class RatePolicy> +template< + class EndpointSequence, + class RangeConnectHandler, + class> +BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,void(error_code, typename Protocol::endpoint)) +basic_stream<Protocol, Executor, RatePolicy>:: +async_connect( + EndpointSequence const& endpoints, + RangeConnectHandler&& handler) +{ + return net::async_initiate< + RangeConnectHandler, + void(error_code, typename Protocol::endpoint)>( + typename ops::run_connect_range_op{}, + handler, + this, + endpoints, + detail::any_endpoint{}); +} + +template<class Protocol, class Executor, class RatePolicy> +template< + class EndpointSequence, + class ConnectCondition, + class RangeConnectHandler, + class> +BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,void (error_code, typename Protocol::endpoint)) +basic_stream<Protocol, Executor, RatePolicy>:: +async_connect( + EndpointSequence const& endpoints, + ConnectCondition connect_condition, + RangeConnectHandler&& handler) +{ + return net::async_initiate< + RangeConnectHandler, + void(error_code, typename Protocol::endpoint)>( + typename ops::run_connect_range_op{}, + handler, + this, + endpoints, + connect_condition); +} + +template<class Protocol, class Executor, class RatePolicy> +template< + class Iterator, + class IteratorConnectHandler> +BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,void (error_code, Iterator)) +basic_stream<Protocol, Executor, RatePolicy>:: +async_connect( + Iterator begin, Iterator end, + IteratorConnectHandler&& handler) +{ + return net::async_initiate< + IteratorConnectHandler, + void(error_code, Iterator)>( + typename ops::run_connect_iter_op{}, + handler, + this, + begin, end, + detail::any_endpoint{}); +} + +template<class Protocol, class Executor, class RatePolicy> +template< + class Iterator, + class ConnectCondition, + class IteratorConnectHandler> +BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,void (error_code, Iterator)) +basic_stream<Protocol, Executor, RatePolicy>:: +async_connect( + Iterator begin, Iterator end, + ConnectCondition connect_condition, + IteratorConnectHandler&& handler) +{ + return net::async_initiate< + IteratorConnectHandler, + void(error_code, Iterator)>( + typename ops::run_connect_iter_op{}, + handler, + this, + begin, end, + connect_condition); +} + +//------------------------------------------------------------------------------ + +template<class Protocol, class Executor, class RatePolicy> +template<class MutableBufferSequence, class ReadHandler> +BOOST_BEAST_ASYNC_RESULT2(ReadHandler) +basic_stream<Protocol, Executor, RatePolicy>:: +async_read_some( + MutableBufferSequence const& buffers, + ReadHandler&& handler) +{ + static_assert(net::is_mutable_buffer_sequence< + MutableBufferSequence>::value, + "MutableBufferSequence type requirements not met"); + return net::async_initiate< + ReadHandler, + void(error_code, std::size_t)>( + typename ops::run_read_op{}, + handler, + this, + buffers); +} + +template<class Protocol, class Executor, class RatePolicy> +template<class ConstBufferSequence, class WriteHandler> +BOOST_BEAST_ASYNC_RESULT2(WriteHandler) +basic_stream<Protocol, Executor, RatePolicy>:: +async_write_some( + ConstBufferSequence const& buffers, + WriteHandler&& handler) +{ + static_assert(net::is_const_buffer_sequence< + ConstBufferSequence>::value, + "ConstBufferSequence type requirements not met"); + return net::async_initiate< + WriteHandler, + void(error_code, std::size_t)>( + typename ops::run_write_op{}, + handler, + this, + buffers); +} + +//------------------------------------------------------------------------------ +// +// Customization points +// + +#if ! BOOST_BEAST_DOXYGEN + +template< + class Protocol, class Executor, class RatePolicy> +void +beast_close_socket( + basic_stream<Protocol, Executor, RatePolicy>& stream) +{ + error_code ec; + stream.socket().close(ec); +} + +template< + class Protocol, class Executor, class RatePolicy> +void +teardown( + role_type role, + basic_stream<Protocol, Executor, RatePolicy>& stream, + error_code& ec) +{ + using beast::websocket::teardown; + teardown(role, stream.socket(), ec); +} + +template< + class Protocol, class Executor, class RatePolicy, + class TeardownHandler> +void +async_teardown( + role_type role, + basic_stream<Protocol, Executor, RatePolicy>& stream, + TeardownHandler&& handler) +{ + using beast::websocket::async_teardown; + async_teardown(role, stream.socket(), + std::forward<TeardownHandler>(handler)); +} + +#endif + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/impl/buffered_read_stream.hpp b/boost/beast/core/impl/buffered_read_stream.hpp new file mode 100644 index 0000000000..184881164f --- /dev/null +++ b/boost/beast/core/impl/buffered_read_stream.hpp @@ -0,0 +1,242 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_IMPL_BUFFERED_READ_STREAM_HPP +#define BOOST_BEAST_IMPL_BUFFERED_READ_STREAM_HPP + +#include <boost/beast/core/async_base.hpp> +#include <boost/beast/core/bind_handler.hpp> +#include <boost/beast/core/error.hpp> +#include <boost/beast/core/read_size.hpp> +#include <boost/beast/core/stream_traits.hpp> +#include <boost/beast/core/detail/type_traits.hpp> +#include <boost/asio/post.hpp> +#include <boost/throw_exception.hpp> + +namespace boost { +namespace beast { + + +template<class Stream, class DynamicBuffer> +struct buffered_read_stream<Stream, DynamicBuffer>::ops +{ + +template<class MutableBufferSequence, class Handler> +class read_op + : public async_base<Handler, + beast::executor_type<buffered_read_stream>> +{ + buffered_read_stream& s_; + MutableBufferSequence b_; + int step_ = 0; + +public: + read_op(read_op&&) = default; + read_op(read_op const&) = delete; + + template<class Handler_> + read_op( + Handler_&& h, + buffered_read_stream& s, + MutableBufferSequence const& b) + : async_base< + Handler, beast::executor_type<buffered_read_stream>>( + std::forward<Handler_>(h), s.get_executor()) + , s_(s) + , b_(b) + { + (*this)({}, 0); + } + + void + operator()( + error_code ec, + std::size_t bytes_transferred) + { + // VFALCO TODO Rewrite this using reenter/yield + switch(step_) + { + case 0: + if(s_.buffer_.size() == 0) + { + if(s_.capacity_ == 0) + { + // read (unbuffered) + step_ = 1; + return s_.next_layer_.async_read_some( + b_, std::move(*this)); + } + // read + step_ = 2; + return s_.next_layer_.async_read_some( + s_.buffer_.prepare(read_size( + s_.buffer_, s_.capacity_)), + std::move(*this)); + } + step_ = 3; + return net::post( + s_.get_executor(), + beast::bind_front_handler( + std::move(*this), ec, 0)); + + case 1: + // upcall + break; + + case 2: + s_.buffer_.commit(bytes_transferred); + BOOST_FALLTHROUGH; + + case 3: + bytes_transferred = + net::buffer_copy(b_, s_.buffer_.data()); + s_.buffer_.consume(bytes_transferred); + break; + } + this->complete_now(ec, bytes_transferred); + } +}; + +struct run_read_op +{ + template<class ReadHandler, class Buffers> + void + operator()( + ReadHandler&& h, + buffered_read_stream* s, + Buffers const& b) + { + // If you get an error on the following line it means + // that your handler does not meet the documented type + // requirements for the handler. + + static_assert( + beast::detail::is_invocable<ReadHandler, + void(error_code, std::size_t)>::value, + "ReadHandler type requirements not met"); + + read_op< + Buffers, + typename std::decay<ReadHandler>::type>( + std::forward<ReadHandler>(h), *s, b); + } +}; + +}; + +//------------------------------------------------------------------------------ + +template<class Stream, class DynamicBuffer> +template<class... Args> +buffered_read_stream<Stream, DynamicBuffer>:: +buffered_read_stream(Args&&... args) + : next_layer_(std::forward<Args>(args)...) +{ +} + +template<class Stream, class DynamicBuffer> +template<class ConstBufferSequence, class WriteHandler> +BOOST_BEAST_ASYNC_RESULT2(WriteHandler) +buffered_read_stream<Stream, DynamicBuffer>:: +async_write_some( + ConstBufferSequence const& buffers, + WriteHandler&& handler) +{ + static_assert(is_async_write_stream<next_layer_type>::value, + "AsyncWriteStream type requirements not met"); + static_assert(net::is_const_buffer_sequence< + ConstBufferSequence>::value, + "ConstBufferSequence type requirements not met"); + static_assert(detail::is_invocable<WriteHandler, + void(error_code, std::size_t)>::value, + "WriteHandler type requirements not met"); + return next_layer_.async_write_some(buffers, + std::forward<WriteHandler>(handler)); +} + +template<class Stream, class DynamicBuffer> +template<class MutableBufferSequence> +std::size_t +buffered_read_stream<Stream, DynamicBuffer>:: +read_some( + MutableBufferSequence const& buffers) +{ + static_assert(is_sync_read_stream<next_layer_type>::value, + "SyncReadStream type requirements not met"); + static_assert(net::is_mutable_buffer_sequence< + MutableBufferSequence>::value, + "MutableBufferSequence type requirements not met"); + error_code ec; + auto n = read_some(buffers, ec); + if(ec) + BOOST_THROW_EXCEPTION(system_error{ec}); + return n; +} + +template<class Stream, class DynamicBuffer> +template<class MutableBufferSequence> +std::size_t +buffered_read_stream<Stream, DynamicBuffer>:: +read_some(MutableBufferSequence const& buffers, + error_code& ec) +{ + static_assert(is_sync_read_stream<next_layer_type>::value, + "SyncReadStream type requirements not met"); + static_assert(net::is_mutable_buffer_sequence< + MutableBufferSequence>::value, + "MutableBufferSequence type requirements not met"); + if(buffer_.size() == 0) + { + if(capacity_ == 0) + return next_layer_.read_some(buffers, ec); + buffer_.commit(next_layer_.read_some( + buffer_.prepare(read_size(buffer_, + capacity_)), ec)); + if(ec) + return 0; + } + else + { + ec = {}; + } + auto bytes_transferred = + net::buffer_copy(buffers, buffer_.data()); + buffer_.consume(bytes_transferred); + return bytes_transferred; +} + +template<class Stream, class DynamicBuffer> +template<class MutableBufferSequence, class ReadHandler> +BOOST_BEAST_ASYNC_RESULT2(ReadHandler) +buffered_read_stream<Stream, DynamicBuffer>:: +async_read_some( + MutableBufferSequence const& buffers, + ReadHandler&& handler) +{ + static_assert(is_async_read_stream<next_layer_type>::value, + "AsyncReadStream type requirements not met"); + static_assert(net::is_mutable_buffer_sequence< + MutableBufferSequence>::value, + "MutableBufferSequence type requirements not met"); + if(buffer_.size() == 0 && capacity_ == 0) + return next_layer_.async_read_some(buffers, + std::forward<ReadHandler>(handler)); + return net::async_initiate< + ReadHandler, + void(error_code, std::size_t)>( + typename ops::run_read_op{}, + handler, + this, + buffers); +} + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/impl/buffered_read_stream.ipp b/boost/beast/core/impl/buffered_read_stream.ipp deleted file mode 100644 index b199ea0687..0000000000 --- a/boost/beast/core/impl/buffered_read_stream.ipp +++ /dev/null @@ -1,261 +0,0 @@ -// -// Copyright (c) 2016-2017 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) -// -// Official repository: https://github.com/boostorg/beast -// - -#ifndef BOOST_BEAST_IMPL_BUFFERED_READ_STREAM_IPP -#define BOOST_BEAST_IMPL_BUFFERED_READ_STREAM_IPP - -#include <boost/beast/core/bind_handler.hpp> -#include <boost/beast/core/error.hpp> -#include <boost/beast/core/handler_ptr.hpp> -#include <boost/beast/core/read_size.hpp> -#include <boost/beast/core/type_traits.hpp> -#include <boost/beast/core/detail/config.hpp> -#include <boost/asio/associated_allocator.hpp> -#include <boost/asio/associated_executor.hpp> -#include <boost/asio/executor_work_guard.hpp> -#include <boost/asio/handler_continuation_hook.hpp> -#include <boost/asio/handler_invoke_hook.hpp> -#include <boost/asio/post.hpp> -#include <boost/throw_exception.hpp> - -namespace boost { -namespace beast { - -template<class Stream, class DynamicBuffer> -template<class MutableBufferSequence, class Handler> -class buffered_read_stream< - Stream, DynamicBuffer>::read_some_op -{ - buffered_read_stream& s_; - boost::asio::executor_work_guard<decltype( - std::declval<Stream&>().get_executor())> wg_; - MutableBufferSequence b_; - Handler h_; - int step_ = 0; - -public: - read_some_op(read_some_op&&) = default; - read_some_op(read_some_op const&) = delete; - - template<class DeducedHandler, class... Args> - read_some_op(DeducedHandler&& h, - buffered_read_stream& s, - MutableBufferSequence const& b) - : s_(s) - , wg_(s_.get_executor()) - , b_(b) - , h_(std::forward<DeducedHandler>(h)) - { - } - - using allocator_type = - boost::asio::associated_allocator_t<Handler>; - - allocator_type - get_allocator() const noexcept - { - return (boost::asio::get_associated_allocator)(h_); - } - - using executor_type = - boost::asio::associated_executor_t<Handler, decltype( - std::declval<buffered_read_stream&>().get_executor())>; - - executor_type - get_executor() const noexcept - { - return (boost::asio::get_associated_executor)( - h_, s_.get_executor()); - } - - void - operator()(error_code const& ec, - std::size_t bytes_transferred); - - friend - bool asio_handler_is_continuation(read_some_op* op) - { - using boost::asio::asio_handler_is_continuation; - return asio_handler_is_continuation( - std::addressof(op->h_)); - } - - template<class Function> - friend - void asio_handler_invoke(Function&& f, read_some_op* op) - { - using boost::asio::asio_handler_invoke; - asio_handler_invoke(f, std::addressof(op->h_)); - } -}; - -template<class Stream, class DynamicBuffer> -template<class MutableBufferSequence, class Handler> -void -buffered_read_stream<Stream, DynamicBuffer>:: -read_some_op<MutableBufferSequence, Handler>::operator()( - error_code const& ec, std::size_t bytes_transferred) -{ - switch(step_) - { - case 0: - if(s_.buffer_.size() == 0) - { - if(s_.capacity_ == 0) - { - // read (unbuffered) - step_ = 1; - return s_.next_layer_.async_read_some( - b_, std::move(*this)); - } - - // read - step_ = 2; - return s_.next_layer_.async_read_some( - s_.buffer_.prepare(read_size( - s_.buffer_, s_.capacity_)), - std::move(*this)); - - } - step_ = 3; - return boost::asio::post( - s_.get_executor(), - bind_handler(std::move(*this), ec, 0)); - - case 1: - // upcall - break; - - case 2: - s_.buffer_.commit(bytes_transferred); - BOOST_FALLTHROUGH; - - case 3: - bytes_transferred = - boost::asio::buffer_copy(b_, s_.buffer_.data()); - s_.buffer_.consume(bytes_transferred); - break; - } - h_(ec, bytes_transferred); -} - -//------------------------------------------------------------------------------ - -template<class Stream, class DynamicBuffer> -template<class... Args> -buffered_read_stream<Stream, DynamicBuffer>:: -buffered_read_stream(Args&&... args) - : next_layer_(std::forward<Args>(args)...) -{ -} - -template<class Stream, class DynamicBuffer> -template<class ConstBufferSequence, class WriteHandler> -BOOST_ASIO_INITFN_RESULT_TYPE( - WriteHandler, void(error_code, std::size_t)) -buffered_read_stream<Stream, DynamicBuffer>:: -async_write_some( - ConstBufferSequence const& buffers, - WriteHandler&& handler) -{ - static_assert(is_async_write_stream<next_layer_type>::value, - "AsyncWriteStream requirements not met"); - static_assert(boost::asio::is_const_buffer_sequence< - ConstBufferSequence>::value, - "ConstBufferSequence requirements not met"); - static_assert(is_completion_handler<WriteHandler, - void(error_code, std::size_t)>::value, - "WriteHandler requirements not met"); - return next_layer_.async_write_some(buffers, - std::forward<WriteHandler>(handler)); -} - -template<class Stream, class DynamicBuffer> -template<class MutableBufferSequence> -std::size_t -buffered_read_stream<Stream, DynamicBuffer>:: -read_some( - MutableBufferSequence const& buffers) -{ - static_assert(is_sync_read_stream<next_layer_type>::value, - "SyncReadStream requirements not met"); - static_assert(boost::asio::is_mutable_buffer_sequence< - MutableBufferSequence>::value, - "MutableBufferSequence requirements not met"); - error_code ec; - auto n = read_some(buffers, ec); - if(ec) - BOOST_THROW_EXCEPTION(system_error{ec}); - return n; -} - -template<class Stream, class DynamicBuffer> -template<class MutableBufferSequence> -std::size_t -buffered_read_stream<Stream, DynamicBuffer>:: -read_some(MutableBufferSequence const& buffers, - error_code& ec) -{ - static_assert(is_sync_read_stream<next_layer_type>::value, - "SyncReadStream requirements not met"); - static_assert(boost::asio::is_mutable_buffer_sequence< - MutableBufferSequence>::value, - "MutableBufferSequence requirements not met"); - using boost::asio::buffer_size; - using boost::asio::buffer_copy; - if(buffer_.size() == 0) - { - if(capacity_ == 0) - return next_layer_.read_some(buffers, ec); - buffer_.commit(next_layer_.read_some( - buffer_.prepare(read_size(buffer_, - capacity_)), ec)); - if(ec) - return 0; - } - else - { - ec.assign(0, ec.category()); - } - auto bytes_transferred = - buffer_copy(buffers, buffer_.data()); - buffer_.consume(bytes_transferred); - return bytes_transferred; -} - -template<class Stream, class DynamicBuffer> -template<class MutableBufferSequence, class ReadHandler> -BOOST_ASIO_INITFN_RESULT_TYPE( - ReadHandler, void(error_code, std::size_t)) -buffered_read_stream<Stream, DynamicBuffer>:: -async_read_some( - MutableBufferSequence const& buffers, - ReadHandler&& handler) -{ - static_assert(is_async_read_stream<next_layer_type>::value, - "AsyncReadStream requirements not met"); - static_assert(boost::asio::is_mutable_buffer_sequence< - MutableBufferSequence>::value, - "MutableBufferSequence requirements not met"); - if(buffer_.size() == 0 && capacity_ == 0) - return next_layer_.async_read_some(buffers, - std::forward<ReadHandler>(handler)); - BOOST_BEAST_HANDLER_INIT( - ReadHandler, void(error_code, std::size_t)); - read_some_op<MutableBufferSequence, BOOST_ASIO_HANDLER_TYPE( - ReadHandler, void(error_code, std::size_t))>{ - std::move(init.completion_handler), *this, buffers}( - error_code{}, 0); - return init.result.get(); -} - -} // beast -} // boost - -#endif diff --git a/boost/beast/core/impl/buffers_adapter.ipp b/boost/beast/core/impl/buffers_adaptor.hpp index f077c521f0..91caad5463 100644 --- a/boost/beast/core/impl/buffers_adapter.ipp +++ b/boost/beast/core/impl/buffers_adaptor.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -7,37 +7,85 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_IMPL_BUFFERS_ADAPTER_IPP -#define BOOST_BEAST_IMPL_BUFFERS_ADAPTER_IPP +#ifndef BOOST_BEAST_IMPL_BUFFERS_ADAPTOR_HPP +#define BOOST_BEAST_IMPL_BUFFERS_ADAPTOR_HPP +#include <boost/beast/core/buffer_traits.hpp> #include <boost/beast/core/detail/type_traits.hpp> #include <boost/asio/buffer.hpp> +#include <boost/config/workaround.hpp> #include <boost/throw_exception.hpp> #include <algorithm> #include <cstring> #include <iterator> #include <stdexcept> +#include <type_traits> #include <utility> namespace boost { namespace beast { +//------------------------------------------------------------------------------ + +#if BOOST_WORKAROUND(BOOST_MSVC, < 1910) +# pragma warning (push) +# pragma warning (disable: 4521) // multiple copy constructors specified +# pragma warning (disable: 4522) // multiple assignment operators specified +#endif + template<class MutableBufferSequence> -class buffers_adapter<MutableBufferSequence>:: - const_buffers_type +template<bool isMutable> +class buffers_adaptor<MutableBufferSequence>:: + readable_bytes { - buffers_adapter const* b_; + buffers_adaptor const* b_; public: - using value_type = boost::asio::const_buffer; + using value_type = typename + std::conditional<isMutable, + net::mutable_buffer, + net::const_buffer>::type; class const_iterator; - const_buffers_type() = delete; - const_buffers_type( - const_buffers_type const&) = default; - const_buffers_type& operator=( - const_buffers_type const&) = default; + readable_bytes() = delete; + +#if BOOST_WORKAROUND(BOOST_MSVC, < 1910) + readable_bytes( + readable_bytes const& other) + : b_(other.b_) + { + } + + readable_bytes& operator=( + readable_bytes const& other) + { + b_ = other.b_; + return *this; + } +#else + readable_bytes( + readable_bytes const&) = default; + readable_bytes& operator=( + readable_bytes const&) = default; +#endif + + template<bool isMutable_ = isMutable, class = + typename std::enable_if<! isMutable_>::type> + readable_bytes( + readable_bytes<true> const& other) noexcept + : b_(other.b_) + { + } + + template<bool isMutable_ = isMutable, class = + typename std::enable_if<! isMutable_>::type> + readable_bytes& operator=( + readable_bytes<true> const& other) noexcept + { + b_ = other.b_; + return *this; + } const_iterator begin() const; @@ -46,23 +94,34 @@ public: end() const; private: - friend class buffers_adapter; + friend class buffers_adaptor; - const_buffers_type(buffers_adapter const& b) + readable_bytes(buffers_adaptor const& b) : b_(&b) { } }; +#if BOOST_WORKAROUND(BOOST_MSVC, < 1910) +# pragma warning (pop) +#endif + +//------------------------------------------------------------------------------ + template<class MutableBufferSequence> -class buffers_adapter<MutableBufferSequence>:: - const_buffers_type::const_iterator +template<bool isMutable> +class buffers_adaptor<MutableBufferSequence>:: + readable_bytes<isMutable>:: + const_iterator { - iter_type it_; - buffers_adapter const* b_ = nullptr; + iter_type it_{}; + buffers_adaptor const* b_ = nullptr; public: - using value_type = boost::asio::const_buffer; + using value_type = typename + std::conditional<isMutable, + net::mutable_buffer, + net::const_buffer>::type; using pointer = value_type const*; using reference = value_type; using difference_type = std::ptrdiff_t; @@ -70,28 +129,13 @@ public: 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_ == nullptr) ? - ( - other.b_ == nullptr || - other.it_ == other.b_->end_impl() - ):( - (other.b_ == nullptr) ? - ( - it_ == b_->end_impl() - ): ( - b_ == other.b_ && - it_ == other.it_ - ) - ); + return b_ == other.b_ && it_ == other.it_; } bool @@ -105,7 +149,7 @@ public: { value_type const b = *it_; return value_type{b.data(), - (b_->out_ == boost::asio::buffer_sequence_end(b_->bs_) || + (b_->out_ == net::buffer_sequence_end(b_->bs_) || it_ != b_->out_) ? b.size() : b_->out_pos_} + (it_ == b_->begin_ ? b_->in_pos_ : 0); } @@ -144,10 +188,11 @@ public: } private: - friend class const_buffers_type; + friend class readable_bytes; - const_iterator(buffers_adapter const& b, - iter_type iter) + const_iterator( + buffers_adaptor const& b, + iter_type iter) : it_(iter) , b_(&b) { @@ -155,18 +200,22 @@ private: }; template<class MutableBufferSequence> +template<bool isMutable> auto -buffers_adapter<MutableBufferSequence>:: -const_buffers_type::begin() const -> +buffers_adaptor<MutableBufferSequence>:: +readable_bytes<isMutable>:: +begin() const -> const_iterator { return const_iterator{*b_, b_->begin_}; } template<class MutableBufferSequence> +template<bool isMutable> auto -buffers_adapter<MutableBufferSequence>:: -const_buffers_type::end() const -> +buffers_adaptor<MutableBufferSequence>:: +readable_bytes<isMutable>:: +readable_bytes::end() const -> const_iterator { return const_iterator{*b_, b_->end_impl()}; @@ -175,13 +224,13 @@ const_buffers_type::end() const -> //------------------------------------------------------------------------------ template<class MutableBufferSequence> -class buffers_adapter<MutableBufferSequence>:: +class buffers_adaptor<MutableBufferSequence>:: mutable_buffers_type { - buffers_adapter const* b_; + buffers_adaptor const* b_; public: - using value_type = boost::asio::mutable_buffer; + using value_type = net::mutable_buffer; class const_iterator; @@ -198,24 +247,24 @@ public: end() const; private: - friend class buffers_adapter; + friend class buffers_adaptor; mutable_buffers_type( - buffers_adapter const& b) + buffers_adaptor const& b) : b_(&b) { } }; template<class MutableBufferSequence> -class buffers_adapter<MutableBufferSequence>:: +class buffers_adaptor<MutableBufferSequence>:: mutable_buffers_type::const_iterator { - iter_type it_; - buffers_adapter const* b_ = nullptr; + iter_type it_{}; + buffers_adaptor const* b_ = nullptr; public: - using value_type = boost::asio::mutable_buffer; + using value_type = net::mutable_buffer; using pointer = value_type const*; using reference = value_type; using difference_type = std::ptrdiff_t; @@ -223,28 +272,13 @@ public: 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_ == nullptr) ? - ( - other.b_ == nullptr || - other.it_ == other.b_->end_ - ):( - (other.b_ == nullptr) ? - ( - it_ == b_->end_ - ): ( - b_ == other.b_ && - it_ == other.it_ - ) - ); + return b_ == other.b_ && it_ == other.it_; } bool @@ -299,7 +333,7 @@ public: private: friend class mutable_buffers_type; - const_iterator(buffers_adapter const& b, + const_iterator(buffers_adaptor const& b, iter_type iter) : it_(iter) , b_(&b) @@ -308,9 +342,8 @@ private: }; template<class MutableBufferSequence> -inline auto -buffers_adapter<MutableBufferSequence>:: +buffers_adaptor<MutableBufferSequence>:: mutable_buffers_type:: begin() const -> const_iterator @@ -319,9 +352,8 @@ begin() const -> } template<class MutableBufferSequence> -inline auto -buffers_adapter<MutableBufferSequence>:: +buffers_adaptor<MutableBufferSequence>:: mutable_buffers_type:: end() const -> const_iterator @@ -333,7 +365,7 @@ end() const -> template<class MutableBufferSequence> auto -buffers_adapter<MutableBufferSequence>:: +buffers_adaptor<MutableBufferSequence>:: end_impl() const -> iter_type { @@ -341,71 +373,97 @@ end_impl() const -> } template<class MutableBufferSequence> -buffers_adapter<MutableBufferSequence>:: -buffers_adapter(buffers_adapter&& other) - : buffers_adapter(std::move(other), - std::distance<iter_type>(boost::asio::buffer_sequence_begin(other.bs_), other.begin_), - std::distance<iter_type>(boost::asio::buffer_sequence_begin(other.bs_), other.out_), - std::distance<iter_type>(boost::asio::buffer_sequence_begin(other.bs_), other.end_)) +buffers_adaptor<MutableBufferSequence>:: +buffers_adaptor( + buffers_adaptor const& other, + std::size_t nbegin, + std::size_t nout, + std::size_t nend) + : 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_) { } template<class MutableBufferSequence> -buffers_adapter<MutableBufferSequence>:: -buffers_adapter(buffers_adapter const& other) - : buffers_adapter(other, - std::distance<iter_type>(boost::asio::buffer_sequence_begin(other.bs_), other.begin_), - std::distance<iter_type>(boost::asio::buffer_sequence_begin(other.bs_), other.out_), - std::distance<iter_type>(boost::asio::buffer_sequence_begin(other.bs_), other.end_)) +buffers_adaptor<MutableBufferSequence>:: +buffers_adaptor(MutableBufferSequence const& bs) + : bs_(bs) + , begin_(net::buffer_sequence_begin(bs_)) + , out_ (net::buffer_sequence_begin(bs_)) + , end_ (net::buffer_sequence_begin(bs_)) + , max_size_( + [&bs] + { + return buffer_bytes(bs); + }()) { } template<class MutableBufferSequence> -auto -buffers_adapter<MutableBufferSequence>:: -operator=(buffers_adapter&& other) -> - buffers_adapter& +template<class... Args> +buffers_adaptor<MutableBufferSequence>:: +buffers_adaptor( + boost::in_place_init_t, Args&&... args) + : bs_{std::forward<Args>(args)...} + , begin_(net::buffer_sequence_begin(bs_)) + , out_ (net::buffer_sequence_begin(bs_)) + , end_ (net::buffer_sequence_begin(bs_)) + , max_size_( + [&] + { + return buffer_bytes(bs_); + }()) +{ +} + +template<class MutableBufferSequence> +buffers_adaptor<MutableBufferSequence>:: +buffers_adaptor(buffers_adaptor const& other) + : buffers_adaptor( + other, + std::distance<iter_type>( + net::buffer_sequence_begin(other.bs_), + other.begin_), + std::distance<iter_type>( + net::buffer_sequence_begin(other.bs_), + other.out_), + std::distance<iter_type>( + net::buffer_sequence_begin(other.bs_), + other.end_)) { - auto const nbegin = std::distance<iter_type>( - boost::asio::buffer_sequence_begin(other.bs_), - other.begin_); - auto const nout = std::distance<iter_type>( - boost::asio::buffer_sequence_begin(other.bs_), - other.out_); - auto const nend = std::distance<iter_type>( - boost::asio::buffer_sequence_begin(other.bs_), - other.end_); - bs_ = std::move(other.bs_); - begin_ = std::next(boost::asio::buffer_sequence_begin(bs_), nbegin); - out_ = std::next(boost::asio::buffer_sequence_begin(bs_), nout); - end_ = std::next(boost::asio::buffer_sequence_begin(bs_), 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<class MutableBufferSequence> auto -buffers_adapter<MutableBufferSequence>:: -operator=(buffers_adapter const& other) -> - buffers_adapter& +buffers_adaptor<MutableBufferSequence>:: +operator=(buffers_adaptor const& other) -> + buffers_adaptor& { + if(this == &other) + return *this; auto const nbegin = std::distance<iter_type>( - boost::asio::buffer_sequence_begin(other.bs_), + net::buffer_sequence_begin(other.bs_), other.begin_); auto const nout = std::distance<iter_type>( - boost::asio::buffer_sequence_begin(other.bs_), + net::buffer_sequence_begin(other.bs_), other.out_); auto const nend = std::distance<iter_type>( - boost::asio::buffer_sequence_begin(other.bs_), + net::buffer_sequence_begin(other.bs_), other.end_); bs_ = other.bs_; - begin_ = std::next(boost::asio::buffer_sequence_begin(bs_), nbegin); - out_ = std::next(boost::asio::buffer_sequence_begin(bs_), nout); - end_ = std::next(boost::asio::buffer_sequence_begin(bs_), nend); + begin_ = std::next( + net::buffer_sequence_begin(bs_), nbegin); + out_ = std::next( + net::buffer_sequence_begin(bs_), nout); + end_ = std::next( + net::buffer_sequence_begin(bs_), nend); max_size_ = other.max_size_; in_pos_ = other.in_pos_; in_size_ = other.in_size_; @@ -414,47 +472,43 @@ operator=(buffers_adapter const& other) -> return *this; } +// + template<class MutableBufferSequence> -buffers_adapter<MutableBufferSequence>:: -buffers_adapter(MutableBufferSequence const& bs) - : bs_(bs) - , begin_(boost::asio::buffer_sequence_begin(bs_)) - , out_ (boost::asio::buffer_sequence_begin(bs_)) - , end_ (boost::asio::buffer_sequence_begin(bs_)) - , max_size_(boost::asio::buffer_size(bs_)) +auto +buffers_adaptor<MutableBufferSequence>:: +data() const noexcept -> + const_buffers_type { + return const_buffers_type{*this}; } template<class MutableBufferSequence> -template<class... Args> -buffers_adapter<MutableBufferSequence>:: -buffers_adapter(boost::in_place_init_t, Args&&... args) - : bs_{std::forward<Args>(args)...} - , begin_(boost::asio::buffer_sequence_begin(bs_)) - , out_ (boost::asio::buffer_sequence_begin(bs_)) - , end_ (boost::asio::buffer_sequence_begin(bs_)) - , max_size_(boost::asio::buffer_size(bs_)) +auto +buffers_adaptor<MutableBufferSequence>:: +data() noexcept -> + mutable_data_type { + return mutable_data_type{*this}; } template<class MutableBufferSequence> auto -buffers_adapter<MutableBufferSequence>:: +buffers_adaptor<MutableBufferSequence>:: prepare(std::size_t n) -> mutable_buffers_type { - using boost::asio::buffer_size; end_ = out_; - if(end_ != boost::asio::buffer_sequence_end(bs_)) + if(end_ != net::buffer_sequence_end(bs_)) { - auto size = buffer_size(*end_) - out_pos_; + auto size = buffer_bytes(*end_) - out_pos_; if(n > size) { n -= size; while(++end_ != - boost::asio::buffer_sequence_end(bs_)) + net::buffer_sequence_end(bs_)) { - size = buffer_size(*end_); + size = buffer_bytes(*end_); if(n < size) { out_end_ = n; @@ -475,23 +529,22 @@ prepare(std::size_t n) -> } if(n > 0) BOOST_THROW_EXCEPTION(std::length_error{ - "buffer overflow"}); + "buffers_adaptor too long"}); return mutable_buffers_type{*this}; } template<class MutableBufferSequence> void -buffers_adapter<MutableBufferSequence>:: -commit(std::size_t n) +buffers_adaptor<MutableBufferSequence>:: +commit(std::size_t n) noexcept { - using boost::asio::buffer_size; if(out_ == end_) return; auto const last = std::prev(end_); while(out_ != last) { auto const avail = - buffer_size(*out_) - out_pos_; + buffer_bytes(*out_) - out_pos_; if(n < avail) { out_pos_ += n; @@ -504,10 +557,11 @@ commit(std::size_t n) in_size_ += avail; } - n = (std::min)(n, out_end_ - out_pos_); + n = std::min<std::size_t>( + n, out_end_ - out_pos_); out_pos_ += n; in_size_ += n; - if(out_pos_ == buffer_size(*out_)) + if(out_pos_ == buffer_bytes(*out_)) { ++out_; out_pos_ = 0; @@ -516,25 +570,14 @@ commit(std::size_t n) } template<class MutableBufferSequence> -inline -auto -buffers_adapter<MutableBufferSequence>:: -data() const -> - const_buffers_type -{ - return const_buffers_type{*this}; -} - -template<class MutableBufferSequence> void -buffers_adapter<MutableBufferSequence>:: -consume(std::size_t n) +buffers_adaptor<MutableBufferSequence>:: +consume(std::size_t n) noexcept { - using boost::asio::buffer_size; while(begin_ != out_) { auto const avail = - buffer_size(*begin_) - in_pos_; + buffer_bytes(*begin_) - in_pos_; if(n < avail) { in_size_ -= n; diff --git a/boost/beast/core/impl/buffers_cat.hpp b/boost/beast/core/impl/buffers_cat.hpp new file mode 100644 index 0000000000..255153f2bd --- /dev/null +++ b/boost/beast/core/impl/buffers_cat.hpp @@ -0,0 +1,443 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_IMPL_BUFFERS_CAT_HPP +#define BOOST_BEAST_IMPL_BUFFERS_CAT_HPP + +#include <boost/beast/core/detail/tuple.hpp> +#include <boost/beast/core/detail/type_traits.hpp> +#include <boost/beast/core/detail/variant.hpp> +#include <boost/asio/buffer.hpp> +#include <cstdint> +#include <iterator> +#include <new> +#include <stdexcept> +#include <utility> + +namespace boost { +namespace beast { + +#if defined(_MSC_VER) && ! defined(__clang__) +# define BOOST_BEAST_UNREACHABLE() __assume(false) +# define BOOST_BEAST_UNREACHABLE_RETURN(v) __assume(false) +#else +# define BOOST_BEAST_UNREACHABLE() __builtin_unreachable() +# define BOOST_BEAST_UNREACHABLE_RETURN(v) \ + do { __builtin_unreachable(); return v; } while(false) +#endif + +#ifdef BOOST_BEAST_TESTS + +#define BOOST_BEAST_LOGIC_ERROR(s) \ + do { \ + BOOST_THROW_EXCEPTION(std::logic_error((s))); \ + BOOST_BEAST_UNREACHABLE(); \ + } while(false) + +#define BOOST_BEAST_LOGIC_ERROR_RETURN(v, s) \ + do { \ + BOOST_THROW_EXCEPTION(std::logic_error(s)); \ + BOOST_BEAST_UNREACHABLE_RETURN(v); \ + } while(false) + +#else + +#define BOOST_BEAST_LOGIC_ERROR(s) \ + do { \ + BOOST_ASSERT_MSG(false, s); \ + BOOST_BEAST_UNREACHABLE(); \ + } while(false) + +#define BOOST_BEAST_LOGIC_ERROR_RETURN(v, s) \ + do { \ + BOOST_ASSERT_MSG(false, (s)); \ + BOOST_BEAST_UNREACHABLE_RETURN(v); \ + } while(false) + +#endif + +namespace detail { + +struct buffers_cat_view_iterator_base +{ + struct past_end + { + char unused = 0; // make g++8 happy + + net::mutable_buffer + operator*() const + { + BOOST_BEAST_LOGIC_ERROR_RETURN({}, + "Dereferencing a one-past-the-end iterator"); + } + + operator bool() const noexcept + { + return true; + } + }; +}; + +} // detail + +template<class... Bn> +class buffers_cat_view<Bn...>::const_iterator + : private detail::buffers_cat_view_iterator_base +{ + // VFALCO The logic to skip empty sequences fails + // if there is just one buffer in the list. + static_assert(sizeof...(Bn) >= 2, + "A minimum of two sequences are required"); + + detail::tuple<Bn...> const* bn_ = nullptr; + detail::variant< + buffers_iterator_type<Bn>..., past_end> it_{}; + + friend class buffers_cat_view<Bn...>; + + template<std::size_t I> + using C = std::integral_constant<std::size_t, I>; + +public: + using value_type = typename + buffers_cat_view<Bn...>::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 const& other) = default; + const_iterator& operator=( + const_iterator const& other) = default; + + 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); + + const_iterator& + operator--(); + + const_iterator + operator--(int); + +private: + const_iterator( + detail::tuple<Bn...> const& bn, + std::true_type); + + const_iterator( + detail::tuple<Bn...> const& bn, + std::false_type); + + struct dereference + { + const_iterator const& self; + + reference + operator()(mp11::mp_size_t<0>) + { + BOOST_BEAST_LOGIC_ERROR_RETURN({}, + "Dereferencing a default-constructed iterator"); + } + + template<class I> + reference operator()(I) + { + return *self.it_.template get<I::value>(); + } + }; + + struct increment + { + const_iterator& self; + + void + operator()(mp11::mp_size_t<0>) + { + BOOST_BEAST_LOGIC_ERROR( + "Incrementing a default-constructed iterator"); + } + + template<std::size_t I> + void + operator()(mp11::mp_size_t<I>) + { + ++self.it_.template get<I>(); + next(mp11::mp_size_t<I>{}); + } + + template<std::size_t I> + void + next(mp11::mp_size_t<I>) + { + auto& it = self.it_.template get<I>(); + for(;;) + { + if (it == net::buffer_sequence_end( + detail::get<I-1>(*self.bn_))) + break; + if(net::const_buffer(*it).size() > 0) + return; + ++it; + } + self.it_.template emplace<I+1>( + net::buffer_sequence_begin( + detail::get<I>(*self.bn_))); + next(mp11::mp_size_t<I+1>{}); + } + + void + operator()(mp11::mp_size_t<sizeof...(Bn)>) + { + auto constexpr I = sizeof...(Bn); + ++self.it_.template get<I>(); + next(mp11::mp_size_t<I>{}); + } + + void + next(mp11::mp_size_t<sizeof...(Bn)>) + { + auto constexpr I = sizeof...(Bn); + auto& it = self.it_.template get<I>(); + for(;;) + { + if (it == net::buffer_sequence_end( + detail::get<I-1>(*self.bn_))) + break; + if(net::const_buffer(*it).size() > 0) + return; + ++it; + } + // end + self.it_.template emplace<I+1>(); + } + + void + operator()(mp11::mp_size_t<sizeof...(Bn)+1>) + { + BOOST_BEAST_LOGIC_ERROR( + "Incrementing a one-past-the-end iterator"); + } + }; + + struct decrement + { + const_iterator& self; + + void + operator()(mp11::mp_size_t<0>) + { + BOOST_BEAST_LOGIC_ERROR( + "Decrementing a default-constructed iterator"); + } + + void + operator()(mp11::mp_size_t<1>) + { + auto constexpr I = 1; + + auto& it = self.it_.template get<I>(); + for(;;) + { + if(it == net::buffer_sequence_begin( + detail::get<I-1>(*self.bn_))) + { + BOOST_BEAST_LOGIC_ERROR( + "Decrementing an iterator to the beginning"); + } + --it; + if(net::const_buffer(*it).size() > 0) + return; + } + } + + template<std::size_t I> + void + operator()(mp11::mp_size_t<I>) + { + auto& it = self.it_.template get<I>(); + for(;;) + { + if(it == net::buffer_sequence_begin( + detail::get<I-1>(*self.bn_))) + break; + --it; + if(net::const_buffer(*it).size() > 0) + return; + } + self.it_.template emplace<I-1>( + net::buffer_sequence_end( + detail::get<I-2>(*self.bn_))); + (*this)(mp11::mp_size_t<I-1>{}); + } + + void + operator()(mp11::mp_size_t<sizeof...(Bn)+1>) + { + auto constexpr I = sizeof...(Bn)+1; + self.it_.template emplace<I-1>( + net::buffer_sequence_end( + detail::get<I-2>(*self.bn_))); + (*this)(mp11::mp_size_t<I-1>{}); + } + }; +}; + +//------------------------------------------------------------------------------ + +template<class... Bn> +buffers_cat_view<Bn...>:: +const_iterator:: +const_iterator( + detail::tuple<Bn...> const& bn, + std::true_type) + : bn_(&bn) +{ + // one past the end + it_.template emplace<sizeof...(Bn)+1>(); +} + +template<class... Bn> +buffers_cat_view<Bn...>:: +const_iterator:: +const_iterator( + detail::tuple<Bn...> const& bn, + std::false_type) + : bn_(&bn) +{ + it_.template emplace<1>( + net::buffer_sequence_begin( + detail::get<0>(*bn_))); + increment{*this}.next( + mp11::mp_size_t<1>{}); +} + +template<class... Bn> +bool +buffers_cat_view<Bn...>:: +const_iterator:: +operator==(const_iterator const& other) const +{ + return bn_ == other.bn_ && it_ == other.it_; +} + +template<class... Bn> +auto +buffers_cat_view<Bn...>:: +const_iterator:: +operator*() const -> + reference +{ + return mp11::mp_with_index< + sizeof...(Bn) + 2>( + it_.index(), + dereference{*this}); +} + +template<class... Bn> +auto +buffers_cat_view<Bn...>:: +const_iterator:: +operator++() -> + const_iterator& +{ + mp11::mp_with_index< + sizeof...(Bn) + 2>( + it_.index(), + increment{*this}); + return *this; +} + +template<class... Bn> +auto +buffers_cat_view<Bn...>:: +const_iterator:: +operator++(int) -> + const_iterator +{ + auto temp = *this; + ++(*this); + return temp; +} + +template<class... Bn> +auto +buffers_cat_view<Bn...>:: +const_iterator:: +operator--() -> + const_iterator& +{ + mp11::mp_with_index< + sizeof...(Bn) + 2>( + it_.index(), + decrement{*this}); + return *this; +} + +template<class... Bn> +auto +buffers_cat_view<Bn...>:: +const_iterator:: +operator--(int) -> + const_iterator +{ + auto temp = *this; + --(*this); + return temp; +} + +//------------------------------------------------------------------------------ + +template<class... Bn> +buffers_cat_view<Bn...>:: +buffers_cat_view(Bn const&... bn) + : bn_(bn...) +{ +} + + +template<class... Bn> +auto +buffers_cat_view<Bn...>::begin() const -> + const_iterator +{ + return const_iterator{bn_, std::false_type{}}; +} + +template<class... Bn> +auto +buffers_cat_view<Bn...>::end() const-> + const_iterator +{ + return const_iterator{bn_, std::true_type{}}; +} + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/impl/buffers_cat.ipp b/boost/beast/core/impl/buffers_cat.ipp deleted file mode 100644 index 9de1187f36..0000000000 --- a/boost/beast/core/impl/buffers_cat.ipp +++ /dev/null @@ -1,389 +0,0 @@ -// -// Copyright (c) 2016-2017 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) -// -// Official repository: https://github.com/boostorg/beast -// - -#ifndef BOOST_BEAST_IMPL_BUFFERS_CAT_IPP -#define BOOST_BEAST_IMPL_BUFFERS_CAT_IPP - -#include <boost/beast/core/detail/type_traits.hpp> -#include <boost/beast/core/detail/variant.hpp> -#include <boost/asio/buffer.hpp> -#include <boost/throw_exception.hpp> -#include <cstdint> -#include <iterator> -#include <new> -#include <stdexcept> -#include <tuple> -#include <utility> - -namespace boost { -namespace beast { - -template<class... Bn> -class buffers_cat_view<Bn...>::const_iterator -{ - // VFALCO The logic to skip empty sequences fails - // if there is just one buffer in the list. - static_assert(sizeof...(Bn) >= 2, - "A minimum of two sequences are required"); - - struct past_end - { - char unused = 0; // make g++8 happy - - operator bool() const noexcept - { - return true; - } - }; - - std::tuple<Bn...> const* bn_ = nullptr; - detail::variant<typename - detail::buffer_sequence_iterator<Bn>::type..., - past_end> it_; - - friend class buffers_cat_view<Bn...>; - - template<std::size_t I> - using C = std::integral_constant<std::size_t, I>; - -public: - using value_type = typename - detail::common_buffers_type<Bn...>::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; - - bool - operator!=(const_iterator const& other) const - { - return ! (*this == other); - } - - reference - operator*() const; - - pointer - operator->() const = delete; - - const_iterator& - operator++(); - - const_iterator - operator++(int); - - // deprecated - const_iterator& - operator--(); - - // deprecated - const_iterator - operator--(int); - -private: - const_iterator( - std::tuple<Bn...> const& bn, bool at_end); - - template<std::size_t I> - void - construct(C<I> const&) - { - if(boost::asio::buffer_size( - std::get<I>(*bn_)) != 0) - { - it_.template emplace<I+1>( - boost::asio::buffer_sequence_begin( - std::get<I>(*bn_))); - return; - } - construct(C<I+1>{}); - } - - void - construct(C<sizeof...(Bn)-1> const&) - { - auto constexpr I = sizeof...(Bn)-1; - it_.template emplace<I+1>( - boost::asio::buffer_sequence_begin( - std::get<I>(*bn_))); - } - - void - construct(C<sizeof...(Bn)> const&) - { - // end - auto constexpr I = sizeof...(Bn); - it_.template emplace<I+1>(); - } - - template<std::size_t I> - void - next(C<I> const&) - { - if(boost::asio::buffer_size( - std::get<I>(*bn_)) != 0) - { - it_.template emplace<I+1>( - boost::asio::buffer_sequence_begin( - std::get<I>(*bn_))); - return; - } - next(C<I+1>{}); - } - - void - next(C<sizeof...(Bn)> const&) - { - // end - auto constexpr I = sizeof...(Bn); - it_.template emplace<I+1>(); - } - - template<std::size_t I> - void - prev(C<I> const&) - { - if(boost::asio::buffer_size( - std::get<I>(*bn_)) != 0) - { - it_.template emplace<I+1>( - boost::asio::buffer_sequence_end( - std::get<I>(*bn_))); - return; - } - prev(C<I-1>{}); - } - - void - prev(C<0> const&) - { - auto constexpr I = 0; - it_.template emplace<I+1>( - boost::asio::buffer_sequence_end( - std::get<I>(*bn_))); - } - - template<std::size_t I> - reference - dereference(C<I> const&) const - { - if(it_.index() == I+1) - return *it_.template get<I+1>(); - return dereference(C<I+1>{}); - } - - [[noreturn]] - reference - dereference(C<sizeof...(Bn)> const&) const - { - BOOST_THROW_EXCEPTION(std::logic_error{ - "invalid iterator"}); - } - - template<std::size_t I> - void - increment(C<I> const&) - { - if(it_.index() == I+1) - { - if(++it_.template get<I+1>() != - boost::asio::buffer_sequence_end( - std::get<I>(*bn_))) - return; - return next(C<I+1>{}); - } - increment(C<I+1>{}); - } - - [[noreturn]] - void - increment(C<sizeof...(Bn)> const&) - { - BOOST_THROW_EXCEPTION(std::logic_error{ - "invalid iterator"}); - } - - void - decrement(C<sizeof...(Bn)> const&) - { - auto constexpr I = sizeof...(Bn); - if(it_.index() == I+1) - prev(C<I-1>{}); - decrement(C<I-1>{}); - } - - template<std::size_t I> - void - decrement(C<I> const&) - { - if(it_.index() == I+1) - { - if(it_.template get<I+1>() != - boost::asio::buffer_sequence_begin( - std::get<I>(*bn_))) - { - --it_.template get<I+1>(); - return; - } - prev(C<I-1>{}); - } - decrement(C<I-1>{}); - } - - void - decrement(C<0> const&) - { - auto constexpr I = 0; - if(it_.template get<I+1>() != - boost::asio::buffer_sequence_begin( - std::get<I>(*bn_))) - { - --it_.template get<I+1>(); - return; - } - BOOST_THROW_EXCEPTION(std::logic_error{ - "invalid iterator"}); - } -}; - -//------------------------------------------------------------------------------ - -template<class... Bn> -buffers_cat_view<Bn...>:: -const_iterator:: -const_iterator( - std::tuple<Bn...> const& bn, bool at_end) - : bn_(&bn) -{ - if(! at_end) - construct(C<0>{}); - else - construct(C<sizeof...(Bn)>{}); -} - -template<class... Bn> -bool -buffers_cat_view<Bn...>:: -const_iterator:: -operator==(const_iterator const& other) const -{ - return - (bn_ == nullptr) ? - ( - other.bn_ == nullptr || - other.it_.index() == sizeof...(Bn) - ):( - (other.bn_ == nullptr) ? - ( - it_.index() == sizeof...(Bn) - ): ( - bn_ == other.bn_ && - it_ == other.it_ - ) - ); -} - -template<class... Bn> -auto -buffers_cat_view<Bn...>:: -const_iterator:: -operator*() const -> - reference -{ - return dereference(C<0>{}); -} - -template<class... Bn> -auto -buffers_cat_view<Bn...>:: -const_iterator:: -operator++() -> - const_iterator& -{ - increment(C<0>{}); - return *this; -} - -template<class... Bn> -auto -buffers_cat_view<Bn...>:: -const_iterator:: -operator++(int) -> - const_iterator -{ - auto temp = *this; - ++(*this); - return temp; -} - -template<class... Bn> -auto -buffers_cat_view<Bn...>:: -const_iterator:: -operator--() -> - const_iterator& -{ - decrement(C<sizeof...(Bn)>{}); - return *this; -} - -template<class... Bn> -auto -buffers_cat_view<Bn...>:: -const_iterator:: -operator--(int) -> - const_iterator -{ - auto temp = *this; - --(*this); - return temp; -} - -//------------------------------------------------------------------------------ - -template<class... Bn> -buffers_cat_view<Bn...>:: -buffers_cat_view(Bn const&... bn) - : bn_(bn...) -{ -} - - -template<class... Bn> -inline -auto -buffers_cat_view<Bn...>::begin() const -> - const_iterator -{ - return const_iterator{bn_, false}; -} - -template<class... Bn> -inline -auto -buffers_cat_view<Bn...>::end() const -> - const_iterator -{ - return const_iterator{bn_, true}; -} - -} // beast -} // boost - -#endif diff --git a/boost/beast/core/impl/buffers_prefix.hpp b/boost/beast/core/impl/buffers_prefix.hpp new file mode 100644 index 0000000000..e4a484bcee --- /dev/null +++ b/boost/beast/core/impl/buffers_prefix.hpp @@ -0,0 +1,326 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_IMPL_BUFFERS_PREFIX_HPP +#define BOOST_BEAST_IMPL_BUFFERS_PREFIX_HPP + +#include <boost/beast/core/buffer_traits.hpp> +#include <boost/config/workaround.hpp> +#include <algorithm> +#include <cstdint> +#include <iterator> +#include <stdexcept> +#include <type_traits> +#include <utility> + +namespace boost { +namespace beast { + +template<class Buffers> +class buffers_prefix_view<Buffers>::const_iterator +{ + friend class buffers_prefix_view<Buffers>; + + buffers_prefix_view const* b_ = nullptr; + std::size_t remain_ = 0; + iter_type it_{}; + +public: +#if BOOST_WORKAROUND(BOOST_MSVC, < 1910) + using value_type = typename std::conditional< + boost::is_convertible<typename + std::iterator_traits<iter_type>::value_type, + net::mutable_buffer>::value, + net::mutable_buffer, + net::const_buffer>::type; +#else + using value_type = buffers_type<Buffers>; +#endif + + BOOST_STATIC_ASSERT(std::is_same< + typename const_iterator::value_type, + typename buffers_prefix_view::value_type>::value); + + 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 const& 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 + { + value_type v(*it_); + if(remain_ < v.size()) + return {v.data(), remain_}; + return v; + } + + pointer + operator->() const = delete; + + const_iterator& + operator++() + { + value_type const v = *it_++; + remain_ -= v.size(); + return *this; + } + + const_iterator + operator++(int) + { + auto temp = *this; + value_type const v = *it_++; + remain_ -= v.size(); + return temp; + } + + const_iterator& + operator--() + { + value_type const v = *--it_; + remain_ += v.size(); + return *this; + } + + const_iterator + operator--(int) + { + auto temp = *this; + value_type const v = *--it_; + remain_ += v.size(); + return temp; + } + +private: + const_iterator( + buffers_prefix_view const& b, + std::true_type) + : b_(&b) + , remain_(b.remain_) + , it_(b_->end_) + { + } + + const_iterator( + buffers_prefix_view const& b, + std::false_type) + : b_(&b) + , remain_(b_->size_) + , it_(net::buffer_sequence_begin(b_->bs_)) + { + } +}; + +//------------------------------------------------------------------------------ + +template<class Buffers> +void +buffers_prefix_view<Buffers>:: +setup(std::size_t size) +{ + size_ = 0; + remain_ = 0; + end_ = net::buffer_sequence_begin(bs_); + auto const last = bs_.end(); + while(end_ != last) + { + auto const len = buffer_bytes(*end_++); + if(len >= size) + { + size_ += size; + + // by design, this subtraction can wrap + BOOST_STATIC_ASSERT(std::is_unsigned< + decltype(remain_)>::value); + remain_ = size - len; + break; + } + size -= len; + size_ += len; + } +} + +template<class Buffers> +buffers_prefix_view<Buffers>:: +buffers_prefix_view( + buffers_prefix_view const& other, + std::size_t dist) + : bs_(other.bs_) + , size_(other.size_) + , remain_(other.remain_) + , end_(std::next(bs_.begin(), dist)) +{ +} + +template<class Buffers> +buffers_prefix_view<Buffers>:: +buffers_prefix_view(buffers_prefix_view const& other) + : buffers_prefix_view(other, + std::distance<iter_type>( + net::buffer_sequence_begin(other.bs_), + other.end_)) +{ +} + +template<class Buffers> +auto +buffers_prefix_view<Buffers>:: +operator=(buffers_prefix_view const& other) -> + buffers_prefix_view& +{ + auto const dist = std::distance<iter_type>( + net::buffer_sequence_begin(other.bs_), + other.end_); + bs_ = other.bs_; + size_ = other.size_; + remain_ = other.remain_; + end_ = std::next( + net::buffer_sequence_begin(bs_), + dist); + return *this; +} + +template<class Buffers> +buffers_prefix_view<Buffers>:: +buffers_prefix_view( + std::size_t size, + Buffers const& bs) + : bs_(bs) +{ + setup(size); +} + +template<class Buffers> +template<class... Args> +buffers_prefix_view<Buffers>:: +buffers_prefix_view( + std::size_t size, + boost::in_place_init_t, + Args&&... args) + : bs_(std::forward<Args>(args)...) +{ + setup(size); +} + +template<class Buffers> +auto +buffers_prefix_view<Buffers>:: +begin() const -> + const_iterator +{ + return const_iterator{ + *this, std::false_type{}}; +} + +template<class Buffers> +auto +buffers_prefix_view<Buffers>:: +end() const -> + const_iterator +{ + return const_iterator{ + *this, std::true_type{}}; +} + +//------------------------------------------------------------------------------ + +template<> +class buffers_prefix_view<net::const_buffer> + : public net::const_buffer +{ +public: + using net::const_buffer::const_buffer; + buffers_prefix_view(buffers_prefix_view const&) = default; + buffers_prefix_view& operator=(buffers_prefix_view const&) = default; + + buffers_prefix_view( + std::size_t size, + net::const_buffer buffer) + : net::const_buffer( + buffer.data(), + std::min<std::size_t>(size, buffer.size()) + #if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + , buffer.get_debug_check() + #endif + ) + { + } + + template<class... Args> + buffers_prefix_view( + std::size_t size, + boost::in_place_init_t, + Args&&... args) + : buffers_prefix_view(size, + net::const_buffer( + std::forward<Args>(args)...)) + { + } +}; + +//------------------------------------------------------------------------------ + +template<> +class buffers_prefix_view<net::mutable_buffer> + : public net::mutable_buffer +{ +public: + using net::mutable_buffer::mutable_buffer; + buffers_prefix_view(buffers_prefix_view const&) = default; + buffers_prefix_view& operator=(buffers_prefix_view const&) = default; + + buffers_prefix_view( + std::size_t size, + net::mutable_buffer buffer) + : net::mutable_buffer( + buffer.data(), + std::min<std::size_t>(size, buffer.size()) + #if defined(BOOST_ASIO_ENABLE_BUFFER_DEBUGGING) + , buffer.get_debug_check() + #endif + ) + { + } + + template<class... Args> + buffers_prefix_view( + std::size_t size, + boost::in_place_init_t, + Args&&... args) + : buffers_prefix_view(size, + net::mutable_buffer( + std::forward<Args>(args)...)) + { + } +}; + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/impl/buffers_prefix.ipp b/boost/beast/core/impl/buffers_prefix.ipp deleted file mode 100644 index c595455d60..0000000000 --- a/boost/beast/core/impl/buffers_prefix.ipp +++ /dev/null @@ -1,277 +0,0 @@ -// -// Copyright (c) 2016-2017 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) -// -// Official repository: https://github.com/boostorg/beast -// - -#ifndef BOOST_BEAST_IMPL_BUFFERS_PREFIX_IPP -#define BOOST_BEAST_IMPL_BUFFERS_PREFIX_IPP - -#include <algorithm> -#include <cstdint> -#include <iterator> -#include <stdexcept> -#include <type_traits> -#include <utility> - -namespace boost { -namespace beast { - -namespace detail { - -inline -boost::asio::const_buffer -buffers_prefix(std::size_t size, - boost::asio::const_buffer buffer) -{ - return {buffer.data(), - (std::min)(size, buffer.size())}; -} - -inline -boost::asio::mutable_buffer -buffers_prefix(std::size_t size, - boost::asio::mutable_buffer buffer) -{ - return {buffer.data(), - (std::min)(size, buffer.size())}; -} - -} // detail - -template<class BufferSequence> -class buffers_prefix_view<BufferSequence>::const_iterator -{ - friend class buffers_prefix_view<BufferSequence>; - - buffers_prefix_view const* b_ = nullptr; - std::size_t remain_; - iter_type it_; - -public: - using value_type = typename std::conditional< - boost::is_convertible<typename - std::iterator_traits<iter_type>::value_type, - boost::asio::mutable_buffer>::value, - boost::asio::mutable_buffer, - boost::asio::const_buffer>::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_ == nullptr) ? - ( - other.b_ == nullptr || - other.it_ == other.b_->end_ - ):( - (other.b_ == nullptr) ? - ( - it_ == b_->end_ - ): ( - b_ == other.b_ && - it_ == other.it_ - ) - ); - } - - bool - operator!=(const_iterator const& other) const - { - return !(*this == other); - } - - reference - operator*() const - { - return detail::buffers_prefix(remain_, *it_); - } - - pointer - operator->() const = delete; - - const_iterator& - operator++() - { - remain_ -= boost::asio::buffer_size(*it_++); - return *this; - } - - const_iterator - operator++(int) - { - auto temp = *this; - remain_ -= boost::asio::buffer_size(*it_++); - return temp; - } - - const_iterator& - operator--() - { - remain_ += boost::asio::buffer_size(*--it_); - return *this; - } - - const_iterator - operator--(int) - { - auto temp = *this; - remain_ += boost::asio::buffer_size(*--it_); - return temp; - } - -private: - const_iterator(buffers_prefix_view const& b, - std::true_type) - : b_(&b) - , remain_(b.remain_) - , it_(b_->end_) - { - } - - const_iterator(buffers_prefix_view const& b, - std::false_type) - : b_(&b) - , remain_(b_->size_) - , it_(boost::asio::buffer_sequence_begin(b_->bs_)) - { - } -}; - -//------------------------------------------------------------------------------ - -template<class BufferSequence> -void -buffers_prefix_view<BufferSequence>:: -setup(std::size_t size) -{ - size_ = 0; - remain_ = 0; - end_ = boost::asio::buffer_sequence_begin(bs_); - auto const last = bs_.end(); - while(end_ != last) - { - auto const len = - boost::asio::buffer_size(*end_++); - if(len >= size) - { - size_ += size; - remain_ = size - len; - break; - } - size -= len; - size_ += len; - } -} - -template<class BufferSequence> -buffers_prefix_view<BufferSequence>:: -buffers_prefix_view(buffers_prefix_view&& other) - : buffers_prefix_view(std::move(other), - std::distance<iter_type>( - boost::asio::buffer_sequence_begin(other.bs_), - other.end_)) -{ -} - -template<class BufferSequence> -buffers_prefix_view<BufferSequence>:: -buffers_prefix_view(buffers_prefix_view const& other) - : buffers_prefix_view(other, - std::distance<iter_type>( - boost::asio::buffer_sequence_begin(other.bs_), - other.end_)) -{ -} - -template<class BufferSequence> -auto -buffers_prefix_view<BufferSequence>:: -operator=(buffers_prefix_view&& other) -> - buffers_prefix_view& -{ - auto const dist = std::distance<iter_type>( - boost::asio::buffer_sequence_begin(other.bs_), - other.end_); - bs_ = std::move(other.bs_); - size_ = other.size_; - remain_ = other.remain_; - end_ = std::next( - boost::asio::buffer_sequence_begin(bs_), - dist); - return *this; -} - -template<class BufferSequence> -auto -buffers_prefix_view<BufferSequence>:: -operator=(buffers_prefix_view const& other) -> - buffers_prefix_view& -{ - auto const dist = std::distance<iter_type>( - boost::asio::buffer_sequence_begin(other.bs_), - other.end_); - bs_ = other.bs_; - size_ = other.size_; - remain_ = other.remain_; - end_ = std::next( - boost::asio::buffer_sequence_begin(bs_), - dist); - return *this; -} - -template<class BufferSequence> -buffers_prefix_view<BufferSequence>:: -buffers_prefix_view(std::size_t size, - BufferSequence const& bs) - : bs_(bs) -{ - setup(size); -} - -template<class BufferSequence> -template<class... Args> -buffers_prefix_view<BufferSequence>:: -buffers_prefix_view(std::size_t size, - boost::in_place_init_t, Args&&... args) - : bs_(std::forward<Args>(args)...) -{ - setup(size); -} - -template<class BufferSequence> -inline -auto -buffers_prefix_view<BufferSequence>::begin() const -> - const_iterator -{ - return const_iterator{*this, std::false_type{}}; -} - -template<class BufferSequence> -inline -auto -buffers_prefix_view<BufferSequence>::end() const -> - const_iterator -{ - return const_iterator{*this, std::true_type{}}; -} - -} // beast -} // boost - -#endif diff --git a/boost/beast/core/impl/buffers_suffix.ipp b/boost/beast/core/impl/buffers_suffix.hpp index 62b5f4634e..9822b4df70 100644 --- a/boost/beast/core/impl/buffers_suffix.ipp +++ b/boost/beast/core/impl/buffers_suffix.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -7,10 +7,11 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_IMPL_BUFFERS_SUFFIX_IPP -#define BOOST_BEAST_IMPL_BUFFERS_SUFFIX_IPP +#ifndef BOOST_BEAST_IMPL_BUFFERS_SUFFIX_HPP +#define BOOST_BEAST_IMPL_BUFFERS_SUFFIX_HPP -#include <boost/beast/core/type_traits.hpp> +#include <boost/beast/core/buffer_traits.hpp> +#include <boost/beast/core/buffer_traits.hpp> #include <boost/type_traits.hpp> #include <algorithm> #include <cstdint> @@ -26,19 +27,22 @@ class buffers_suffix<Buffers>::const_iterator { friend class buffers_suffix<Buffers>; - using iter_type = typename - detail::buffer_sequence_iterator<Buffers>::type; + using iter_type = buffers_iterator_type<Buffers>; - iter_type it_; + iter_type it_{}; buffers_suffix const* b_ = nullptr; public: +#if BOOST_WORKAROUND(BOOST_MSVC, < 1910) using value_type = typename std::conditional< boost::is_convertible<typename std::iterator_traits<iter_type>::value_type, - boost::asio::mutable_buffer>::value, - boost::asio::mutable_buffer, - boost::asio::const_buffer>::type; + net::mutable_buffer>::value, + net::mutable_buffer, + net::const_buffer>::type; +#else + using value_type = buffers_type<Buffers>; +#endif using pointer = value_type const*; using reference = value_type; using difference_type = std::ptrdiff_t; @@ -46,28 +50,15 @@ public: 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( + const_iterator const& other) = default; + const_iterator& operator=( + const_iterator const& other) = default; bool operator==(const_iterator const& other) const { - return - (b_ == nullptr) ? - ( - other.b_ == nullptr || - other.it_ == boost::asio::buffer_sequence_end(other.b_->bs_) - ):( - (other.b_ == nullptr) ? - ( - it_ == boost::asio::buffer_sequence_end(b_->bs_) - ): ( - b_ == other.b_ && - it_ == other.it_ - ) - ); + return b_ == other.b_ && it_ == other.it_; } bool @@ -79,9 +70,9 @@ public: reference operator*() const { - return it_ == b_->begin_ - ? value_type{*it_} + b_->skip_ - : *it_; + if(it_ == b_->begin_) + return value_type(*it_) + b_->skip_; + return value_type(*it_); } pointer @@ -118,8 +109,9 @@ public: } private: - const_iterator(buffers_suffix const& b, - iter_type it) + const_iterator( + buffers_suffix const& b, + iter_type it) : it_(it) , b_(&b) { @@ -131,17 +123,7 @@ private: template<class Buffers> buffers_suffix<Buffers>:: buffers_suffix() - : begin_(boost::asio::buffer_sequence_begin(bs_)) -{ -} - -template<class Buffers> -buffers_suffix<Buffers>:: -buffers_suffix(buffers_suffix&& other) - : buffers_suffix(std::move(other), - std::distance<iter_type>( - boost::asio::buffer_sequence_begin( - other.bs_), other.begin_)) + : begin_(net::buffer_sequence_begin(bs_)) { } @@ -150,7 +132,7 @@ buffers_suffix<Buffers>:: buffers_suffix(buffers_suffix const& other) : buffers_suffix(other, std::distance<iter_type>( - boost::asio::buffer_sequence_begin( + net::buffer_sequence_begin( other.bs_), other.begin_)) { } @@ -159,12 +141,12 @@ template<class Buffers> buffers_suffix<Buffers>:: buffers_suffix(Buffers const& bs) : bs_(bs) - , begin_(boost::asio::buffer_sequence_begin(bs_)) + , begin_(net::buffer_sequence_begin(bs_)) { static_assert( - boost::asio::is_const_buffer_sequence<Buffers>::value|| - boost::asio::is_mutable_buffer_sequence<Buffers>::value, - "BufferSequence requirements not met"); + net::is_const_buffer_sequence<Buffers>::value || + net::is_mutable_buffer_sequence<Buffers>::value, + "BufferSequence type requirements not met"); } template<class Buffers> @@ -172,7 +154,7 @@ template<class... Args> buffers_suffix<Buffers>:: buffers_suffix(boost::in_place_init_t, Args&&... args) : bs_(std::forward<Args>(args)...) - , begin_(boost::asio::buffer_sequence_begin(bs_)) + , begin_(net::buffer_sequence_begin(bs_)) { static_assert(sizeof...(Args) > 0, "Missing constructor arguments"); @@ -184,38 +166,20 @@ buffers_suffix(boost::in_place_init_t, Args&&... args) template<class Buffers> auto buffers_suffix<Buffers>:: -operator=(buffers_suffix&& other) -> - buffers_suffix& -{ - auto const dist = std::distance<iter_type>( - boost::asio::buffer_sequence_begin(other.bs_), - other.begin_); - bs_ = std::move(other.bs_); - begin_ = std::next( - boost::asio::buffer_sequence_begin(bs_), - dist); - skip_ = other.skip_; - return *this; -} - -template<class Buffers> -auto -buffers_suffix<Buffers>:: operator=(buffers_suffix const& other) -> buffers_suffix& { auto const dist = std::distance<iter_type>( - boost::asio::buffer_sequence_begin(other.bs_), + net::buffer_sequence_begin(other.bs_), other.begin_); bs_ = other.bs_; begin_ = std::next( - boost::asio::buffer_sequence_begin(bs_), dist); + net::buffer_sequence_begin(bs_), dist); skip_ = other.skip_; return *this; } template<class Buffers> -inline auto buffers_suffix<Buffers>:: begin() const -> @@ -225,14 +189,13 @@ begin() const -> } template<class Buffers> -inline auto buffers_suffix<Buffers>:: end() const -> const_iterator { return const_iterator{*this, - boost::asio::buffer_sequence_end(bs_)}; + net::buffer_sequence_end(bs_)}; } template<class Buffers> @@ -240,13 +203,12 @@ void buffers_suffix<Buffers>:: consume(std::size_t amount) { - using boost::asio::buffer_size; auto const end = - boost::asio::buffer_sequence_end(bs_); + net::buffer_sequence_end(bs_); for(;amount > 0 && begin_ != end; ++begin_) { auto const len = - buffer_size(*begin_) - skip_; + buffer_bytes(*begin_) - skip_; if(amount < len) { skip_ += amount; diff --git a/boost/beast/core/impl/error.hpp b/boost/beast/core/impl/error.hpp new file mode 100644 index 0000000000..a02caeefe2 --- /dev/null +++ b/boost/beast/core/impl/error.hpp @@ -0,0 +1,44 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_IMPL_ERROR_HPP +#define BOOST_BEAST_IMPL_ERROR_HPP + +#include <type_traits> + +namespace boost { +namespace system { +template<> +struct is_error_code_enum<::boost::beast::error> +{ + static bool const value = true; +}; +template<> +struct is_error_condition_enum<::boost::beast::condition> +{ + static bool const value = true; +}; +} // system +} // boost + +namespace boost { +namespace beast { + +BOOST_BEAST_DECL +error_code +make_error_code(error e); + +BOOST_BEAST_DECL +error_condition +make_error_condition(condition c); + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/impl/error.ipp b/boost/beast/core/impl/error.ipp new file mode 100644 index 0000000000..0b3015775b --- /dev/null +++ b/boost/beast/core/impl/error.ipp @@ -0,0 +1,99 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_IMPL_ERROR_IPP +#define BOOST_BEAST_IMPL_ERROR_IPP + +#include <boost/beast/core/error.hpp> + +namespace boost { +namespace beast { + +namespace detail { + +class error_codes : public error_category +{ +public: + const char* + name() const noexcept override + { + return "boost.beast"; + } + + BOOST_BEAST_DECL + std::string + message(int ev) const override + { + switch(static_cast<error>(ev)) + { + default: + case error::timeout: return + "The socket was closed due to a timeout"; + } + } + + BOOST_BEAST_DECL + error_condition + default_error_condition(int ev) const noexcept override + { + switch(static_cast<error>(ev)) + { + default: + // return {ev, *this}; + case error::timeout: + return condition::timeout; + } + } +}; + +class error_conditions : public error_category +{ +public: + BOOST_BEAST_DECL + const char* + name() const noexcept override + { + return "boost.beast"; + } + + BOOST_BEAST_DECL + std::string + message(int cv) const override + { + switch(static_cast<condition>(cv)) + { + default: + case condition::timeout: + return "The operation timed out"; + } + } +}; + +} // detail + +error_code +make_error_code(error e) +{ + static detail::error_codes const cat{}; + return error_code{static_cast< + std::underlying_type<error>::type>(e), cat}; +} + +error_condition +make_error_condition(condition c) +{ + static detail::error_conditions const cat{}; + return error_condition{static_cast< + std::underlying_type<condition>::type>(c), cat}; +} + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/impl/file_posix.ipp b/boost/beast/core/impl/file_posix.ipp index 8136bb83c2..0f7f42ac8d 100644 --- a/boost/beast/core/impl/file_posix.ipp +++ b/boost/beast/core/impl/file_posix.ipp @@ -1,5 +1,5 @@ // -// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2015-2019 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) @@ -10,8 +10,21 @@ #ifndef BOOST_BEAST_CORE_IMPL_FILE_POSIX_IPP #define BOOST_BEAST_CORE_IMPL_FILE_POSIX_IPP +#include <boost/beast/core/file_posix.hpp> + +#if BOOST_BEAST_USE_POSIX_FILE + +#include <boost/core/exchange.hpp> +#include <limits> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/stat.h> +#include <unistd.h> +#include <limits.h> + #if ! defined(BOOST_BEAST_NO_POSIX_FADVISE) -# if defined(__APPLE__) || (defined(ANDROID) && (__ANDROID_API__ < 21)) +# if defined(__APPLE__) || (defined(__ANDROID__) && (__ANDROID_API__ < 21)) # define BOOST_BEAST_NO_POSIX_FADVISE # endif #endif @@ -24,112 +37,92 @@ # endif #endif -#include <boost/core/exchange.hpp> -#include <limits> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <sys/stat.h> -#include <unistd.h> -#include <limits.h> - namespace boost { namespace beast { -namespace detail { - -inline int -file_posix_close(int fd) +file_posix:: +native_close(native_handle_type& fd) { - for(;;) +/* https://github.com/boostorg/beast/issues/1445 + + This function is tuned for Linux / Mac OS: + + * only calls close() once + * returns the error directly to the caller + * does not loop on EINTR + + If this is incorrect for the platform, then the + caller will need to implement their own type + meeting the File requirements and use the correct + behavior. + + See: + http://man7.org/linux/man-pages/man2/close.2.html +*/ + int ev = 0; + if(fd != -1) { - if(! ::close(fd)) - break; - int const ev = errno; - if(errno != EINTR) - return ev; + if(::close(fd) != 0) + ev = errno; + fd = -1; } - return 0; + return ev; } -} // detail - -inline file_posix:: ~file_posix() { - if(fd_ != -1) - detail::file_posix_close(fd_); + native_close(fd_); } -inline file_posix:: file_posix(file_posix&& other) : fd_(boost::exchange(other.fd_, -1)) { } -inline file_posix& file_posix:: operator=(file_posix&& other) { if(&other == this) return *this; - if(fd_ != -1) - detail::file_posix_close(fd_); + native_close(fd_); fd_ = other.fd_; other.fd_ = -1; return *this; } -inline void file_posix:: native_handle(native_handle_type fd) { - if(fd_ != -1) - detail::file_posix_close(fd_); + native_close(fd_); fd_ = fd; } -inline void file_posix:: close(error_code& ec) { - if(fd_ != -1) - { - auto const ev = - detail::file_posix_close(fd_); - if(ev) - ec.assign(ev, generic_category()); - else - ec.assign(0, ec.category()); - fd_ = -1; - } + auto const ev = native_close(fd_); + if(ev) + ec.assign(ev, system_category()); else - { - ec.assign(0, ec.category()); - } + ec = {}; } -inline void file_posix:: open(char const* path, file_mode mode, error_code& ec) { - if(fd_ != -1) - { - auto const ev = - detail::file_posix_close(fd_); - if(ev) - ec.assign(ev, generic_category()); - else - ec.assign(0, ec.category()); - fd_ = -1; - } + auto const ev = native_close(fd_); + if(ev) + ec.assign(ev, system_category()); + else + ec = {}; + int f = 0; #if BOOST_BEAST_USE_POSIX_FADVISE int advise = 0; @@ -172,21 +165,14 @@ open(char const* path, file_mode mode, error_code& ec) break; case file_mode::append: - f = O_RDWR | O_CREAT | O_TRUNC; - #if BOOST_BEAST_USE_POSIX_FADVISE - advise = POSIX_FADV_SEQUENTIAL; - #endif - break; - - case file_mode::append_new: - f = O_RDWR | O_CREAT | O_EXCL; + f = O_WRONLY | O_CREAT | O_TRUNC; #if BOOST_BEAST_USE_POSIX_FADVISE advise = POSIX_FADV_SEQUENTIAL; #endif break; case file_mode::append_existing: - f = O_RDWR | O_EXCL; + f = O_WRONLY; #if BOOST_BEAST_USE_POSIX_FADVISE advise = POSIX_FADV_SEQUENTIAL; #endif @@ -200,7 +186,7 @@ open(char const* path, file_mode mode, error_code& ec) auto const ev = errno; if(ev != EINTR) { - ec.assign(ev, generic_category()); + ec.assign(ev, system_category()); return; } } @@ -208,82 +194,77 @@ open(char const* path, file_mode mode, error_code& ec) if(::posix_fadvise(fd_, 0, 0, advise)) { auto const ev = errno; - detail::file_posix_close(fd_); - fd_ = -1; - ec.assign(ev, generic_category()); + native_close(fd_); + ec.assign(ev, system_category()); return; } #endif - ec.assign(0, ec.category()); + ec = {}; } -inline std::uint64_t file_posix:: size(error_code& ec) const { if(fd_ == -1) { - ec = make_error_code(errc::invalid_argument); + ec = make_error_code(errc::bad_file_descriptor); return 0; } struct stat st; if(::fstat(fd_, &st) != 0) { - ec.assign(errno, generic_category()); + ec.assign(errno, system_category()); return 0; } - ec.assign(0, ec.category()); + ec = {}; return st.st_size; } -inline std::uint64_t file_posix:: pos(error_code& ec) const { if(fd_ == -1) { - ec = make_error_code(errc::invalid_argument); + ec = make_error_code(errc::bad_file_descriptor); return 0; } auto const result = ::lseek(fd_, 0, SEEK_CUR); if(result == (off_t)-1) { - ec.assign(errno, generic_category()); + ec.assign(errno, system_category()); return 0; } - ec.assign(0, ec.category()); + ec = {}; return result; } -inline void file_posix:: seek(std::uint64_t offset, error_code& ec) { if(fd_ == -1) { - ec = make_error_code(errc::invalid_argument); + ec = make_error_code(errc::bad_file_descriptor); return; } auto const result = ::lseek(fd_, offset, SEEK_SET); if(result == static_cast<off_t>(-1)) { - ec.assign(errno, generic_category()); + ec.assign(errno, system_category()); return; } - ec.assign(0, ec.category()); + ec = {}; } -inline std::size_t file_posix:: read(void* buffer, std::size_t n, error_code& ec) const { if(fd_ == -1) { - ec = make_error_code(errc::invalid_argument); + ec = make_error_code(errc::bad_file_descriptor); return 0; } std::size_t nread = 0; @@ -297,7 +278,7 @@ read(void* buffer, std::size_t n, error_code& ec) const auto const ev = errno; if(ev == EINTR) continue; - ec.assign(ev, generic_category()); + ec.assign(ev, system_category()); return nread; } if(result == 0) @@ -312,14 +293,13 @@ read(void* buffer, std::size_t n, error_code& ec) const return nread; } -inline std::size_t file_posix:: write(void const* buffer, std::size_t n, error_code& ec) { if(fd_ == -1) { - ec = make_error_code(errc::invalid_argument); + ec = make_error_code(errc::bad_file_descriptor); return 0; } std::size_t nwritten = 0; @@ -333,7 +313,7 @@ write(void const* buffer, std::size_t n, error_code& ec) auto const ev = errno; if(ev == EINTR) continue; - ec.assign(ev, generic_category()); + ec.assign(ev, system_category()); return nwritten; } n -= result; @@ -347,3 +327,5 @@ write(void const* buffer, std::size_t n, error_code& ec) } // boost #endif + +#endif diff --git a/boost/beast/core/impl/file_stdio.ipp b/boost/beast/core/impl/file_stdio.ipp index 531a8b4748..30adae621b 100644 --- a/boost/beast/core/impl/file_stdio.ipp +++ b/boost/beast/core/impl/file_stdio.ipp @@ -1,5 +1,5 @@ // -// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2015-2019 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) @@ -10,13 +10,14 @@ #ifndef BOOST_BEAST_CORE_IMPL_FILE_STDIO_IPP #define BOOST_BEAST_CORE_IMPL_FILE_STDIO_IPP +#include <boost/beast/core/file_stdio.hpp> +#include <boost/config/workaround.hpp> #include <boost/core/exchange.hpp> #include <limits> namespace boost { namespace beast { -inline file_stdio:: ~file_stdio() { @@ -24,14 +25,12 @@ file_stdio:: fclose(f_); } -inline file_stdio:: file_stdio(file_stdio&& other) : f_(boost::exchange(other.f_, nullptr)) { } -inline file_stdio& file_stdio:: operator=(file_stdio&& other) @@ -45,7 +44,6 @@ operator=(file_stdio&& other) return *this; } -inline void file_stdio:: native_handle(FILE* f) @@ -55,7 +53,6 @@ native_handle(FILE* f) f_ = f; } -inline void file_stdio:: close(error_code& ec) @@ -70,10 +67,9 @@ close(error_code& ec) return; } } - ec.assign(0, ec.category()); + ec = {}; } -inline void file_stdio:: open(char const* path, file_mode mode, error_code& ec) @@ -87,17 +83,83 @@ open(char const* path, file_mode mode, error_code& ec) switch(mode) { default: - case file_mode::read: s = "rb"; break; - case file_mode::scan: s = "rb"; break; - case file_mode::write: s = "wb"; break; - case file_mode::write_new: s = "wbx"; break; - case file_mode::write_existing: s = "wb"; break; - case file_mode::append: s = "ab"; break; - case file_mode::append_new: s = "abx"; break; - case file_mode::append_existing: s = "ab"; break; - } -#if BOOST_MSVC - auto const ev = fopen_s(&f_, path, s); + case file_mode::read: + s = "rb"; + break; + + case file_mode::scan: + #ifdef BOOST_MSVC + s = "rbS"; + #else + s = "rb"; + #endif + break; + + case file_mode::write: + s = "wb+"; + break; + + case file_mode::write_new: + { +#if BOOST_WORKAROUND(BOOST_MSVC, < 1910) + FILE* f0; + auto const ev = ::fopen_s(&f0, path, "rb"); + if(! ev) + { + std::fclose(f0); + ec = make_error_code(errc::file_exists); + return; + } + else if(ev != + errc::no_such_file_or_directory) + { + ec.assign(ev, generic_category()); + return; + } + s = "wb"; +#else + + s = "wbx"; +#endif + break; + } + + case file_mode::write_existing: + s = "rb+"; + break; + + case file_mode::append: + s = "ab"; + break; + + case file_mode::append_existing: + { +#ifdef BOOST_MSVC + FILE* f0; + auto const ev = + ::fopen_s(&f0, path, "rb+"); + if(ev) + { + ec.assign(ev, generic_category()); + return; + } +#else + auto const f0 = + std::fopen(path, "rb+"); + if(! f0) + { + ec.assign(errno, generic_category()); + return; + } +#endif + std::fclose(f0); + s = "ab"; + break; + } + } + +#ifdef BOOST_MSVC + auto const ev = ::fopen_s(&f_, path, s); if(ev) { f_ = nullptr; @@ -112,17 +174,16 @@ open(char const* path, file_mode mode, error_code& ec) return; } #endif - ec.assign(0, ec.category()); + ec = {}; } -inline std::uint64_t file_stdio:: size(error_code& ec) const { if(! f_) { - ec = make_error_code(errc::invalid_argument); + ec = make_error_code(errc::bad_file_descriptor); return 0; } long pos = std::ftell(f_); @@ -148,18 +209,17 @@ size(error_code& ec) const if(result != 0) ec.assign(errno, generic_category()); else - ec.assign(0, ec.category()); + ec = {}; return size; } -inline std::uint64_t file_stdio:: pos(error_code& ec) const { if(! f_) { - ec = make_error_code(errc::invalid_argument); + ec = make_error_code(errc::bad_file_descriptor); return 0; } long pos = std::ftell(f_); @@ -168,18 +228,17 @@ pos(error_code& ec) const ec.assign(errno, generic_category()); return 0; } - ec.assign(0, ec.category()); + ec = {}; return pos; } -inline void file_stdio:: seek(std::uint64_t offset, error_code& ec) { if(! f_) { - ec = make_error_code(errc::invalid_argument); + ec = make_error_code(errc::bad_file_descriptor); return; } if(offset > (std::numeric_limits<long>::max)()) @@ -192,17 +251,16 @@ seek(std::uint64_t offset, error_code& ec) if(result != 0) ec.assign(errno, generic_category()); else - ec.assign(0, ec.category()); + ec = {}; } -inline std::size_t file_stdio:: read(void* buffer, std::size_t n, error_code& ec) const { if(! f_) { - ec = make_error_code(errc::invalid_argument); + ec = make_error_code(errc::bad_file_descriptor); return 0; } auto nread = std::fread(buffer, 1, n, f_); @@ -214,14 +272,13 @@ read(void* buffer, std::size_t n, error_code& ec) const return nread; } -inline std::size_t file_stdio:: write(void const* buffer, std::size_t n, error_code& ec) { if(! f_) { - ec = make_error_code(errc::invalid_argument); + ec = make_error_code(errc::bad_file_descriptor); return 0; } auto nwritten = std::fwrite(buffer, 1, n, f_); diff --git a/boost/beast/core/impl/file_win32.ipp b/boost/beast/core/impl/file_win32.ipp index 1183e3853f..8944a6bb45 100644 --- a/boost/beast/core/impl/file_win32.ipp +++ b/boost/beast/core/impl/file_win32.ipp @@ -1,5 +1,5 @@ // -// Copyright (c) 2015-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2015-2019 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) @@ -10,6 +10,10 @@ #ifndef BOOST_BEAST_CORE_IMPL_FILE_WIN32_IPP #define BOOST_BEAST_CORE_IMPL_FILE_WIN32_IPP +#include <boost/beast/core/file_win32.hpp> + +#if BOOST_BEAST_USE_WIN32_FILE + #include <boost/core/exchange.hpp> #include <boost/winapi/access_rights.hpp> #include <boost/winapi/error_codes.hpp> @@ -25,7 +29,7 @@ namespace detail { // VFALCO Can't seem to get boost/detail/winapi to work with // this so use the non-Ex version for now. -inline +BOOST_BEAST_DECL boost::winapi::BOOL_ set_file_pointer_ex( boost::winapi::HANDLE_ hFile, @@ -51,7 +55,6 @@ set_file_pointer_ex( } // detail -inline file_win32:: ~file_win32() { @@ -59,14 +62,13 @@ file_win32:: boost::winapi::CloseHandle(h_); } -inline file_win32:: file_win32(file_win32&& other) - : h_(boost::exchange(other.h_, boost::winapi::INVALID_HANDLE_VALUE_)) + : h_(boost::exchange(other.h_, + boost::winapi::INVALID_HANDLE_VALUE_)) { } -inline file_win32& file_win32:: operator=(file_win32&& other) @@ -80,7 +82,6 @@ operator=(file_win32&& other) return *this; } -inline void file_win32:: native_handle(native_handle_type h) @@ -90,7 +91,6 @@ native_handle(native_handle_type h) h_ = h; } -inline void file_win32:: close(error_code& ec) @@ -101,16 +101,15 @@ close(error_code& ec) ec.assign(boost::winapi::GetLastError(), system_category()); else - ec.assign(0, ec.category()); + ec = {}; h_ = boost::winapi::INVALID_HANDLE_VALUE_; } else { - ec.assign(0, ec.category()); + ec = {}; } } -inline void file_win32:: open(char const* path, file_mode mode, error_code& ec) @@ -180,13 +179,6 @@ open(char const* path, file_mode mode, error_code& ec) flags_and_attributes = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN break; - case file_mode::append_new: - desired_access = boost::winapi::GENERIC_READ_ | - boost::winapi::GENERIC_WRITE_; - creation_disposition = boost::winapi::CREATE_NEW_; - flags_and_attributes = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN - break; - case file_mode::append_existing: desired_access = boost::winapi::GENERIC_READ_ | boost::winapi::GENERIC_WRITE_; @@ -206,17 +198,16 @@ open(char const* path, file_mode mode, error_code& ec) ec.assign(boost::winapi::GetLastError(), system_category()); else - ec.assign(0, ec.category()); + ec = {}; } -inline std::uint64_t file_win32:: size(error_code& ec) const { if(h_ == boost::winapi::INVALID_HANDLE_VALUE_) { - ec = make_error_code(errc::invalid_argument); + ec = make_error_code(errc::bad_file_descriptor); return 0; } boost::winapi::LARGE_INTEGER_ fileSize; @@ -226,18 +217,17 @@ size(error_code& ec) const system_category()); return 0; } - ec.assign(0, ec.category()); + ec = {}; return fileSize.QuadPart; } -inline std::uint64_t file_win32:: pos(error_code& ec) { if(h_ == boost::winapi::INVALID_HANDLE_VALUE_) { - ec = make_error_code(errc::invalid_argument); + ec = make_error_code(errc::bad_file_descriptor); return 0; } boost::winapi::LARGE_INTEGER_ in; @@ -250,18 +240,17 @@ pos(error_code& ec) system_category()); return 0; } - ec.assign(0, ec.category()); + ec = {}; return out.QuadPart; } -inline void file_win32:: seek(std::uint64_t offset, error_code& ec) { if(h_ == boost::winapi::INVALID_HANDLE_VALUE_) { - ec = make_error_code(errc::invalid_argument); + ec = make_error_code(errc::bad_file_descriptor); return; } boost::winapi::LARGE_INTEGER_ in; @@ -273,17 +262,16 @@ seek(std::uint64_t offset, error_code& ec) system_category()); return; } - ec.assign(0, ec.category()); + ec = {}; } -inline std::size_t file_win32:: read(void* buffer, std::size_t n, error_code& ec) { if(h_ == boost::winapi::INVALID_HANDLE_VALUE_) { - ec = make_error_code(errc::invalid_argument); + ec = make_error_code(errc::bad_file_descriptor); return 0; } std::size_t nread = 0; @@ -304,7 +292,7 @@ read(void* buffer, std::size_t n, error_code& ec) if(dwError != boost::winapi::ERROR_HANDLE_EOF_) ec.assign(dwError, system_category()); else - ec.assign(0, ec.category()); + ec = {}; return nread; } if(bytesRead == 0) @@ -313,18 +301,17 @@ read(void* buffer, std::size_t n, error_code& ec) nread += bytesRead; buffer = static_cast<char*>(buffer) + bytesRead; } - ec.assign(0, ec.category()); + ec = {}; return nread; } -inline std::size_t file_win32:: write(void const* buffer, std::size_t n, error_code& ec) { if(h_ == boost::winapi::INVALID_HANDLE_VALUE_) { - ec = make_error_code(errc::invalid_argument); + ec = make_error_code(errc::bad_file_descriptor); return 0; } std::size_t nwritten = 0; @@ -345,7 +332,7 @@ write(void const* buffer, std::size_t n, error_code& ec) if(dwError != boost::winapi::ERROR_HANDLE_EOF_) ec.assign(dwError, system_category()); else - ec.assign(0, ec.category()); + ec = {}; return nwritten; } if(bytesWritten == 0) @@ -354,7 +341,7 @@ write(void const* buffer, std::size_t n, error_code& ec) nwritten += bytesWritten; buffer = static_cast<char const*>(buffer) + bytesWritten; } - ec.assign(0, ec.category()); + ec = {}; return nwritten; } @@ -362,3 +349,5 @@ write(void const* buffer, std::size_t n, error_code& ec) } // boost #endif + +#endif diff --git a/boost/beast/core/impl/flat_buffer.ipp b/boost/beast/core/impl/flat_buffer.hpp index afc843ac0d..f7fd51673d 100644 --- a/boost/beast/core/impl/flat_buffer.ipp +++ b/boost/beast/core/impl/flat_buffer.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -13,40 +13,46 @@ #include <boost/core/exchange.hpp> #include <boost/assert.hpp> #include <boost/throw_exception.hpp> +#include <memory> #include <stdexcept> namespace boost { namespace beast { -/* Memory is laid out thusly: +/* Layout: - begin_ ..|.. in_ ..|.. out_ ..|.. last_ ..|.. end_ + begin_ in_ out_ last_ end_ + |<------->|<---------->|<---------->|<------->| + | readable | writable | */ template<class Allocator> basic_flat_buffer<Allocator>:: ~basic_flat_buffer() { - if(begin_) - alloc_traits::deallocate( - this->get(), begin_, dist(begin_, end_)); + if(! begin_) + return; + alloc_traits::deallocate( + this->get(), begin_, capacity()); } template<class Allocator> basic_flat_buffer<Allocator>:: -basic_flat_buffer() +basic_flat_buffer() noexcept(default_nothrow) : begin_(nullptr) , in_(nullptr) , out_(nullptr) , last_(nullptr) , end_(nullptr) - , max_((std::numeric_limits<std::size_t>::max)()) + , max_(alloc_traits::max_size( + this->get())) { } template<class Allocator> basic_flat_buffer<Allocator>:: -basic_flat_buffer(std::size_t limit) +basic_flat_buffer( + std::size_t limit) noexcept(default_nothrow) : begin_(nullptr) , in_(nullptr) , out_(nullptr) @@ -58,21 +64,26 @@ basic_flat_buffer(std::size_t limit) template<class Allocator> basic_flat_buffer<Allocator>:: -basic_flat_buffer(Allocator const& alloc) - : boost::empty_value<base_alloc_type>(boost::empty_init_t(), alloc) +basic_flat_buffer(Allocator const& alloc) noexcept + : boost::empty_value<base_alloc_type>( + boost::empty_init_t{}, alloc) , begin_(nullptr) , in_(nullptr) , out_(nullptr) , last_(nullptr) , end_(nullptr) - , max_((std::numeric_limits<std::size_t>::max)()) + , max_(alloc_traits::max_size( + this->get())) { } template<class Allocator> basic_flat_buffer<Allocator>:: -basic_flat_buffer(std::size_t limit, Allocator const& alloc) - : boost::empty_value<base_alloc_type>(boost::empty_init_t(), alloc) +basic_flat_buffer( + std::size_t limit, + Allocator const& alloc) noexcept + : boost::empty_value<base_alloc_type>( + boost::empty_init_t{}, alloc) , begin_(nullptr) , in_(nullptr) , out_(nullptr) @@ -84,24 +95,25 @@ basic_flat_buffer(std::size_t limit, Allocator const& alloc) template<class Allocator> basic_flat_buffer<Allocator>:: -basic_flat_buffer(basic_flat_buffer&& other) - : boost::empty_value<base_alloc_type>(boost::empty_init_t(), - std::move(other.get())) +basic_flat_buffer(basic_flat_buffer&& other) noexcept + : boost::empty_value<base_alloc_type>( + boost::empty_init_t{}, std::move(other.get())) , begin_(boost::exchange(other.begin_, nullptr)) , in_(boost::exchange(other.in_, nullptr)) , out_(boost::exchange(other.out_, nullptr)) - , last_(out_) + , last_(boost::exchange(other.last_, nullptr)) , end_(boost::exchange(other.end_, nullptr)) , max_(other.max_) { - other.last_ = nullptr; } template<class Allocator> basic_flat_buffer<Allocator>:: -basic_flat_buffer(basic_flat_buffer&& other, - Allocator const& alloc) - : boost::empty_value<base_alloc_type>(boost::empty_init_t(), alloc) +basic_flat_buffer( + basic_flat_buffer&& other, + Allocator const& alloc) + : boost::empty_value<base_alloc_type>( + boost::empty_init_t{}, alloc) { if(this->get() != other.get()) { @@ -112,28 +124,31 @@ basic_flat_buffer(basic_flat_buffer&& other, end_ = nullptr; max_ = other.max_; copy_from(other); - other.reset(); - } - else - { - begin_ = other.begin_; - in_ = other.in_; - out_ = other.out_; - last_ = out_; - end_ = other.end_; - max_ = other.max_; - other.begin_ = nullptr; - other.in_ = nullptr; - other.out_ = nullptr; - other.last_ = nullptr; - other.end_ = nullptr; + other.clear(); + other.shrink_to_fit(); + return; } + + begin_ = other.begin_; + in_ = other.in_; + out_ = other.out_; + last_ = other.out_; // invalidate + end_ = other.end_; + max_ = other.max_; + BOOST_ASSERT( + alloc_traits::max_size(this->get()) == + alloc_traits::max_size(other.get())); + other.begin_ = nullptr; + other.in_ = nullptr; + other.out_ = nullptr; + other.last_ = nullptr; + other.end_ = nullptr; } template<class Allocator> basic_flat_buffer<Allocator>:: basic_flat_buffer(basic_flat_buffer const& other) - : boost::empty_value<base_alloc_type>(boost::empty_init_t(), + : boost::empty_value<base_alloc_type>(boost::empty_init_t{}, alloc_traits::select_on_container_copy_construction( other.get())) , begin_(nullptr) @@ -148,9 +163,11 @@ basic_flat_buffer(basic_flat_buffer const& other) template<class Allocator> basic_flat_buffer<Allocator>:: -basic_flat_buffer(basic_flat_buffer const& other, - Allocator const& alloc) - : boost::empty_value<base_alloc_type>(boost::empty_init_t(), alloc) +basic_flat_buffer( + basic_flat_buffer const& other, + Allocator const& alloc) + : boost::empty_value<base_alloc_type>( + boost::empty_init_t{}, alloc) , begin_(nullptr) , in_(nullptr) , out_(nullptr) @@ -165,7 +182,8 @@ template<class Allocator> template<class OtherAlloc> basic_flat_buffer<Allocator>:: basic_flat_buffer( - basic_flat_buffer<OtherAlloc> const& other) + basic_flat_buffer<OtherAlloc> const& other) + noexcept(default_nothrow) : begin_(nullptr) , in_(nullptr) , out_(nullptr) @@ -179,9 +197,11 @@ basic_flat_buffer( template<class Allocator> template<class OtherAlloc> basic_flat_buffer<Allocator>:: -basic_flat_buffer(basic_flat_buffer<OtherAlloc> const& other, - Allocator const& alloc) - : boost::empty_value<base_alloc_type>(boost::empty_init_t(), alloc) +basic_flat_buffer( + basic_flat_buffer<OtherAlloc> const& other, + Allocator const& alloc) + : boost::empty_value<base_alloc_type>( + boost::empty_init_t{}, alloc) , begin_(nullptr) , in_(nullptr) , out_(nullptr) @@ -195,12 +215,12 @@ basic_flat_buffer(basic_flat_buffer<OtherAlloc> const& other, template<class Allocator> auto basic_flat_buffer<Allocator>:: -operator=(basic_flat_buffer&& other) -> +operator=(basic_flat_buffer&& other) noexcept -> basic_flat_buffer& { - if(this != &other) - move_assign(other, std::integral_constant<bool, - alloc_traits::propagate_on_container_move_assignment::value>{}); + if(this == &other) + return *this; + move_assign(other, pocma{}); return *this; } @@ -210,9 +230,9 @@ basic_flat_buffer<Allocator>:: operator=(basic_flat_buffer const& other) -> basic_flat_buffer& { - if(this != &other) - copy_assign(other, std::integral_constant<bool, - alloc_traits::propagate_on_container_copy_assignment::value>{}); + if(this == &other) + return *this; + copy_assign(other, pocca{}); return *this; } @@ -220,15 +240,64 @@ template<class Allocator> template<class OtherAlloc> auto basic_flat_buffer<Allocator>:: -operator=(basic_flat_buffer<OtherAlloc> const& other) -> +operator=( + basic_flat_buffer<OtherAlloc> const& other) -> basic_flat_buffer& { - reset(); - max_ = other.max_; copy_from(other); return *this; } +template<class Allocator> +void +basic_flat_buffer<Allocator>:: +reserve(std::size_t n) +{ + if(max_ < n) + max_ = n; + if(n > capacity()) + prepare(n - size()); +} + +template<class Allocator> +void +basic_flat_buffer<Allocator>:: +shrink_to_fit() +{ + auto const len = size(); + if(len == capacity()) + return; + char* p; + if(len > 0) + { + BOOST_ASSERT(begin_); + BOOST_ASSERT(in_); + p = alloc(len); + std::memcpy(p, in_, len); + } + else + { + p = nullptr; + } + alloc_traits::deallocate( + this->get(), begin_, this->capacity()); + begin_ = p; + in_ = begin_; + out_ = begin_ + len; + last_ = out_; + end_ = out_; +} + +template<class Allocator> +void +basic_flat_buffer<Allocator>:: +clear() noexcept +{ + in_ = begin_; + out_ = begin_; + last_ = begin_; +} + //------------------------------------------------------------------------------ template<class Allocator> @@ -237,34 +306,36 @@ basic_flat_buffer<Allocator>:: prepare(std::size_t n) -> mutable_buffers_type { + auto const len = size(); + if(len > max_ || n > (max_ - len)) + BOOST_THROW_EXCEPTION(std::length_error{ + "basic_flat_buffer too long"}); if(n <= dist(out_, end_)) { // existing capacity is sufficient last_ = out_ + n; return{out_, n}; } - auto const len = size(); if(n <= capacity() - len) { // after a memmove, // existing capacity is sufficient if(len > 0) + { + BOOST_ASSERT(begin_); + BOOST_ASSERT(in_); std::memmove(begin_, in_, len); + } in_ = begin_; out_ = in_ + len; last_ = out_ + n; return {out_, n}; } - // enforce maximum capacity - if(n > max_ - len) - BOOST_THROW_EXCEPTION(std::length_error{ - "basic_flat_buffer overflow"}); // allocate a new buffer auto const new_size = (std::min<std::size_t>)( max_, (std::max<std::size_t>)(2 * len, len + n)); - auto const p = alloc_traits::allocate( - this->get(), new_size); + auto const p = alloc(new_size); if(begin_) { BOOST_ASSERT(p); @@ -284,7 +355,7 @@ prepare(std::size_t n) -> template<class Allocator> void basic_flat_buffer<Allocator>:: -consume(std::size_t n) +consume(std::size_t n) noexcept { if(n >= dist(in_, out_)) { @@ -295,69 +366,54 @@ consume(std::size_t n) in_ += n; } +//------------------------------------------------------------------------------ + template<class Allocator> +template<class OtherAlloc> void basic_flat_buffer<Allocator>:: -shrink_to_fit() +copy_from( + basic_flat_buffer<OtherAlloc> const& other) { - auto const len = size(); - if(len == capacity()) - return; - char* p; - if(len > 0) + std::size_t const n = other.size(); + if(n == 0 || n > capacity()) { - BOOST_ASSERT(begin_); - BOOST_ASSERT(in_); - p = alloc_traits::allocate( - this->get(), len); - std::memcpy(p, in_, len); + if(begin_ != nullptr) + { + alloc_traits::deallocate( + this->get(), begin_, + this->capacity()); + begin_ = nullptr; + in_ = nullptr; + out_ = nullptr; + last_ = nullptr; + end_ = nullptr; + } + if(n == 0) + return; + begin_ = alloc(n); + in_ = begin_; + out_ = begin_ + n; + last_ = begin_ + n; + end_ = begin_ + n; } - else + in_ = begin_; + out_ = begin_ + n; + last_ = begin_ + n; + if(begin_) { - p = nullptr; + BOOST_ASSERT(other.begin_); + std::memcpy(begin_, other.begin_, n); } - alloc_traits::deallocate( - this->get(), begin_, dist(begin_, end_)); - begin_ = p; - in_ = begin_; - out_ = begin_ + len; - last_ = out_; - end_ = out_; -} - -//------------------------------------------------------------------------------ - -template<class Allocator> -inline -void -basic_flat_buffer<Allocator>:: -reset() -{ - consume(size()); - shrink_to_fit(); } template<class Allocator> -template<class DynamicBuffer> -inline -void -basic_flat_buffer<Allocator>:: -copy_from(DynamicBuffer const& buffer) -{ - if(buffer.size() == 0) - return; - using boost::asio::buffer_copy; - commit(buffer_copy( - prepare(buffer.size()), buffer.data())); -} - -template<class Allocator> -inline void basic_flat_buffer<Allocator>:: move_assign(basic_flat_buffer& other, std::true_type) { - reset(); + clear(); + shrink_to_fit(); this->get() = std::move(other.get()); begin_ = other.begin_; in_ = other.in_; @@ -373,16 +429,15 @@ move_assign(basic_flat_buffer& other, std::true_type) } template<class Allocator> -inline void basic_flat_buffer<Allocator>:: move_assign(basic_flat_buffer& other, std::false_type) { - reset(); if(this->get() != other.get()) { copy_from(other); - other.reset(); + other.clear(); + other.shrink_to_fit(); } else { @@ -391,30 +446,27 @@ move_assign(basic_flat_buffer& other, std::false_type) } template<class Allocator> -inline void basic_flat_buffer<Allocator>:: copy_assign(basic_flat_buffer const& other, std::true_type) { - reset(); max_ = other.max_; this->get() = other.get(); copy_from(other); } template<class Allocator> -inline void basic_flat_buffer<Allocator>:: copy_assign(basic_flat_buffer const& other, std::false_type) { - reset(); + clear(); + shrink_to_fit(); max_ = other.max_; copy_from(other); } template<class Allocator> -inline void basic_flat_buffer<Allocator>:: swap(basic_flat_buffer& other) @@ -424,7 +476,6 @@ swap(basic_flat_buffer& other) } template<class Allocator> -inline void basic_flat_buffer<Allocator>:: swap(basic_flat_buffer& other, std::true_type) @@ -441,7 +492,6 @@ swap(basic_flat_buffer& other, std::true_type) } template<class Allocator> -inline void basic_flat_buffer<Allocator>:: swap(basic_flat_buffer& other, std::false_type) @@ -466,6 +516,17 @@ swap( lhs.swap(rhs); } +template<class Allocator> +char* +basic_flat_buffer<Allocator>:: +alloc(std::size_t n) +{ + if(n > alloc_traits::max_size(this->get())) + BOOST_THROW_EXCEPTION(std::length_error( + "A basic_flat_buffer exceeded the allocator's maximum size")); + return alloc_traits::allocate(this->get(), n); +} + } // beast } // boost diff --git a/boost/beast/core/impl/flat_static_buffer.hpp b/boost/beast/core/impl/flat_static_buffer.hpp new file mode 100644 index 0000000000..c812dd0cfc --- /dev/null +++ b/boost/beast/core/impl/flat_static_buffer.hpp @@ -0,0 +1,43 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_HPP +#define BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_HPP + +namespace boost { +namespace beast { + +template<std::size_t N> +flat_static_buffer<N>:: +flat_static_buffer( + flat_static_buffer const& other) + : flat_static_buffer_base(buf_, N) +{ + this->commit(net::buffer_copy( + this->prepare(other.size()), other.data())); +} + +template<std::size_t N> +auto +flat_static_buffer<N>:: +operator=(flat_static_buffer const& other) -> + flat_static_buffer<N>& +{ + if(this == &other) + return *this; + this->consume(this->size()); + this->commit(net::buffer_copy( + this->prepare(other.size()), other.data())); + return *this; +} + +} // beast +} // boost + +#endif
\ No newline at end of file diff --git a/boost/beast/core/impl/flat_static_buffer.ipp b/boost/beast/core/impl/flat_static_buffer.ipp index 54bf292116..31f158ebe7 100644 --- a/boost/beast/core/impl/flat_static_buffer.ipp +++ b/boost/beast/core/impl/flat_static_buffer.ipp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -10,82 +10,36 @@ #ifndef BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_IPP #define BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_IPP -#include <boost/beast/core/detail/type_traits.hpp> -#include <boost/asio/buffer.hpp> +#include <boost/beast/core/flat_static_buffer.hpp> #include <boost/throw_exception.hpp> #include <algorithm> #include <cstring> #include <iterator> +#include <memory> #include <stdexcept> namespace boost { namespace beast { -/* Memory is laid out thusly: +/* Layout: - begin_ ..|.. in_ ..|.. out_ ..|.. last_ ..|.. end_ + begin_ in_ out_ last_ end_ + |<------->|<---------->|<---------->|<------->| + | readable | writable | */ -inline -auto -flat_static_buffer_base:: -data() const -> - const_buffers_type -{ - return {in_, dist(in_, out_)}; -} - -inline void flat_static_buffer_base:: -reset() -{ - reset_impl(); -} - -inline -auto -flat_static_buffer_base:: -prepare(std::size_t n) -> - mutable_buffers_type -{ - return prepare_impl(n); -} - -inline -void -flat_static_buffer_base:: -reset(void* p, std::size_t n) -{ - reset_impl(p, n); -} - -template<class> -void -flat_static_buffer_base:: -reset_impl() +clear() noexcept { in_ = begin_; out_ = begin_; last_ = begin_; } -template<class> -void -flat_static_buffer_base:: -reset_impl(void* p, std::size_t n) -{ - begin_ = static_cast<char*>(p); - in_ = begin_; - out_ = begin_; - last_ = begin_; - end_ = begin_ + n; -} - -template<class> auto flat_static_buffer_base:: -prepare_impl(std::size_t n) -> +prepare(std::size_t n) -> mutable_buffers_type { if(n <= dist(out_, end_)) @@ -105,10 +59,9 @@ prepare_impl(std::size_t n) -> return {out_, n}; } -template<class> void flat_static_buffer_base:: -consume_impl(std::size_t n) +consume(std::size_t n) noexcept { if(n >= size()) { @@ -119,32 +72,18 @@ consume_impl(std::size_t n) in_ += n; } -//------------------------------------------------------------------------------ - -template<std::size_t N> -flat_static_buffer<N>:: -flat_static_buffer(flat_static_buffer const& other) - : flat_static_buffer_base(buf_, N) -{ - using boost::asio::buffer_copy; - this->commit(buffer_copy( - this->prepare(other.size()), other.data())); -} - -template<std::size_t N> -auto -flat_static_buffer<N>:: -operator=(flat_static_buffer const& other) -> - flat_static_buffer<N>& +void +flat_static_buffer_base:: +reset(void* p, std::size_t n) noexcept { - using boost::asio::buffer_copy; - this->consume(this->size()); - this->commit(buffer_copy( - this->prepare(other.size()), other.data())); - return *this; + begin_ = static_cast<char*>(p); + in_ = begin_; + out_ = begin_; + last_ = begin_; + end_ = begin_ + n; } } // beast } // boost -#endif +#endif
\ No newline at end of file diff --git a/boost/beast/core/impl/flat_stream.hpp b/boost/beast/core/impl/flat_stream.hpp new file mode 100644 index 0000000000..2972131198 --- /dev/null +++ b/boost/beast/core/impl/flat_stream.hpp @@ -0,0 +1,276 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_IMPL_FLAT_STREAM_HPP +#define BOOST_BEAST_CORE_IMPL_FLAT_STREAM_HPP + +#include <boost/beast/core/async_base.hpp> +#include <boost/beast/core/buffers_prefix.hpp> +#include <boost/beast/core/static_buffer.hpp> +#include <boost/beast/core/stream_traits.hpp> +#include <boost/beast/websocket/teardown.hpp> +#include <boost/asio/buffer.hpp> +#include <boost/asio/coroutine.hpp> +#include <memory> + +namespace boost { +namespace beast { + +template<class NextLayer> +struct flat_stream<NextLayer>::ops +{ + +template<class Handler> +class write_op + : public async_base<Handler, + beast::executor_type<flat_stream>> + , public net::coroutine +{ +public: + template< + class ConstBufferSequence, + class Handler_> + write_op( + Handler_&& h, + flat_stream<NextLayer>& s, + ConstBufferSequence const& b) + : async_base<Handler, + beast::executor_type<flat_stream>>( + std::forward<Handler_>(h), + s.get_executor()) + { + auto const result = + flatten(b, max_size); + if(result.flatten) + { + s.buffer_.clear(); + s.buffer_.commit(net::buffer_copy( + s.buffer_.prepare(result.size), + b, result.size)); + s.stream_.async_write_some( + s.buffer_.data(), std::move(*this)); + } + else + { + s.buffer_.clear(); + s.buffer_.shrink_to_fit(); + s.stream_.async_write_some( + beast::buffers_prefix( + result.size, b), std::move(*this)); + } + } + + void + operator()( + boost::system::error_code ec, + std::size_t bytes_transferred) + { + this->complete_now(ec, bytes_transferred); + } +}; + +struct run_write_op +{ + template<class WriteHandler, class Buffers> + void + operator()( + WriteHandler&& h, + flat_stream* s, + Buffers const& b) + { + // If you get an error on the following line it means + // that your handler does not meet the documented type + // requirements for the handler. + + static_assert( + beast::detail::is_invocable<WriteHandler, + void(error_code, std::size_t)>::value, + "WriteHandler type requirements not met"); + + write_op< + typename std::decay<WriteHandler>::type>( + std::forward<WriteHandler>(h), *s, b); + } +}; + +}; + +//------------------------------------------------------------------------------ + +template<class NextLayer> +template<class... Args> +flat_stream<NextLayer>:: +flat_stream(Args&&... args) + : stream_(std::forward<Args>(args)...) +{ +} + +template<class NextLayer> +template<class MutableBufferSequence> +std::size_t +flat_stream<NextLayer>:: +read_some(MutableBufferSequence const& buffers) +{ + static_assert(boost::beast::is_sync_read_stream<next_layer_type>::value, + "SyncReadStream type requirements not met"); + static_assert(net::is_mutable_buffer_sequence< + MutableBufferSequence>::value, + "MutableBufferSequence type requirements not met"); + error_code ec; + auto n = read_some(buffers, ec); + if(ec) + BOOST_THROW_EXCEPTION(boost::system::system_error{ec}); + return n; +} + +template<class NextLayer> +template<class MutableBufferSequence> +std::size_t +flat_stream<NextLayer>:: +read_some(MutableBufferSequence const& buffers, error_code& ec) +{ + static_assert(boost::beast::is_sync_read_stream<next_layer_type>::value, + "SyncReadStream type requirements not met"); + static_assert(net::is_mutable_buffer_sequence< + MutableBufferSequence>::value, + "MutableBufferSequence type requirements not met"); + return stream_.read_some(buffers, ec); +} + +template<class NextLayer> +template< + class MutableBufferSequence, + class ReadHandler> +BOOST_BEAST_ASYNC_RESULT2(ReadHandler) +flat_stream<NextLayer>:: +async_read_some( + MutableBufferSequence const& buffers, + ReadHandler&& handler) +{ + static_assert(boost::beast::is_async_read_stream<next_layer_type>::value, + "AsyncReadStream type requirements not met"); + static_assert(net::is_mutable_buffer_sequence< + MutableBufferSequence >::value, + "MutableBufferSequence type requirements not met"); + return stream_.async_read_some( + buffers, std::forward<ReadHandler>(handler)); +} + +template<class NextLayer> +template<class ConstBufferSequence> +std::size_t +flat_stream<NextLayer>:: +write_some(ConstBufferSequence const& buffers) +{ + static_assert(boost::beast::is_sync_write_stream<next_layer_type>::value, + "SyncWriteStream type requirements not met"); + static_assert(net::is_const_buffer_sequence< + ConstBufferSequence>::value, + "ConstBufferSequence type requirements not met"); + error_code ec; + auto n = write_some(buffers, ec); + if(ec) + BOOST_THROW_EXCEPTION(boost::system::system_error{ec}); + return n; +} + +template<class NextLayer> +template<class ConstBufferSequence> +std::size_t +flat_stream<NextLayer>:: +stack_write_some( + std::size_t size, + ConstBufferSequence const& buffers, + error_code& ec) +{ + static_buffer<max_stack> b; + b.commit(net::buffer_copy( + b.prepare(size), buffers)); + return stream_.write_some(b.data(), ec); +} + +template<class NextLayer> +template<class ConstBufferSequence> +std::size_t +flat_stream<NextLayer>:: +write_some(ConstBufferSequence const& buffers, error_code& ec) +{ + static_assert(boost::beast::is_sync_write_stream<next_layer_type>::value, + "SyncWriteStream type requirements not met"); + static_assert(net::is_const_buffer_sequence< + ConstBufferSequence>::value, + "ConstBufferSequence type requirements not met"); + auto const result = flatten(buffers, max_size); + if(result.flatten) + { + if(result.size <= max_stack) + return stack_write_some(result.size, buffers, ec); + + buffer_.clear(); + buffer_.commit(net::buffer_copy( + buffer_.prepare(result.size), + buffers)); + return stream_.write_some(buffer_.data(), ec); + } + buffer_.clear(); + buffer_.shrink_to_fit(); + return stream_.write_some( + boost::beast::buffers_prefix(result.size, buffers), ec); +} + +template<class NextLayer> +template< + class ConstBufferSequence, + class WriteHandler> +BOOST_BEAST_ASYNC_RESULT2(WriteHandler) +flat_stream<NextLayer>:: +async_write_some( + ConstBufferSequence const& buffers, + WriteHandler&& handler) +{ + static_assert(boost::beast::is_async_write_stream<next_layer_type>::value, + "AsyncWriteStream type requirements not met"); + static_assert(net::is_const_buffer_sequence< + ConstBufferSequence>::value, + "ConstBufferSequence type requirements not met"); + return net::async_initiate< + WriteHandler, + void(error_code, std::size_t)>( + typename ops::run_write_op{}, + handler, + this, + buffers); +} + +template<class NextLayer> +void +teardown( + boost::beast::role_type role, + flat_stream<NextLayer>& s, + error_code& ec) +{ + using boost::beast::websocket::teardown; + teardown(role, s.next_layer(), ec); +} + +template<class NextLayer, class TeardownHandler> +void +async_teardown( + boost::beast::role_type role, + flat_stream<NextLayer>& s, + TeardownHandler&& handler) +{ + using boost::beast::websocket::async_teardown; + async_teardown(role, s.next_layer(), std::move(handler)); +} + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/impl/handler_ptr.ipp b/boost/beast/core/impl/handler_ptr.hpp index 06bdd81c40..48260abc2b 100644 --- a/boost/beast/core/impl/handler_ptr.ipp +++ b/boost/beast/core/impl/handler_ptr.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -22,15 +22,15 @@ void handler_ptr<T, Handler>:: clear() { - typename beast::detail::allocator_traits< - boost::asio::associated_allocator_t< - Handler>>::template rebind_alloc<T> alloc( - boost::asio::get_associated_allocator( - handler())); - beast::detail::allocator_traits< - decltype(alloc)>::destroy(alloc, t_); - beast::detail::allocator_traits< - decltype(alloc)>::deallocate(alloc, t_, 1); + using A = typename detail::allocator_traits< + net::associated_allocator_t< + Handler>>::template rebind_alloc<T>; + using alloc_traits = + beast::detail::allocator_traits<A>; + A alloc( + net::get_associated_allocator(handler())); + alloc_traits::destroy(alloc, t_); + alloc_traits::deallocate(alloc, t_, 1); t_ = nullptr; } @@ -41,7 +41,7 @@ handler_ptr<T, Handler>:: if(t_) { clear(); - handler().~Handler(); + h_.~Handler(); } } @@ -52,8 +52,9 @@ handler_ptr(handler_ptr&& other) { if(other.t_) { - new(&h_) Handler(std::move(other.handler())); - other.handler().~Handler(); + ::new(static_cast<void*>(std::addressof(h_))) + Handler(std::move(other.h_)); + other.h_.~Handler(); other.t_ = nullptr; } } @@ -64,25 +65,27 @@ handler_ptr<T, Handler>:: handler_ptr(DeducedHandler&& h, Args&&... args) { BOOST_STATIC_ASSERT(! std::is_array<T>::value); - typename beast::detail::allocator_traits< - boost::asio::associated_allocator_t< - Handler>>::template rebind_alloc<T> alloc{ - boost::asio::get_associated_allocator(h)}; - using A = decltype(alloc); + using A = typename detail::allocator_traits< + net::associated_allocator_t< + Handler>>::template rebind_alloc<T>; + using alloc_traits = + beast::detail::allocator_traits<A>; + A alloc{net::get_associated_allocator(h)}; bool destroy = false; auto deleter = [&alloc, &destroy](T* p) { if(destroy) - beast::detail::allocator_traits<A>::destroy(alloc, p); - beast::detail::allocator_traits<A>::deallocate(alloc, p, 1); + alloc_traits::destroy(alloc, p); + alloc_traits::deallocate(alloc, p, 1); }; std::unique_ptr<T, decltype(deleter)> t{ - beast::detail::allocator_traits<A>::allocate(alloc, 1), deleter}; - beast::detail::allocator_traits<A>::construct(alloc, t.get(), + alloc_traits::allocate(alloc, 1), deleter}; + alloc_traits::construct(alloc, t.get(), static_cast<DeducedHandler const&>(h), std::forward<Args>(args)...); destroy = true; - new(&h_) Handler(std::forward<DeducedHandler>(h)); + ::new(static_cast<void*>(std::addressof(h_))) + Handler(std::forward<DeducedHandler>(h)); t_ = t.release(); } @@ -100,8 +103,8 @@ release_handler() -> }; std::unique_ptr< Handler, decltype(deleter)> destroyer{ - &handler(), deleter}; - return std::move(handler()); + std::addressof(h_), deleter}; + return std::move(h_); } template<class T, class Handler> @@ -119,8 +122,8 @@ invoke(Args&&... args) }; std::unique_ptr< Handler, decltype(deleter)> destroyer{ - &handler(), deleter}; - handler()(std::forward<Args>(args)...); + std::addressof(h_), deleter}; + h_(std::forward<Args>(args)...); } } // beast diff --git a/boost/beast/core/impl/multi_buffer.ipp b/boost/beast/core/impl/multi_buffer.hpp index fb48424a3a..643f60ce2b 100644 --- a/boost/beast/core/impl/multi_buffer.ipp +++ b/boost/beast/core/impl/multi_buffer.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -7,10 +7,12 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_IMPL_MULTI_BUFFER_IPP -#define BOOST_BEAST_IMPL_MULTI_BUFFER_IPP +#ifndef BOOST_BEAST_IMPL_MULTI_BUFFER_HPP +#define BOOST_BEAST_IMPL_MULTI_BUFFER_HPP +#include <boost/beast/core/buffer_traits.hpp> #include <boost/beast/core/detail/type_traits.hpp> +#include <boost/config/workaround.hpp> #include <boost/core/exchange.hpp> #include <boost/assert.hpp> #include <boost/throw_exception.hpp> @@ -18,6 +20,7 @@ #include <exception> #include <sstream> #include <string> +#include <type_traits> #include <utility> namespace boost { @@ -27,40 +30,39 @@ namespace beast { 1 Input and output contained entirely in one element: - 0 out_ - |<-------------+------------------------------------------->| - in_pos_ out_pos_ out_end_ + 0 out_ + |<------+-----------+--------------------------------+----->| + in_pos_ out_pos_ out_end_ 2 Output contained in first and second elements: - out_ - |<------+----------+------->| |<----------+-------------->| - in_pos_ out_pos_ out_end_ + out_ + |<------+-----------+------>| |<-------------------+----->| + in_pos_ out_pos_ out_end_ 3 Output contained in the second element: - out_ - |<------------+------------>| |<----+-------------------->| - in_pos_ out_pos_ out_end_ + out_ + |<------+------------------>| |<----+--------------+----->| + in_pos_ out_pos_ out_end_ 4 Output contained in second and third elements: - out_ - |<-----+-------->| |<-------+------>| |<--------------->| - in_pos_ out_pos_ out_end_ + out_ + |<------+------->| |<-------+------>| |<---------+----->| + in_pos_ out_pos_ out_end_ 5 Input sequence is empty: - out_ - |<------+------------------>| |<-----------+------------->| - out_pos_ out_end_ + out_ + |<------+------------------>| |<-------------------+----->| + out_pos_ out_end_ in_pos_ - 6 Output sequence is empty: out_ @@ -86,6 +88,7 @@ namespace beast { in_pos_ out_pos_ == 0 out_end_ == 0 */ +//------------------------------------------------------------------------------ template<class Allocator> class basic_multi_buffer<Allocator>::element @@ -93,105 +96,131 @@ class basic_multi_buffer<Allocator>::element boost::intrusive::link_mode< boost::intrusive::normal_link>> { - using size_type = - typename detail::allocator_traits<Allocator>::size_type; + using size_type = typename + detail::allocator_traits<Allocator>::size_type; size_type const size_; public: element(element const&) = delete; - element& operator=(element const&) = delete; explicit - element(size_type n) + element(size_type n) noexcept : size_(n) { } size_type - size() const + size() const noexcept { return size_; } char* - data() const + data() const noexcept { return const_cast<char*>( reinterpret_cast<char const*>(this + 1)); } }; +//------------------------------------------------------------------------------ + +#if BOOST_WORKAROUND(BOOST_MSVC, < 1910) +# pragma warning (push) +# pragma warning (disable: 4521) // multiple copy constructors specified +# pragma warning (disable: 4522) // multiple assignment operators specified +#endif + template<class Allocator> -class basic_multi_buffer<Allocator>::const_buffers_type +template<bool isMutable> +class basic_multi_buffer<Allocator>::readable_bytes { basic_multi_buffer const* b_; friend class basic_multi_buffer; explicit - const_buffers_type(basic_multi_buffer const& b); + readable_bytes( + basic_multi_buffer const& b) noexcept + : b_(&b) + { + } public: - using value_type = boost::asio::const_buffer; + using value_type = typename + std::conditional< + isMutable, + net::mutable_buffer, + net::const_buffer>::type; class const_iterator; - const_buffers_type() = delete; - 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; - - friend - std::size_t - buffer_size(const_buffers_type const& buffers) + readable_bytes() = delete; +#if BOOST_WORKAROUND(BOOST_MSVC, < 1910) + readable_bytes(readable_bytes const& other) + : b_(other.b_) { - return buffers.b_->size(); } -}; - -template<class Allocator> -class basic_multi_buffer<Allocator>::mutable_buffers_type -{ - basic_multi_buffer const* b_; - friend class basic_multi_buffer; - - explicit - mutable_buffers_type(basic_multi_buffer const& b); + readable_bytes& operator=(readable_bytes const& other) + { + b_ = other.b_; + return *this; + } +#else + readable_bytes(readable_bytes const&) = default; + readable_bytes& operator=(readable_bytes const&) = default; +#endif -public: - using value_type = mutable_buffer; + template< + bool isMutable_ = isMutable, + class = typename std::enable_if<! isMutable_>::type> + readable_bytes( + readable_bytes<true> const& other) noexcept + : b_(other.b_) + { + } - class const_iterator; + template< + bool isMutable_ = isMutable, + class = typename std::enable_if<! isMutable_>::type> + readable_bytes& operator=( + readable_bytes<true> const& other) noexcept + { + b_ = other.b_; + return *this; + } - mutable_buffers_type() = delete; - mutable_buffers_type(mutable_buffers_type const&) = default; - mutable_buffers_type& operator=(mutable_buffers_type const&) = default; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; - const_iterator - begin() const; - - const_iterator - end() const; + std::size_t + buffer_bytes() const noexcept + { + return b_->size(); + } }; +#if BOOST_WORKAROUND(BOOST_MSVC, < 1910) +# pragma warning (pop) +#endif + //------------------------------------------------------------------------------ template<class Allocator> -class basic_multi_buffer<Allocator>::const_buffers_type::const_iterator +template<bool isMutable> +class + basic_multi_buffer<Allocator>:: + readable_bytes<isMutable>:: + const_iterator { basic_multi_buffer const* b_ = nullptr; typename list_type::const_iterator it_; public: using value_type = - typename const_buffers_type::value_type; + typename readable_bytes::value_type; using pointer = value_type const*; using reference = value_type; using difference_type = std::ptrdiff_t; @@ -199,32 +228,33 @@ public: 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_multi_buffer const& b, - typename list_type::const_iterator const& it) + const_iterator( + const_iterator const& other) = default; + const_iterator& operator=( + const_iterator const& other) = default; + + const_iterator( + basic_multi_buffer const& b, typename + list_type::const_iterator const& it) noexcept : b_(&b) , it_(it) { } bool - operator==(const_iterator const& other) const + operator==(const_iterator const& other) const noexcept { return b_ == other.b_ && it_ == other.it_; } bool - operator!=(const_iterator const& other) const + operator!=(const_iterator const& other) const noexcept { return !(*this == other); } reference - operator*() const + operator*() const noexcept { auto const& e = *it_; return value_type{e.data(), @@ -237,14 +267,14 @@ public: operator->() const = delete; const_iterator& - operator++() + operator++() noexcept { ++it_; return *this; } const_iterator - operator++(int) + operator++(int) noexcept { auto temp = *this; ++(*this); @@ -252,14 +282,14 @@ public: } const_iterator& - operator--() + operator--() noexcept { --it_; return *this; } const_iterator - operator--(int) + operator--(int) noexcept { auto temp = *this; --(*this); @@ -267,36 +297,34 @@ public: } }; -template<class Allocator> -basic_multi_buffer<Allocator>:: -const_buffers_type:: -const_buffers_type( - basic_multi_buffer const& b) - : b_(&b) -{ -} +//------------------------------------------------------------------------------ template<class Allocator> -auto -basic_multi_buffer<Allocator>:: -const_buffers_type:: -begin() const -> - const_iterator +class basic_multi_buffer<Allocator>::mutable_buffers_type { - return const_iterator{*b_, b_->list_.begin()}; -} + basic_multi_buffer const* b_; -template<class Allocator> -auto -basic_multi_buffer<Allocator>:: -const_buffers_type:: -end() const -> - const_iterator -{ - return const_iterator{*b_, b_->out_ == - b_->list_.end() ? b_->list_.end() : - std::next(b_->out_)}; -} + friend class basic_multi_buffer; + + explicit + mutable_buffers_type( + basic_multi_buffer const& b) noexcept + : b_(&b) + { + } + +public: + using value_type = net::mutable_buffer; + + class const_iterator; + + mutable_buffers_type() = delete; + mutable_buffers_type(mutable_buffers_type const&) = default; + mutable_buffers_type& operator=(mutable_buffers_type const&) = default; + + const_iterator begin() const noexcept; + const_iterator end() const noexcept; +}; //------------------------------------------------------------------------------ @@ -307,8 +335,8 @@ class basic_multi_buffer<Allocator>::mutable_buffers_type::const_iterator typename list_type::const_iterator it_; public: - using value_type = - typename mutable_buffers_type::value_type; + using value_type = typename + mutable_buffers_type::value_type; using pointer = value_type const*; using reference = value_type; using difference_type = std::ptrdiff_t; @@ -316,32 +344,31 @@ public: 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_multi_buffer const& b, - typename list_type::const_iterator const& it) + const_iterator( + basic_multi_buffer const& b, + typename list_type::const_iterator const& it) noexcept : b_(&b) , it_(it) { } bool - operator==(const_iterator const& other) const + operator==(const_iterator const& other) const noexcept { return b_ == other.b_ && it_ == other.it_; } bool - operator!=(const_iterator const& other) const + operator!=(const_iterator const& other) const noexcept { return !(*this == other); } reference - operator*() const + operator*() const noexcept { auto const& e = *it_; return value_type{e.data(), @@ -354,14 +381,14 @@ public: operator->() const = delete; const_iterator& - operator++() + operator++() noexcept { ++it_; return *this; } const_iterator - operator++(int) + operator++(int) noexcept { auto temp = *this; ++(*this); @@ -369,14 +396,14 @@ public: } const_iterator& - operator--() + operator--() noexcept { --it_; return *this; } const_iterator - operator--(int) + operator--(int) noexcept { auto temp = *this; --(*this); @@ -384,20 +411,37 @@ public: } }; +//------------------------------------------------------------------------------ + template<class Allocator> +template<bool isMutable> +auto basic_multi_buffer<Allocator>:: -mutable_buffers_type:: -mutable_buffers_type( - basic_multi_buffer const& b) - : b_(&b) +readable_bytes<isMutable>:: +begin() const noexcept -> + const_iterator { + return const_iterator{*b_, b_->list_.begin()}; +} + +template<class Allocator> +template<bool isMutable> +auto +basic_multi_buffer<Allocator>:: +readable_bytes<isMutable>:: +end() const noexcept -> + const_iterator +{ + return const_iterator{*b_, b_->out_ == + b_->list_.end() ? b_->list_.end() : + std::next(b_->out_)}; } template<class Allocator> auto basic_multi_buffer<Allocator>:: mutable_buffers_type:: -begin() const -> +begin() const noexcept -> const_iterator { return const_iterator{*b_, b_->out_}; @@ -407,7 +451,7 @@ template<class Allocator> auto basic_multi_buffer<Allocator>:: mutable_buffers_type:: -end() const -> +end() const noexcept -> const_iterator { return const_iterator{*b_, b_->list_.end()}; @@ -419,19 +463,21 @@ template<class Allocator> basic_multi_buffer<Allocator>:: ~basic_multi_buffer() { - delete_list(); + destroy(list_); } template<class Allocator> basic_multi_buffer<Allocator>:: -basic_multi_buffer() - : out_(list_.end()) +basic_multi_buffer() noexcept(default_nothrow) + : max_(alloc_traits::max_size(this->get())) + , out_(list_.end()) { } template<class Allocator> basic_multi_buffer<Allocator>:: -basic_multi_buffer(std::size_t limit) +basic_multi_buffer( + std::size_t limit) noexcept(default_nothrow) : max_(limit) , out_(list_.end()) { @@ -439,17 +485,20 @@ basic_multi_buffer(std::size_t limit) template<class Allocator> basic_multi_buffer<Allocator>:: -basic_multi_buffer(Allocator const& alloc) - : boost::empty_value< - base_alloc_type>(boost::empty_init_t(), alloc) +basic_multi_buffer( + Allocator const& alloc) noexcept + : boost::empty_value<base_alloc_type>( + boost::empty_init_t(), alloc) + , max_(alloc_traits::max_size(this->get())) , out_(list_.end()) { } template<class Allocator> basic_multi_buffer<Allocator>:: -basic_multi_buffer(std::size_t limit, - Allocator const& alloc) +basic_multi_buffer( + std::size_t limit, + Allocator const& alloc) noexcept : boost::empty_value< base_alloc_type>(boost::empty_init_t(), alloc) , max_(limit) @@ -459,9 +508,10 @@ basic_multi_buffer(std::size_t limit, template<class Allocator> basic_multi_buffer<Allocator>:: -basic_multi_buffer(basic_multi_buffer&& other) - : boost::empty_value< - base_alloc_type>(boost::empty_init_t(), std::move(other.get())) +basic_multi_buffer( + basic_multi_buffer&& other) noexcept + : boost::empty_value<base_alloc_type>( + boost::empty_init_t(), std::move(other.get())) , max_(other.max_) , in_size_(boost::exchange(other.in_size_, 0)) , in_pos_(boost::exchange(other.in_pos_, 0)) @@ -477,8 +527,9 @@ basic_multi_buffer(basic_multi_buffer&& other) template<class Allocator> basic_multi_buffer<Allocator>:: -basic_multi_buffer(basic_multi_buffer&& other, - Allocator const& alloc) +basic_multi_buffer( + basic_multi_buffer&& other, + Allocator const& alloc) : boost::empty_value< base_alloc_type>(boost::empty_init_t(), alloc) , max_(other.max_) @@ -487,33 +538,34 @@ basic_multi_buffer(basic_multi_buffer&& other, { out_ = list_.end(); copy_from(other); - other.reset(); - } - else - { - 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; + other.clear(); + other.shrink_to_fit(); + return; } + + 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<class Allocator> basic_multi_buffer<Allocator>:: -basic_multi_buffer(basic_multi_buffer const& other) - : boost::empty_value< - base_alloc_type>(boost::empty_init_t(), alloc_traits:: - select_on_container_copy_construction( - other.get())) +basic_multi_buffer( + basic_multi_buffer const& other) + : boost::empty_value<base_alloc_type>( + boost::empty_init_t(), alloc_traits:: + select_on_container_copy_construction( + other.get())) , max_(other.max_) , out_(list_.end()) { @@ -522,10 +574,11 @@ basic_multi_buffer(basic_multi_buffer const& other) template<class Allocator> basic_multi_buffer<Allocator>:: -basic_multi_buffer(basic_multi_buffer const& other, - Allocator const& alloc) - : boost::empty_value< - base_alloc_type>(boost::empty_init_t(), alloc) +basic_multi_buffer( + basic_multi_buffer const& other, + Allocator const& alloc) + : boost::empty_value<base_alloc_type>( + boost::empty_init_t(), alloc) , max_(other.max_) , out_(list_.end()) { @@ -564,10 +617,9 @@ operator=(basic_multi_buffer&& other) -> { if(this == &other) return *this; - reset(); + clear(); max_ = other.max_; - move_assign(other, std::integral_constant<bool, - alloc_traits::propagate_on_container_move_assignment::value>{}); + move_assign(other, pocma{}); return *this; } @@ -579,8 +631,7 @@ basic_multi_buffer& { if(this == &other) return *this; - copy_assign(other, std::integral_constant<bool, - alloc_traits::propagate_on_container_copy_assignment::value>{}); + copy_assign(other, pocca{}); return *this; } @@ -592,16 +643,16 @@ operator=( basic_multi_buffer<OtherAlloc> const& other) -> basic_multi_buffer& { - reset(); - max_ = other.max_; copy_from(other); return *this; } +//------------------------------------------------------------------------------ + template<class Allocator> std::size_t basic_multi_buffer<Allocator>:: -capacity() const +capacity() const noexcept { auto pos = out_; if(pos == list_.end()) @@ -615,7 +666,7 @@ capacity() const template<class Allocator> auto basic_multi_buffer<Allocator>:: -data() const -> +data() const noexcept -> const_buffers_type { return const_buffers_type(*this); @@ -624,12 +675,190 @@ data() const -> template<class Allocator> auto basic_multi_buffer<Allocator>:: +data() noexcept -> + mutable_data_type +{ + return mutable_data_type(*this); +} + +template<class Allocator> +void +basic_multi_buffer<Allocator>:: +reserve(std::size_t n) +{ + // VFALCO The amount needs to be adjusted for + // the sizeof(element) plus padding + if(n > alloc_traits::max_size(this->get())) + BOOST_THROW_EXCEPTION(std::length_error( + "A basic_multi_buffer exceeded the allocator's maximum size")); + std::size_t total = in_size_; + if(n <= total) + return; + if(out_ != list_.end()) + { + total += out_->size() - out_pos_; + if(n <= total) + return; + for(auto it = out_;;) + { + if(++it == list_.end()) + break; + total += it->size(); + if(n <= total) + return; + } + } + BOOST_ASSERT(n > total); + (void)prepare(n - size()); +} + +template<class Allocator> +void +basic_multi_buffer<Allocator>:: +shrink_to_fit() +{ + // empty list + if(list_.empty()) + return; + + // zero readable bytes + if(in_size_ == 0) + { + destroy(list_); + list_.clear(); + out_ = list_.end(); + in_size_ = 0; + in_pos_ = 0; + out_pos_ = 0; + out_end_ = 0; + #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK + debug_check(); + #endif + return; + } + + // one or more unused output buffers + if(out_ != list_.end()) + { + if(out_ != list_.iterator_to(list_.back())) + { + // unused list + list_type extra; + extra.splice( + extra.end(), + list_, + std::next(out_), + list_.end()); + destroy(extra); + #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK + debug_check(); + #endif + } + + // unused out_ + BOOST_ASSERT(out_ == + list_.iterator_to(list_.back())); + if(out_pos_ == 0) + { + BOOST_ASSERT(out_ != list_.begin()); + auto& e = *out_; + list_.erase(out_); + out_ = list_.end(); + destroy(e); + out_end_ = 0; + #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK + debug_check(); + #endif + } + } + + auto const replace = + [&](iter pos, element& e) + { + auto it = + list_.insert(pos, e); + auto& e0 = *pos; + list_.erase(pos); + destroy(e0); + return it; + }; + + // partial last buffer + if(list_.size() > 1 && out_ != list_.end()) + { + BOOST_ASSERT(out_ == + list_.iterator_to(list_.back())); + BOOST_ASSERT(out_pos_ != 0); + auto& e = alloc(out_pos_); + std::memcpy( + e.data(), + out_->data(), + out_pos_); + replace(out_, e); + out_ = list_.end(); + out_pos_ = 0; + out_end_ = 0; + #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK + debug_check(); + #endif + } + + // partial first buffer + if(in_pos_ != 0) + { + if(out_ != list_.begin()) + { + auto const n = + list_.front().size() - in_pos_; + auto& e = alloc(n); + std::memcpy( + e.data(), + list_.front().data() + in_pos_, + n); + replace(list_.begin(), e); + in_pos_ = 0; + } + else + { + BOOST_ASSERT(list_.size() == 1); + BOOST_ASSERT(out_pos_ > in_pos_); + auto const n = out_pos_ - in_pos_; + auto& e = alloc(n); + std::memcpy( + e.data(), + list_.front().data() + in_pos_, + n); + replace(list_.begin(), e); + in_pos_ = 0; + out_ = list_.end(); + } + #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK + debug_check(); + #endif + } +} + +template<class Allocator> +void +basic_multi_buffer<Allocator>:: +clear() noexcept +{ + out_ = list_.begin(); + in_size_ = 0; + in_pos_ = 0; + out_pos_ = 0; + out_end_ = 0; +} + +template<class Allocator> +auto +basic_multi_buffer<Allocator>:: prepare(size_type n) -> mutable_buffers_type { - if(in_size_ + n > max_) + if(in_size_ > max_ || n > (max_ - in_size_)) BOOST_THROW_EXCEPTION(std::length_error{ - "dynamic buffer overflow"}); + "basic_multi_buffer too long"}); list_type reuse; std::size_t total = in_size_; // put all empty buffers on reuse list @@ -684,15 +913,7 @@ prepare(size_type n) -> BOOST_ASSERT(total <= max_); if(! reuse.empty() || n > 0) { - for(auto it = reuse.begin(); it != reuse.end();) - { - auto& e = *it++; - reuse.erase(list_.iterator_to(e)); - auto const len = sizeof(e) + e.size(); - alloc_traits::destroy(this->get(), &e); - alloc_traits::deallocate(this->get(), - reinterpret_cast<char*>(&e), len); - } + destroy(reuse); if(n > 0) { static auto const growth_factor = 2.0f; @@ -704,10 +925,7 @@ prepare(size_type n) -> in_size_ * growth_factor - in_size_), 512, n})); - auto& e = *reinterpret_cast<element*>(static_cast< - void*>(alloc_traits::allocate(this->get(), - sizeof(element) + size))); - alloc_traits::construct(this->get(), &e, size); + auto& e = alloc(size); list_.push_back(e); if(out_ == list_.end()) out_ = list_.iterator_to(e); @@ -723,7 +941,7 @@ prepare(size_type n) -> template<class Allocator> void basic_multi_buffer<Allocator>:: -commit(size_type n) +commit(size_type n) noexcept { if(list_.empty()) return; @@ -770,7 +988,7 @@ commit(size_type n) template<class Allocator> void basic_multi_buffer<Allocator>:: -consume(size_type n) +consume(size_type n) noexcept { if(list_.empty()) return; @@ -795,7 +1013,7 @@ consume(size_type n) auto& e = list_.front(); list_.erase(list_.iterator_to(e)); auto const len = sizeof(e) + e.size(); - alloc_traits::destroy(this->get(), &e); + e.~element(); alloc_traits::deallocate(this->get(), reinterpret_cast<char*>(&e), len); #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK @@ -836,72 +1054,23 @@ consume(size_type n) } template<class Allocator> -inline -void -basic_multi_buffer<Allocator>:: -delete_list() -{ - for(auto iter = list_.begin(); iter != list_.end();) - { - auto& e = *iter++; - auto const len = sizeof(e) + e.size(); - alloc_traits::destroy(this->get(), &e); - alloc_traits::deallocate(this->get(), - reinterpret_cast<char*>(&e), len); - } -} - -template<class Allocator> -inline -void -basic_multi_buffer<Allocator>:: -reset() -{ - delete_list(); - list_.clear(); - out_ = list_.end(); - in_size_ = 0; - in_pos_ = 0; - out_pos_ = 0; - out_end_ = 0; -} - -template<class Allocator> -template<class DynamicBuffer> -inline +template<class OtherAlloc> void basic_multi_buffer<Allocator>:: -copy_from(DynamicBuffer const& buffer) +copy_from(basic_multi_buffer<OtherAlloc> const& other) { - if(buffer.size() == 0) + clear(); + max_ = other.max_; + if(other.size() == 0) return; - using boost::asio::buffer_copy; - commit(buffer_copy( - prepare(buffer.size()), buffer.data())); + commit(net::buffer_copy( + prepare(other.size()), other.data())); } template<class Allocator> -inline void basic_multi_buffer<Allocator>:: -move_assign(basic_multi_buffer& other, std::false_type) -{ - if(this->get() != other.get()) - { - copy_from(other); - other.reset(); - } - else - { - move_assign(other, std::true_type{}); - } -} - -template<class Allocator> -inline -void -basic_multi_buffer<Allocator>:: -move_assign(basic_multi_buffer& other, std::true_type) +move_assign(basic_multi_buffer& other, std::true_type) noexcept { this->get() = std::move(other.get()); auto const at_end = @@ -913,6 +1082,7 @@ move_assign(basic_multi_buffer& other, std::true_type) in_pos_ = other.in_pos_; out_pos_ = other.out_pos_; out_end_ = other.out_end_; + max_ = other.max_; other.in_size_ = 0; other.out_ = other.list_.end(); @@ -922,45 +1092,55 @@ move_assign(basic_multi_buffer& other, std::true_type) } template<class Allocator> -inline +void +basic_multi_buffer<Allocator>:: +move_assign(basic_multi_buffer& other, std::false_type) +{ + if(this->get() != other.get()) + { + copy_from(other); + other.clear(); + other.shrink_to_fit(); + } + else + { + move_assign(other, std::true_type{}); + } +} + +template<class Allocator> void basic_multi_buffer<Allocator>:: copy_assign( basic_multi_buffer const& other, std::false_type) { - reset(); - max_ = other.max_; copy_from(other); } template<class Allocator> -inline void basic_multi_buffer<Allocator>:: copy_assign( basic_multi_buffer const& other, std::true_type) { - reset(); - max_ = other.max_; + clear(); this->get() = other.get(); copy_from(other); } template<class Allocator> -inline void basic_multi_buffer<Allocator>:: -swap(basic_multi_buffer& other) +swap(basic_multi_buffer& other) noexcept { swap(other, typename alloc_traits::propagate_on_container_swap{}); } template<class Allocator> -inline void basic_multi_buffer<Allocator>:: -swap(basic_multi_buffer& other, std::true_type) +swap(basic_multi_buffer& other, std::true_type) noexcept { using std::swap; auto const at_end0 = @@ -981,10 +1161,9 @@ swap(basic_multi_buffer& other, std::true_type) } template<class Allocator> -inline void basic_multi_buffer<Allocator>:: -swap(basic_multi_buffer& other, std::false_type) +swap(basic_multi_buffer& other, std::false_type) noexcept { BOOST_ASSERT(this->get() == other.get()); using std::swap; @@ -1008,7 +1187,7 @@ template<class Allocator> void swap( basic_multi_buffer<Allocator>& lhs, - basic_multi_buffer<Allocator>& rhs) + basic_multi_buffer<Allocator>& rhs) noexcept { lhs.swap(rhs); } @@ -1016,11 +1195,54 @@ swap( template<class Allocator> void basic_multi_buffer<Allocator>:: +destroy(list_type& list) noexcept +{ + for(auto it = list.begin(); + it != list.end();) + destroy(*it++); +} + +template<class Allocator> +void +basic_multi_buffer<Allocator>:: +destroy(const_iter it) +{ + auto& e = list_.erase(it); + destroy(e); +} + +template<class Allocator> +void +basic_multi_buffer<Allocator>:: +destroy(element& e) +{ + auto const len = sizeof(e) + e.size(); + e.~element(); + alloc_traits::deallocate(this->get(), + reinterpret_cast<char*>(&e), len); +} + +template<class Allocator> +auto +basic_multi_buffer<Allocator>:: +alloc(std::size_t size) -> + element& +{ + if(size > alloc_traits::max_size(this->get())) + BOOST_THROW_EXCEPTION(std::length_error( + "A basic_multi_buffer exceeded the allocator's maximum size")); + return *::new(alloc_traits::allocate( + this->get(), + sizeof(element) + size)) element(size); +} + +template<class Allocator> +void +basic_multi_buffer<Allocator>:: debug_check() const { #ifndef NDEBUG - using boost::asio::buffer_size; - BOOST_ASSERT(buffer_size(data()) == in_size_); + BOOST_ASSERT(buffer_bytes(data()) == in_size_); if(list_.empty()) { BOOST_ASSERT(in_pos_ == 0); diff --git a/boost/beast/core/impl/read_size.ipp b/boost/beast/core/impl/read_size.hpp index fa52571e09..1896c014b8 100644 --- a/boost/beast/core/impl/read_size.ipp +++ b/boost/beast/core/impl/read_size.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -7,9 +7,10 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_IMPL_READ_SIZE_IPP -#define BOOST_BEAST_IMPL_READ_SIZE_IPP +#ifndef BOOST_BEAST_IMPL_READ_SIZE_HPP +#define BOOST_BEAST_IMPL_READ_SIZE_HPP +#include <boost/asio/buffer.hpp> #include <boost/assert.hpp> #include <stdexcept> #include <type_traits> @@ -43,21 +44,20 @@ read_size(DynamicBuffer& buffer, std::size_t max_size, std::false_type) { static_assert( - boost::asio::is_dynamic_buffer<DynamicBuffer>::value, - "DynamicBuffer requirements not met"); + net::is_dynamic_buffer<DynamicBuffer>::value, + "DynamicBuffer type requirements not met"); BOOST_ASSERT(max_size >= 1); auto const size = buffer.size(); auto const limit = buffer.max_size() - size; BOOST_ASSERT(size <= buffer.max_size()); - return (std::min<std::size_t>)( - (std::max<std::size_t>)(512, buffer.capacity() - size), - (std::min<std::size_t>)(max_size, limit)); + return std::min<std::size_t>( + std::max<std::size_t>(512, buffer.capacity() - size), + std::min<std::size_t>(max_size, limit)); } } // detail template<class DynamicBuffer> -inline std::size_t read_size( DynamicBuffer& buffer, std::size_t max_size) diff --git a/boost/beast/core/impl/saved_handler.hpp b/boost/beast/core/impl/saved_handler.hpp new file mode 100644 index 0000000000..a2642f59da --- /dev/null +++ b/boost/beast/core/impl/saved_handler.hpp @@ -0,0 +1,151 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_IMPL_SAVED_HANDLER_HPP +#define BOOST_BEAST_CORE_IMPL_SAVED_HANDLER_HPP + +#include <boost/beast/core/detail/allocator.hpp> +#include <boost/asio/associated_allocator.hpp> +#include <boost/asio/associated_executor.hpp> +#include <boost/asio/executor_work_guard.hpp> +#include <boost/assert.hpp> +#include <boost/core/empty_value.hpp> +#include <boost/core/exchange.hpp> +#include <utility> + +namespace boost { +namespace beast { + +//------------------------------------------------------------------------------ + +class saved_handler::base +{ +protected: + ~base() = default; + +public: + base() = default; + virtual void destroy() = 0; + virtual void invoke() = 0; +}; + +//------------------------------------------------------------------------------ + +template<class Handler, class Alloc> +class saved_handler::impl final : public base +{ + using alloc_type = typename + beast::detail::allocator_traits< + Alloc>::template rebind_alloc<impl>; + + using alloc_traits = + beast::detail::allocator_traits<alloc_type>; + + struct ebo_pair : boost::empty_value<alloc_type> + { + Handler h; + + template<class Handler_> + ebo_pair( + alloc_type const& a, + Handler_&& h_) + : boost::empty_value<alloc_type>( + boost::empty_init_t{}, a) + , h(std::forward<Handler_>(h_)) + { + } + }; + + ebo_pair v_; + net::executor_work_guard< + net::associated_executor_t<Handler>> wg2_; + +public: + template<class Handler_> + impl(alloc_type const& a, Handler_&& h) + : v_(a, std::forward<Handler_>(h)) + , wg2_(net::get_associated_executor(v_.h)) + { + } + + void + destroy() override + { + auto v = std::move(v_); + alloc_traits::destroy(v.get(), this); + alloc_traits::deallocate(v.get(), this, 1); + } + + void + invoke() override + { + auto v = std::move(v_); + alloc_traits::destroy(v.get(), this); + alloc_traits::deallocate(v.get(), this, 1); + v.h(); + } +}; + +//------------------------------------------------------------------------------ + +template<class Handler, class Allocator> +void +saved_handler:: +emplace(Handler&& handler, Allocator const& alloc) +{ + // Can't delete a handler before invoking + BOOST_ASSERT(! has_value()); + using handler_type = + typename std::decay<Handler>::type; + using alloc_type = typename + detail::allocator_traits<Allocator>:: + template rebind_alloc<impl< + handler_type, Allocator>>; + using alloc_traits = + beast::detail::allocator_traits<alloc_type>; + struct storage + { + alloc_type a; + impl<Handler, Allocator>* p; + + explicit + storage(Allocator const& a_) + : a(a_) + , p(alloc_traits::allocate(a, 1)) + { + } + + ~storage() + { + if(p) + alloc_traits::deallocate(a, p, 1); + } + }; + storage s(alloc); + alloc_traits::construct(s.a, s.p, + s.a, std::forward<Handler>(handler)); + p_ = boost::exchange(s.p, nullptr); +} + +template<class Handler> +void +saved_handler:: +emplace(Handler&& handler) +{ + // Can't delete a handler before invoking + BOOST_ASSERT(! has_value()); + emplace( + std::forward<Handler>(handler), + net::get_associated_allocator(handler)); +} + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/impl/saved_handler.ipp b/boost/beast/core/impl/saved_handler.ipp new file mode 100644 index 0000000000..30ecbc8e15 --- /dev/null +++ b/boost/beast/core/impl/saved_handler.ipp @@ -0,0 +1,76 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_IMPL_SAVED_HANDLER_IPP +#define BOOST_BEAST_CORE_IMPL_SAVED_HANDLER_IPP + +#include <boost/beast/core/saved_handler.hpp> +#include <boost/core/exchange.hpp> + +namespace boost { +namespace beast { + +saved_handler:: +~saved_handler() +{ + if(p_) + p_->destroy(); +} + +saved_handler:: +saved_handler(saved_handler&& other) noexcept + : p_(boost::exchange(other.p_, nullptr)) +{ +} + +saved_handler& +saved_handler:: +operator=(saved_handler&& other) noexcept +{ + // Can't delete a handler before invoking + BOOST_ASSERT(! has_value()); + p_ = boost::exchange(other.p_, nullptr); + return *this; +} + +bool +saved_handler:: +reset() noexcept +{ + if(! p_) + return false; + boost::exchange(p_, nullptr)->destroy(); + return true; +} + +void +saved_handler:: +invoke() +{ + // Can't invoke without a value + BOOST_ASSERT(has_value()); + boost::exchange( + p_, nullptr)->invoke(); +} + +bool +saved_handler:: +maybe_invoke() +{ + if(! p_) + return false; + boost::exchange( + p_, nullptr)->invoke(); + return true; +} + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/impl/static_buffer.hpp b/boost/beast/core/impl/static_buffer.hpp new file mode 100644 index 0000000000..dee13bfbe5 --- /dev/null +++ b/boost/beast/core/impl/static_buffer.hpp @@ -0,0 +1,50 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_IMPL_STATIC_BUFFER_HPP +#define BOOST_BEAST_IMPL_STATIC_BUFFER_HPP + +#include <boost/beast/core/detail/type_traits.hpp> +#include <boost/asio/buffer.hpp> +#include <boost/throw_exception.hpp> +#include <algorithm> +#include <cstring> +#include <iterator> +#include <stdexcept> + +namespace boost { +namespace beast { + +template<std::size_t N> +static_buffer<N>:: +static_buffer(static_buffer const& other) noexcept + : static_buffer_base(buf_, N) +{ + this->commit(net::buffer_copy( + this->prepare(other.size()), other.data())); +} + +template<std::size_t N> +auto +static_buffer<N>:: +operator=(static_buffer const& other) noexcept -> + static_buffer<N>& +{ + if(this == &other) + return *this; + this->consume(this->size()); + this->commit(net::buffer_copy( + this->prepare(other.size()), other.data())); + return *this; +} + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/impl/static_buffer.ipp b/boost/beast/core/impl/static_buffer.ipp index bd498c97a1..c26ff429b8 100644 --- a/boost/beast/core/impl/static_buffer.ipp +++ b/boost/beast/core/impl/static_buffer.ipp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -10,6 +10,7 @@ #ifndef BOOST_BEAST_IMPL_STATIC_BUFFER_IPP #define BOOST_BEAST_IMPL_STATIC_BUFFER_IPP +#include <boost/beast/core/static_buffer.hpp> #include <boost/beast/core/detail/type_traits.hpp> #include <boost/asio/buffer.hpp> #include <boost/throw_exception.hpp> @@ -21,148 +22,111 @@ namespace boost { namespace beast { -inline static_buffer_base:: -static_buffer_base(void* p, std::size_t size) +static_buffer_base( + void* p, std::size_t size) noexcept : begin_(static_cast<char*>(p)) , capacity_(size) { } -inline +void +static_buffer_base:: +clear() noexcept +{ + in_off_ = 0; + in_size_ = 0; + out_size_ = 0; +} + auto static_buffer_base:: -data() const -> +data() const noexcept -> const_buffers_type { - using boost::asio::const_buffer; - const_buffers_type result; if(in_off_ + in_size_ <= capacity_) - { - result[0] = const_buffer{begin_ + in_off_, in_size_}; - result[1] = const_buffer{begin_, 0}; - } - else - { - result[0] = const_buffer{begin_ + in_off_, capacity_ - in_off_}; - result[1] = const_buffer{begin_, in_size_ - (capacity_ - in_off_)}; - } - return result; + return { + net::const_buffer{ + begin_ + in_off_, in_size_}, + net::const_buffer{ + begin_, 0}}; + return { + net::const_buffer{ + begin_ + in_off_, capacity_ - in_off_}, + net::const_buffer{ + begin_, in_size_ - (capacity_ - in_off_)}}; } -inline auto static_buffer_base:: -mutable_data() -> - mutable_buffers_type +data() noexcept -> + mutable_data_type { - using boost::asio::mutable_buffer; - mutable_buffers_type result; if(in_off_ + in_size_ <= capacity_) - { - result[0] = mutable_buffer{begin_ + in_off_, in_size_}; - result[1] = mutable_buffer{begin_, 0}; - } - else - { - result[0] = mutable_buffer{begin_ + in_off_, capacity_ - in_off_}; - result[1] = mutable_buffer{begin_, in_size_ - (capacity_ - in_off_)}; - } - return result; + return { + net::mutable_buffer{ + begin_ + in_off_, in_size_}, + net::mutable_buffer{ + begin_, 0}}; + return { + net::mutable_buffer{ + begin_ + in_off_, capacity_ - in_off_}, + net::mutable_buffer{ + begin_, in_size_ - (capacity_ - in_off_)}}; } -inline auto static_buffer_base:: -prepare(std::size_t size) -> +prepare(std::size_t n) -> mutable_buffers_type { - using boost::asio::mutable_buffer; - if(size > capacity_ - in_size_) + using net::mutable_buffer; + if(n > capacity_ - in_size_) BOOST_THROW_EXCEPTION(std::length_error{ - "buffer overflow"}); - out_size_ = size; - auto const out_off = (in_off_ + in_size_) % capacity_; - mutable_buffers_type result; + "static_buffer overflow"}); + out_size_ = n; + auto const out_off = + (in_off_ + in_size_) % capacity_; if(out_off + out_size_ <= capacity_ ) - { - result[0] = mutable_buffer{begin_ + out_off, out_size_}; - result[1] = mutable_buffer{begin_, 0}; - } - else - { - result[0] = mutable_buffer{begin_ + out_off, capacity_ - out_off}; - result[1] = mutable_buffer{begin_, out_size_ - (capacity_ - out_off)}; - } - return result; + return { + net::mutable_buffer{ + begin_ + out_off, out_size_}, + net::mutable_buffer{ + begin_, 0}}; + return { + net::mutable_buffer{ + begin_ + out_off, capacity_ - out_off}, + net::mutable_buffer{ + begin_, out_size_ - (capacity_ - out_off)}}; } -inline void static_buffer_base:: -commit(std::size_t size) +commit(std::size_t n) noexcept { - in_size_ += (std::min)(size, out_size_); + in_size_ += (std::min)(n, out_size_); out_size_ = 0; } -inline void static_buffer_base:: -consume(std::size_t size) +consume(std::size_t n) noexcept { - if(size < in_size_) + if(n < in_size_) { - in_off_ = (in_off_ + size) % capacity_; - in_size_ -= size; + in_off_ = (in_off_ + n) % capacity_; + in_size_ -= n; } else { // rewind the offset, so the next call to prepare // can have a longer contiguous segment. this helps - // algorithms optimized for larger buffesr. + // algorithms optimized for larger buffers. in_off_ = 0; in_size_ = 0; } } -inline -void -static_buffer_base:: -reset(void* p, std::size_t size) -{ - begin_ = static_cast<char*>(p); - capacity_ = size; - in_off_ = 0; - in_size_ = 0; - out_size_ = 0; -} - -//------------------------------------------------------------------------------ - -template<std::size_t N> -static_buffer<N>:: -static_buffer(static_buffer const& other) - : static_buffer_base(buf_, N) -{ - using boost::asio::buffer_copy; - this->commit(buffer_copy( - this->prepare(other.size()), other.data())); -} - -template<std::size_t N> -auto -static_buffer<N>:: -operator=(static_buffer const& other) -> - static_buffer<N>& -{ - using boost::asio::buffer_copy; - this->consume(this->size()); - this->commit(buffer_copy( - this->prepare(other.size()), other.data())); - return *this; -} - } // beast } // boost diff --git a/boost/beast/core/impl/static_string.ipp b/boost/beast/core/impl/static_string.hpp index 29571dfeee..c668b837fa 100644 --- a/boost/beast/core/impl/static_string.ipp +++ b/boost/beast/core/impl/static_string.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_IMPL_STATIC_STRING_IPP -#define BOOST_BEAST_IMPL_STATIC_STRING_IPP +#ifndef BOOST_BEAST_IMPL_STATIC_STRING_HPP +#define BOOST_BEAST_IMPL_STATIC_STRING_HPP #include <boost/beast/core/detail/static_string.hpp> #include <boost/beast/core/detail/type_traits.hpp> @@ -65,7 +65,12 @@ template<std::size_t N, class CharT, class Traits> static_string<N, CharT, Traits>:: static_string(CharT const* s) { - assign(s); + auto const count = Traits::length(s); + if(count > max_size()) + BOOST_THROW_EXCEPTION(std::length_error{ + "count > max_size()"}); + n_ = count; + Traits::copy(&s_[0], s, n_ + 1); } template<std::size_t N, class CharT, class Traits> @@ -120,6 +125,21 @@ static_string(T const& t, size_type pos, size_type n) template<std::size_t N, class CharT, class Traits> auto static_string<N, CharT, Traits>:: +operator=(CharT const* s) -> + static_string& +{ + auto const count = Traits::length(s); + if(count > max_size()) + BOOST_THROW_EXCEPTION(std::length_error{ + "count > max_size()"}); + n_ = count; + Traits::copy(&s_[0], s, n_ + 1); + return *this; +} + +template<std::size_t N, class CharT, class Traits> +auto +static_string<N, CharT, Traits>:: assign(size_type count, CharT ch) -> static_string& { @@ -139,7 +159,9 @@ assign(static_string const& str) -> static_string& { n_ = str.n_; - Traits::copy(&s_[0], &str.s_[0], n_ + 1); + auto const n = n_ + 1; + BOOST_BEAST_ASSUME(n != 0); + Traits::copy(&s_[0], &str.s_[0], n); return *this; } @@ -463,6 +485,8 @@ resize(std::size_t n) if(n > max_size()) BOOST_THROW_EXCEPTION(std::length_error{ "n > max_size()"}); + if(n > n_) + Traits::assign(&s_[n_], n - n_, CharT{}); n_ = n; term(); } @@ -593,7 +617,7 @@ to_static_string(Integer x, std::false_type) } // detail -template<class Integer> +template<class Integer, class> static_string<detail::max_digits(sizeof(Integer))> to_static_string(Integer x) { diff --git a/boost/beast/core/impl/string_param.ipp b/boost/beast/core/impl/string_param.hpp index a60771cc12..9d9278382a 100644 --- a/boost/beast/core/impl/string_param.ipp +++ b/boost/beast/core/impl/string_param.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_IMPL_STRING_PARAM_IPP -#define BOOST_BEAST_IMPL_STRING_PARAM_IPP +#ifndef BOOST_BEAST_IMPL_STRING_PARAM_HPP +#define BOOST_BEAST_IMPL_STRING_PARAM_HPP namespace boost { namespace beast { diff --git a/boost/beast/core/make_printable.hpp b/boost/beast/core/make_printable.hpp new file mode 100644 index 0000000000..cead41a96b --- /dev/null +++ b/boost/beast/core/make_printable.hpp @@ -0,0 +1,107 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_MAKE_PRINTABLE_HPP +#define BOOST_BEAST_MAKE_PRINTABLE_HPP + +#include <boost/beast/core/detail/config.hpp> +#include <boost/beast/core/buffer_traits.hpp> +#include <boost/asio/buffer.hpp> +#include <ostream> + +namespace boost { +namespace beast { + +namespace detail { + +template<class Buffers> +class make_printable_adaptor +{ + Buffers b_; + +public: + explicit + make_printable_adaptor(Buffers const& b) + : b_(b) + { + } + + template<class B> + friend + std::ostream& + operator<<(std::ostream& os, + make_printable_adaptor<B> const& v); +}; + +template<class Buffers> +std::ostream& +operator<<(std::ostream& os, + make_printable_adaptor<Buffers> const& v) +{ + for( + auto it = net::buffer_sequence_begin(v.b_), + end = net::buffer_sequence_end(v.b_); + it != end; + ++it) + { + net::const_buffer cb = *it; + os.write(static_cast<char const*>( + cb.data()), cb.size()); + } + return os; +} + +} // detail + +/** Helper to permit a buffer sequence to be printed to a std::ostream + + This function is used to wrap a buffer sequence to allow it to + be interpreted as characters and written to a `std::ostream` such + as `std::cout`. No character translation is performed; unprintable + and null characters will be transferred as-is to the output stream. + + @par Example + This function prints the size and contents of a buffer sequence + to standard output: + @code + template <class ConstBufferSequence> + void + print (ConstBufferSequence const& buffers) + { + std::cout << + "Buffer size: " << buffer_bytes(buffers) << " bytes\n" + "Buffer data: '" << make_printable(buffers) << "'\n"; + } + @endcode + + @param buffers An object meeting the requirements of + <em>ConstBufferSequence</em> to be streamed. The implementation + will make a copy of this object. Ownership of the underlying + memory is not transferred, the application is still responsible + for managing its lifetime. +*/ +template<class ConstBufferSequence> +#if BOOST_BEAST_DOXYGEN +__implementation_defined__ +#else +detail::make_printable_adaptor<ConstBufferSequence> +#endif +make_printable(ConstBufferSequence const& buffers) +{ + static_assert(net::is_const_buffer_sequence< + ConstBufferSequence>::value, + "ConstBufferSequence type requirements not met"); + return detail::make_printable_adaptor< + ConstBufferSequence>{buffers}; +} + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/multi_buffer.hpp b/boost/beast/core/multi_buffer.hpp index 046ed83a29..2cf477a83b 100644 --- a/boost/beast/core/multi_buffer.hpp +++ b/boost/beast/core/multi_buffer.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -23,14 +23,39 @@ namespace boost { namespace beast { -/** A @b DynamicBuffer that uses multiple buffers internally. +/** A dynamic buffer providing sequences of variable length. - 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. + A dynamic buffer encapsulates memory storage that may be + automatically resized as required, where the memory is + divided into two regions: readable bytes followed by + writable bytes. These memory regions are internal to + the dynamic buffer, but direct access to the elements + is provided to permit them to be efficiently used with + I/O operations. - @note Meets the requirements of @b DynamicBuffer. + The implementation uses a sequence of one or more byte + arrays of varying sizes to represent the readable and + writable bytes. Additional byte array objects are + appended to the sequence to accommodate changes in the + desired size. The behavior and implementation of this + container is most similar to `std::deque`. + + Objects of this type meet the requirements of <em>DynamicBuffer</em> + and have the following additional properties: + + @li A mutable buffer sequence representing the readable + bytes is returned by @ref data when `this` is non-const. + + @li Buffer sequences representing the readable and writable + bytes, returned by @ref data and @ref prepare, may have + length greater than one. + + @li A configurable maximum size may be set upon construction + and adjusted afterwards. Calls to @ref prepare that would + exceed this size will throw `std::length_error`. + + @li Sequences previously obtained using @ref data remain + valid after calls to @ref prepare or @ref commit. @tparam Allocator The allocator to use for managing memory. */ @@ -46,31 +71,41 @@ class basic_multi_buffer detail::allocator_traits<Allocator>:: template rebind_alloc<char>; + static bool constexpr default_nothrow = + std::is_nothrow_default_constructible<Allocator>::value; + // Storage for the list of buffers representing the input // and output sequences. The allocation for each element // contains `element` followed by raw storage bytes. class element; - using alloc_traits = detail::allocator_traits<base_alloc_type>; + template<bool> + class readable_bytes; + + using alloc_traits = + beast::detail::allocator_traits<base_alloc_type>; using list_type = typename boost::intrusive::make_list<element, boost::intrusive::constant_time_size<true>>::type; using iter = typename list_type::iterator; using const_iter = typename list_type::const_iterator; using size_type = typename alloc_traits::size_type; - using const_buffer = boost::asio::const_buffer; - using mutable_buffer = boost::asio::mutable_buffer; + + using pocma = typename + alloc_traits::propagate_on_container_move_assignment; + + using pocca = typename + alloc_traits::propagate_on_container_copy_assignment; static_assert(std::is_base_of<std::bidirectional_iterator_tag, typename std::iterator_traits<iter>::iterator_category>::value, - "BidirectionalIterator requirements not met"); + "BidirectionalIterator type requirements not met"); static_assert(std::is_base_of<std::bidirectional_iterator_tag, typename std::iterator_traits<const_iter>::iterator_category>::value, - "BidirectionalIterator requirements not met"); + "BidirectionalIterator type requirements not met"); - std::size_t max_ = - (std::numeric_limits<std::size_t>::max)(); + std::size_t max_; list_type list_; // list of allocated buffers iter out_; // element that contains out_pos_ size_type in_size_ = 0; // size of the input sequence @@ -82,233 +117,431 @@ public: /// The type of allocator used. using allocator_type = Allocator; -#if BOOST_BEAST_DOXYGEN - /// The type used to represent the input sequence as a list of buffers. - using const_buffers_type = implementation_defined; - - /// The type used to represent the output sequence as a list of buffers. - using mutable_buffers_type = implementation_defined; - -#else - class const_buffers_type; - - class mutable_buffers_type; - -#endif - /// Destructor ~basic_multi_buffer(); /** Constructor - Upon construction, capacity will be zero. + After construction, @ref capacity will return zero, and + @ref max_size will return the largest value which may + be passed to the allocator's `allocate` function. */ - basic_multi_buffer(); + basic_multi_buffer() noexcept(default_nothrow); + + /** Constructor - /** Constructor. + After construction, @ref capacity will return zero, and + @ref max_size will return the specified value of `limit`. - @param limit The setting for @ref max_size. + @param limit The desired maximum size. */ explicit - basic_multi_buffer(std::size_t limit); + basic_multi_buffer( + std::size_t limit) noexcept(default_nothrow); - /** Constructor. + /** Constructor - @param alloc The allocator to use. + After construction, @ref capacity will return zero, and + @ref max_size will return the largest value which may + be passed to the allocator's `allocate` function. + + @param alloc The allocator to use for the object. + + @esafe + + No-throw guarantee. */ explicit - basic_multi_buffer(Allocator const& alloc); + basic_multi_buffer(Allocator const& alloc) noexcept; - /** Constructor. + /** Constructor - @param limit The setting for @ref max_size. + After construction, @ref capacity will return zero, and + @ref max_size will return the specified value of `limit`. - @param alloc The allocator to use. + @param limit The desired maximum size. + + @param alloc The allocator to use for the object. + + @esafe + + No-throw guarantee. */ basic_multi_buffer( - std::size_t limit, Allocator const& alloc); + std::size_t limit, Allocator const& alloc) noexcept; - /** Move constructor + /** Move Constructor - After the move, `*this` will have an empty output sequence. + The container is constructed with the contents of `other` + using move semantics. The maximum size will be the same + as the moved-from object. - @param other The object to move from. After the move, - The object's state will be as if constructed using - its current allocator and limit. + Buffer sequences previously obtained from `other` using + @ref data or @ref prepare remain valid after the move. + + @param other The object to move from. After the move, the + moved-from object will have zero capacity, zero readable + bytes, and zero writable bytes. + + @esafe + + No-throw guarantee. */ - basic_multi_buffer(basic_multi_buffer&& other); + basic_multi_buffer(basic_multi_buffer&& other) noexcept; + + /** Move Constructor - /** Move constructor + Using `alloc` as the allocator for the new container, the + contents of `other` are moved. If `alloc != other.get_allocator()`, + this results in a copy. The maximum size will be the same + as the moved-from object. - After the move, `*this` will have an empty output sequence. + Buffer sequences previously obtained from `other` using + @ref data or @ref prepare become invalid after the move. @param other The object to move from. After the move, - The object's state will be as if constructed using - its current allocator and limit. + the moved-from object will have zero capacity, zero readable + bytes, and zero writable bytes. - @param alloc The allocator to use. + @param alloc The allocator to use for the object. + + @throws std::length_error if `other.size()` exceeds the + maximum allocation size of `alloc`. */ - basic_multi_buffer(basic_multi_buffer&& other, + basic_multi_buffer( + basic_multi_buffer&& other, Allocator const& alloc); - /** Copy constructor. + /** Copy Constructor + + This container is constructed with the contents of `other` + using copy semantics. The maximum size will be the same + as the copied object. @param other The object to copy from. + + @throws std::length_error if `other.size()` exceeds the + maximum allocation size of the allocator. */ basic_multi_buffer(basic_multi_buffer const& other); - /** Copy constructor + /** Copy Constructor + + This container is constructed with the contents of `other` + using copy semantics and the specified allocator. The maximum + size will be the same as the copied object. @param other The object to copy from. - @param alloc The allocator to use. + @param alloc The allocator to use for the object. + + @throws std::length_error if `other.size()` exceeds the + maximum allocation size of `alloc`. */ basic_multi_buffer(basic_multi_buffer const& other, Allocator const& alloc); - /** Copy constructor. + /** Copy Constructor + + This container is constructed with the contents of `other` + using copy semantics. The maximum size will be the same + as the copied object. @param other The object to copy from. + + @throws std::length_error if `other.size()` exceeds the + maximum allocation size of the allocator. */ template<class OtherAlloc> basic_multi_buffer(basic_multi_buffer< OtherAlloc> const& other); - /** Copy constructor. + /** Copy Constructor + + This container is constructed with the contents of `other` + using copy semantics. The maximum size will be the same + as the copied object. @param other The object to copy from. - @param alloc The allocator to use. + @param alloc The allocator to use for the object. + + @throws std::length_error if `other.size()` exceeds the + maximum allocation size of `alloc`. */ template<class OtherAlloc> - basic_multi_buffer(basic_multi_buffer< - OtherAlloc> const& other, allocator_type const& alloc); + basic_multi_buffer( + basic_multi_buffer<OtherAlloc> const& other, + allocator_type const& alloc); + + /** Move Assignment - /** Move assignment + The container is assigned with the contents of `other` + using move semantics. The maximum size will be the same + as the moved-from object. - After the move, `*this` will have an empty output sequence. + Buffer sequences previously obtained from `other` using + @ref data or @ref prepare remain valid after the move. @param other The object to move from. After the move, - The object's state will be as if constructed using - its current allocator and limit. + the moved-from object will have zero capacity, zero readable + bytes, and zero writable bytes. */ basic_multi_buffer& operator=(basic_multi_buffer&& other); - /** Copy assignment + /** Copy Assignment - After the copy, `*this` will have an empty output sequence. + The container is assigned with the contents of `other` + using copy semantics. The maximum size will be the same + as the copied object. + + After the copy, `this` will have zero writable bytes. @param other The object to copy from. + + @throws std::length_error if `other.size()` exceeds the + maximum allocation size of the allocator. */ - basic_multi_buffer& operator=(basic_multi_buffer const& other); + basic_multi_buffer& operator=( + basic_multi_buffer const& other); + + /** Copy Assignment - /** Copy assignment + The container is assigned with the contents of `other` + using copy semantics. The maximum size will be the same + as the copied object. - After the copy, `*this` will have an empty output sequence. + After the copy, `this` will have zero writable bytes. @param other The object to copy from. + + @throws std::length_error if `other.size()` exceeds the + maximum allocation size of the allocator. */ template<class OtherAlloc> basic_multi_buffer& operator=( basic_multi_buffer<OtherAlloc> const& other); - /// Returns a copy of the associated allocator. + /// Returns a copy of the allocator used. allocator_type get_allocator() const { return this->get(); } - /// Returns the size of the input sequence. + /** Set the maximum allowed capacity + + This function changes the currently configured upper limit + on capacity to the specified value. + + @param n The maximum number of bytes ever allowed for capacity. + + @esafe + + No-throw guarantee. + */ + void + max_size(std::size_t n) noexcept + { + max_ = n; + } + + /** Guarantee a minimum capacity + + This function adjusts the internal storage (if necessary) + to guarantee space for at least `n` bytes. + + Buffer sequences previously obtained using @ref data remain + valid, while buffer sequences previously obtained using + @ref prepare become invalid. + + @param n The minimum number of byte for the new capacity. + If this value is greater than the maximum size, then the + maximum size will be adjusted upwards to this value. + + @throws std::length_error if n is larger than the maximum + allocation size of the allocator. + + @esafe + + Strong guarantee. + */ + void + reserve(std::size_t n); + + /** Reallocate the buffer to fit the readable bytes exactly. + + Buffer sequences previously obtained using @ref data or + @ref prepare become invalid. + + @esafe + + Strong guarantee. + */ + void + shrink_to_fit(); + + /** Set the size of the readable and writable bytes to zero. + + This clears the buffer without changing capacity. + Buffer sequences previously obtained using @ref data or + @ref prepare become invalid. + + @esafe + + No-throw guarantee. + */ + void + clear() noexcept; + + /// Exchange two dynamic buffers + template<class Alloc> + friend + void + swap( + basic_multi_buffer<Alloc>& lhs, + basic_multi_buffer<Alloc>& rhs) noexcept; + + //-------------------------------------------------------------------------- + +#if BOOST_BEAST_DOXYGEN + /// The ConstBufferSequence used to represent the readable bytes. + using const_buffers_type = __implementation_defined__; + + /// The MutableBufferSequence used to represent the readable bytes. + using mutable_data_type = __implementation_defined__; + + /// The MutableBufferSequence used to represent the writable bytes. + using mutable_buffers_type = __implementation_defined__; +#else + using const_buffers_type = readable_bytes<false>; + using mutable_data_type = readable_bytes<true>; + class mutable_buffers_type; +#endif + + /// Returns the number of readable bytes. size_type - size() const + size() const noexcept { return in_size_; } - /// Returns the permitted maximum sum of the sizes of the input and output sequence. + /// Return the maximum number of bytes, both readable and writable, that can ever be held. size_type - max_size() const + max_size() const noexcept { return max_; } - /// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation. + /// Return the maximum number of bytes, both readable and writable, that can be held without requiring an allocation. std::size_t - capacity() const; + capacity() const noexcept; - /** Get a list of buffers that represents the input sequence. + /** Returns a constant buffer sequence representing the readable bytes - @note These buffers remain valid across subsequent calls to `prepare`. + @note The sequence may contain multiple contiguous memory regions. */ const_buffers_type - data() const; + data() const noexcept; - /** Get a list of buffers that represents the output sequence, with the given size. + /** Returns a constant buffer sequence representing the readable bytes - @note Buffers representing the input sequence acquired prior to - this call remain valid. + @note The sequence may contain multiple contiguous memory regions. */ - mutable_buffers_type - prepare(size_type n); + const_buffers_type + cdata() const noexcept + { + return data(); + } - /** Move bytes from the output sequence to the input sequence. + /** Returns a mutable buffer sequence representing the readable bytes. - @note Buffers representing the input sequence acquired prior to - this call remain valid. + @note The sequence may contain multiple contiguous memory regions. */ - void - commit(size_type n); + mutable_data_type + data() noexcept; - /// Remove bytes from the input sequence. - void - consume(size_type n); + /** Returns a mutable buffer sequence representing writable bytes. + + Returns a mutable buffer sequence representing the writable + bytes containing exactly `n` bytes of storage. Memory may be + reallocated as needed. - template<class Alloc> - friend - void - swap( - basic_multi_buffer<Alloc>& lhs, - basic_multi_buffer<Alloc>& rhs); + All buffer sequences previously obtained using @ref prepare are + invalidated. Buffer sequences previously obtained using @ref data + remain valid. -private: - template<class OtherAlloc> - friend class basic_multi_buffer; + @param n The desired number of bytes in the returned buffer + sequence. - void - delete_list(); + @throws std::length_error if `size() + n` exceeds `max_size()`. - void - reset(); + @esafe - template<class DynamicBuffer> - void - copy_from(DynamicBuffer const& other); + Strong guarantee. + */ + mutable_buffers_type + prepare(size_type n); - void - move_assign(basic_multi_buffer& other, std::false_type); + /** Append writable bytes to the readable bytes. - void - move_assign(basic_multi_buffer& other, std::true_type); + Appends n bytes from the start of the writable bytes to the + end of the readable bytes. The remainder of the writable bytes + are discarded. If n is greater than the number of writable + bytes, all writable bytes are appended to the readable bytes. - void - copy_assign(basic_multi_buffer const& other, std::false_type); + All buffer sequences previously obtained using @ref prepare are + invalidated. Buffer sequences previously obtained using @ref data + remain valid. - void - copy_assign(basic_multi_buffer const& other, std::true_type); + @param n The number of bytes to append. If this number + is greater than the number of writable bytes, all + writable bytes are appended. - void - swap(basic_multi_buffer&); + @esafe + No-throw guarantee. + */ void - swap(basic_multi_buffer&, std::true_type); + commit(size_type n) noexcept; - void - swap(basic_multi_buffer&, std::false_type); + /** Remove bytes from beginning of the readable bytes. + + Removes n bytes from the beginning of the readable bytes. + + All buffers sequences previously obtained using + @ref data or @ref prepare are invalidated. + + @param n The number of bytes to remove. If this number + is greater than the number of readable bytes, all + readable bytes are removed. + + @esafe + No-throw guarantee. + */ void - debug_check() const; + consume(size_type n) noexcept; + +private: + template<class OtherAlloc> + friend class basic_multi_buffer; + + template<class OtherAlloc> + void copy_from(basic_multi_buffer<OtherAlloc> const&); + void move_assign(basic_multi_buffer& other, std::false_type); + void move_assign(basic_multi_buffer& other, std::true_type) noexcept; + void copy_assign(basic_multi_buffer const& other, std::false_type); + void copy_assign(basic_multi_buffer const& other, std::true_type); + void swap(basic_multi_buffer&) noexcept; + void swap(basic_multi_buffer&, std::true_type) noexcept; + void swap(basic_multi_buffer&, std::false_type) noexcept; + void destroy(list_type& list) noexcept; + void destroy(const_iter it); + void destroy(element& e); + element& alloc(std::size_t size); + void debug_check() const; }; /// A typical multi buffer @@ -317,6 +550,6 @@ using multi_buffer = basic_multi_buffer<std::allocator<char>>; } // beast } // boost -#include <boost/beast/core/impl/multi_buffer.ipp> +#include <boost/beast/core/impl/multi_buffer.hpp> -#endif +#endif
\ No newline at end of file diff --git a/boost/beast/core/ostream.hpp b/boost/beast/core/ostream.hpp index 1f00289bd1..46899b6041 100644 --- a/boost/beast/core/ostream.hpp +++ b/boost/beast/core/ostream.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -11,54 +11,21 @@ #define BOOST_BEAST_WRITE_OSTREAM_HPP #include <boost/beast/core/detail/config.hpp> -#include <boost/beast/core/type_traits.hpp> #include <boost/beast/core/detail/ostream.hpp> #include <type_traits> #include <streambuf> #include <utility> +#ifdef BOOST_BEAST_ALLOW_DEPRECATED +#include <boost/beast/core/make_printable.hpp> +#endif + namespace boost { namespace beast { -/** Return an object representing a @b ConstBufferSequence. - - This function wraps a reference to a buffer sequence and permits - the following operation: - - @li `operator<<` to `std::ostream`. No character translation is - performed; unprintable and null characters will be transferred - as-is to the output stream. +/** Return an output stream that formats values into a <em>DynamicBuffer</em>. - @par Example - @code - multi_buffer b; - ... - std::cout << buffers(b.data()) << std::endl; - @endcode - - @param b An object meeting the requirements of @b ConstBufferSequence - to be streamed. The implementation will make a copy of this object. - Ownership of the underlying memory is not transferred, the application - is still responsible for managing its lifetime. -*/ -template<class ConstBufferSequence> -#if BOOST_BEAST_DOXYGEN -implementation_defined -#else -detail::buffers_helper<ConstBufferSequence> -#endif -buffers(ConstBufferSequence const& b) -{ - static_assert(boost::asio::is_const_buffer_sequence< - ConstBufferSequence>::value, - "ConstBufferSequence requirements not met"); - return detail::buffers_helper< - ConstBufferSequence>{b}; -} - -/** Return an output stream that formats values into a @b DynamicBuffer. - - This function wraps the caller provided @b DynamicBuffer into + This function wraps the caller provided <em>DynamicBuffer</em> into a `std::ostream` derived class, to allow `operator<<` stream style formatting operations. @@ -70,7 +37,7 @@ buffers(ConstBufferSequence const& b) @note Calling members of the underlying buffer before the output stream is destroyed results in undefined behavior. - @param buffer An object meeting the requirements of @b DynamicBuffer + @param buffer An object meeting the requirements of <em>DynamicBuffer</em> into which the formatted output will be placed. @return An object derived from `std::ostream` which redirects output @@ -82,7 +49,7 @@ buffers(ConstBufferSequence const& b) */ template<class DynamicBuffer> #if BOOST_BEAST_DOXYGEN -implementation_defined +__implementation_defined__ #else detail::ostream_helper< DynamicBuffer, char, std::char_traits<char>, @@ -91,13 +58,32 @@ detail::ostream_helper< ostream(DynamicBuffer& buffer) { static_assert( - boost::asio::is_dynamic_buffer<DynamicBuffer>::value, - "DynamicBuffer requirements not met"); + net::is_dynamic_buffer<DynamicBuffer>::value, + "DynamicBuffer type requirements not met"); return detail::ostream_helper< DynamicBuffer, char, std::char_traits<char>, detail::basic_streambuf_movable::value>{buffer}; } +//------------------------------------------------------------------------------ + +#ifdef BOOST_BEAST_ALLOW_DEPRECATED +template<class T> +detail::make_printable_adaptor<T> +buffers(T const& t) +{ + return make_printable(t); +} +#else +template<class T> +void buffers(T const&) +{ + static_assert(sizeof(T) == 0, + "The function buffers() is deprecated, use make_printable() instead, " + "or define BOOST_BEAST_ALLOW_DEPRECATED to silence this error."); +} +#endif + } // beast } // boost diff --git a/boost/beast/core/rate_policy.hpp b/boost/beast/core/rate_policy.hpp new file mode 100644 index 0000000000..1f8abb9b6a --- /dev/null +++ b/boost/beast/core/rate_policy.hpp @@ -0,0 +1,220 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_RATE_POLICY_HPP +#define BOOST_BEAST_CORE_RATE_POLICY_HPP + +#include <boost/beast/core/detail/config.hpp> +#include <cstdint> +#include <limits> + +namespace boost { +namespace beast { + +/** Helper class to assist implementing a <em>RatePolicy</em>. + + This class is used by the implementation to gain access to the + private members of a user-defined object meeting the requirements + of <em>RatePolicy</em>. To use it, simply declare it as a friend + in your class: + + @par Example + @code + class custom_rate_policy + { + friend class beast::rate_policy_access; + ... + @endcode + + @par Concepts + + @li <em>RatePolicy</em> + + @see beast::basic_stream +*/ +class rate_policy_access +{ +private: + template<class, class, class> + friend class basic_stream; + + template<class Policy> + static + std::size_t + available_read_bytes(Policy& policy) + { + return policy.available_read_bytes(); + } + + template<class Policy> + static + std::size_t + available_write_bytes(Policy& policy) + { + return policy.available_write_bytes(); + } + + template<class Policy> + static + void + transfer_read_bytes( + Policy& policy, std::size_t n) + { + return policy.transfer_read_bytes(n); + } + + template<class Policy> + static + void + transfer_write_bytes( + Policy& policy, std::size_t n) + { + return policy.transfer_write_bytes(n); + } + + template<class Policy> + static + void + on_timer(Policy& policy) + { + return policy.on_timer(); + } +}; + +//------------------------------------------------------------------------------ + +/** A rate policy with unlimited throughput. + + This rate policy object does not apply any rate limit. + + @par Concepts + + @li <em>RatePolicy</em> + + @see beast::basic_stream, beast::tcp_stream +*/ +class unlimited_rate_policy +{ + friend class rate_policy_access; + + static std::size_t constexpr all = + (std::numeric_limits<std::size_t>::max)(); + + std::size_t + available_read_bytes() const noexcept + { + return all; + } + + std::size_t + available_write_bytes() const noexcept + { + return all; + } + + void + transfer_read_bytes(std::size_t) const noexcept + { + } + + void + transfer_write_bytes(std::size_t) const noexcept + { + } + + void + on_timer() const noexcept + { + } +}; + +//------------------------------------------------------------------------------ + +/** A rate policy with simple, configurable limits on reads and writes. + + This rate policy allows for simple individual limits on the amount + of bytes per second allowed for reads and writes. + + @par Concepts + + @li <em>RatePolicy</em> + + @see beast::basic_stream +*/ +class simple_rate_policy +{ + friend class rate_policy_access; + + static std::size_t constexpr all = + std::numeric_limits<std::size_t>::max(); + + std::size_t rd_remain_ = all; + std::size_t wr_remain_ = all; + std::size_t rd_limit_ = all; + std::size_t wr_limit_ = all; + + std::size_t + available_read_bytes() const noexcept + { + return rd_remain_; + } + + std::size_t + available_write_bytes() const noexcept + { + return wr_remain_; + } + + void + transfer_read_bytes(std::size_t n) noexcept + { + if( rd_remain_ != all) + rd_remain_ = + (n < rd_remain_) ? rd_remain_ - n : 0; + } + + void + transfer_write_bytes(std::size_t n) noexcept + { + if( wr_remain_ != all) + wr_remain_ = + (n < wr_remain_) ? wr_remain_ - n : 0; + } + + void + on_timer() noexcept + { + rd_remain_ = rd_limit_; + wr_remain_ = wr_limit_; + } + +public: + /// Set the limit of bytes per second to read + void + read_limit(std::size_t bytes_per_second) noexcept + { + rd_limit_ = bytes_per_second; + if( rd_remain_ > bytes_per_second) + rd_remain_ = bytes_per_second; + } + + /// Set the limit of bytes per second to write + void + write_limit(std::size_t bytes_per_second) noexcept + { + wr_limit_ = bytes_per_second; + if( wr_remain_ > bytes_per_second) + wr_remain_ = bytes_per_second; + } +}; + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/read_size.hpp b/boost/beast/core/read_size.hpp index d9aaaa61ab..7d33d38f0b 100644 --- a/boost/beast/core/read_size.hpp +++ b/boost/beast/core/read_size.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -11,7 +11,6 @@ #define BOOST_BEAST_READ_SIZE_HELPER_HPP #include <boost/beast/core/detail/config.hpp> -#include <boost/beast/core/type_traits.hpp> #include <boost/throw_exception.hpp> namespace boost { @@ -59,6 +58,6 @@ read_size_or_throw(DynamicBuffer& buffer, } // beast } // boost -#include <boost/beast/core/impl/read_size.ipp> +#include <boost/beast/core/impl/read_size.hpp> #endif diff --git a/boost/beast/core/role.hpp b/boost/beast/core/role.hpp new file mode 100644 index 0000000000..2301417244 --- /dev/null +++ b/boost/beast/core/role.hpp @@ -0,0 +1,50 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_ROLE_HPP +#define BOOST_BEAST_ROLE_HPP + +#include <boost/beast/core/detail/config.hpp> + +namespace boost { +namespace beast { + +/** The role of local or remote peer. + + Whether the endpoint is a client or server affects the + behavior of teardown. + The teardown behavior also depends on the type of the stream + being torn down. + + The default implementation of teardown for regular + TCP/IP sockets is as follows: + + @li In the client role, a TCP/IP shutdown is sent after + reading all remaining data on the connection. + + @li In the server role, a TCP/IP shutdown is sent before + reading all remaining data on the connection. + + When the next layer type is a `net::ssl::stream`, + the connection is closed by performing the SSL closing + handshake corresponding to the role type, client or server. +*/ +enum class role_type +{ + /// The stream is operating as a client. + client, + + /// The stream is operating as a server. + server +}; + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/saved_handler.hpp b/boost/beast/core/saved_handler.hpp new file mode 100644 index 0000000000..a4599d429f --- /dev/null +++ b/boost/beast/core/saved_handler.hpp @@ -0,0 +1,137 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_SAVED_HANDLER_HPP +#define BOOST_BEAST_CORE_SAVED_HANDLER_HPP + +#include <boost/beast/core/detail/config.hpp> + +namespace boost { +namespace beast { + +/** An invocable, nullary function object which holds a completion handler. + + This container can hold a type-erased instance of any completion + handler, or it can be empty. When the container holds a value, + the implementation maintains an instance of `net::executor_work_guard` + for the handler's associated executor. Memory is dynamically allocated + to store the completion handler, and the allocator may optionally + be specified. Otherwise, the implementation uses the handler's + associated allocator. +*/ +class saved_handler +{ + class base; + + template<class, class> + class impl; + + base* p_ = nullptr; + +public: + /// Default Constructor + saved_handler() = default; + + /// Copy Constructor (deleted) + saved_handler(saved_handler const&) = delete; + + /// Copy Assignment (deleted) + saved_handler& operator=(saved_handler const&) = delete; + + /// Destructor + BOOST_BEAST_DECL + ~saved_handler(); + + /// Move Constructor + BOOST_BEAST_DECL + saved_handler(saved_handler&& other) noexcept; + + /// Move Assignment + BOOST_BEAST_DECL + saved_handler& + operator=(saved_handler&& other) noexcept; + + /// Returns `true` if `*this` contains a completion handler. + bool + has_value() const noexcept + { + return p_ != nullptr; + } + + /** Store a completion handler in the container. + + Requires `this->has_value() == false`. + + @param handler The completion handler to store. + The implementation takes ownership of the handler by performing a decay-copy. + + @param alloc The allocator to use. + */ + template<class Handler, class Allocator> + void + emplace(Handler&& handler, Allocator const& alloc); + + /** Store a completion handler in the container. + + Requires `this->has_value() == false`. The + implementation will use the handler's associated + allocator to obtian storage. + + @param handler The completion handler to store. + The implementation takes ownership of the handler by performing a decay-copy. + */ + template<class Handler> + void + emplace(Handler&& handler); + + /** Discard the saved handler, if one exists. + + If `*this` contains an object, it is destroyed. + + @returns `true` if an object was destroyed. + */ + BOOST_BEAST_DECL + bool + reset() noexcept; + + /** Unconditionally invoke the stored completion handler. + + Requires `this->has_value() == true`. Any dynamic memory + used is deallocated before the stored completion handler + is invoked. The executor work guard is also reset before + the invocation. + */ + BOOST_BEAST_DECL + void + invoke(); + + /** Conditionally invoke the stored completion handler. + + Invokes the stored completion handler if + `this->has_value() == true`, otherwise does nothing. Any + dynamic memory used is deallocated before the stored completion + handler is invoked. The executor work guard is also reset before + the invocation. + + @return `true` if the invocation took place. + */ + BOOST_BEAST_DECL + bool + maybe_invoke(); +}; + +} // beast +} // boost + +#include <boost/beast/core/impl/saved_handler.hpp> +#ifdef BOOST_BEAST_HEADER_ONLY +#include <boost/beast/core/impl/saved_handler.ipp> +#endif + +#endif diff --git a/boost/beast/core/span.hpp b/boost/beast/core/span.hpp index 4aa7788501..e170729472 100644 --- a/boost/beast/core/span.hpp +++ b/boost/beast/core/span.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) diff --git a/boost/beast/core/static_buffer.hpp b/boost/beast/core/static_buffer.hpp index fa59638bb7..1e44237164 100644 --- a/boost/beast/core/static_buffer.hpp +++ b/boost/beast/core/static_buffer.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -11,7 +11,9 @@ #define BOOST_BEAST_STATIC_BUFFER_HPP #include <boost/beast/core/detail/config.hpp> +#include <boost/beast/core/detail/buffers_pair.hpp> #include <boost/asio/buffer.hpp> +#include <boost/assert.hpp> #include <algorithm> #include <array> #include <cstddef> @@ -20,23 +22,36 @@ namespace boost { namespace beast { -/** A circular @b DynamicBuffer with a fixed size internal buffer. +/** A dynamic buffer providing a fixed size, circular buffer. - This implements a circular dynamic buffer. Calls to @ref prepare - never require moving memory. The buffer sequences returned may - be up to length two. - Ownership of the underlying storage belongs to the derived class. + A dynamic buffer encapsulates memory storage that may be + automatically resized as required, where the memory is + divided into two regions: readable bytes followed by + writable bytes. These memory regions are internal to + the dynamic buffer, but direct access to the elements + is provided to permit them to be efficiently used with + I/O operations. - @note Variables are usually declared using the template class - @ref static_buffer; 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 - @ref static_buffer_base. + Objects of this type meet the requirements of <em>DynamicBuffer</em> + and have the following additional properties: + + @li A mutable buffer sequence representing the readable + bytes is returned by @ref data when `this` is non-const. + + @li Buffer sequences representing the readable and writable + bytes, returned by @ref data and @ref prepare, may have + length up to two. - When used with @ref static_buffer this implements a dynamic - buffer using no memory allocations. + @li All operations execute in constant time. + + @li Ownership of the underlying storage belongs to the + derived class. + + @note Variables are usually declared using the template class + @ref static_buffer; however, to reduce the number of template + instantiations, objects should be passed `static_buffer_base&`. - @see @ref static_buffer + @see static_buffer */ class static_buffer_base { @@ -50,14 +65,6 @@ class static_buffer_base static_buffer_base& operator=(static_buffer_base const&) = delete; public: - /// The type used to represent the input sequence as a list of buffers. - using const_buffers_type = - std::array<boost::asio::const_buffer, 2>; - - /// The type used to represent the output sequence as a list of buffers. - using mutable_buffers_type = - std::array<boost::asio::mutable_buffer, 2>; - /** Constructor This creates a dynamic buffer using the provided storage area. @@ -66,98 +73,167 @@ public: @param size The number of valid bytes pointed to by `p`. */ - static_buffer_base(void* p, std::size_t size); + BOOST_BEAST_DECL + static_buffer_base(void* p, std::size_t size) noexcept; + + /** Clear the readable and writable bytes to zero. + + This function causes the readable and writable bytes + to become empty. The capacity is not changed. + + Buffer sequences previously obtained using @ref data or + @ref prepare become invalid. + + @esafe + + No-throw guarantee. + */ + BOOST_BEAST_DECL + void + clear() noexcept; + + //-------------------------------------------------------------------------- + +#if BOOST_BEAST_DOXYGEN + /// The ConstBufferSequence used to represent the readable bytes. + using const_buffers_type = __implementation_defined__; + + /// The MutableBufferSequence used to represent the readable bytes. + using mutable_data_type = __implementation_defined__; - /// Return the size of the input sequence. + /// The MutableBufferSequence used to represent the writable bytes. + using mutable_buffers_type = __implementation_defined__; +#else + using const_buffers_type = detail::buffers_pair<false>; + using mutable_data_type = detail::buffers_pair<true>; + using mutable_buffers_type = detail::buffers_pair<true>; +#endif + + /// Returns the number of readable bytes. std::size_t - size() const + size() const noexcept { return in_size_; } - /// Return the maximum sum of the input and output sequence sizes. + /// Return the maximum number of bytes, both readable and writable, that can ever be held. std::size_t - max_size() const + max_size() const noexcept { return capacity_; } - /// Return the maximum sum of input and output sizes that can be held without an allocation. + /// Return the maximum number of bytes, both readable and writable, that can be held without requiring an allocation. std::size_t - capacity() const + capacity() const noexcept { return capacity_; } - /** Get a list of buffers that represent the input sequence. - */ + /// Returns a constant buffer sequence representing the readable bytes + BOOST_BEAST_DECL + const_buffers_type + data() const noexcept; + + /// Returns a constant buffer sequence representing the readable bytes const_buffers_type - data() const; + cdata() const noexcept + { + return data(); + } - /** Get a mutable list of buffers that represent the input sequence. - */ - mutable_buffers_type - mutable_data(); + /// Returns a mutable buffer sequence representing the readable bytes + BOOST_BEAST_DECL + mutable_data_type + data() noexcept; + + /** Returns a mutable buffer sequence representing writable bytes. + + Returns a mutable buffer sequence representing the writable + bytes containing exactly `n` bytes of storage. Memory may be + reallocated as needed. + + All buffers sequences previously obtained using + @ref data or @ref prepare are invalidated. - /** Get a list of buffers that represent the output sequence, with the given size. + @param n The desired number of bytes in the returned buffer + sequence. - @param size The number of bytes to request. + @throws std::length_error if `size() + n` exceeds `max_size()`. - @throws std::length_error if the size would exceed the capacity. + @esafe + + Strong guarantee. */ + BOOST_BEAST_DECL mutable_buffers_type - prepare(std::size_t size); + prepare(std::size_t n); - /** Move bytes from the output sequence to the input sequence. + /** Append writable bytes to the readable bytes. - @param size The number of bytes to commit. If this is greater - than the size of the output sequence, the entire output - sequence is committed. - */ - void - commit(std::size_t size); + Appends n bytes from the start of the writable bytes to the + end of the readable bytes. The remainder of the writable bytes + are discarded. If n is greater than the number of writable + bytes, all writable bytes are appended to the readable bytes. - /** Remove bytes from the input sequence. + All buffers sequences previously obtained using + @ref data or @ref prepare are invalidated. - @param size The number of bytes to consume. If this is greater - than the size of the input sequence, the entire input sequence - is consumed. + @param n The number of bytes to append. If this number + is greater than the number of writable bytes, all + writable bytes are appended. + + @esafe + + No-throw guarantee. */ + BOOST_BEAST_DECL void - consume(std::size_t size); + commit(std::size_t n) noexcept; -protected: - /** Constructor + /** Remove bytes from beginning of the readable bytes. - The buffer will be in an undefined state. It is necessary - for the derived class to call @ref reset in order to - initialize the object. - */ - static_buffer_base() = default; + Removes n bytes from the beginning of the readable bytes. - /** Reset the pointed-to buffer. + All buffers sequences previously obtained using + @ref data or @ref prepare are invalidated. - This function resets the internal state to the buffer provided. - All input and output sequences are invalidated. This function - allows the derived class to construct its members before - initializing the static buffer. + @param n The number of bytes to remove. If this number + is greater than the number of readable bytes, all + readable bytes are removed. - @param p A pointer to valid storage of at least `n` bytes. + @esafe - @param size The number of valid bytes pointed to by `p`. + No-throw guarantee. */ + BOOST_BEAST_DECL void - reset(void* p, std::size_t size); + consume(std::size_t n) noexcept; }; //------------------------------------------------------------------------------ -/** A circular @b DynamicBuffer with a fixed size internal buffer. +/** A dynamic buffer providing a fixed size, circular buffer. + + A dynamic buffer encapsulates memory storage that may be + automatically resized as required, where the memory is + divided into two regions: readable bytes followed by + writable bytes. These memory regions are internal to + the dynamic buffer, but direct access to the elements + is provided to permit them to be efficiently used with + I/O operations. + + Objects of this type meet the requirements of <em>DynamicBuffer</em> + and have the following additional properties: + + @li A mutable buffer sequence representing the readable + bytes is returned by @ref data when `this` is non-const. - This implements a circular dynamic buffer. Calls to @ref prepare - never require moving memory. The buffer sequences returned may - be up to length two. - Ownership of the underlying storage belongs to the derived class. + @li Buffer sequences representing the readable and writable + bytes, returned by @ref data and @ref prepare, may have + length up to two. + + @li All operations execute in constant time. @tparam N The number of bytes in the internal buffer. @@ -165,7 +241,7 @@ protected: objects of this type in a deduced context, the signature of the receiving function should use @ref static_buffer_base instead. - @see @ref static_buffer_base + @see static_buffer_base */ template<std::size_t N> class static_buffer : public static_buffer_base @@ -174,41 +250,41 @@ class static_buffer : public static_buffer_base public: /// Constructor - static_buffer(static_buffer const&); - - /// Constructor - static_buffer() + static_buffer() noexcept : static_buffer_base(buf_, N) { } + /// Constructor + static_buffer(static_buffer const&) noexcept; + /// Assignment - static_buffer& operator=(static_buffer const&); + static_buffer& operator=(static_buffer const&) noexcept; /// Returns the @ref static_buffer_base portion of this object static_buffer_base& - base() + base() noexcept { return *this; } /// Returns the @ref static_buffer_base portion of this object static_buffer_base const& - base() const + base() const noexcept { return *this; } /// Return the maximum sum of the input and output sequence sizes. std::size_t constexpr - max_size() const + max_size() const noexcept { return N; } /// Return the maximum sum of input and output sizes that can be held without an allocation. std::size_t constexpr - capacity() const + capacity() const noexcept { return N; } @@ -217,6 +293,9 @@ public: } // beast } // boost +#include <boost/beast/core/impl/static_buffer.hpp> +#ifdef BOOST_BEAST_HEADER_ONLY #include <boost/beast/core/impl/static_buffer.ipp> +#endif #endif diff --git a/boost/beast/core/static_string.hpp b/boost/beast/core/static_string.hpp index 991c2e64a5..fc60a72e7d 100644 --- a/boost/beast/core/static_string.hpp +++ b/boost/beast/core/static_string.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -35,7 +35,7 @@ namespace beast { @note The stored string is always null-terminated. - @see @ref to_static_string + @see to_static_string */ template< std::size_t N, @@ -171,10 +171,7 @@ public: /// Assign from null-terminated string. static_string& - operator=(CharT const* s) - { - return assign(s); - } + operator=(CharT const* s); /// Assign from single character. static_string& @@ -1100,13 +1097,19 @@ operator<<(std::basic_ostream<CharT, Traits>& os, maximum size large enough to hold the longest possible decimal representation of any integer of the given type. */ -template<class Integer> +template< + class Integer +#ifndef BOOST_BEAST_DOXYGEN + ,class = typename std::enable_if< + std::is_integral<Integer>::value>::type +#endif +> static_string<detail::max_digits(sizeof(Integer))> to_static_string(Integer x); } // beast } // boost -#include <boost/beast/core/impl/static_string.ipp> +#include <boost/beast/core/impl/static_string.hpp> #endif diff --git a/boost/beast/core/stream_traits.hpp b/boost/beast/core/stream_traits.hpp new file mode 100644 index 0000000000..f1945ea93a --- /dev/null +++ b/boost/beast/core/stream_traits.hpp @@ -0,0 +1,544 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_STREAM_TRAITS_HPP +#define BOOST_BEAST_STREAM_TRAITS_HPP + +#include <boost/beast/core/detail/config.hpp> +#include <boost/beast/core/detail/static_const.hpp> +#include <boost/beast/core/detail/stream_traits.hpp> +#include <boost/asio/basic_socket.hpp> + +namespace boost { +namespace beast { + +/** A trait to determine the lowest layer type of a stack of stream layers. + + If `t.next_layer()` is well-defined for an object `t` of type `T`, + then `lowest_layer_type<T>` will be an alias for + `lowest_layer_type<decltype(t.next_layer())>`, + otherwise it will be the type + `std::remove_reference<T>`. + + @param T The type to determine the lowest layer type of. + + @return The type of the lowest layer. +*/ +template<class T> +#if BOOST_BEAST_DOXYGEN +using lowest_layer_type = __see_below__; +#else +using lowest_layer_type = detail::lowest_layer_type<T>; +#endif + +/** Return the lowest layer in a stack of stream layers. + + If `t.next_layer()` is well-defined, returns + `get_lowest_layer(t.next_layer())`. Otherwise, it returns `t`. + + A stream layer is an object of class type which wraps another object through + composition, and meets some or all of the named requirements of the wrapped + type while optionally changing behavior. Examples of stream layers include + `net::ssl::stream` or @ref beast::websocket::stream. The owner of a stream + layer can interact directly with the wrapper, by passing it to stream + algorithms. Or, the owner can obtain a reference to the wrapped object by + calling `next_layer()` and accessing its members. This is necessary when it is + desired to access functionality in the next layer which is not available + in the wrapper. For example, @ref websocket::stream permits reading and + writing, but in order to establish the underlying connection, members + of the wrapped stream (such as `connect`) must be invoked directly. + + Usually the last object in the chain of composition is the concrete socket + object (for example, a `net::basic_socket` or a class derived from it). + The function @ref get_lowest_layer exists to easily obtain the concrete + socket when it is desired to perform an action that is not prescribed by + a named requirement, such as changing a socket option, cancelling all + pending asynchronous I/O, or closing the socket (perhaps by using + @ref close_socket). + + @par Example + @code + // Set non-blocking mode on a stack of stream + // layers with a regular socket at the lowest layer. + template <class Stream> + void set_non_blocking (Stream& stream) + { + error_code ec; + // A compile error here means your lowest layer is not the right type! + get_lowest_layer(stream).non_blocking(true, ec); + if(ec) + throw system_error{ec}; + } + @endcode + + @param t The layer in a stack of layered objects for which the lowest layer is returned. + + @see close_socket, lowest_layer_type +*/ +template<class T> +lowest_layer_type<T>& +get_lowest_layer(T& t) noexcept +{ + return detail::get_lowest_layer_impl( + t, detail::has_next_layer<T>{}); +} + +//------------------------------------------------------------------------------ + +/** A trait to determine the return type of get_executor. + + This type alias will be the type of values returned by + by calling member `get_exector` on an object of type `T&`. + + @param T The type to query + + @return The type of values returned from `get_executor`. +*/ +// Workaround for ICE on gcc 4.8 +#if BOOST_BEAST_DOXYGEN +template<class T> +using executor_type = __see_below__; +#elif BOOST_WORKAROUND(BOOST_GCC, < 40900) +template<class T> +using executor_type = + typename std::decay<T>::type::executor_type; +#else +template<class T> +using executor_type = + decltype(std::declval<T&>().get_executor()); +#endif + +/** Determine if `T` has the `get_executor` member function. + + Metafunctions are used to perform compile time checking of template + types. This type will be `std::true_type` if `T` has the member + function with the correct signature, else type will be `std::false_type`. + + @par Example + + Use with tag dispatching: + + @code + template<class T> + void maybe_hello(T const& t, std::true_type) + { + net::post( + t.get_executor(), + [] + { + std::cout << "Hello, world!" << std::endl; + }); + } + + template<class T> + void maybe_hello(T const&, std::false_type) + { + // T does not have get_executor + } + + template<class T> + void maybe_hello(T const& t) + { + maybe_hello(t, has_get_executor<T>{}); + } + @endcode + + Use with `static_assert`: + + @code + struct stream + { + using executor_type = net::io_context::executor_type; + executor_type get_executor() noexcept; + }; + + static_assert(has_get_executor<stream>::value, "Missing get_executor member"); + @endcode +*/ +#if BOOST_BEAST_DOXYGEN +template<class T> +using has_get_executor = __see_below__; +#else +template<class T, class = void> +struct has_get_executor : std::false_type {}; + +template<class T> +struct has_get_executor<T, boost::void_t<decltype( + std::declval<T&>().get_executor())>> : std::true_type {}; +#endif + +//------------------------------------------------------------------------------ + +/** Determine if at type meets the requirements of <em>SyncReadStream</em>. + + Metafunctions are used to perform compile time checking of template + types. This type will be `std::true_type` if `T` meets the requirements, + else the type will be `std::false_type`. + + @par Example + Use with `static_assert`: + @code + template<class SyncReadStream> + void f(SyncReadStream& stream) + { + static_assert(is_sync_read_stream<SyncReadStream>::value, + "SyncReadStream type requirements not met"); + ... + @endcode + + Use with `std::enable_if` (SFINAE): + @code + template<class SyncReadStream> + typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type + f(SyncReadStream& stream); + @endcode +*/ +#if BOOST_BEAST_DOXYGEN +template<class T> +using is_sync_read_stream = __see_below__; +#else +template<class T, class = void> +struct is_sync_read_stream : std::false_type {}; + +template<class T> +struct is_sync_read_stream<T, boost::void_t<decltype( + std::declval<std::size_t&>() = std::declval<T&>().read_some( + std::declval<detail::MutableBufferSequence>()), + std::declval<std::size_t&>() = std::declval<T&>().read_some( + std::declval<detail::MutableBufferSequence>(), + std::declval<boost::system::error_code&>()) + )>> : std::true_type {}; +#endif + +/** Determine if `T` meets the requirements of <em>SyncWriteStream</em>. + + Metafunctions are used to perform compile time checking of template + types. This type will be `std::true_type` if `T` meets the requirements, + else the type will be `std::false_type`. + + @par Example + + Use with `static_assert`: + + @code + template<class SyncReadStream> + void f(SyncReadStream& stream) + { + static_assert(is_sync_read_stream<SyncReadStream>::value, + "SyncReadStream type requirements not met"); + ... + @endcode + + Use with `std::enable_if` (SFINAE): + + @code + template<class SyncReadStream> + typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type + f(SyncReadStream& stream); + @endcode +*/ +#if BOOST_BEAST_DOXYGEN +template<class T> +using is_sync_write_stream = __see_below__; +#else +template<class T, class = void> +struct is_sync_write_stream : std::false_type {}; + +template<class T> +struct is_sync_write_stream<T, boost::void_t<decltype( + ( + std::declval<std::size_t&>() = std::declval<T&>().write_some( + std::declval<detail::ConstBufferSequence>())) + ,std::declval<std::size_t&>() = std::declval<T&>().write_some( + std::declval<detail::ConstBufferSequence>(), + std::declval<boost::system::error_code&>()) + )>> : std::true_type {}; +#endif + +/** Determine if `T` meets the requirements of @b SyncStream. + + Metafunctions are used to perform compile time checking of template + types. This type will be `std::true_type` if `T` meets the requirements, + else the type will be `std::false_type`. + + @par Example + + Use with `static_assert`: + + @code + template<class SyncStream> + void f(SyncStream& stream) + { + static_assert(is_sync_stream<SyncStream>::value, + "SyncStream type requirements not met"); + ... + @endcode + + Use with `std::enable_if` (SFINAE): + + @code + template<class SyncStream> + typename std::enable_if<is_sync_stream<SyncStream>::value>::type + f(SyncStream& stream); + @endcode +*/ +#if BOOST_BEAST_DOXYGEN +template<class T> +using is_sync_stream = __see_below__; +#else +template<class T> +using is_sync_stream = std::integral_constant<bool, + is_sync_read_stream<T>::value && is_sync_write_stream<T>::value>; +#endif + +//------------------------------------------------------------------------------ + +/** Determine if `T` meets the requirements of <em>AsyncReadStream</em>. + + Metafunctions are used to perform compile time checking of template + types. This type will be `std::true_type` if `T` meets the requirements, + else the type will be `std::false_type`. + + @par Example + + Use with `static_assert`: + + @code + template<class AsyncReadStream> + void f(AsyncReadStream& stream) + { + static_assert(is_async_read_stream<AsyncReadStream>::value, + "AsyncReadStream type requirements not met"); + ... + @endcode + + Use with `std::enable_if` (SFINAE): + + @code + template<class AsyncReadStream> + typename std::enable_if<is_async_read_stream<AsyncReadStream>::value>::type + f(AsyncReadStream& stream); + @endcode +*/ +#if BOOST_BEAST_DOXYGEN +template<class T> +using is_async_read_stream = __see_below__; +#else +template<class T, class = void> +struct is_async_read_stream : std::false_type {}; + +template<class T> +struct is_async_read_stream<T, boost::void_t<decltype( + std::declval<T&>().async_read_some( + std::declval<detail::MutableBufferSequence>(), + std::declval<detail::ReadHandler>()) + )>> : std::integral_constant<bool, + has_get_executor<T>::value + > {}; +#endif + +/** Determine if `T` meets the requirements of <em>AsyncWriteStream</em>. + + Metafunctions are used to perform compile time checking of template + types. This type will be `std::true_type` if `T` meets the requirements, + else the type will be `std::false_type`. + + @par Example + + Use with `static_assert`: + + @code + template<class AsyncWriteStream> + void f(AsyncWriteStream& stream) + { + static_assert(is_async_write_stream<AsyncWriteStream>::value, + "AsyncWriteStream type requirements not met"); + ... + @endcode + + Use with `std::enable_if` (SFINAE): + + @code + template<class AsyncWriteStream> + typename std::enable_if<is_async_write_stream<AsyncWriteStream>::value>::type + f(AsyncWriteStream& stream); + @endcode +*/ +#if BOOST_BEAST_DOXYGEN +template<class T> +using is_async_write_stream = __see_below__; +#else +template<class T, class = void> +struct is_async_write_stream : std::false_type {}; + +template<class T> +struct is_async_write_stream<T, boost::void_t<decltype( + std::declval<T&>().async_write_some( + std::declval<detail::ConstBufferSequence>(), + std::declval<detail::WriteHandler>()) + )>> : std::integral_constant<bool, + has_get_executor<T>::value + > {}; +#endif + +/** Determine if `T` meets the requirements of @b AsyncStream. + + Metafunctions are used to perform compile time checking of template + types. This type will be `std::true_type` if `T` meets the requirements, + else the type will be `std::false_type`. + + @par Example + + Use with `static_assert`: + + @code + template<class AsyncStream> + void f(AsyncStream& stream) + { + static_assert(is_async_stream<AsyncStream>::value, + "AsyncStream type requirements not met"); + ... + @endcode + + Use with `std::enable_if` (SFINAE): + + @code + template<class AsyncStream> + typename std::enable_if<is_async_stream<AsyncStream>::value>::type + f(AsyncStream& stream); + @endcode +*/ +#if BOOST_BEAST_DOXYGEN +template<class T> +using is_async_stream = __see_below__; +#else +template<class T> +using is_async_stream = std::integral_constant<bool, + is_async_read_stream<T>::value && is_async_write_stream<T>::value>; +#endif + +//------------------------------------------------------------------------------ + +/** Default socket close function. + + This function is not meant to be called directly. Instead, it + is called automatically when using @ref close_socket. To enable + closure of user-defined types or classes derived from a particular + user-defined type, this function should be overloaded in the + corresponding namespace for the type in question. + + @see close_socket +*/ +template< + class Protocol, + class Executor> +void +beast_close_socket( + net::basic_socket< + Protocol, Executor>& sock) +{ + boost::system::error_code ec; + sock.close(ec); +} + +namespace detail { + +struct close_socket_impl +{ + template<class T> + void + operator()(T& t) const + { + using beast::beast_close_socket; + beast_close_socket(t); + } +}; + +} // detail + +/** Close a socket or socket-like object. + + This function attempts to close an object representing a socket. + In this context, a socket is an object for which an unqualified + call to the function `void beast_close_socket(Socket&)` is + well-defined. The function `beast_close_socket` is a + <em>customization point</em>, allowing user-defined types to + provide an algorithm for performing the close operation by + overloading this function for the type in question. + + Since the customization point is a function call, the normal + rules for finding the correct overload are applied including + the rules for argument-dependent lookup ("ADL"). This permits + classes derived from a type for which a customization is provided + to inherit the customization point. + + An overload for the networking class template `net::basic_socket` + is provided, which implements the close algorithm for all socket-like + objects (hence the name of this customization point). When used + in conjunction with @ref get_lowest_layer, a generic algorithm + operating on a layered stream can perform a closure of the underlying + socket without knowing the exact list of concrete types. + + @par Example 1 + The following generic function synchronously sends a message + on the stream, then closes the socket. + @code + template <class WriteStream> + void hello_and_close (WriteStream& stream) + { + net::write(stream, net::const_buffer("Hello, world!", 13)); + close_socket(get_lowest_layer(stream)); + } + @endcode + + To enable closure of user defined types, it is necessary to provide + an overload of the function `beast_close_socket` for the type. + + @par Example 2 + The following code declares a user-defined type which contains a + private socket, and provides an overload of the customization + point which closes the private socket. + @code + class my_socket + { + net::ip::tcp::socket sock_; + + public: + my_socket(net::io_context& ioc) + : sock_(ioc) + { + } + + friend void beast_close_socket(my_socket& s) + { + error_code ec; + s.sock_.close(ec); + // ignore the error + } + }; + @endcode + + @param sock The socket to close. If the customization point is not + defined for the type of this object, or one of its base classes, + then a compiler error results. + + @see beast_close_socket +*/ +#if BOOST_BEAST_DOXYGEN +template<class Socket> +void +close_socket(Socket& sock); +#else +BOOST_BEAST_INLINE_VARIABLE(close_socket, detail::close_socket_impl) +#endif + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/string.hpp b/boost/beast/core/string.hpp index de94d2e639..976bfca48a 100644 --- a/boost/beast/core/string.hpp +++ b/boost/beast/core/string.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -64,24 +64,24 @@ iequals( auto p1 = lhs.data(); auto p2 = rhs.data(); char a, b; + // fast loop while(n--) { a = *p1++; b = *p2++; if(a != b) - { - // slow loop - do - { - if(ascii_tolower(a) != ascii_tolower(b)) - return false; - a = *p1++; - b = *p2++; - } - while(n--); - return true; - } + goto slow; + } + return true; +slow: + do + { + if(ascii_tolower(a) != ascii_tolower(b)) + return false; + a = *p1++; + b = *p2++; } + while(n--); return true; } diff --git a/boost/beast/core/string_param.hpp b/boost/beast/core/string_param.hpp index a068cc4091..67ef69469c 100644 --- a/boost/beast/core/string_param.hpp +++ b/boost/beast/core/string_param.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -125,6 +125,6 @@ public: } // beast } // boost -#include <boost/beast/core/impl/string_param.ipp> +#include <boost/beast/core/impl/string_param.hpp> #endif diff --git a/boost/beast/core/tcp_stream.hpp b/boost/beast/core/tcp_stream.hpp new file mode 100644 index 0000000000..a96dd2aed1 --- /dev/null +++ b/boost/beast/core/tcp_stream.hpp @@ -0,0 +1,34 @@ +// +// Copyright (c) 2016-2019 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) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_CORE_TCP_STREAM_HPP +#define BOOST_BEAST_CORE_TCP_STREAM_HPP + +#include <boost/beast/core/detail/config.hpp> +#include <boost/beast/core/basic_stream.hpp> +#include <boost/beast/core/rate_policy.hpp> +#include <boost/asio/executor.hpp> +#include <boost/asio/ip/tcp.hpp> + +namespace boost { +namespace beast { + +/** A TCP/IP stream socket with timeouts and a polymorphic executor. + + @see basic_stream +*/ +using tcp_stream = basic_stream< + net::ip::tcp, + net::executor, + unlimited_rate_policy>; + +} // beast +} // boost + +#endif diff --git a/boost/beast/core/type_traits.hpp b/boost/beast/core/type_traits.hpp index d400e7f615..2f42301800 100644 --- a/boost/beast/core/type_traits.hpp +++ b/boost/beast/core/type_traits.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2016-2019 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) @@ -10,22 +10,19 @@ #ifndef BOOST_BEAST_TYPE_TRAITS_HPP #define BOOST_BEAST_TYPE_TRAITS_HPP +#ifndef BOOST_BEAST_DOXYGEN + #include <boost/beast/core/detail/config.hpp> -#include <boost/beast/core/file_base.hpp> -#include <boost/beast/core/detail/type_traits.hpp> -#include <boost/asio/buffer.hpp> -#include <type_traits> +#include <boost/beast/core/detail/is_invocable.hpp> +#include <boost/config/pragma_message.hpp> +#include <type_traits.hpp> + +BOOST_PRAGMA_MESSAGE("<boost/beast/core/type_traits.hpp> is DEPRECATED and will be removed in a future release.") namespace boost { namespace beast { -//------------------------------------------------------------------------------ -// -// Handler concepts -// -//------------------------------------------------------------------------------ - -/** Determine if `T` meets the requirements of @b CompletionHandler. +/** Determine if `T` meets the requirements of <em>CompletionHandler</em>. This trait checks whether a type meets the requirements for a completion handler, and is also callable with the specified signature. @@ -34,446 +31,28 @@ namespace beast { else the type will be `std::false_type`. @par Example - Use with `static_assert`: - @code struct handler { void operator()(error_code&); }; - static_assert(is_completion_handler<handler, void(error_code&)>::value, "Not a completion handler"); @endcode */ template<class T, class Signature> #if BOOST_BEAST_DOXYGEN -using is_completion_handler = std::integral_constant<bool, ...>; +using is_completion_handler = __see_below__ #else using is_completion_handler = std::integral_constant<bool, std::is_move_constructible<typename std::decay<T>::type>::value && detail::is_invocable<T, Signature>::value>; #endif -//------------------------------------------------------------------------------ -// -// Stream concepts -// -//------------------------------------------------------------------------------ - -/** Determine if `T` has the `get_executor` member function. - - Metafunctions are used to perform compile time checking of template - types. This type will be `std::true_type` if `T` has the member - function with the correct signature, else type will be `std::false_type`. - - @par Example - - Use with tag dispatching: - - @code - template<class T> - void maybe_hello(T& t, std::true_type) - { - boost::asio::post( - t.get_executor(), - [] - { - std::cout << "Hello, world!" << std::endl; - }); - } - - template<class T> - void maybe_hello(T&, std::false_type) - { - // T does not have get_executor - } - - template<class T> - void maybe_hello(T& t) - { - maybe_hello(t, has_get_executor<T>{}); - } - @endcode - - Use with `static_assert`: - - @code - struct stream - { - using executor_type = boost::asio::io_context::executor_type; - executor_type get_executor() noexcept; - }; - - static_assert(has_get_executor<stream>::value, "Missing get_executor member"); - @endcode -*/ -#if BOOST_BEAST_DOXYGEN -template<class T> -struct has_get_executor : std::integral_constant<bool, ...>{}; -#else -template<class T, class = void> -struct has_get_executor : std::false_type {}; - -template<class T> -struct has_get_executor<T, beast::detail::void_t<decltype( - std::declval<T&>().get_executor())>> : std::true_type {}; -#endif - -/** Alias for `T::lowest_layer_type` if it exists, else `T` - - This will be a type alias for `T::lowest_layer_type` - if it exists, else it will be an alias for `T`. - - @par Example - - Declaring a wrapper: - - @code - template<class Stream> - struct stream_wrapper - { - using next_layer_type = typename std::remove_reference<Stream>::type; - using lowest_layer_type = get_lowest_layer<stream_type>; - }; - @endcode - - Defining a metafunction: - - @code - /// Alias for `std::true_type` if `T` wraps another stream - template<class T> - using is_stream_wrapper : std::integral_constant<bool, - ! std::is_same<T, get_lowest_layer<T>>::value> {}; - @endcode -*/ -#if BOOST_BEAST_DOXYGEN -template<class T> -struct get_lowest_layer; -#else -template<class T> -using get_lowest_layer = typename detail::get_lowest_layer_helper<T>::type; -#endif - -/** Determine if `T` meets the requirements of @b AsyncReadStream. - - Metafunctions are used to perform compile time checking of template - types. This type will be `std::true_type` if `T` meets the requirements, - else the type will be `std::false_type`. - - @par Example - - Use with `static_assert`: - - @code - template<class AsyncReadStream> - void f(AsyncReadStream& stream) - { - static_assert(is_async_read_stream<AsyncReadStream>::value, - "AsyncReadStream requirements not met"); - ... - @endcode - - Use with `std::enable_if` (SFINAE): - - @code - template<class AsyncReadStream> - typename std::enable_if<is_async_read_stream<AsyncReadStream>::value>::type - f(AsyncReadStream& stream); - @endcode -*/ -#if BOOST_BEAST_DOXYGEN -template<class T> -struct is_async_read_stream : std::integral_constant<bool, ...>{}; -#else -template<class T, class = void> -struct is_async_read_stream : std::false_type {}; - -template<class T> -struct is_async_read_stream<T, detail::void_t<decltype( - std::declval<T>().async_read_some( - std::declval<detail::MutableBufferSequence>(), - std::declval<detail::ReadHandler>()) - )>> : std::integral_constant<bool, - has_get_executor<T>::value - > {}; -#endif - -/** Determine if `T` meets the requirements of @b AsyncWriteStream. - - Metafunctions are used to perform compile time checking of template - types. This type will be `std::true_type` if `T` meets the requirements, - else the type will be `std::false_type`. - - @par Example - - Use with `static_assert`: - - @code - template<class AsyncWriteStream> - void f(AsyncWriteStream& stream) - { - static_assert(is_async_write_stream<AsyncWriteStream>::value, - "AsyncWriteStream requirements not met"); - ... - @endcode - - Use with `std::enable_if` (SFINAE): - - @code - template<class AsyncWriteStream> - typename std::enable_if<is_async_write_stream<AsyncWriteStream>::value>::type - f(AsyncWriteStream& stream); - @endcode -*/ -#if BOOST_BEAST_DOXYGEN -template<class T> -struct is_async_write_stream : std::integral_constant<bool, ...>{}; -#else -template<class T, class = void> -struct is_async_write_stream : std::false_type {}; - -template<class T> -struct is_async_write_stream<T, detail::void_t<decltype( - std::declval<T>().async_write_some( - std::declval<detail::ConstBufferSequence>(), - std::declval<detail::WriteHandler>()) - )>> : std::integral_constant<bool, - has_get_executor<T>::value - > {}; -#endif - -/** Determine if `T` meets the requirements of @b SyncReadStream. - - Metafunctions are used to perform compile time checking of template - types. This type will be `std::true_type` if `T` meets the requirements, - else the type will be `std::false_type`. - - @par Example - - Use with `static_assert`: - - @code - template<class SyncReadStream> - void f(SyncReadStream& stream) - { - static_assert(is_sync_read_stream<SyncReadStream>::value, - "SyncReadStream requirements not met"); - ... - @endcode - - Use with `std::enable_if` (SFINAE): - - @code - template<class SyncReadStream> - typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type - f(SyncReadStream& stream); - @endcode -*/ -#if BOOST_BEAST_DOXYGEN -template<class T> -struct is_sync_read_stream : std::integral_constant<bool, ...>{}; -#else -template<class T, class = void> -struct is_sync_read_stream : std::false_type {}; - -template<class T> -struct is_sync_read_stream<T, detail::void_t<decltype( - std::declval<std::size_t&>() = std::declval<T>().read_some( - std::declval<detail::MutableBufferSequence>()), - std::declval<std::size_t&>() = std::declval<T>().read_some( - std::declval<detail::MutableBufferSequence>(), - std::declval<boost::system::error_code&>()) - )>> : std::true_type {}; -#endif - -/** Determine if `T` meets the requirements of @b SyncWriteStream. - - Metafunctions are used to perform compile time checking of template - types. This type will be `std::true_type` if `T` meets the requirements, - else the type will be `std::false_type`. - - @par Example - - Use with `static_assert`: - - @code - template<class SyncReadStream> - void f(SyncReadStream& stream) - { - static_assert(is_sync_read_stream<SyncReadStream>::value, - "SyncReadStream requirements not met"); - ... - @endcode - - Use with `std::enable_if` (SFINAE): - - @code - template<class SyncReadStream> - typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type - f(SyncReadStream& stream); - @endcode -*/ -#if BOOST_BEAST_DOXYGEN -template<class T> -struct is_sync_write_stream : std::integral_constant<bool, ...>{}; -#else -template<class T, class = void> -struct is_sync_write_stream : std::false_type {}; - -template<class T> -struct is_sync_write_stream<T, detail::void_t<decltype( - std::declval<std::size_t&>() = std::declval<T&>().write_some( - std::declval<detail::ConstBufferSequence>()), - std::declval<std::size_t&>() = std::declval<T&>().write_some( - std::declval<detail::ConstBufferSequence>(), - std::declval<boost::system::error_code&>()) - )>> : std::true_type {}; -#endif - -/** Determine if `T` meets the requirements of @b AsyncStream. - - Metafunctions are used to perform compile time checking of template - types. This type will be `std::true_type` if `T` meets the requirements, - else the type will be `std::false_type`. - - @par Example - - Use with `static_assert`: - - @code - template<class AsyncStream> - void f(AsyncStream& stream) - { - static_assert(is_async_stream<AsyncStream>::value, - "AsyncStream requirements not met"); - ... - @endcode - - Use with `std::enable_if` (SFINAE): - - @code - template<class AsyncStream> - typename std::enable_if<is_async_stream<AsyncStream>::value>::type - f(AsyncStream& stream); - @endcode -*/ -#if BOOST_BEAST_DOXYGEN -template<class T> -struct is_async_stream : std::integral_constant<bool, ...>{}; -#else -template<class T> -using is_async_stream = std::integral_constant<bool, - is_async_read_stream<T>::value && is_async_write_stream<T>::value>; -#endif - -/** Determine if `T` meets the requirements of @b SyncStream. - - Metafunctions are used to perform compile time checking of template - types. This type will be `std::true_type` if `T` meets the requirements, - else the type will be `std::false_type`. - - @par Example - - Use with `static_assert`: - - @code - template<class SyncStream> - void f(SyncStream& stream) - { - static_assert(is_sync_stream<SyncStream>::value, - "SyncStream requirements not met"); - ... - @endcode - - Use with `std::enable_if` (SFINAE): - - @code - template<class SyncStream> - typename std::enable_if<is_sync_stream<SyncStream>::value>::type - f(SyncStream& stream); - @endcode -*/ -#if BOOST_BEAST_DOXYGEN -template<class T> -struct is_sync_stream : std::integral_constant<bool, ...>{}; -#else -template<class T> -using is_sync_stream = std::integral_constant<bool, - is_sync_read_stream<T>::value && is_sync_write_stream<T>::value>; -#endif - -//------------------------------------------------------------------------------ -// -// File concepts -// -//------------------------------------------------------------------------------ - -/** Determine if `T` meets the requirements of @b File. - - Metafunctions are used to perform compile time checking of template - types. This type will be `std::true_type` if `T` meets the requirements, - else the type will be `std::false_type`. - - @par Example - - Use with `static_assert`: - - @code - template<class File> - void f(File& file) - { - static_assert(is_file<File>::value, - "File requirements not met"); - ... - @endcode - - Use with `std::enable_if` (SFINAE): - - @code - template<class File> - typename std::enable_if<is_file<File>::value>::type - f(File& file); - @endcode -*/ -#if BOOST_BEAST_DOXYGEN -template<class T> -struct is_file : std::integral_constant<bool, ...>{}; -#else -template<class T, class = void> -struct is_file : std::false_type {}; - -template<class T> -struct is_file<T, detail::void_t<decltype( - std::declval<bool&>() = std::declval<T const&>().is_open(), - std::declval<T&>().close(std::declval<error_code&>()), - std::declval<T&>().open( - std::declval<char const*>(), - std::declval<file_mode>(), - std::declval<error_code&>()), - std::declval<std::uint64_t&>() = std::declval<T&>().size( - std::declval<error_code&>()), - std::declval<std::uint64_t&>() = std::declval<T&>().pos( - std::declval<error_code&>()), - std::declval<T&>().seek( - std::declval<std::uint64_t>(), - std::declval<error_code&>()), - std::declval<std::size_t&>() = std::declval<T&>().read( - std::declval<void*>(), - std::declval<std::size_t>(), - std::declval<error_code&>()), - std::declval<std::size_t&>() = std::declval<T&>().write( - std::declval<void const*>(), - std::declval<std::size_t>(), - std::declval<error_code&>()) - )>> : std::integral_constant<bool, - std::is_default_constructible<T>::value && - std::is_destructible<T>::value - > {}; -#endif - } // beast } // boost #endif + +#endif |