feat():initial version

This commit is contained in:
2026-06-09 20:16:47 +08:00
commit 85fbb3188c
14809 changed files with 3044607 additions and 0 deletions

View File

@@ -0,0 +1,325 @@
//
// 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_HTTP_ICY_STREAM_HPP
#define BOOST_BEAST_HTTP_ICY_STREAM_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/logic/tribool.hpp>
#include <type_traits>
namespace boost {
namespace beast {
namespace http {
/** Stream wrapper to process Shoutcast HTTP responses
This wrapper replaces the word "ICY" in the first
HTTP response received on the connection, with "HTTP/1.1".
This allows the Beast parser to be used with Shoutcast
servers, which send a non-standard HTTP message as the
response.
For asynchronous operations, the application must ensure
that they are are all performed within the same implicit
or explicit strand.
@par Thread Safety
@e Distinct @e objects: Safe.@n
@e Shared @e objects: Unsafe.
The application must also ensure that all asynchronous
operations are performed within the same implicit or explicit strand.
@par Example
To use the @ref icy_stream template with an @ref tcp_stream
you would write:
@code
http::icy_stream<tcp_stream> is(ioc);
@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 <em>SyncStream</em> concept.
For asynchronous operations, the type must support the
<em>AsyncStream</em> concept.
@note A stream object must not be moved or destroyed while there
are pending asynchronous operations associated with it.
@par Concepts
<em>AsyncStream</em>, <em>SyncStream</em>
*/
template<class NextLayer>
class icy_stream
{
NextLayer stream_;
char buf_[8];
unsigned char n_ = 0;
bool detect_ = true;
struct ops;
static
net::const_buffer
version()
{
return {"HTTP/1.1", 8};
}
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 = typename next_layer_type::executor_type;
icy_stream(icy_stream&&) = default;
icy_stream(icy_stream const&) = default;
icy_stream& operator=(icy_stream&&) = default;
icy_stream& operator=(icy_stream const&) = default;
/** Destructor
The treatment of pending operations will be the same as that
of the next layer.
*/
~icy_stream() = default;
/** Constructor
Arguments, if any, are forwarded to the next layer's constructor.
*/
template<class... Args>
explicit
icy_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()
{
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
{
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 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(
const boost::system::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 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,
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler =
net::default_completion_token_t<executor_type>
>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler =
net::default_completion_token_t<executor_type>{});
/** 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 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& 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 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,
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler =
net::default_completion_token_t<executor_type>
>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
async_write_some(
ConstBufferSequence const& buffers,
WriteHandler&& handler =
net::default_completion_token_t<executor_type>{});
};
} // http
} // beast
} // boost
#include <boost/beast/_experimental/http/impl/icy_stream.hpp>
#endif

View File

@@ -0,0 +1,352 @@
//
// 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_ICY_STREAM_HPP
#define BOOST_BEAST_CORE_IMPL_ICY_STREAM_HPP
#include <boost/beast/core/async_base.hpp>
#include <boost/beast/core/buffer_traits.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/coroutine.hpp>
#include <boost/assert.hpp>
#include <boost/throw_exception.hpp>
#include <cstring>
#include <memory>
#include <utility>
namespace boost {
namespace beast {
namespace http {
namespace detail {
template<class ConstBufferSequence>
boost::tribool
is_icy(ConstBufferSequence const& buffers)
{
char buf[3];
auto const n = net::buffer_copy(
net::mutable_buffer(buf, 3),
buffers);
if(n >= 1 && buf[0] != 'I')
return false;
if(n >= 2 && buf[1] != 'C')
return false;
if(n >= 3 && buf[2] != 'Y')
return false;
if(n < 3)
return boost::indeterminate;
return true;
}
} // detail
template<class NextLayer>
struct icy_stream<NextLayer>::ops
{
template<class Buffers, class Handler>
class read_op
: public beast::async_base<Handler,
beast::executor_type<icy_stream>>
, public asio::coroutine
{
icy_stream& s_;
Buffers b_;
std::size_t n_ = 0;
error_code ec_;
bool match_ = false;
public:
template<class Handler_>
read_op(
Handler_&& h,
icy_stream& s,
Buffers const& b)
: async_base<Handler,
beast::executor_type<icy_stream>>(
std::forward<Handler_>(h), s.get_executor())
, s_(s)
, b_(b)
{
(*this)({}, 0, false);
}
void
operator()(
error_code ec,
std::size_t bytes_transferred,
bool cont = true)
{
BOOST_ASIO_CORO_REENTER(*this)
{
if(s_.detect_)
{
BOOST_ASSERT(s_.n_ == 0);
for(;;)
{
// Try to read the first three characters
BOOST_ASIO_CORO_YIELD
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"http::icy_stream::async_read_some"));
s_.next_layer().async_read_some(
net::mutable_buffer(
s_.buf_ + s_.n_, 3 - s_.n_),
std::move(*this));
}
s_.n_ += static_cast<char>(bytes_transferred);
if(ec)
goto upcall;
auto result = detail::is_icy(
net::const_buffer(s_.buf_, s_.n_));
if(boost::indeterminate(result))
continue;
if(result)
s_.n_ = static_cast<char>(net::buffer_copy(
net::buffer(s_.buf_, sizeof(s_.buf_)),
icy_stream::version()));
break;
}
s_.detect_ = false;
}
if(s_.n_ > 0)
{
bytes_transferred = net::buffer_copy(
b_, net::const_buffer(s_.buf_, s_.n_));
s_.n_ -= static_cast<char>(bytes_transferred);
std::memmove(
s_.buf_,
s_.buf_ + bytes_transferred,
sizeof(s_.buf_) - bytes_transferred);
}
else
{
BOOST_ASIO_CORO_YIELD
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"http::icy_stream::async_read_some"));
s_.next_layer().async_read_some(
b_, std::move(*this));
}
}
upcall:
if(! cont)
{
ec_ = ec;
n_ = bytes_transferred;
BOOST_ASIO_CORO_YIELD
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"http::icy_stream::async_read_some"));
s_.next_layer().async_read_some(
net::mutable_buffer{},
std::move(*this));
}
ec = ec_;
bytes_transferred = n_;
}
this->complete_now(ec, bytes_transferred);
}
}
};
struct run_read_op
{
template<class ReadHandler, class Buffers>
void
operator()(
ReadHandler&& h,
icy_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 NextLayer>
template<class... Args>
icy_stream<NextLayer>::
icy_stream(Args&&... args)
: stream_(std::forward<Args>(args)...)
{
std::memset(buf_, 0, sizeof(buf_));
}
template<class NextLayer>
template<class MutableBufferSequence>
std::size_t
icy_stream<NextLayer>::
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 NextLayer>
template<class MutableBufferSequence>
std::size_t
icy_stream<NextLayer>::
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");
std::size_t bytes_transferred;
if(detect_)
{
BOOST_ASSERT(n_ == 0);
for(;;)
{
// Try to read the first three characters
bytes_transferred = next_layer().read_some(
net::mutable_buffer(buf_ + n_, 3 - n_), ec);
n_ += static_cast<char>(bytes_transferred);
if(ec)
return 0;
auto result = detail::is_icy(
net::const_buffer(buf_, n_));
if(boost::indeterminate(result))
continue;
if(result)
n_ = static_cast<char>(net::buffer_copy(
net::buffer(buf_, sizeof(buf_)),
icy_stream::version()));
break;
}
detect_ = false;
}
if(n_ > 0)
{
bytes_transferred = net::buffer_copy(
buffers, net::const_buffer(buf_, n_));
n_ -= static_cast<char>(bytes_transferred);
std::memmove(
buf_,
buf_ + bytes_transferred,
sizeof(buf_) - bytes_transferred);
}
else
{
bytes_transferred =
next_layer().read_some(buffers, ec);
}
return bytes_transferred;
}
template<class NextLayer>
template<
class MutableBufferSequence,
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
icy_stream<NextLayer>::
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");
return net::async_initiate<
ReadHandler,
void(error_code, std::size_t)>(
typename ops::run_read_op{},
handler,
this,
buffers);
}
template<class NextLayer>
template<class MutableBufferSequence>
std::size_t
icy_stream<NextLayer>::
write_some(MutableBufferSequence const& buffers)
{
static_assert(is_sync_write_stream<next_layer_type>::value,
"SyncWriteStream type requirements not met");
static_assert(net::is_const_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence type requirements not met");
return stream_.write_some(buffers);
}
template<class NextLayer>
template<class MutableBufferSequence>
std::size_t
icy_stream<NextLayer>::
write_some(MutableBufferSequence const& buffers, error_code& ec)
{
static_assert(is_sync_write_stream<next_layer_type>::value,
"SyncWriteStream type requirements not met");
static_assert(net::is_const_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence type requirements not met");
return stream_.write_some(buffers, ec);
}
template<class NextLayer>
template<
class MutableBufferSequence,
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
icy_stream<NextLayer>::
async_write_some(
MutableBufferSequence 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<
MutableBufferSequence>::value,
"MutableBufferSequence type requirements not met");
return stream_.async_write_some(
buffers, std::forward<WriteHandler>(handler));
}
} // http
} // beast
} // boost
#endif

View File

@@ -0,0 +1,40 @@
//
// 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_TEST_ERROR_HPP
#define BOOST_BEAST_TEST_ERROR_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/error.hpp>
namespace boost {
namespace beast {
namespace test {
/// Error codes returned from unit testing algorithms
enum class error
{
/** The test stream generated a simulated testing error
This error is returned by a @ref fail_count object
when it generates a simulated error.
*/
test_failure = 1
};
} // test
} // beast
} // boost
#include <boost/beast/_experimental/test/impl/error.hpp>
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/_experimental/test/impl/error.ipp>
#endif
#endif

View File

@@ -0,0 +1,70 @@
//
// 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_TEST_FAIL_COUNT_HPP
#define BOOST_BEAST_TEST_FAIL_COUNT_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/_experimental/test/error.hpp>
#include <cstdlib>
namespace boost {
namespace beast {
namespace test {
/** A countdown to simulated failure
On the Nth operation, the class will fail with the specified
error code, or the default error code of @ref error::test_failure.
Instances of this class may be used to build objects which
are specifically designed to aid in writing unit tests, for
interfaces which can throw exceptions or return `error_code`
values representing failure.
*/
class fail_count
{
std::size_t n_;
std::size_t i_ = 0;
error_code ec_;
public:
fail_count(fail_count&&) = default;
/** Construct a counter
@param n The 0-based index of the operation to fail on or after
@param ev An optional error code to use when generating a simulated failure
*/
BOOST_BEAST_DECL
explicit
fail_count(
std::size_t n,
error_code ev = error::test_failure);
/// Throw an exception on the Nth failure
BOOST_BEAST_DECL
void
fail();
/// Set an error code on the Nth failure
BOOST_BEAST_DECL
bool
fail(error_code& ec);
};
} // test
} // beast
} // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/_experimental/test/impl/fail_count.ipp>
#endif
#endif

View File

@@ -0,0 +1,188 @@
//
// 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_TEST_HANDLER_HPP
#define BOOST_BEAST_TEST_HANDLER_HPP
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/core/exchange.hpp>
#include <boost/optional.hpp>
namespace boost {
namespace beast {
namespace test {
/** A CompletionHandler used for testing.
This completion handler is used by tests to ensure correctness
of behavior. It is designed as a single type to reduce template
instantiations, with configurable settings through constructor
arguments. Typically this type will be used in type lists and
not instantiated directly; instances of this class are returned
by the helper functions listed below.
@see success_handler, @ref fail_handler, @ref any_handler
*/
class handler
{
boost::optional<error_code> ec_;
bool pass_ = false;
public:
handler() = default;
explicit
handler(error_code ec)
: ec_(ec)
{
}
explicit
handler(boost::none_t)
{
}
handler(handler&& other)
: ec_(other.ec_)
, pass_(boost::exchange(other.pass_, true))
{
}
~handler()
{
BEAST_EXPECT(pass_);
}
template<class... Args>
void
operator()(error_code ec, Args&&...)
{
BEAST_EXPECT(! pass_); // can't call twice
BEAST_EXPECTS(! ec_ || ec == *ec_,
ec.message());
pass_ = true;
}
void
operator()()
{
BEAST_EXPECT(! pass_); // can't call twice
BEAST_EXPECT(! ec_);
pass_ = true;
}
template<class Arg0, class... Args,
class = typename std::enable_if<
! std::is_convertible<Arg0, error_code>::value>::type>
void
operator()(Arg0&&, Args&&...)
{
BEAST_EXPECT(! pass_); // can't call twice
BEAST_EXPECT(! ec_);
pass_ = true;
}
};
/** Return a test CompletionHandler which requires success.
The returned handler can be invoked with any signature whose
first parameter is an `error_code`. The handler fails the test
if:
@li The handler is destroyed without being invoked, or
@li The handler is invoked with a non-successful error code.
*/
inline
handler
success_handler() noexcept
{
return handler(error_code{});
}
/** Return a test CompletionHandler which requires invocation.
The returned handler can be invoked with any signature.
The handler fails the test if:
@li The handler is destroyed without being invoked.
*/
inline
handler
any_handler() noexcept
{
return handler(boost::none);
}
/** Return a test CompletionHandler which requires a specific error code.
This handler can be invoked with any signature whose first
parameter is an `error_code`. The handler fails the test if:
@li The handler is destroyed without being invoked.
@li The handler is invoked with an error code different from
what is specified.
@param ec The error code to specify.
*/
inline
handler
fail_handler(error_code ec) noexcept
{
return handler(ec);
}
/** Run an I/O context.
This function runs and dispatches handlers on the specified
I/O context, until one of the following conditions is true:
@li The I/O context runs out of work.
@param ioc The I/O context to run
*/
inline
void
run(net::io_context& ioc)
{
ioc.run();
ioc.restart();
}
/** Run an I/O context for a certain amount of time.
This function runs and dispatches handlers on the specified
I/O context, until one of the following conditions is true:
@li The I/O context runs out of work.
@li No completions occur and the specified amount of time has elapsed.
@param ioc The I/O context to run
@param elapsed The maximum amount of time to run for.
*/
template<class Rep, class Period>
void
run_for(
net::io_context& ioc,
std::chrono::duration<Rep, Period> elapsed)
{
ioc.run_for(elapsed);
ioc.restart();
}
} // test
} // beast
} // boost
#endif

View File

@@ -0,0 +1,40 @@
//
// 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_TEST_IMPL_ERROR_HPP
#define BOOST_BEAST_TEST_IMPL_ERROR_HPP
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/string.hpp>
#include <type_traits>
namespace boost {
namespace system {
template<>
struct is_error_code_enum<
boost::beast::test::error>
: std::true_type
{
};
} // system
} // boost
namespace boost {
namespace beast {
namespace test {
BOOST_BEAST_DECL
error_code
make_error_code(error e) noexcept;
} // test
} // beast
} // boost
#endif

View File

@@ -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_TEST_IMPL_ERROR_IPP
#define BOOST_BEAST_TEST_IMPL_ERROR_IPP
#include <boost/beast/_experimental/test/error.hpp>
namespace boost {
namespace beast {
namespace test {
namespace detail {
class error_codes : public error_category
{
public:
BOOST_BEAST_DECL
const char*
name() const noexcept override
{
return "boost.beast.test";
}
BOOST_BEAST_DECL
std::string
message(int ev) const override
{
switch(static_cast<error>(ev))
{
default:
case error::test_failure: return
"An automatic unit test failure occurred";
}
}
BOOST_BEAST_DECL
error_condition
default_error_condition(int ev) const noexcept override
{
return error_condition{ev, *this};
}
};
} // detail
error_code
make_error_code(error e) noexcept
{
static detail::error_codes const cat{};
return error_code{static_cast<
std::underlying_type<error>::type>(e), cat};
}
} // test
} // beast
} // boost
#endif

View File

@@ -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_TEST_IMPL_FAIL_COUNT_IPP
#define BOOST_BEAST_TEST_IMPL_FAIL_COUNT_IPP
#include <boost/beast/_experimental/test/fail_count.hpp>
#include <boost/throw_exception.hpp>
namespace boost {
namespace beast {
namespace test {
fail_count::
fail_count(
std::size_t n,
error_code ev)
: n_(n)
, ec_(ev)
{
}
void
fail_count::
fail()
{
if(i_ < n_)
++i_;
if(i_ == n_)
BOOST_THROW_EXCEPTION(system_error{ec_});
}
bool
fail_count::
fail(error_code& ec)
{
if(i_ < n_)
++i_;
if(i_ == n_)
{
ec = ec_;
return true;
}
ec = {};
return false;
}
} // test
} // beast
} // boost
#endif

View File

@@ -0,0 +1,495 @@
//
// 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_TEST_IMPL_STREAM_HPP
#define BOOST_BEAST_TEST_IMPL_STREAM_HPP
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/beast/core/detail/service_base.hpp>
#include <boost/beast/core/detail/is_invocable.hpp>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/post.hpp>
#include <mutex>
#include <stdexcept>
#include <vector>
namespace boost {
namespace beast {
namespace test {
//------------------------------------------------------------------------------
struct stream::service_impl
{
std::mutex m_;
std::vector<state*> v_;
BOOST_BEAST_DECL
void
remove(state& impl);
};
class stream::service
: public beast::detail::service_base<service>
{
boost::shared_ptr<service_impl> sp_;
BOOST_BEAST_DECL
void
shutdown() override;
public:
BOOST_BEAST_DECL
explicit
service(net::execution_context& ctx);
BOOST_BEAST_DECL
static
auto
make_impl(
net::io_context& ctx,
test::fail_count* fc) ->
boost::shared_ptr<state>;
};
//------------------------------------------------------------------------------
template<class Handler, class Buffers>
class stream::read_op : public stream::read_op_base
{
using ex1_type =
net::io_context::executor_type;
using ex2_type
= net::associated_executor_t<Handler, ex1_type>;
struct lambda
{
Handler h_;
boost::weak_ptr<state> wp_;
Buffers b_;
#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
net::any_io_executor wg2_;
#else // defined(BOOST_ASIO_NO_TS_EXECUTORS)
net::executor_work_guard<ex2_type> wg2_;
#endif // defined(BOOST_ASIO_NO_TS_EXECUTORS)
lambda(lambda&&) = default;
lambda(lambda const&) = default;
template<class Handler_>
lambda(
Handler_&& h,
boost::shared_ptr<state> const& s,
Buffers const& b)
: h_(std::forward<Handler_>(h))
, wp_(s)
, b_(b)
#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
, wg2_(net::prefer(
net::get_associated_executor(
h_, s->ioc.get_executor()),
net::execution::outstanding_work.tracked))
#else // defined(BOOST_ASIO_NO_TS_EXECUTORS)
, wg2_(net::get_associated_executor(
h_, s->ioc.get_executor()))
#endif // defined(BOOST_ASIO_NO_TS_EXECUTORS)
{
}
using allocator_type = net::associated_allocator_t<Handler>;
allocator_type get_allocator() const noexcept
{
return net::get_associated_allocator(h_);
}
void
operator()(error_code ec)
{
std::size_t bytes_transferred = 0;
auto sp = wp_.lock();
if(! sp)
ec = net::error::operation_aborted;
if(! ec)
{
std::lock_guard<std::mutex> lock(sp->m);
BOOST_ASSERT(! sp->op);
if(sp->b.size() > 0)
{
bytes_transferred =
net::buffer_copy(
b_, sp->b.data(), sp->read_max);
sp->b.consume(bytes_transferred);
sp->nread_bytes += bytes_transferred;
}
else if (buffer_bytes(b_) > 0)
{
ec = net::error::eof;
}
}
#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
net::dispatch(wg2_,
beast::bind_front_handler(std::move(h_),
ec, bytes_transferred));
wg2_ = net::any_io_executor(); // probably unnecessary
#else // defined(BOOST_ASIO_NO_TS_EXECUTORS)
net::dispatch(wg2_.get_executor(),
beast::bind_front_handler(std::move(h_),
ec, bytes_transferred));
wg2_.reset();
#endif // defined(BOOST_ASIO_NO_TS_EXECUTORS)
}
};
lambda fn_;
#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
net::any_io_executor wg1_;
#else // defined(BOOST_ASIO_NO_TS_EXECUTORS)
net::executor_work_guard<ex1_type> wg1_;
#endif // defined(BOOST_ASIO_NO_TS_EXECUTORS)
public:
template<class Handler_>
read_op(
Handler_&& h,
boost::shared_ptr<state> const& s,
Buffers const& b)
: fn_(std::forward<Handler_>(h), s, b)
#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
, wg1_(net::prefer(s->ioc.get_executor(),
net::execution::outstanding_work.tracked))
#else // defined(BOOST_ASIO_NO_TS_EXECUTORS)
, wg1_(s->ioc.get_executor())
#endif // defined(BOOST_ASIO_NO_TS_EXECUTORS)
{
}
void
operator()(error_code ec) override
{
#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
net::post(wg1_, beast::bind_front_handler(std::move(fn_), ec));
wg1_ = net::any_io_executor(); // probably unnecessary
#else // defined(BOOST_ASIO_NO_TS_EXECUTORS)
net::post(wg1_.get_executor(),
beast::bind_front_handler(std::move(fn_), ec));
wg1_.reset();
#endif // defined(BOOST_ASIO_NO_TS_EXECUTORS)
}
};
struct stream::run_read_op
{
template<
class ReadHandler,
class MutableBufferSequence>
void
operator()(
ReadHandler&& h,
boost::shared_ptr<state> const& in,
MutableBufferSequence const& buffers)
{
// 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");
initiate_read(
in,
std::unique_ptr<read_op_base>{
new read_op<
typename std::decay<ReadHandler>::type,
MutableBufferSequence>(
std::move(h),
in,
buffers)},
buffer_bytes(buffers));
}
};
struct stream::run_write_op
{
template<
class WriteHandler,
class ConstBufferSequence>
void
operator()(
WriteHandler&& h,
boost::shared_ptr<state> in_,
boost::weak_ptr<state> out_,
ConstBufferSequence const& buffers)
{
// 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");
++in_->nwrite;
auto const upcall = [&](error_code ec, std::size_t n)
{
net::post(
in_->ioc.get_executor(),
beast::bind_front_handler(std::move(h), ec, n));
};
// test failure
error_code ec;
std::size_t n = 0;
if(in_->fc && in_->fc->fail(ec))
return upcall(ec, n);
// A request to write 0 bytes to a stream is a no-op.
if(buffer_bytes(buffers) == 0)
return upcall(ec, n);
// connection closed
auto out = out_.lock();
if(! out)
return upcall(net::error::connection_reset, n);
// copy buffers
n = std::min<std::size_t>(
buffer_bytes(buffers), in_->write_max);
{
std::lock_guard<std::mutex> lock(out->m);
n = net::buffer_copy(out->b.prepare(n), buffers);
out->b.commit(n);
out->nwrite_bytes += n;
out->notify_read();
}
BOOST_ASSERT(! ec);
upcall(ec, n);
}
};
//------------------------------------------------------------------------------
template<class MutableBufferSequence>
std::size_t
stream::
read_some(MutableBufferSequence const& buffers)
{
static_assert(net::is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence type requirements not met");
error_code ec;
auto const n = read_some(buffers, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template<class MutableBufferSequence>
std::size_t
stream::
read_some(MutableBufferSequence const& buffers,
error_code& ec)
{
static_assert(net::is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence type requirements not met");
++in_->nread;
// test failure
if(in_->fc && in_->fc->fail(ec))
return 0;
// A request to read 0 bytes from a stream is a no-op.
if(buffer_bytes(buffers) == 0)
{
ec = {};
return 0;
}
std::unique_lock<std::mutex> lock{in_->m};
BOOST_ASSERT(! in_->op);
in_->cv.wait(lock,
[&]()
{
return
in_->b.size() > 0 ||
in_->code != status::ok;
});
// deliver bytes before eof
if(in_->b.size() > 0)
{
auto const n = net::buffer_copy(
buffers, in_->b.data(), in_->read_max);
in_->b.consume(n);
in_->nread_bytes += n;
return n;
}
// deliver error
BOOST_ASSERT(in_->code != status::ok);
ec = net::error::eof;
return 0;
}
template<class MutableBufferSequence, BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
stream::
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)>(
run_read_op{},
handler,
in_,
buffers);
}
template<class ConstBufferSequence>
std::size_t
stream::
write_some(ConstBufferSequence const& buffers)
{
static_assert(net::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence type requirements not met");
error_code ec;
auto const bytes_transferred =
write_some(buffers, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return bytes_transferred;
}
template<class ConstBufferSequence>
std::size_t
stream::
write_some(
ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(net::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence type requirements not met");
++in_->nwrite;
// test failure
if(in_->fc && in_->fc->fail(ec))
return 0;
// A request to write 0 bytes to a stream is a no-op.
if(buffer_bytes(buffers) == 0)
{
ec = {};
return 0;
}
// connection closed
auto out = out_.lock();
if(! out)
{
ec = net::error::connection_reset;
return 0;
}
// copy buffers
auto n = std::min<std::size_t>(
buffer_bytes(buffers), in_->write_max);
{
std::lock_guard<std::mutex> lock(out->m);
n = net::buffer_copy(out->b.prepare(n), buffers);
out->b.commit(n);
out->nwrite_bytes += n;
out->notify_read();
}
return n;
}
template<class ConstBufferSequence, BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
stream::
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)>(
run_write_op{},
handler,
in_,
out_,
buffers);
}
//------------------------------------------------------------------------------
template<class TeardownHandler>
void
async_teardown(
role_type,
stream& s,
TeardownHandler&& handler)
{
error_code ec;
if( s.in_->fc &&
s.in_->fc->fail(ec))
return net::post(
s.get_executor(),
beast::bind_front_handler(
std::move(handler), ec));
s.close();
if( s.in_->fc &&
s.in_->fc->fail(ec))
ec = net::error::eof;
else
ec = {};
net::post(
s.get_executor(),
beast::bind_front_handler(
std::move(handler), ec));
}
//------------------------------------------------------------------------------
template<class Arg1, class... ArgN>
stream
connect(stream& to, Arg1&& arg1, ArgN&&... argn)
{
stream from{
std::forward<Arg1>(arg1),
std::forward<ArgN>(argn)...};
from.connect(to);
return from;
}
} // test
} // beast
} // boost
#endif

View File

@@ -0,0 +1,381 @@
//
// 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_TEST_IMPL_STREAM_IPP
#define BOOST_BEAST_TEST_IMPL_STREAM_IPP
#include <boost/beast/_experimental/test/stream.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/make_shared.hpp>
#include <stdexcept>
#include <vector>
namespace boost {
namespace beast {
namespace test {
//------------------------------------------------------------------------------
stream::
service::
service(net::execution_context& ctx)
: beast::detail::service_base<service>(ctx)
, sp_(boost::make_shared<service_impl>())
{
}
void
stream::
service::
shutdown()
{
std::vector<std::unique_ptr<read_op_base>> v;
std::lock_guard<std::mutex> g1(sp_->m_);
v.reserve(sp_->v_.size());
for(auto p : sp_->v_)
{
std::lock_guard<std::mutex> g2(p->m);
v.emplace_back(std::move(p->op));
p->code = status::eof;
}
}
auto
stream::
service::
make_impl(
net::io_context& ctx,
test::fail_count* fc) ->
boost::shared_ptr<state>
{
auto& svc = net::use_service<service>(ctx);
auto sp = boost::make_shared<state>(ctx, svc.sp_, fc);
std::lock_guard<std::mutex> g(svc.sp_->m_);
svc.sp_->v_.push_back(sp.get());
return sp;
}
void
stream::
service_impl::
remove(state& impl)
{
std::lock_guard<std::mutex> g(m_);
*std::find(
v_.begin(), v_.end(),
&impl) = std::move(v_.back());
v_.pop_back();
}
//------------------------------------------------------------------------------
void stream::initiate_read(
boost::shared_ptr<state> const& in_,
std::unique_ptr<stream::read_op_base>&& op,
std::size_t buf_size)
{
std::unique_lock<std::mutex> lock(in_->m);
++in_->nread;
if(in_->op != nullptr)
BOOST_THROW_EXCEPTION(
std::logic_error{"in_->op != nullptr"});
// test failure
error_code ec;
if(in_->fc && in_->fc->fail(ec))
{
lock.unlock();
(*op)(ec);
return;
}
// A request to read 0 bytes from a stream is a no-op.
if(buf_size == 0 || buffer_bytes(in_->b.data()) > 0)
{
lock.unlock();
(*op)(ec);
return;
}
// deliver error
if(in_->code != status::ok)
{
lock.unlock();
(*op)(net::error::eof);
return;
}
// complete when bytes available or closed
in_->op = std::move(op);
}
stream::
state::
state(
net::io_context& ioc_,
boost::weak_ptr<service_impl> wp_,
fail_count* fc_)
: ioc(ioc_)
, wp(std::move(wp_))
, fc(fc_)
{
}
stream::
state::
~state()
{
// cancel outstanding read
if(op != nullptr)
(*op)(net::error::operation_aborted);
}
void
stream::
state::
remove() noexcept
{
auto sp = wp.lock();
// If this goes off, it means the lifetime of a test::stream object
// extended beyond the lifetime of the associated execution context.
BOOST_ASSERT(sp);
sp->remove(*this);
}
void
stream::
state::
notify_read()
{
if(op)
{
auto op_ = std::move(op);
op_->operator()(error_code{});
}
else
{
cv.notify_all();
}
}
void
stream::
state::
cancel_read()
{
std::unique_ptr<read_op_base> p;
{
std::lock_guard<std::mutex> lock(m);
code = status::eof;
p = std::move(op);
}
if(p != nullptr)
(*p)(net::error::operation_aborted);
}
//------------------------------------------------------------------------------
stream::
~stream()
{
close();
in_->remove();
}
stream::
stream(stream&& other)
{
auto in = service::make_impl(
other.in_->ioc, other.in_->fc);
in_ = std::move(other.in_);
out_ = std::move(other.out_);
other.in_ = in;
}
stream&
stream::
operator=(stream&& other)
{
close();
auto in = service::make_impl(
other.in_->ioc, other.in_->fc);
in_->remove();
in_ = std::move(other.in_);
out_ = std::move(other.out_);
other.in_ = in;
return *this;
}
//------------------------------------------------------------------------------
stream::
stream(net::io_context& ioc)
: in_(service::make_impl(ioc, nullptr))
{
}
stream::
stream(
net::io_context& ioc,
fail_count& fc)
: in_(service::make_impl(ioc, &fc))
{
}
stream::
stream(
net::io_context& ioc,
string_view s)
: in_(service::make_impl(ioc, nullptr))
{
in_->b.commit(net::buffer_copy(
in_->b.prepare(s.size()),
net::buffer(s.data(), s.size())));
}
stream::
stream(
net::io_context& ioc,
fail_count& fc,
string_view s)
: in_(service::make_impl(ioc, &fc))
{
in_->b.commit(net::buffer_copy(
in_->b.prepare(s.size()),
net::buffer(s.data(), s.size())));
}
void
stream::
connect(stream& remote)
{
BOOST_ASSERT(! out_.lock());
BOOST_ASSERT(! remote.out_.lock());
std::lock(in_->m, remote.in_->m);
std::lock_guard<std::mutex> guard1{in_->m, std::adopt_lock};
std::lock_guard<std::mutex> guard2{remote.in_->m, std::adopt_lock};
out_ = remote.in_;
remote.out_ = in_;
in_->code = status::ok;
remote.in_->code = status::ok;
}
string_view
stream::
str() const
{
auto const bs = in_->b.data();
if(buffer_bytes(bs) == 0)
return {};
net::const_buffer const b = *net::buffer_sequence_begin(bs);
return {static_cast<char const*>(b.data()), b.size()};
}
void
stream::
append(string_view s)
{
std::lock_guard<std::mutex> lock{in_->m};
in_->b.commit(net::buffer_copy(
in_->b.prepare(s.size()),
net::buffer(s.data(), s.size())));
}
void
stream::
clear()
{
std::lock_guard<std::mutex> lock{in_->m};
in_->b.consume(in_->b.size());
}
void
stream::
close()
{
in_->cancel_read();
// disconnect
{
auto out = out_.lock();
out_.reset();
// notify peer
if(out)
{
std::lock_guard<std::mutex> lock(out->m);
if(out->code == status::ok)
{
out->code = status::eof;
out->notify_read();
}
}
}
}
void
stream::
close_remote()
{
std::lock_guard<std::mutex> lock{in_->m};
if(in_->code == status::ok)
{
in_->code = status::eof;
in_->notify_read();
}
}
void
teardown(
role_type,
stream& s,
boost::system::error_code& ec)
{
if( s.in_->fc &&
s.in_->fc->fail(ec))
return;
s.close();
if( s.in_->fc &&
s.in_->fc->fail(ec))
ec = net::error::eof;
else
ec = {};
}
//------------------------------------------------------------------------------
stream
connect(stream& to)
{
#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
stream from{net::query(to.get_executor(), net::execution::context)};
#else // defined(BOOST_ASIO_NO_TS_EXECUTORS)
stream from{to.get_executor().context()};
#endif // defined(BOOST_ASIO_NO_TS_EXECUTORS)
from.connect(to);
return from;
}
void
connect(stream& s1, stream& s2)
{
s1.connect(s2);
}
} // test
} // beast
} // boost
#endif

View File

@@ -0,0 +1,629 @@
//
// 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_TEST_STREAM_HPP
#define BOOST_BEAST_TEST_STREAM_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/beast/core/role.hpp>
#include <boost/beast/core/string.hpp>
#include <boost/beast/_experimental/test/fail_count.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/post.hpp>
#include <boost/assert.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/throw_exception.hpp>
#include <condition_variable>
#include <limits>
#include <memory>
#include <mutex>
#include <utility>
#if ! BOOST_BEAST_DOXYGEN
namespace boost {
namespace asio {
namespace ssl {
template<typename> class stream;
} // ssl
} // asio
} // boost
#endif
namespace boost {
namespace beast {
namespace test {
/** A two-way socket useful for unit testing
An instance of this class simulates a traditional socket,
while also providing features useful for unit testing.
Each endpoint maintains an independent buffer called
the input area. Writes from one endpoint append data
to the peer's pending input area. When an endpoint performs
a read and data is present in the input area, the data is
delivered to the blocking or asynchronous operation. Otherwise
the operation is blocked or deferred until data is made
available, or until the endpoints become disconnected.
These streams may be used anywhere an algorithm accepts a
reference to a synchronous or asynchronous read or write
stream. It is possible to use a test stream in a call to
`net::read_until`, or in a call to
@ref boost::beast::http::async_write for example.
As with Boost.Asio I/O objects, a @ref stream constructs
with a reference to the `net::io_context` to use for
handling asynchronous I/O. For asynchronous operations, the
stream follows the same rules as a traditional asio socket
with respect to how completion handlers for asynchronous
operations are performed.
To facilitate testing, these streams support some additional
features:
@li The input area, represented by a @ref beast::basic_flat_buffer,
may be directly accessed by the caller to inspect the contents
before or after the remote endpoint writes data. This allows
a unit test to verify that the received data matches.
@li Data may be manually appended to the input area. This data
will delivered in the next call to
@ref stream::read_some or @ref stream::async_read_some.
This allows predefined test vectors to be set up for testing
read algorithms.
@li The stream may be constructed with a fail count. The
stream will eventually fail with a predefined error after a
certain number of operations, where the number of operations
is controlled by the test. When a test loops over a range of
operation counts, it is possible to exercise every possible
point of failure in the algorithm being tested. When used
correctly the technique allows the tests to reach a high
percentage of code coverage.
@par Thread Safety
@e Distinct @e objects: Safe.@n
@e Shared @e objects: Unsafe.
The application must also ensure that all asynchronous
operations are performed within the same implicit or explicit strand.
@par Concepts
@li <em>SyncReadStream</em>
@li <em>SyncWriteStream</em>
@li <em>AsyncReadStream</em>
@li <em>AsyncWriteStream</em>
*/
class stream
{
struct state;
boost::shared_ptr<state> in_;
boost::weak_ptr<state> out_;
enum class status
{
ok,
eof,
};
class service;
struct service_impl;
struct read_op_base
{
virtual ~read_op_base() = default;
virtual void operator()(error_code ec) = 0;
};
struct state
{
friend class stream;
net::io_context& ioc;
boost::weak_ptr<service_impl> wp;
std::mutex m;
flat_buffer b;
std::condition_variable cv;
std::unique_ptr<read_op_base> op;
status code = status::ok;
fail_count* fc = nullptr;
std::size_t nread = 0;
std::size_t nread_bytes = 0;
std::size_t nwrite = 0;
std::size_t nwrite_bytes = 0;
std::size_t read_max =
(std::numeric_limits<std::size_t>::max)();
std::size_t write_max =
(std::numeric_limits<std::size_t>::max)();
BOOST_BEAST_DECL
state(
net::io_context& ioc_,
boost::weak_ptr<service_impl> wp_,
fail_count* fc_);
BOOST_BEAST_DECL
~state();
BOOST_BEAST_DECL
void
remove() noexcept;
BOOST_BEAST_DECL
void
notify_read();
BOOST_BEAST_DECL
void
cancel_read();
};
template<class Handler, class Buffers>
class read_op;
struct run_read_op;
struct run_write_op;
BOOST_BEAST_DECL
static
void
initiate_read(
boost::shared_ptr<state> const& in,
std::unique_ptr<read_op_base>&& op,
std::size_t buf_size);
#if ! BOOST_BEAST_DOXYGEN
// boost::asio::ssl::stream needs these
// DEPRECATED
template<class>
friend class boost::asio::ssl::stream;
// DEPRECATED
using lowest_layer_type = stream;
// DEPRECATED
lowest_layer_type&
lowest_layer() noexcept
{
return *this;
}
// DEPRECATED
lowest_layer_type const&
lowest_layer() const noexcept
{
return *this;
}
#endif
public:
using buffer_type = flat_buffer;
/** Destructor
If an asynchronous read operation is pending, it will
simply be discarded with no notification to the completion
handler.
If a connection is established while the stream is destroyed,
the peer will see the error `net::error::connection_reset`
when performing any reads or writes.
*/
BOOST_BEAST_DECL
~stream();
/** Move Constructor
Moving the stream while asynchronous operations are pending
results in undefined behavior.
*/
BOOST_BEAST_DECL
stream(stream&& other);
/** Move Assignment
Moving the stream while asynchronous operations are pending
results in undefined behavior.
*/
BOOST_BEAST_DECL
stream&
operator=(stream&& other);
/** Construct a stream
The stream will be created in a disconnected state.
@param ioc The `io_context` object that the stream will use to
dispatch handlers for any asynchronous operations.
*/
BOOST_BEAST_DECL
explicit
stream(net::io_context& ioc);
/** Construct a stream
The stream will be created in a disconnected state.
@param ioc The `io_context` object that the stream will use to
dispatch handlers for any asynchronous operations.
@param fc The @ref fail_count to associate with the stream.
Each I/O operation performed on the stream will increment the
fail count. When the fail count reaches its internal limit,
a simulated failure error will be raised.
*/
BOOST_BEAST_DECL
stream(
net::io_context& ioc,
fail_count& fc);
/** Construct a stream
The stream will be created in a disconnected state.
@param ioc The `io_context` object that the stream will use to
dispatch handlers for any asynchronous operations.
@param s A string which will be appended to the input area, not
including the null terminator.
*/
BOOST_BEAST_DECL
stream(
net::io_context& ioc,
string_view s);
/** Construct a stream
The stream will be created in a disconnected state.
@param ioc The `io_context` object that the stream will use to
dispatch handlers for any asynchronous operations.
@param fc The @ref fail_count to associate with the stream.
Each I/O operation performed on the stream will increment the
fail count. When the fail count reaches its internal limit,
a simulated failure error will be raised.
@param s A string which will be appended to the input area, not
including the null terminator.
*/
BOOST_BEAST_DECL
stream(
net::io_context& ioc,
fail_count& fc,
string_view s);
/// Establish a connection
BOOST_BEAST_DECL
void
connect(stream& remote);
/// The type of the executor associated with the object.
using executor_type =
net::io_context::executor_type;
/// Return the executor associated with the object.
executor_type
get_executor() noexcept
{
return in_->ioc.get_executor();
};
/// Set the maximum number of bytes returned by read_some
void
read_size(std::size_t n) noexcept
{
in_->read_max = n;
}
/// Set the maximum number of bytes returned by write_some
void
write_size(std::size_t n) noexcept
{
in_->write_max = n;
}
/// Direct input buffer access
buffer_type&
buffer() noexcept
{
return in_->b;
}
/// Returns a string view representing the pending input data
BOOST_BEAST_DECL
string_view
str() const;
/// Appends a string to the pending input data
BOOST_BEAST_DECL
void
append(string_view s);
/// Clear the pending input area
BOOST_BEAST_DECL
void
clear();
/// Return the number of reads
std::size_t
nread() const noexcept
{
return in_->nread;
}
/// Return the number of bytes read
std::size_t
nread_bytes() const noexcept
{
return in_->nread_bytes;
}
/// Return the number of writes
std::size_t
nwrite() const noexcept
{
return in_->nwrite;
}
/// Return the number of bytes written
std::size_t
nwrite_bytes() const noexcept
{
return in_->nwrite_bytes;
}
/** Close the stream.
The other end of the connection will see
`error::eof` after reading all the remaining data.
*/
BOOST_BEAST_DECL
void
close();
/** Close the other end of the stream.
This end of the connection will see
`error::eof` after reading all the remaining data.
*/
BOOST_BEAST_DECL
void
close_remote();
/** 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& ec, // 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 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,
BOOST_BEAST_ASYNC_TPARAM2 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,
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
async_write_some(
ConstBufferSequence const& buffers,
WriteHandler&& handler);
#if ! BOOST_BEAST_DOXYGEN
friend
BOOST_BEAST_DECL
void
teardown(
role_type,
stream& s,
boost::system::error_code& ec);
template<class TeardownHandler>
friend
BOOST_BEAST_DECL
void
async_teardown(
role_type role,
stream& s,
TeardownHandler&& handler);
#endif
};
#if ! BOOST_BEAST_DOXYGEN
inline
void
beast_close_socket(stream& s)
{
s.close();
}
#endif
#if BOOST_BEAST_DOXYGEN
/** Return a new stream connected to the given stream
@param to The stream to connect to.
@param args Optional arguments forwarded to the new stream's constructor.
@return The new, connected stream.
*/
template<class... Args>
stream
connect(stream& to, Args&&... args);
#else
BOOST_BEAST_DECL
stream
connect(stream& to);
BOOST_BEAST_DECL
void
connect(stream& s1, stream& s2);
template<class Arg1, class... ArgN>
stream
connect(stream& to, Arg1&& arg1, ArgN&&... argn);
#endif
} // test
} // beast
} // boost
#include <boost/beast/_experimental/test/impl/stream.hpp>
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/_experimental/test/impl/stream.ipp>
#endif
#endif

View File

@@ -0,0 +1,77 @@
//
// 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_TEST_TCP_HPP
#define BOOST_BEAST_TEST_TCP_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/get_io_context.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/beast/_experimental/test/handler.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <chrono>
namespace boost {
namespace beast {
namespace test {
/** Connect two TCP sockets together.
*/
template<class Executor>
bool
connect(
net::basic_stream_socket<net::ip::tcp, Executor>& s1,
net::basic_stream_socket<net::ip::tcp, Executor>& s2)
{
auto ioc1 = beast::detail::get_io_context(s1);
auto ioc2 = beast::detail::get_io_context(s2);
if(! BEAST_EXPECT(ioc1 != nullptr))
return false;
if(! BEAST_EXPECT(ioc2 != nullptr))
return false;
if(! BEAST_EXPECT(ioc1 == ioc2))
return false;
auto& ioc = *ioc1;
try
{
net::basic_socket_acceptor<
net::ip::tcp, Executor> a(s1.get_executor());
auto ep = net::ip::tcp::endpoint(
net::ip::make_address_v4("127.0.0.1"), 0);
a.open(ep.protocol());
a.set_option(
net::socket_base::reuse_address(true));
a.bind(ep);
a.listen(0);
ep = a.local_endpoint();
a.async_accept(s2, test::success_handler());
s1.async_connect(ep, test::success_handler());
run(ioc);
if(! BEAST_EXPECT(
s1.remote_endpoint() == s2.local_endpoint()))
return false;
if(! BEAST_EXPECT(
s2.remote_endpoint() == s1.local_endpoint()))
return false;
}
catch(std::exception const& e)
{
beast::unit_test::suite::this_suite()->fail(
e.what(), __FILE__, __LINE__);
return false;
}
return true;
}
} // test
} // beast
} // boost
#endif

View File

@@ -0,0 +1,59 @@
//
// 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_UNIT_TEST_AMOUNT_HPP
#define BOOST_BEAST_UNIT_TEST_AMOUNT_HPP
#include <cstddef>
#include <ostream>
#include <string>
namespace boost {
namespace beast {
namespace unit_test {
/** Utility for producing nicely composed output of amounts with units. */
class amount
{
private:
std::size_t n_;
std::string const& what_;
public:
amount(amount const&) = default;
amount& operator=(amount const&) = delete;
template<class = void>
amount(std::size_t n, std::string const& what);
friend
std::ostream&
operator<<(std::ostream& s, amount const& t);
};
template<class>
amount::amount(std::size_t n, std::string const& what)
: n_(n)
, what_(what)
{
}
inline
std::ostream&
operator<<(std::ostream& s, amount const& t)
{
s << t.n_ << " " << t.what_ <<((t.n_ != 1) ? "s" : "");
return s;
}
} // unit_test
} // beast
} // boost
#endif

View File

@@ -0,0 +1,95 @@
//
// 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_UNIT_TEST_DETAIL_CONST_CONTAINER_HPP
#define BOOST_BEAST_UNIT_TEST_DETAIL_CONST_CONTAINER_HPP
namespace boost {
namespace beast {
namespace unit_test {
namespace detail {
/** Adapter to constrain a container interface.
The interface allows for limited read only operations. Derived classes
provide additional behavior.
*/
template<class Container>
class const_container
{
private:
using cont_type = Container;
cont_type m_cont;
protected:
cont_type& cont()
{
return m_cont;
}
cont_type const& cont() const
{
return m_cont;
}
public:
using value_type = typename cont_type::value_type;
using size_type = typename cont_type::size_type;
using difference_type = typename cont_type::difference_type;
using iterator = typename cont_type::const_iterator;
using const_iterator = typename cont_type::const_iterator;
/** Returns `true` if the container is empty. */
bool
empty() const
{
return m_cont.empty();
}
/** Returns the number of items in the container. */
size_type
size() const
{
return m_cont.size();
}
/** Returns forward iterators for traversal. */
/** @{ */
const_iterator
begin() const
{
return m_cont.cbegin();
}
const_iterator
cbegin() const
{
return m_cont.cbegin();
}
const_iterator
end() const
{
return m_cont.cend();
}
const_iterator
cend() const
{
return m_cont.cend();
}
/** @} */
};
} // detail
} // unit_test
} // beast
} // boost
#endif

View File

@@ -0,0 +1,131 @@
//
// 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_UNIT_TEST_DSTREAM_HPP
#define BOOST_BEAST_UNIT_TEST_DSTREAM_HPP
#include <boost/config.hpp>
#include <ios>
#include <memory>
#include <ostream>
#include <sstream>
#include <streambuf>
#include <string>
#ifdef BOOST_WINDOWS
#include <boost/winapi/basic_types.hpp>
#include <boost/winapi/debugapi.hpp>
#endif
namespace boost {
namespace beast {
namespace unit_test {
#ifdef BOOST_WINDOWS
namespace detail {
template<class CharT, class Traits, class Allocator>
class dstream_buf
: public std::basic_stringbuf<CharT, Traits, Allocator>
{
using ostream = std::basic_ostream<CharT, Traits>;
ostream& os_;
bool dbg_;
template<class T>
void write(T const*) = delete;
void write(char const* s)
{
if(dbg_)
boost::winapi::OutputDebugStringA(s);
os_ << s;
}
void write(wchar_t const* s)
{
if(dbg_)
boost::winapi::OutputDebugStringW(s);
os_ << s;
}
public:
explicit
dstream_buf(ostream& os)
: os_(os)
, dbg_(boost::winapi::IsDebuggerPresent() != 0)
{
}
~dstream_buf()
{
sync();
}
int
sync() override
{
write(this->str().c_str());
this->str("");
return 0;
}
};
} // detail
/** std::ostream with Visual Studio IDE redirection.
Instances of this stream wrap a specified `std::ostream`
(such as `std::cout` or `std::cerr`). If the IDE debugger
is attached when the stream is created, output will be
additionally copied to the Visual Studio Output window.
*/
template<
class CharT,
class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>
>
class basic_dstream
: public std::basic_ostream<CharT, Traits>
{
detail::dstream_buf<
CharT, Traits, Allocator> buf_;
public:
/** Construct a stream.
@param os The output stream to wrap.
*/
explicit
basic_dstream(std::ostream& os)
: std::basic_ostream<CharT, Traits>(&buf_)
, buf_(os)
{
if(os.flags() & std::ios::unitbuf)
std::unitbuf(*this);
}
};
using dstream = basic_dstream<char>;
using dwstream = basic_dstream<wchar_t>;
#else
using dstream = std::ostream&;
using dwstream = std::wostream&;
#endif
} // unit_test
} // beast
} // boost
#endif

View File

@@ -0,0 +1,55 @@
//
// 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_UNIT_TEST_GLOBAL_SUITES_HPP
#define BOOST_BEAST_UNIT_TEST_GLOBAL_SUITES_HPP
#include <boost/beast/_experimental/unit_test/suite_list.hpp>
namespace boost {
namespace beast {
namespace unit_test {
namespace detail {
/// Holds test suites registered during static initialization.
inline
suite_list&
global_suites()
{
static suite_list s;
return s;
}
template<class Suite>
struct insert_suite
{
insert_suite(char const* name, char const* module,
char const* library, bool manual)
{
global_suites().insert<Suite>(
name, module, library, manual);
}
};
} // detail
/// Holds test suites registered during static initialization.
inline
suite_list const&
global_suites()
{
return detail::global_suites();
}
} // unit_test
} // beast
} // boost
#endif

View File

@@ -0,0 +1,87 @@
//
// 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
//
#include <boost/beast/_experimental/unit_test/amount.hpp>
#include <boost/beast/_experimental/unit_test/dstream.hpp>
#include <boost/beast/_experimental/unit_test/global_suites.hpp>
#include <boost/beast/_experimental/unit_test/match.hpp>
#include <boost/beast/_experimental/unit_test/reporter.hpp>
#include <boost/beast/_experimental/unit_test/suite.hpp>
#include <boost/config.hpp>
#include <cstdlib>
#include <iostream>
#include <vector>
#ifdef BOOST_MSVC
# ifndef WIN32_LEAN_AND_MEAN // VC_EXTRALEAN
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# undef WIN32_LEAN_AND_MEAN
# else
# include <windows.h>
# endif
#endif
// Simple main used to produce stand
// alone executables that run unit tests.
int main(int ac, char const* av[])
{
using namespace std;
using namespace boost::beast::unit_test;
dstream log(std::cerr);
std::unitbuf(log);
#ifdef BOOST_MSVC
{
int flags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
flags |= _CRTDBG_LEAK_CHECK_DF;
_CrtSetDbgFlag(flags);
}
#endif
if(ac == 2)
{
std::string const s{av[1]};
if(s == "-h" || s == "--help")
{
log <<
"Usage:\n"
" " << av[0] << ": { <suite-name>... }" <<
std::endl;
return EXIT_SUCCESS;
}
}
reporter r(log);
bool failed;
if(ac > 1)
{
std::vector<selector> v;
v.reserve(ac - 1);
for(int i = 1; i < ac; ++i)
v.emplace_back(selector::automatch, av[i]);
auto pred =
[&v](suite_info const& si) mutable
{
for(auto& p : v)
if(p(si))
return true;
return false;
};
failed = r.run_each_if(global_suites(), pred);
}
else
{
failed = r.run_each(global_suites());
}
if(failed)
return EXIT_FAILURE;
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,177 @@
//
// 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_UNIT_TEST_MATCH_HPP
#define BOOST_BEAST_UNIT_TEST_MATCH_HPP
#include <boost/beast/_experimental/unit_test/suite_info.hpp>
#include <string>
namespace boost {
namespace beast {
namespace unit_test {
// Predicate for implementing matches
class selector
{
public:
enum mode_t
{
// Run all tests except manual ones
all,
// Run tests that match in any field
automatch,
// Match on suite
suite,
// Match on library
library,
// Match on module (used internally)
module,
// Match nothing (used internally)
none
};
private:
mode_t mode_;
std::string pat_;
std::string library_;
public:
template<class = void>
explicit
selector(mode_t mode, std::string const& pattern = "");
template<class = void>
bool
operator()(suite_info const& s);
};
//------------------------------------------------------------------------------
template<class>
selector::selector(mode_t mode, std::string const& pattern)
: mode_(mode)
, pat_(pattern)
{
if(mode_ == automatch && pattern.empty())
mode_ = all;
}
template<class>
bool
selector::operator()(suite_info const& s)
{
switch(mode_)
{
case automatch:
// suite or full name
if(s.name() == pat_ || s.full_name() == pat_)
{
mode_ = none;
return true;
}
// check module
if(pat_ == s.module())
{
mode_ = module;
library_ = s.library();
return ! s.manual();
}
// check library
if(pat_ == s.library())
{
mode_ = library;
return ! s.manual();
}
return false;
case suite:
return pat_ == s.name();
case module:
return pat_ == s.module() && ! s.manual();
case library:
return pat_ == s.library() && ! s.manual();
case none:
return false;
case all:
default:
// fall through
break;
};
return ! s.manual();
}
//------------------------------------------------------------------------------
// Utility functions for producing predicates to select suites.
/** Returns a predicate that implements a smart matching rule.
The predicate checks the suite, module, and library fields of the
suite_info in that order. When it finds a match, it changes modes
depending on what was found:
If a suite is matched first, then only the suite is selected. The
suite may be marked manual.
If a module is matched first, then only suites from that module
and library not marked manual are selected from then on.
If a library is matched first, then only suites from that library
not marked manual are selected from then on.
*/
inline
selector
match_auto(std::string const& name)
{
return selector(selector::automatch, name);
}
/** Return a predicate that matches all suites not marked manual. */
inline
selector
match_all()
{
return selector(selector::all);
}
/** Returns a predicate that matches a specific suite. */
inline
selector
match_suite(std::string const& name)
{
return selector(selector::suite, name);
}
/** Returns a predicate that matches all suites in a library. */
inline
selector
match_library(std::string const& name)
{
return selector(selector::library, name);
}
} // unit_test
} // beast
} // boost
#endif

View File

@@ -0,0 +1,93 @@
//
// 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_UNIT_TEST_RECORDER_HPP
#define BOOST_BEAST_UNIT_TEST_RECORDER_HPP
#include <boost/beast/_experimental/unit_test/results.hpp>
#include <boost/beast/_experimental/unit_test/runner.hpp>
namespace boost {
namespace beast {
namespace unit_test {
/** A test runner that stores the results. */
class recorder : public runner
{
results m_results;
suite_results m_suite;
case_results m_case;
public:
recorder() = default;
/** Returns a report with the results of all completed suites. */
results const&
report() const
{
return m_results;
}
private:
virtual
void
on_suite_begin(suite_info const& info) override
{
m_suite = suite_results(info.full_name());
}
virtual
void
on_suite_end() override
{
m_results.insert(std::move(m_suite));
}
virtual
void
on_case_begin(std::string const& name) override
{
m_case = case_results(name);
}
virtual
void
on_case_end() override
{
if(m_case.tests.size() > 0)
m_suite.insert(std::move(m_case));
}
virtual
void
on_pass() override
{
m_case.tests.pass();
}
virtual
void
on_fail(std::string const& reason) override
{
m_case.tests.fail(reason);
}
virtual
void
on_log(std::string const& s) override
{
m_case.log.insert(s);
}
};
} // unit_test
} // beast
} // boost
#endif

View File

@@ -0,0 +1,292 @@
//
// 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_UNIT_TEST_REPORTER_HPP
#define BOOST_BEAST_UNIT_TEST_REPORTER_HPP
#include <boost/beast/_experimental/unit_test/amount.hpp>
#include <boost/beast/_experimental/unit_test/recorder.hpp>
#include <algorithm>
#include <chrono>
#include <functional>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <utility>
namespace boost {
namespace beast {
namespace unit_test {
namespace detail {
/** A simple test runner that writes everything to a stream in real time.
The totals are output when the object is destroyed.
*/
template<class = void>
class reporter : public runner
{
private:
using clock_type = std::chrono::steady_clock;
struct case_results
{
std::string name;
std::size_t total = 0;
std::size_t failed = 0;
explicit
case_results(std::string name_ = "")
: name(std::move(name_))
{
}
};
struct suite_results
{
std::string name;
std::size_t cases = 0;
std::size_t total = 0;
std::size_t failed = 0;
typename clock_type::time_point start = clock_type::now();
explicit
suite_results(std::string name_ = "")
: name(std::move(name_))
{
}
void
add(case_results const& r);
};
struct results
{
using run_time = std::pair<std::string,
typename clock_type::duration>;
enum
{
max_top = 10
};
std::size_t suites = 0;
std::size_t cases = 0;
std::size_t total = 0;
std::size_t failed = 0;
std::vector<run_time> top;
typename clock_type::time_point start = clock_type::now();
void
add(suite_results const& r);
};
std::ostream& os_;
results results_;
suite_results suite_results_;
case_results case_results_;
public:
reporter(reporter const&) = delete;
reporter& operator=(reporter const&) = delete;
~reporter();
explicit
reporter(std::ostream& os = std::cout);
private:
static
std::string
fmtdur(typename clock_type::duration const& d);
virtual
void
on_suite_begin(suite_info const& info) override;
virtual
void
on_suite_end() override;
virtual
void
on_case_begin(std::string const& name) override;
virtual
void
on_case_end() override;
virtual
void
on_pass() override;
virtual
void
on_fail(std::string const& reason) override;
virtual
void
on_log(std::string const& s) override;
};
//------------------------------------------------------------------------------
template<class _>
void
reporter<_>::
suite_results::add(case_results const& r)
{
++cases;
total += r.total;
failed += r.failed;
}
template<class _>
void
reporter<_>::
results::add(suite_results const& r)
{
++suites;
total += r.total;
cases += r.cases;
failed += r.failed;
auto const elapsed = clock_type::now() - r.start;
if(elapsed >= std::chrono::seconds{1})
{
auto const iter = std::lower_bound(top.begin(),
top.end(), elapsed,
[](run_time const& t1,
typename clock_type::duration const& t2)
{
return t1.second > t2;
});
if(iter != top.end())
{
top.emplace(iter, r.name, elapsed);
if(top.size() > max_top)
top.resize(max_top);
}
}
}
//------------------------------------------------------------------------------
template<class _>
reporter<_>::
reporter(std::ostream& os)
: os_(os)
{
}
template<class _>
reporter<_>::~reporter()
{
if(results_.top.size() > 0)
{
os_ << "Longest suite times:\n";
for(auto const& i : results_.top)
os_ << std::setw(8) <<
fmtdur(i.second) << " " << i.first << '\n';
}
auto const elapsed = clock_type::now() - results_.start;
os_ <<
fmtdur(elapsed) << ", " <<
amount{results_.suites, "suite"} << ", " <<
amount{results_.cases, "case"} << ", " <<
amount{results_.total, "test"} << " total, " <<
amount{results_.failed, "failure"} <<
std::endl;
}
template<class _>
std::string
reporter<_>::fmtdur(typename clock_type::duration const& d)
{
using namespace std::chrono;
auto const ms = duration_cast<milliseconds>(d);
if(ms < seconds{1})
return std::to_string(ms.count()) + "ms";
std::stringstream ss;
ss << std::fixed << std::setprecision(1) <<
(ms.count()/1000.) << "s";
return ss.str();
}
template<class _>
void
reporter<_>::
on_suite_begin(suite_info const& info)
{
suite_results_ = suite_results{info.full_name()};
}
template<class _>
void
reporter<_>::on_suite_end()
{
results_.add(suite_results_);
}
template<class _>
void
reporter<_>::
on_case_begin(std::string const& name)
{
case_results_ = case_results(name);
os_ << suite_results_.name <<
(case_results_.name.empty() ? "" :
(" " + case_results_.name)) << std::endl;
}
template<class _>
void
reporter<_>::
on_case_end()
{
suite_results_.add(case_results_);
}
template<class _>
void
reporter<_>::
on_pass()
{
++case_results_.total;
}
template<class _>
void
reporter<_>::
on_fail(std::string const& reason)
{
++case_results_.failed;
++case_results_.total;
os_ <<
"#" << case_results_.total << " failed" <<
(reason.empty() ? "" : ": ") << reason << std::endl;
}
template<class _>
void
reporter<_>::
on_log(std::string const& s)
{
os_ << s;
}
} // detail
using reporter = detail::reporter<>;
} // unit_test
} // beast
} // boost
#endif

View File

@@ -0,0 +1,246 @@
//
// 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_UNIT_TEST_RESULTS_HPP
#define BOOST_BEAST_UNIT_TEST_RESULTS_HPP
#include <boost/beast/_experimental/unit_test/detail/const_container.hpp>
#include <string>
#include <vector>
namespace boost {
namespace beast {
namespace unit_test {
/** Holds a set of test condition outcomes in a testcase. */
class case_results
{
public:
/** Holds the result of evaluating one test condition. */
struct test
{
explicit test(bool pass_)
: pass(pass_)
{
}
test(bool pass_, std::string const& reason_)
: pass(pass_)
, reason(reason_)
{
}
bool pass;
std::string reason;
};
private:
class tests_t
: public detail::const_container <std::vector <test>>
{
private:
std::size_t failed_;
public:
tests_t()
: failed_(0)
{
}
/** Returns the total number of test conditions. */
std::size_t
total() const
{
return cont().size();
}
/** Returns the number of failed test conditions. */
std::size_t
failed() const
{
return failed_;
}
/** Register a successful test condition. */
void
pass()
{
cont().emplace_back(true);
}
/** Register a failed test condition. */
void
fail(std::string const& reason = "")
{
++failed_;
cont().emplace_back(false, reason);
}
};
class log_t
: public detail::const_container <std::vector <std::string>>
{
public:
/** Insert a string into the log. */
void
insert(std::string const& s)
{
cont().push_back(s);
}
};
std::string name_;
public:
explicit case_results(std::string const& name = "")
: name_(name)
{
}
/** Returns the name of this testcase. */
std::string const&
name() const
{
return name_;
}
/** Memberspace for a container of test condition outcomes. */
tests_t tests;
/** Memberspace for a container of testcase log messages. */
log_t log;
};
//--------------------------------------------------------------------------
/** Holds the set of testcase results in a suite. */
class suite_results
: public detail::const_container <std::vector <case_results>>
{
private:
std::string name_;
std::size_t total_ = 0;
std::size_t failed_ = 0;
public:
explicit suite_results(std::string const& name = "")
: name_(name)
{
}
/** Returns the name of this suite. */
std::string const&
name() const
{
return name_;
}
/** Returns the total number of test conditions. */
std::size_t
total() const
{
return total_;
}
/** Returns the number of failures. */
std::size_t
failed() const
{
return failed_;
}
/** Insert a set of testcase results. */
/** @{ */
void
insert(case_results&& r)
{
cont().emplace_back(std::move(r));
total_ += r.tests.total();
failed_ += r.tests.failed();
}
void
insert(case_results const& r)
{
cont().push_back(r);
total_ += r.tests.total();
failed_ += r.tests.failed();
}
/** @} */
};
//------------------------------------------------------------------------------
// VFALCO TODO Make this a template class using scoped allocators
/** Holds the results of running a set of testsuites. */
class results
: public detail::const_container <std::vector <suite_results>>
{
private:
std::size_t m_cases;
std::size_t total_;
std::size_t failed_;
public:
results()
: m_cases(0)
, total_(0)
, failed_(0)
{
}
/** Returns the total number of test cases. */
std::size_t
cases() const
{
return m_cases;
}
/** Returns the total number of test conditions. */
std::size_t
total() const
{
return total_;
}
/** Returns the number of failures. */
std::size_t
failed() const
{
return failed_;
}
/** Insert a set of suite results. */
/** @{ */
void
insert(suite_results&& r)
{
m_cases += r.size();
total_ += r.total();
failed_ += r.failed();
cont().emplace_back(std::move(r));
}
void
insert(suite_results const& r)
{
m_cases += r.size();
total_ += r.total();
failed_ += r.failed();
cont().push_back(r);
}
/** @} */
};
} // unit_test
} // beast
} // boost
#endif

View File

@@ -0,0 +1,292 @@
//
// 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_UNIT_TEST_RUNNER_H_INCLUDED
#define BOOST_BEAST_UNIT_TEST_RUNNER_H_INCLUDED
#include <boost/beast/_experimental/unit_test/suite_info.hpp>
#include <boost/assert.hpp>
#include <mutex>
#include <ostream>
#include <string>
namespace boost {
namespace beast {
namespace unit_test {
/** Unit test runner interface.
Derived classes can customize the reporting behavior. This interface is
injected into the unit_test class to receive the results of the tests.
*/
class runner
{
std::string arg_;
bool default_ = false;
bool failed_ = false;
bool cond_ = false;
std::recursive_mutex mutex_;
public:
runner() = default;
virtual ~runner() = default;
runner(runner const&) = delete;
runner& operator=(runner const&) = delete;
/** Set the argument string.
The argument string is available to suites and
allows for customization of the test. Each suite
defines its own syntax for the argument string.
The same argument is passed to all suites.
*/
void
arg(std::string const& s)
{
arg_ = s;
}
/** Returns the argument string. */
std::string const&
arg() const
{
return arg_;
}
/** Run the specified suite.
@return `true` if any conditions failed.
*/
template<class = void>
bool
run(suite_info const& s);
/** Run a sequence of suites.
The expression
`FwdIter::value_type`
must be convertible to `suite_info`.
@return `true` if any conditions failed.
*/
template<class FwdIter>
bool
run(FwdIter first, FwdIter last);
/** Conditionally run a sequence of suites.
pred will be called as:
@code
bool pred(suite_info const&);
@endcode
@return `true` if any conditions failed.
*/
template<class FwdIter, class Pred>
bool
run_if(FwdIter first, FwdIter last, Pred pred = Pred{});
/** Run all suites in a container.
@return `true` if any conditions failed.
*/
template<class SequenceContainer>
bool
run_each(SequenceContainer const& c);
/** Conditionally run suites in a container.
pred will be called as:
@code
bool pred(suite_info const&);
@endcode
@return `true` if any conditions failed.
*/
template<class SequenceContainer, class Pred>
bool
run_each_if(SequenceContainer const& c, Pred pred = Pred{});
protected:
/// Called when a new suite starts.
virtual
void
on_suite_begin(suite_info const&)
{
}
/// Called when a suite ends.
virtual
void
on_suite_end()
{
}
/// Called when a new case starts.
virtual
void
on_case_begin(std::string const&)
{
}
/// Called when a new case ends.
virtual
void
on_case_end()
{
}
/// Called for each passing condition.
virtual
void
on_pass()
{
}
/// Called for each failing condition.
virtual
void
on_fail(std::string const&)
{
}
/// Called when a test logs output.
virtual
void
on_log(std::string const&)
{
}
private:
friend class suite;
// Start a new testcase.
template<class = void>
void
testcase(std::string const& name);
template<class = void>
void
pass();
template<class = void>
void
fail(std::string const& reason);
template<class = void>
void
log(std::string const& s);
};
//------------------------------------------------------------------------------
template<class>
bool
runner::run(suite_info const& s)
{
// Enable 'default' testcase
default_ = true;
failed_ = false;
on_suite_begin(s);
s.run(*this);
// Forgot to call pass or fail.
BOOST_ASSERT(cond_);
on_case_end();
on_suite_end();
return failed_;
}
template<class FwdIter>
bool
runner::run(FwdIter first, FwdIter last)
{
bool failed(false);
for(;first != last; ++first)
failed = run(*first) || failed;
return failed;
}
template<class FwdIter, class Pred>
bool
runner::run_if(FwdIter first, FwdIter last, Pred pred)
{
bool failed(false);
for(;first != last; ++first)
if(pred(*first))
failed = run(*first) || failed;
return failed;
}
template<class SequenceContainer>
bool
runner::run_each(SequenceContainer const& c)
{
bool failed(false);
for(auto const& s : c)
failed = run(s) || failed;
return failed;
}
template<class SequenceContainer, class Pred>
bool
runner::run_each_if(SequenceContainer const& c, Pred pred)
{
bool failed(false);
for(auto const& s : c)
if(pred(s))
failed = run(s) || failed;
return failed;
}
template<class>
void
runner::testcase(std::string const& name)
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
// Name may not be empty
BOOST_ASSERT(default_ || ! name.empty());
// Forgot to call pass or fail
BOOST_ASSERT(default_ || cond_);
if(! default_)
on_case_end();
default_ = false;
cond_ = false;
on_case_begin(name);
}
template<class>
void
runner::pass()
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
if(default_)
testcase("");
on_pass();
cond_ = true;
}
template<class>
void
runner::fail(std::string const& reason)
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
if(default_)
testcase("");
on_fail(reason);
failed_ = true;
cond_ = true;
}
template<class>
void
runner::log(std::string const& s)
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
if(default_)
testcase("");
on_log(s);
}
} // unit_test
} // beast
} // boost
#endif

View File

@@ -0,0 +1,725 @@
//
// 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_UNIT_TEST_SUITE_HPP
#define BOOST_BEAST_UNIT_TEST_SUITE_HPP
#include <boost/beast/_experimental/unit_test/runner.hpp>
#include <boost/throw_exception.hpp>
#include <ostream>
#include <sstream>
#include <string>
namespace boost {
namespace beast {
namespace unit_test {
namespace detail {
template<class String>
std::string
make_reason(String const& reason,
char const* file, int line)
{
std::string s(reason);
if(! s.empty())
s.append(": ");
char const* path = file + strlen(file);
while(path != file)
{
#ifdef _MSC_VER
if(path[-1] == '\\')
#else
if(path[-1] == '/')
#endif
break;
--path;
}
s.append(path);
s.append("(");
s.append(std::to_string(line));
s.append(")");
return s;
}
} // detail
class thread;
enum abort_t
{
no_abort_on_fail,
abort_on_fail
};
/** A testsuite class.
Derived classes execute a series of testcases, where each testcase is
a series of pass/fail tests. To provide a unit test using this class,
derive from it and use the BOOST_BEAST_DEFINE_UNIT_TEST macro in a
translation unit.
*/
class suite
{
private:
bool abort_ = false;
bool aborted_ = false;
runner* runner_ = nullptr;
// This exception is thrown internally to stop the current suite
// in the event of a failure, if the option to stop is set.
struct abort_exception : public std::exception
{
char const*
what() const noexcept override
{
return "test suite aborted";
}
};
template<class CharT, class Traits, class Allocator>
class log_buf
: public std::basic_stringbuf<CharT, Traits, Allocator>
{
suite& suite_;
public:
explicit
log_buf(suite& self)
: suite_(self)
{
}
~log_buf()
{
sync();
}
int
sync() override
{
auto const& s = this->str();
if(s.size() > 0)
suite_.runner_->log(s);
this->str("");
return 0;
}
};
template<
class CharT,
class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>
>
class log_os : public std::basic_ostream<CharT, Traits>
{
log_buf<CharT, Traits, Allocator> buf_;
public:
explicit
log_os(suite& self)
: std::basic_ostream<CharT, Traits>(&buf_)
, buf_(self)
{
}
};
class scoped_testcase;
class testcase_t
{
suite& suite_;
std::stringstream ss_;
public:
explicit
testcase_t(suite& self)
: suite_(self)
{
}
/** Open a new testcase.
A testcase is a series of evaluated test conditions. A test
suite may have multiple test cases. A test is associated with
the last opened testcase. When the test first runs, a default
unnamed case is opened. Tests with only one case may omit the
call to testcase.
@param abort Determines if suite continues running after a failure.
*/
void
operator()(std::string const& name,
abort_t abort = no_abort_on_fail);
scoped_testcase
operator()(abort_t abort);
template<class T>
scoped_testcase
operator<<(T const& t);
};
public:
/** Logging output stream.
Text sent to the log output stream will be forwarded to
the output stream associated with the runner.
*/
log_os<char> log;
/** Memberspace for declaring test cases. */
testcase_t testcase;
/** Returns the "current" running suite.
If no suite is running, nullptr is returned.
*/
static
suite*
this_suite()
{
return *p_this_suite();
}
suite()
: log(*this)
, testcase(*this)
{
}
virtual ~suite() = default;
suite(suite const&) = delete;
suite& operator=(suite const&) = delete;
/** Invokes the test using the specified runner.
Data members are set up here instead of the constructor as a
convenience to writing the derived class to avoid repetition of
forwarded constructor arguments to the base.
Normally this is called by the framework for you.
*/
template<class = void>
void
operator()(runner& r);
/** Record a successful test condition. */
template<class = void>
void
pass();
/** Record a failure.
@param reason Optional text added to the output on a failure.
@param file The source code file where the test failed.
@param line The source code line number where the test failed.
*/
/** @{ */
template<class String>
void
fail(String const& reason, char const* file, int line);
template<class = void>
void
fail(std::string const& reason = "");
/** @} */
/** Evaluate a test condition.
This function provides improved logging by incorporating the
file name and line number into the reported output on failure,
as well as additional text specified by the caller.
@param shouldBeTrue The condition to test. The condition
is evaluated in a boolean context.
@param reason Optional added text to output on a failure.
@param file The source code file where the test failed.
@param line The source code line number where the test failed.
@return `true` if the test condition indicates success.
*/
/** @{ */
template<class Condition>
bool
expect(Condition const& shouldBeTrue)
{
return expect(shouldBeTrue, "");
}
template<class Condition, class String>
bool
expect(Condition const& shouldBeTrue, String const& reason);
template<class Condition>
bool
expect(Condition const& shouldBeTrue,
char const* file, int line)
{
return expect(shouldBeTrue, "", file, line);
}
template<class Condition, class String>
bool
expect(Condition const& shouldBeTrue,
String const& reason, char const* file, int line);
/** @} */
//
// DEPRECATED
//
// Expect an exception from f()
template<class F, class String>
bool
except(F&& f, String const& reason);
template<class F>
bool
except(F&& f)
{
return except(f, "");
}
template<class E, class F, class String>
bool
except(F&& f, String const& reason);
template<class E, class F>
bool
except(F&& f)
{
return except<E>(f, "");
}
template<class F, class String>
bool
unexcept(F&& f, String const& reason);
template<class F>
bool
unexcept(F&& f)
{
return unexcept(f, "");
}
/** Return the argument associated with the runner. */
std::string const&
arg() const
{
return runner_->arg();
}
// DEPRECATED
// @return `true` if the test condition indicates success(a false value)
template<class Condition, class String>
bool
unexpected(Condition shouldBeFalse,
String const& reason);
template<class Condition>
bool
unexpected(Condition shouldBeFalse)
{
return unexpected(shouldBeFalse, "");
}
private:
friend class thread;
static
suite**
p_this_suite()
{
static suite* pts = nullptr;
return &pts;
}
/** Runs the suite. */
virtual
void
run() = 0;
void
propagate_abort();
template<class = void>
void
run(runner& r);
};
//------------------------------------------------------------------------------
// Helper for streaming testcase names
class suite::scoped_testcase
{
private:
suite& suite_;
std::stringstream& ss_;
public:
scoped_testcase& operator=(scoped_testcase const&) = delete;
~scoped_testcase()
{
auto const& name = ss_.str();
if(! name.empty())
suite_.runner_->testcase(name);
}
scoped_testcase(suite& self, std::stringstream& ss)
: suite_(self)
, ss_(ss)
{
ss_.clear();
ss_.str({});
}
template<class T>
scoped_testcase(suite& self,
std::stringstream& ss, T const& t)
: suite_(self)
, ss_(ss)
{
ss_.clear();
ss_.str({});
ss_ << t;
}
template<class T>
scoped_testcase&
operator<<(T const& t)
{
ss_ << t;
return *this;
}
};
//------------------------------------------------------------------------------
inline
void
suite::testcase_t::operator()(
std::string const& name, abort_t abort)
{
suite_.abort_ = abort == abort_on_fail;
suite_.runner_->testcase(name);
}
inline
suite::scoped_testcase
suite::testcase_t::operator()(abort_t abort)
{
suite_.abort_ = abort == abort_on_fail;
return { suite_, ss_ };
}
template<class T>
inline
suite::scoped_testcase
suite::testcase_t::operator<<(T const& t)
{
return { suite_, ss_, t };
}
//------------------------------------------------------------------------------
template<class>
void
suite::
operator()(runner& r)
{
*p_this_suite() = this;
try
{
run(r);
*p_this_suite() = nullptr;
}
catch(...)
{
*p_this_suite() = nullptr;
throw;
}
}
template<class Condition, class String>
bool
suite::
expect(
Condition const& shouldBeTrue, String const& reason)
{
if(shouldBeTrue)
{
pass();
return true;
}
fail(reason);
return false;
}
template<class Condition, class String>
bool
suite::
expect(Condition const& shouldBeTrue,
String const& reason, char const* file, int line)
{
if(shouldBeTrue)
{
pass();
return true;
}
fail(detail::make_reason(reason, file, line));
return false;
}
// DEPRECATED
template<class F, class String>
bool
suite::
except(F&& f, String const& reason)
{
try
{
f();
fail(reason);
return false;
}
catch(...)
{
pass();
}
return true;
}
template<class E, class F, class String>
bool
suite::
except(F&& f, String const& reason)
{
try
{
f();
fail(reason);
return false;
}
catch(E const&)
{
pass();
}
return true;
}
template<class F, class String>
bool
suite::
unexcept(F&& f, String const& reason)
{
try
{
f();
pass();
return true;
}
catch(...)
{
fail(reason);
}
return false;
}
template<class Condition, class String>
bool
suite::
unexpected(
Condition shouldBeFalse, String const& reason)
{
bool const b =
static_cast<bool>(shouldBeFalse);
if(! b)
pass();
else
fail(reason);
return ! b;
}
template<class>
void
suite::
pass()
{
propagate_abort();
runner_->pass();
}
// ::fail
template<class>
void
suite::
fail(std::string const& reason)
{
propagate_abort();
runner_->fail(reason);
if(abort_)
{
aborted_ = true;
BOOST_THROW_EXCEPTION(abort_exception());
}
}
template<class String>
void
suite::
fail(String const& reason, char const* file, int line)
{
fail(detail::make_reason(reason, file, line));
}
inline
void
suite::
propagate_abort()
{
if(abort_ && aborted_)
BOOST_THROW_EXCEPTION(abort_exception());
}
template<class>
void
suite::
run(runner& r)
{
runner_ = &r;
try
{
run();
}
catch(abort_exception const&)
{
// ends the suite
}
catch(std::exception const& e)
{
runner_->fail("unhandled exception: " +
std::string(e.what()));
}
catch(...)
{
runner_->fail("unhandled exception");
}
}
#ifndef BEAST_PASS
#define BEAST_PASS() ::boost::beast::unit_test::suite::this_suite()->pass()
#endif
#ifndef BEAST_FAIL
#define BEAST_FAIL() ::boost::beast::unit_test::suite::this_suite()->fail("", __FILE__, __LINE__)
#endif
#ifndef BEAST_EXPECT
/** Check a precondition.
If the condition is false, the file and line number are reported.
*/
#define BEAST_EXPECT(cond) ::boost::beast::unit_test::suite::this_suite()->expect(cond, __FILE__, __LINE__)
#endif
#ifndef BEAST_EXPECTS
/** Check a precondition.
If the condition is false, the file and line number are reported.
*/
#define BEAST_EXPECTS(cond, reason) ((cond) ? \
(::boost::beast::unit_test::suite::this_suite()->pass(), true) : \
(::boost::beast::unit_test::suite::this_suite()->fail((reason), __FILE__, __LINE__), false))
#endif
/** Ensure an exception is thrown
*/
#define BEAST_THROWS( EXPR, EXCEP ) \
try { \
EXPR; \
BEAST_FAIL(); \
} \
catch(EXCEP const&) { \
BEAST_PASS(); \
} \
catch(...) { \
BEAST_FAIL(); \
}
} // unit_test
} // beast
} // boost
//------------------------------------------------------------------------------
// detail:
// This inserts the suite with the given manual flag
#define BEAST_DEFINE_TESTSUITE_INSERT(Library,Module,Class,manual) \
static ::boost::beast::unit_test::detail::insert_suite <Class##_test> \
Library ## Module ## Class ## _test_instance( \
#Class, #Module, #Library, manual)
//------------------------------------------------------------------------------
// Preprocessor directives for controlling unit test definitions.
// If this is already defined, don't redefine it. This allows
// programs to provide custom behavior for testsuite definitions
//
#ifndef BEAST_DEFINE_TESTSUITE
/** Enables insertion of test suites into the global container.
The default is to insert all test suite definitions into the global
container. If BEAST_DEFINE_TESTSUITE is user defined, this macro
has no effect.
*/
#ifndef BEAST_NO_UNIT_TEST_INLINE
#define BEAST_NO_UNIT_TEST_INLINE 0
#endif
/** Define a unit test suite.
Library Identifies the library.
Module Identifies the module.
Class The type representing the class being tested.
The declaration for the class implementing the test should be the same
as Class ## _test. For example, if Class is aged_ordered_container, the
test class must be declared as:
@code
struct aged_ordered_container_test : beast::unit_test::suite
{
//...
};
@endcode
The macro invocation must appear in the same namespace as the test class.
*/
#if BEAST_NO_UNIT_TEST_INLINE
#define BEAST_DEFINE_TESTSUITE(Class,Module,Library)
#else
#include <boost/beast/_experimental/unit_test/global_suites.hpp>
#define BEAST_DEFINE_TESTSUITE(Library,Module,Class) \
BEAST_DEFINE_TESTSUITE_INSERT(Library,Module,Class,false)
#define BEAST_DEFINE_TESTSUITE_MANUAL(Library,Module,Class) \
BEAST_DEFINE_TESTSUITE_INSERT(Library,Module,Class,true)
#endif
#endif
//------------------------------------------------------------------------------
#endif

View File

@@ -0,0 +1,126 @@
//
// 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_UNIT_TEST_SUITE_INFO_HPP
#define BOOST_BEAST_UNIT_TEST_SUITE_INFO_HPP
#include <cstring>
#include <functional>
#include <string>
#include <utility>
namespace boost {
namespace beast {
namespace unit_test {
class runner;
/** Associates a unit test type with metadata. */
class suite_info
{
using run_type = std::function<void(runner&)>;
std::string name_;
std::string module_;
std::string library_;
bool manual_;
run_type run_;
public:
suite_info(
std::string name,
std::string module,
std::string library,
bool manual,
run_type run)
: name_(std::move(name))
, module_(std::move(module))
, library_(std::move(library))
, manual_(manual)
, run_(std::move(run))
{
}
std::string const&
name() const
{
return name_;
}
std::string const&
module() const
{
return module_;
}
std::string const&
library() const
{
return library_;
}
/// Returns `true` if this suite only runs manually.
bool
manual() const
{
return manual_;
}
/// Return the canonical suite name as a string.
std::string
full_name() const
{
return library_ + "." + module_ + "." + name_;
}
/// Run a new instance of the associated test suite.
void
run(runner& r) const
{
run_(r);
}
friend
bool
operator<(suite_info const& lhs, suite_info const& rhs)
{
return
std::tie(lhs.library_, lhs.module_, lhs.name_) <
std::tie(rhs.library_, rhs.module_, rhs.name_);
}
};
//------------------------------------------------------------------------------
/// Convenience for producing suite_info for a given test type.
template<class Suite>
suite_info
make_suite_info(
std::string name,
std::string module,
std::string library,
bool manual)
{
return suite_info(
std::move(name),
std::move(module),
std::move(library),
manual,
[](runner& r)
{
Suite{}(r);
}
);
}
} // unit_test
} // beast
} // boost
#endif

View File

@@ -0,0 +1,81 @@
//
// 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_UNIT_TEST_SUITE_LIST_HPP
#define BOOST_BEAST_UNIT_TEST_SUITE_LIST_HPP
#include <boost/beast/_experimental/unit_test/suite_info.hpp>
#include <boost/beast/_experimental/unit_test/detail/const_container.hpp>
#include <boost/assert.hpp>
#include <typeindex>
#include <set>
#include <unordered_set>
namespace boost {
namespace beast {
namespace unit_test {
/// A container of test suites.
class suite_list
: public detail::const_container <std::set <suite_info>>
{
private:
#ifndef NDEBUG
std::unordered_set<std::string> names_;
std::unordered_set<std::type_index> classes_;
#endif
public:
/** Insert a suite into the set.
The suite must not already exist.
*/
template<class Suite>
void
insert(
char const* name,
char const* module,
char const* library,
bool manual);
};
//------------------------------------------------------------------------------
template<class Suite>
void
suite_list::insert(
char const* name,
char const* module,
char const* library,
bool manual)
{
#ifndef NDEBUG
{
std::string s;
s = std::string(library) + "." + module + "." + name;
auto const result(names_.insert(s));
BOOST_ASSERT(result.second); // Duplicate name
}
{
auto const result(classes_.insert(
std::type_index(typeid(Suite))));
BOOST_ASSERT(result.second); // Duplicate type
}
#endif
cont().emplace(make_suite_info<Suite>(
name, module, library, manual));
}
} // unit_test
} // beast
} // boost
#endif

View File

@@ -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_CORE_HPP
#define BOOST_BEAST_CORE_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/async_base.hpp>
#include <boost/beast/core/basic_stream.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/beast/core/buffered_read_stream.hpp>
#include <boost/beast/core/buffers_adaptor.hpp>
#include <boost/beast/core/buffers_cat.hpp>
#include <boost/beast/core/buffers_prefix.hpp>
#include <boost/beast/core/buffers_range.hpp>
#include <boost/beast/core/buffers_suffix.hpp>
#include <boost/beast/core/buffers_to_string.hpp>
#include <boost/beast/core/detect_ssl.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/file.hpp>
#include <boost/beast/core/file_base.hpp>
#include <boost/beast/core/file_posix.hpp>
#include <boost/beast/core/file_stdio.hpp>
#include <boost/beast/core/file_win32.hpp>
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/beast/core/flat_static_buffer.hpp>
#include <boost/beast/core/flat_stream.hpp>
#include <boost/beast/core/make_printable.hpp>
#include <boost/beast/core/multi_buffer.hpp>
#include <boost/beast/core/ostream.hpp>
#include <boost/beast/core/rate_policy.hpp>
#include <boost/beast/core/read_size.hpp>
#include <boost/beast/core/role.hpp>
#include <boost/beast/core/saved_handler.hpp>
#include <boost/beast/core/span.hpp>
#include <boost/beast/core/static_buffer.hpp>
#include <boost/beast/core/static_string.hpp>
#include <boost/beast/core/stream_traits.hpp>
#include <boost/beast/core/string.hpp>
#include <boost/beast/core/tcp_stream.hpp>
#endif

View File

@@ -0,0 +1,726 @@
//
// 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/beast/core/detail/work_guard.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/bind_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/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->complete(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 || net::execution::is_executor<Executor1>::value,
"Executor type requirements not met");
Handler h_;
detail::select_work_guard_t<Executor1> wg1_;
public:
/** 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 =
#if BOOST_BEAST_DOXYGEN
__implementation_defined__;
#else
typename
net::associated_executor<
Handler,
typename detail::select_work_guard_t<Executor1>::executor_type
>::type;
#endif
private:
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_(detail::make_work_guard(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;
virtual ~async_base() = default;
async_base(async_base const&) = delete;
async_base& operator=(async_base const&) = delete;
/** 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>;
/** 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,132 @@
//
// 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_BIND_HANDLER_HPP
#define BOOST_BEAST_BIND_HANDLER_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/bind_handler.hpp>
#include <type_traits>
#include <utility>
namespace boost {
namespace beast {
/** 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 substituted for
placeholders present in the list of bound arguments. Parameters
which are not matched to placeholders are silently discarded.
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_aborted (AsyncReadStream& stream, ReadHandler&& handler)
{
net::post(
stream.get_executor(),
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
arguments may include placeholders, which will operate in
a fashion identical to a call to `std::bind`.
*/
template<class Handler, class... Args>
#if BOOST_BEAST_DOXYGEN
__implementation_defined__
#else
detail::bind_wrapper<
typename std::decay<Handler>::type,
typename std::decay<Args>::type...>
#endif
bind_handler(Handler&& handler, Args&&... args)
{
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
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
} // boost
#endif

View File

@@ -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 net::buffer_size;
return buffer_size(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

View File

@@ -0,0 +1,359 @@
//
// 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_BUFFERED_READ_STREAM_HPP
#define BOOST_BEAST_BUFFERED_READ_STREAM_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/multi_buffer.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>
#include <cstdint>
#include <utility>
namespace boost {
namespace beast {
/** A <em>Stream</em> with attached <em>DynamicBuffer</em> to buffer reads.
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 <em>DynamicBuffer</em> which
is part of the object.
The use-case for this class is different than that of the
`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 `net::ssl::stream::async_handshake`
could make use of this wrapper.
Uses:
@li Transparently leave untouched input acquired in calls
to `net::read_until` behind for subsequent callers.
@li "Preload" a stream with handshake input data acquired
from other sources.
Example:
@code
// Process the next HTTP header on the stream,
// leaving excess bytes behind for the next call.
//
template<class Stream, class DynamicBuffer>
void process_http_message(
buffered_read_stream<Stream, DynamicBuffer>& stream)
{
// Read up to and including the end of the HTTP
// header, leaving the sequence in the stream's
// buffer. read_until may read past the end of the
// headers; the return value will include only the
// part up to the end of the delimiter.
//
std::size_t bytes_transferred =
net::read_until(
stream.next_layer(), stream.buffer(), "\r\n\r\n");
// Use buffers_prefix() to limit the input
// sequence to only the data up to and including
// the trailing "\r\n\r\n".
//
auto header_buffers = buffers_prefix(
bytes_transferred, stream.buffer().data());
...
// Discard the portion of the input corresponding
// to the HTTP headers.
//
stream.buffer().consume(bytes_transferred);
// Everything we read from the stream
// is part of the content-body.
}
@endcode
@tparam Stream The type of stream to wrap.
@tparam DynamicBuffer The type of stream buffer to use.
*/
template<class Stream, class DynamicBuffer>
class buffered_read_stream
{
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer type requirements not met");
struct ops;
DynamicBuffer buffer_;
std::size_t capacity_ = 0;
Stream next_layer_;
public:
/// The type of the internal buffer
using buffer_type = DynamicBuffer;
/// The type of the next layer.
using next_layer_type =
typename std::remove_reference<Stream>::type;
/** Move constructor.
@note The behavior of move assignment on or from streams
with active or pending operations is undefined.
*/
buffered_read_stream(buffered_read_stream&&) = default;
/** Move assignment.
@note The behavior of move assignment on or from streams
with active or pending operations is undefined.
*/
buffered_read_stream& operator=(buffered_read_stream&&) = default;
/** Construct the wrapping stream.
@param args Parameters forwarded to the `Stream` constructor.
*/
template<class... Args>
explicit
buffered_read_stream(Args&&... args);
/// Get a reference to the next layer.
next_layer_type&
next_layer() noexcept
{
return next_layer_;
}
/// Get a const reference to the next layer.
next_layer_type const&
next_layer() const noexcept
{
return next_layer_;
}
using executor_type =
beast::executor_type<next_layer_type>;
/** 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 next_layer_.get_executor();
}
/** Access the internal buffer.
The internal buffer is returned. It is possible for the
caller to break invariants with this function. For example,
by causing the internal buffer size to increase beyond
the caller defined maximum.
*/
DynamicBuffer&
buffer() noexcept
{
return buffer_;
}
/// Access the internal buffer
DynamicBuffer const&
buffer() const noexcept
{
return buffer_;
}
/** Set the maximum buffer size.
This changes the maximum size of the internal buffer used
to hold read data. No bytes are discarded by this call. If
the buffer size is set to zero, no more data will be buffered.
Thread safety:
The caller is responsible for making sure the call is
made from the same implicit or explicit strand.
@param size The number of bytes in the read buffer.
@note This is a soft limit. If the new maximum size is smaller
than the amount of data in the buffer, no bytes are discarded.
*/
void
capacity(std::size_t size) noexcept
{
capacity_ = size;
}
/** 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 One or more buffers into which the data will be read.
@return The number of bytes read.
@throws system_error Thrown on failure.
*/
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 One or more buffers into which the data will be read.
@param ec Set to the error, if any occurred.
@return The number of bytes read, or 0 on error.
*/
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 data from
the stream. The function call always returns immediately.
@param buffers One or more buffers into which the data
will be read. 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 const& error, // result of operation
std::size_t bytes_transferred // number of bytes transferred
);
@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 MutableBufferSequence,
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler =
net::default_completion_token_t<executor_type>>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler =
net::default_completion_token_t<executor_type>{});
/** Write some data to the stream.
This function is used to write data to the stream.
The function call will block until one or more bytes of the
data has been written successfully, or until an error occurs.
@param buffers One or more data buffers to be written to the stream.
@return The number of bytes written.
@throws system_error Thrown on failure.
*/
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers)
{
static_assert(is_sync_write_stream<next_layer_type>::value,
"SyncWriteStream type requirements not met");
return next_layer_.write_some(buffers);
}
/** Write some data to the stream.
This function is used to write data to the stream.
The function call will block until one or more bytes of the
data has been written successfully, or until an error occurs.
@param buffers One or more data buffers to be written to the stream.
@param ec Set to the error, if any occurred.
@return The number of bytes written.
*/
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers,
error_code& ec)
{
static_assert(is_sync_write_stream<next_layer_type>::value,
"SyncWriteStream type requirements not met");
return next_layer_.write_some(buffers, ec);
}
/** Start an asynchronous write.
This function is used to asynchronously write data from
the stream. The function call always returns immediately.
@param buffers One or more data buffers to be written to
the stream. 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 const& error, // result of operation
std::size_t bytes_transferred // number of bytes transferred
);
@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 ConstBufferSequence,
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler =
net::default_completion_token_t<executor_type>>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
async_write_some(
ConstBufferSequence const& buffers,
WriteHandler&& handler =
net::default_completion_token_t<executor_type>{});
};
} // beast
} // boost
#include <boost/beast/core/impl/buffered_read_stream.hpp>
#endif

View File

@@ -0,0 +1,234 @@
//
// 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 subrange;
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 writable bytes.
using mutable_buffers_type = __implementation_defined__;
#else
using const_buffers_type = subrange<false>;
using mutable_buffers_type = subrange<true>;
#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_buffers_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;
private:
subrange<true>
make_subrange(std::size_t pos, std::size_t n);
subrange<false>
make_subrange(std::size_t pos, std::size_t n) const;
friend struct buffers_adaptor_test_hook;
};
} // beast
} // boost
#include <boost/beast/core/impl/buffers_adaptor.hpp>
#endif

View File

@@ -0,0 +1,105 @@
//
// 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_CAT_HPP
#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>
namespace boost {
namespace beast {
/** A buffer sequence representing a concatenation of buffer sequences.
@see buffers_cat
*/
template<class... Buffers>
class buffers_cat_view
{
detail::tuple<Buffers...> bn_;
public:
/** The type of buffer returned when dereferencing an iterator.
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 = __see_below__;
#else
using value_type = buffers_type<Buffers...>;
#endif
/// The type of iterator used by the concatenated sequence
class const_iterator;
/// Copy Constructor
buffers_cat_view(buffers_cat_view const&) = default;
/// 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 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);
/// Returns an iterator to the first buffer in the sequence
const_iterator
begin() const;
/// Returns an iterator to one past the last buffer in the sequence
const_iterator
end() const;
};
/** Concatenate 1 or more buffer sequences.
This function returns a constant or mutable buffer sequence which,
when iterated, efficiently concatenates the input buffer sequences.
Copies of the arguments passed will be made; however, the returned
object does not take ownership of the underlying memory. The
application is still responsible for managing the lifetime of the
referenced memory.
@param buffers The list of buffer sequences to concatenate.
@return A new buffer sequence that represents the concatenation of
the input buffer sequences. This buffer sequence will be a
<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 buffers_cat_view
*/
#if BOOST_BEAST_DOXYGEN
template<class... BufferSequence>
buffers_cat_view<BufferSequence...>
buffers_cat(BufferSequence const&... buffers)
#else
template<class B1, class... Bn>
buffers_cat_view<B1, Bn...>
buffers_cat(B1 const& b1, Bn const&... bn)
#endif
{
static_assert(
is_const_buffer_sequence<B1, Bn...>::value,
"BufferSequence type requirements not met");
return buffers_cat_view<B1, Bn...>{b1, bn...};
}
} // beast
} // boost
#include <boost/beast/core/impl/buffers_cat.hpp>
#endif

View File

@@ -0,0 +1,201 @@
//
// 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_PREFIX_HPP
#define BOOST_BEAST_BUFFERS_PREFIX_HPP
#include <boost/beast/core/detail/config.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/is_convertible.hpp>
#endif
namespace boost {
namespace beast {
/** 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
with the first byte of the original sequence.
@tparam BufferSequence The buffer sequence to adapt.
*/
template<class BufferSequence>
class buffers_prefix_view
{
using iter_type =
buffers_iterator_type<BufferSequence>;
BufferSequence bs_;
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.
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<
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<BufferSequence>;
#endif
#if BOOST_BEAST_DOXYGEN
/// A bidirectional iterator type that may be used to read elements.
using const_iterator = __implementation_defined__;
#else
class const_iterator;
#endif
/// Copy Constructor
buffers_prefix_view(buffers_prefix_view const&);
/// 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,
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. The copy is maintained for
the lifetime of the view.
*/
buffers_prefix_view(
std::size_t size,
BufferSequence const& buffers);
/** 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,
the resulting sequence will represent the entire
input sequence.
@param args Arguments forwarded to the contained buffer's constructor.
*/
template<class... Args>
buffers_prefix_view(
std::size_t size,
boost::in_place_init_t,
Args&&... args);
/// Returns an iterator to the first buffer in the sequence
const_iterator
begin() const;
/// Returns an iterator to one past the last buffer in the sequence
const_iterator
end() const;
#if ! BOOST_BEAST_DOXYGEN
std::size_t
buffer_bytes_impl() const noexcept
{
return size_;
}
#endif
};
//------------------------------------------------------------------------------
/** Returns a prefix of a constant or mutable 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.
@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 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.
@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>
buffers_prefix_view<BufferSequence>
buffers_prefix(
std::size_t size, BufferSequence const& buffers)
{
static_assert(
net::is_const_buffer_sequence<BufferSequence>::value,
"BufferSequence type requirements not met");
return buffers_prefix_view<BufferSequence>(size, buffers);
}
/** Returns the first buffer in a buffer sequence
This returns the first buffer in the buffer sequence.
If the buffer sequence is an empty range, the returned
buffer will have a zero buffer size.
@param buffers The buffer sequence. If the sequence is
mutable, the returned buffer sequence will also be mutable.
Otherwise, the returned buffer sequence will be constant.
*/
template<class BufferSequence>
buffers_type<BufferSequence>
buffers_front(BufferSequence const& buffers)
{
auto const first =
net::buffer_sequence_begin(buffers);
if(first == net::buffer_sequence_end(buffers))
return {};
return *first;
}
} // beast
} // boost
#include <boost/beast/core/impl/buffers_prefix.hpp>
#endif

View File

@@ -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

View File

@@ -0,0 +1,146 @@
//
// 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_SUFFIX_HPP
#define BOOST_BEAST_BUFFERS_SUFFIX_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/optional.hpp>
#include <cstdint>
#include <iterator>
#include <utility>
namespace boost {
namespace beast {
/** Adaptor to progressively trim the front of a <em>BufferSequence</em>.
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 adaptor efficiently iterates through a subset of
the buffers wrapped.
The wrapped buffer is not modified, a copy is made instead.
Ownership of the underlying memory is not transferred, the application
is still responsible for managing its lifetime.
@tparam BufferSequence The buffer sequence to wrap.
@par Example
This function writes the entire contents of a buffer sequence
to the specified stream.
@code
template<class SyncWriteStream, class ConstBufferSequence>
void send(SyncWriteStream& stream, ConstBufferSequence const& buffers)
{
buffers_suffix<ConstBufferSequence> bs{buffers};
while(buffer_bytes(bs) > 0)
bs.consume(stream.write_some(bs));
}
@endcode
*/
template<class BufferSequence>
class buffers_suffix
{
using iter_type =
buffers_iterator_type<BufferSequence>;
BufferSequence bs_;
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(
net::buffer_sequence_begin(bs_),
dist))
, skip_(other.skip_)
{
}
public:
/** The type for each element in the list of buffers.
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 = __see_below__;
#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__;
#else
class const_iterator;
#endif
/// Constructor
buffers_suffix();
/// Copy Constructor
buffers_suffix(buffers_suffix const&);
/** Constructor
A copy of the buffer sequence is made. Ownership of the
underlying memory is not transferred or copied.
*/
explicit
buffers_suffix(BufferSequence const& buffers);
/** Constructor
This constructs the buffer sequence in-place from
a list of arguments.
@param args Arguments forwarded to the buffers constructor.
*/
template<class... Args>
explicit
buffers_suffix(boost::in_place_init_t, Args&&... args);
/// Copy Assignment
buffers_suffix& operator=(buffers_suffix const&);
/// Get a bidirectional iterator to the first element.
const_iterator
begin() const;
/// Get a bidirectional iterator to one past the last element.
const_iterator
end() const;
/** Remove bytes from the beginning of the sequence.
@param amount The number of bytes to remove. If this is
larger than the number of bytes remaining, all the
bytes remaining are removed.
*/
void
consume(std::size_t amount);
};
} // beast
} // boost
#include <boost/beast/core/impl/buffers_suffix.hpp>
#endif

View File

@@ -0,0 +1,62 @@
//
// 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_TO_STRING_HPP
#define BOOST_BEAST_BUFFERS_TO_STRING_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/beast/core/buffers_range.hpp>
#include <boost/asio/buffer.hpp>
#include <string>
namespace boost {
namespace beast {
/** Return a string representing the contents of a buffer sequence.
This function returns a string representing an entire buffer
sequence. Nulls and unprintable characters in the buffer
sequence are inserted to the resulting string as-is. No
character conversions are performed.
@param buffers The buffer sequence to convert
@par Example
This function writes a buffer sequence converted to a string
to `std::cout`.
@code
template<class ConstBufferSequence>
void print(ConstBufferSequence const& buffers)
{
std::cout << buffers_to_string(buffers) << std::endl;
}
@endcode
*/
template<class ConstBufferSequence>
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(buffer_bytes(buffers));
for(auto const buffer : buffers_range_ref(buffers))
result.append(static_cast<char const*>(
buffer.data()), buffer.size());
return result;
}
} // beast
} // boost
#endif

View File

@@ -0,0 +1,42 @@
//
// 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_ALLOCATOR_HPP
#define BOOST_BEAST_DETAIL_ALLOCATOR_HPP
#include <boost/config.hpp>
#ifdef BOOST_NO_CXX11_ALLOCATOR
#include <boost/container/allocator_traits.hpp>
#else
#include <memory>
#endif
namespace boost {
namespace beast {
namespace detail {
// This is a workaround for allocator_traits
// implementations which falsely claim C++11
// compatibility.
#ifdef BOOST_NO_CXX11_ALLOCATOR
template<class Alloc>
using allocator_traits = boost::container::allocator_traits<Alloc>;
#else
template<class Alloc>
using allocator_traits = std::allocator_traits<Alloc>;
#endif
} // detail
} // beast
} // boost
#endif

View File

@@ -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

View File

@@ -0,0 +1,88 @@
//
// 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_BASE64_HPP
#define BOOST_BEAST_DETAIL_BASE64_HPP
#include <boost/beast/core/string.hpp>
#include <cctype>
#include <utility>
namespace boost {
namespace beast {
namespace detail {
namespace base64 {
BOOST_BEAST_DECL
char const*
get_alphabet();
BOOST_BEAST_DECL
signed char const*
get_inverse();
/// Returns max chars needed to encode a base64 string
BOOST_BEAST_DECL
std::size_t constexpr
encoded_size(std::size_t n)
{
return 4 * ((n + 2) / 3);
}
/// Returns max bytes needed to decode a base64 string
inline
std::size_t constexpr
decoded_size(std::size_t n)
{
return n / 4 * 3; // requires n&3==0, smaller
}
/** 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.
*/
BOOST_BEAST_DECL
std::size_t
encode(void* dest, void const* src, std::size_t len);
/** 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.
*/
BOOST_BEAST_DECL
std::pair<std::size_t, std::size_t>
decode(void* dest, char const* src, std::size_t len);
} // base64
} // detail
} // beast
} // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/core/detail/base64.ipp>
#endif
#endif

View File

@@ -0,0 +1,202 @@
//
// 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
} // detail
} // beast
} // boost
#endif

View File

@@ -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

View File

@@ -0,0 +1,121 @@
//
// 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());
}
// The allocation hooks are still defined because they trivially forward to
// user hooks. Forward here ensures that the user will get a compile error
// if they build their code with BOOST_ASIO_NO_DEPRECATED.
friend
boost::asio::asio_handler_allocate_is_deprecated
asio_handler_allocate(
std::size_t size, bind_default_executor_wrapper* p)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(p->h_));
}
friend
boost::asio::asio_handler_deallocate_is_deprecated
asio_handler_deallocate(
void* mem, std::size_t size,
bind_default_executor_wrapper* p)
{
using boost::asio::asio_handler_deallocate;
return asio_handler_deallocate(mem, size,
std::addressof(p->h_));
}
friend
bool asio_handler_is_continuation(
bind_default_executor_wrapper* p)
{
using boost::asio::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

View File

@@ -0,0 +1,416 @@
//
// 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_HANDLER_HPP
#define BOOST_BEAST_DETAIL_BIND_HANDLER_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 {
//------------------------------------------------------------------------------
//
// bind_handler
//
//------------------------------------------------------------------------------
template<class Handler, class... Args>
class bind_wrapper
{
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<
std::is_placeholder<typename
std::decay<Arg>::type>::value == 0 &&
boost::is_placeholder<typename
std::decay<Arg>::type>::value == 0,
Arg&&>::type
extract(Arg&& arg, Vals&& vals)
{
boost::ignore_unused(vals);
return std::forward<Arg>(arg);
}
template<class Arg, class Vals>
static
typename std::enable_if<
std::is_placeholder<typename
std::decay<Arg>::type>::value != 0,
tuple_element<std::is_placeholder<
typename std::decay<Arg>::type>::value - 1,
Vals>>::type&&
extract(Arg&&, Vals&& vals)
{
return detail::get<std::is_placeholder<
typename std::decay<Arg>::type>::value - 1>(
std::forward<Vals>(vals));
}
template<class Arg, class Vals>
static
typename std::enable_if<
boost::is_placeholder<typename
std::decay<Arg>::type>::value != 0,
tuple_element<boost::is_placeholder<
typename std::decay<Arg>::type>::value - 1,
Vals>>::type&&
extract(Arg&&, Vals&& vals)
{
return detail::get<boost::is_placeholder<
typename std::decay<Arg>::type>::value - 1>(
std::forward<Vals>(vals));
}
template<class ArgsTuple, std::size_t... S>
static
void
invoke(
Handler& h,
ArgsTuple& args,
tuple<>&&,
mp11::index_sequence<S...>)
{
boost::ignore_unused(args);
h(detail::get<S>(std::move(args))...);
}
template<
class ArgsTuple,
class ValsTuple,
std::size_t... S>
static
void
invoke(
Handler& h,
ArgsTuple& args,
ValsTuple&& vals,
mp11::index_sequence<S...>)
{
boost::ignore_unused(args);
boost::ignore_unused(vals);
h(extract(detail::get<S>(std::move(args)),
std::forward<ValsTuple>(vals))...);
}
public:
using result_type = void; // asio needs this
bind_wrapper(bind_wrapper&&) = default;
bind_wrapper(bind_wrapper const&) = default;
template<
class DeducedHandler,
class... Args_>
explicit
bind_wrapper(
DeducedHandler&& handler,
Args_&&... args)
: h_(std::forward<DeducedHandler>(handler))
, 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...>());
}
//
template<class Function>
friend
boost::asio::asio_handler_invoke_is_deprecated
asio_handler_invoke(
Function&& f, bind_wrapper* op)
{
using boost::asio::asio_handler_invoke;
return asio_handler_invoke(f, std::addressof(op->h_));
}
friend
bool asio_handler_is_continuation(
bind_wrapper* op)
{
using boost::asio::asio_handler_is_continuation;
return asio_handler_is_continuation(
std::addressof(op->h_));
}
friend
boost::asio::asio_handler_allocate_is_deprecated
asio_handler_allocate(
std::size_t size, bind_wrapper* op)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->h_));
}
friend
boost::asio::asio_handler_deallocate_is_deprecated
asio_handler_deallocate(
void* p, std::size_t size, bind_wrapper* op)
{
using boost::asio::asio_handler_deallocate;
return 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
invoke(
std::false_type,
mp11::index_sequence<I...>,
Ts&&... ts)
{
h_( detail::get<I>(std::move(args_))...,
std::forward<Ts>(ts)...);
}
template<std::size_t... I, class... Ts>
void
invoke(
std::true_type,
mp11::index_sequence<I...>,
Ts&&... ts)
{
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
boost::asio::asio_handler_invoke_is_deprecated
asio_handler_invoke(
Function&& f, bind_front_wrapper* op)
{
using boost::asio::asio_handler_invoke;
return asio_handler_invoke(f, std::addressof(op->h_));
}
friend
bool asio_handler_is_continuation(
bind_front_wrapper* op)
{
using boost::asio::asio_handler_is_continuation;
return asio_handler_is_continuation(
std::addressof(op->h_));
}
friend
boost::asio::asio_handler_allocate_is_deprecated
asio_handler_allocate(
std::size_t size, bind_front_wrapper* op)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->h_));
}
friend
boost::asio::asio_handler_deallocate_is_deprecated
asio_handler_deallocate(
void* p, std::size_t size, bind_front_wrapper* op)
{
using boost::asio::asio_handler_deallocate;
return 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::bind_wrapper<Handler, Args...>, Executor>
{
using type = typename
associated_executor<Handler, Executor>::type;
static
type
get(beast::detail::bind_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 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::bind_wrapper<
Handler, Args...>, ...) = delete;
template<class Handler, class... Args>
void
bind(boost::beast::detail::bind_front_wrapper<
Handler, Args...>, ...) = delete;
} // std
//------------------------------------------------------------------------------
#endif

View File

@@ -0,0 +1,84 @@
//
// 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_BUFFER_HPP
#define BOOST_BEAST_CORE_DETAIL_BUFFER_HPP
#include <boost/beast/core/error.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/optional.hpp>
#include <stdexcept>
namespace boost {
namespace beast {
namespace detail {
template<
class DynamicBuffer,
class ErrorValue>
auto
dynamic_buffer_prepare_noexcept(
DynamicBuffer& buffer,
std::size_t size,
error_code& ec,
ErrorValue ev) ->
boost::optional<typename
DynamicBuffer::mutable_buffers_type>
{
if(buffer.max_size() - buffer.size() < size)
{
// length error
ec = ev;
return boost::none;
}
boost::optional<typename
DynamicBuffer::mutable_buffers_type> result;
result.emplace(buffer.prepare(size));
ec = {};
return result;
}
template<
class DynamicBuffer,
class ErrorValue>
auto
dynamic_buffer_prepare(
DynamicBuffer& buffer,
std::size_t size,
error_code& ec,
ErrorValue ev) ->
boost::optional<typename
DynamicBuffer::mutable_buffers_type>
{
#ifndef BOOST_NO_EXCEPTIONS
try
{
boost::optional<typename
DynamicBuffer::mutable_buffers_type> result;
result.emplace(buffer.prepare(size));
ec = {};
return result;
}
catch(std::length_error const&)
{
ec = ev;
}
return boost::none;
#else
return dynamic_buffer_prepare_noexcept(
buffer, size, ec, ev);
#endif
}
} // detail
} // beast
} // boost
#endif

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,133 @@
//
// 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_{};
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;
const_iterator() = default;
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;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
};
explicit
buffers_range_adaptor(BufferSequence const& b)
: b_(b)
{
}
const_iterator
begin() const noexcept
{
return {net::buffer_sequence_begin(b_)};
}
const_iterator
end() const noexcept
{
return {net::buffer_sequence_end(b_)};
}
};
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,71 @@
//
// 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_REF_HPP
#define BOOST_BEAST_DETAIL_BUFFERS_REF_HPP
#include <boost/beast/core/buffer_traits.hpp>
#include <iterator>
#include <memory>
namespace boost {
namespace beast {
namespace detail {
// A very lightweight reference to a buffer sequence
template<class BufferSequence>
class buffers_ref
{
BufferSequence const* buffers_;
public:
using const_iterator =
buffers_iterator_type<BufferSequence>;
using value_type = typename
std::iterator_traits<const_iterator>::value_type;
buffers_ref(buffers_ref const&) = default;
buffers_ref& operator=(buffers_ref const&) = default;
explicit
buffers_ref(BufferSequence const& buffers)
: buffers_(std::addressof(buffers))
{
}
const_iterator
begin() const
{
return net::buffer_sequence_begin(*buffers_);
}
const_iterator
end() const
{
return net::buffer_sequence_end(*buffers_);
}
};
// Return a reference to a buffer sequence
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);
}
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,126 @@
//
// 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
//
//
// This is a derivative work, original copyright follows:
//
/*
Copyright (c) 2015 Orson Peters <orsonpeters@gmail.com>
This software is provided 'as-is', without any express or implied warranty. In no event will the
authors 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 software must not be misrepresented; you must not claim that you wrote the
original software. If you use this software 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 software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef BOOST_BEAST_CORE_DETAIL_CHACHA_HPP
#define BOOST_BEAST_CORE_DETAIL_CHACHA_HPP
#include <cstdint>
#include <limits>
namespace boost {
namespace beast {
namespace detail {
template<std::size_t R>
class chacha
{
alignas(16) std::uint32_t block_[16];
std::uint32_t keysetup_[8];
std::uint64_t ctr_ = 0;
int idx_ = 16;
void 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];
}
void 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)
{
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
}
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)
{
for (int i = 0; i < 6; ++i)
keysetup_[i] = v[i];
keysetup_[6] = v[6] + (stream & 0xffffffff);
keysetup_[7] = v[7] + ((stream >> 32) & 0xffffffff);
}
std::uint32_t
operator()()
{
if(idx_ == 16)
{
idx_ = 0;
++ctr_;
generate_block();
}
return block_[idx_++];
}
};
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,78 @@
//
// Copyright (c) 2019 Damian Jarek(damian.jarek93@gmail.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_CHAR_BUFFER_HPP
#define BOOST_BEAST_CORE_DETAIL_CHAR_BUFFER_HPP
#include <boost/config.hpp>
#include <cstddef>
#include <cstring>
#include <cstdint>
namespace boost {
namespace beast {
namespace detail {
template <std::size_t N>
class char_buffer
{
public:
bool try_push_back(char c)
{
if (size_ == N)
return false;
buf_[size_++] = c;
return true;
}
bool try_append(char const* first, char const* last)
{
std::size_t const n = last - first;
if (n > N - size_)
return false;
std::memmove(&buf_[size_], first, n);
size_ += n;
return true;
}
void clear() noexcept
{
size_ = 0;
}
char* data() noexcept
{
return buf_;
}
char const* data() const noexcept
{
return buf_;
}
std::size_t size() const noexcept
{
return size_;
}
bool empty() const noexcept
{
return size_ == 0;
}
private:
std::size_t size_= 0;
char buf_[N];
};
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,59 @@
//
// 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_CLAMP_HPP
#define BOOST_BEAST_CORE_DETAIL_CLAMP_HPP
#include <cstdlib>
#include <limits>
#include <type_traits>
namespace boost {
namespace beast {
namespace detail {
template<class UInt>
static
std::size_t
clamp(UInt x)
{
if(x >= (std::numeric_limits<std::size_t>::max)())
return (std::numeric_limits<std::size_t>::max)();
return static_cast<std::size_t>(x);
}
template<class UInt>
static
std::size_t
clamp(UInt x, std::size_t limit)
{
if(x >= limit)
return limit;
return static_cast<std::size_t>(x);
}
// return `true` if x + y > z, which are unsigned
template<
class U1, class U2, class U3>
constexpr
bool
sum_exceeds(U1 x, U2 y, U3 z)
{
static_assert(
std::is_unsigned<U1>::value &&
std::is_unsigned<U2>::value &&
std::is_unsigned<U3>::value, "");
return y > z || x > z - y;
}
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,104 @@
//
// 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_CONFIG_HPP
#define BOOST_BEAST_CORE_DETAIL_CONFIG_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:
14.0 (2015) 1900 190023026
14.0 (2015 Update 1) 1900 190023506
14.0 (2015 Update 2) 1900 190023918
14.0 (2015 Update 3) 1900 190024210
*/
#if defined(BOOST_MSVC)
# if BOOST_MSVC_FULL_VER < 190024210
# error Beast requires C++11: Visual Studio 2015 Update 3 or later needed
# endif
#elif defined(BOOST_GCC)
# if(BOOST_GCC < 40801)
# error Beast requires C++11: gcc version 4.8 or later needed
# endif
#else
# if \
defined(BOOST_NO_CXX11_DECLTYPE) || \
defined(BOOST_NO_CXX11_HDR_TUPLE) || \
defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) || \
defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
# error Beast requires C++11: a conforming compiler is needed
# endif
#endif
#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_AUTO_RESULT_TYPE(type, void(::boost::beast::error_code))
#endif
#ifndef BOOST_BEAST_ASYNC_RESULT2
#define BOOST_BEAST_ASYNC_RESULT2(type) \
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(type, void(::boost::beast::error_code, ::std::size_t))
#endif
#ifndef BOOST_BEAST_ASYNC_TPARAM1
#define BOOST_BEAST_ASYNC_TPARAM1 BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::beast::error_code))
#endif
#ifndef BOOST_BEAST_ASYNC_TPARAM2
#define BOOST_BEAST_ASYNC_TPARAM2 BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::beast::error_code, ::std::size_t))
#endif
#endif

View File

@@ -0,0 +1,99 @@
//
// Copyright (c) 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_CPU_INFO_HPP
#define BOOST_BEAST_DETAIL_CPU_INFO_HPP
#include <boost/config.hpp>
#ifndef BOOST_BEAST_NO_INTRINSICS
# if defined(BOOST_MSVC) || ((defined(BOOST_GCC) || defined(BOOST_CLANG)) && defined(__SSE4_2__))
# define BOOST_BEAST_NO_INTRINSICS 0
# else
# define BOOST_BEAST_NO_INTRINSICS 1
# endif
#endif
#if ! BOOST_BEAST_NO_INTRINSICS
#ifdef BOOST_MSVC
#include <intrin.h> // __cpuid
#else
#include <cpuid.h> // __get_cpuid
#endif
namespace boost {
namespace beast {
namespace detail {
/* Portions from Boost,
Copyright Andrey Semashev 2007 - 2015.
*/
template<class = void>
void
cpuid(
std::uint32_t id,
std::uint32_t& eax,
std::uint32_t& ebx,
std::uint32_t& ecx,
std::uint32_t& edx)
{
#ifdef BOOST_MSVC
int regs[4];
__cpuid(regs, id);
eax = regs[0];
ebx = regs[1];
ecx = regs[2];
edx = regs[3];
#else
__get_cpuid(id, &eax, &ebx, &ecx, &edx);
#endif
}
struct cpu_info
{
bool sse42 = false;
cpu_info();
};
inline
cpu_info::
cpu_info()
{
constexpr std::uint32_t SSE42 = 1 << 20;
std::uint32_t eax = 0;
std::uint32_t ebx = 0;
std::uint32_t ecx = 0;
std::uint32_t edx = 0;
cpuid(0, eax, ebx, ecx, edx);
if(eax >= 1)
{
cpuid(1, eax, ebx, ecx, edx);
sse42 = (ecx & SSE42) != 0;
}
}
template<class = void>
cpu_info const&
get_cpu_info()
{
static cpu_info const ci;
return ci;
}
} // detail
} // beast
} // boost
#endif
#endif

View File

@@ -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

View File

@@ -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_GET_IO_CONTEXT_HPP
#define BOOST_BEAST_DETAIL_GET_IO_CONTEXT_HPP
#include <boost/beast/core/stream_traits.hpp>
#ifdef BOOST_ASIO_NO_TS_EXECUTORS
#include <boost/asio/execution.hpp>
#endif
#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(net::query(ex, net::execution::context));
}
inline
net::io_context*
get_io_context(net::strand<
net::io_context::executor_type> const& ex)
{
return get_io_context(ex.get_inner_executor());
}
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 || std::is_same<T, net::any_io_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 get_io_context(*p);
}
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

View File

@@ -0,0 +1,249 @@
//
// 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/beast/core/read_size.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 asio::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_prepare;
BOOST_ASIO_CORO_REENTER(*this)
{
for(;;)
{
max_prepare = beast::read_size(b_, cond_(ec, total_, b_));
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_prepare;
for(;;)
{
max_prepare = beast::read_size(buffer, cond(ec, total, buffer));
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,
BOOST_BEAST_ASYNC_TPARAM2 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

View File

@@ -0,0 +1,69 @@
//
// Copyright (c) 2019 Damian Jarek(damian.jarek93@gmail.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_TEMPORARY_BUFFER_IPP
#define BOOST_BEAST_DETAIL_IMPL_TEMPORARY_BUFFER_IPP
#include <boost/beast/core/detail/temporary_buffer.hpp>
#include <boost/beast/core/detail/clamp.hpp>
#include <boost/core/exchange.hpp>
#include <boost/assert.hpp>
#include <memory>
#include <cstring>
namespace boost {
namespace beast {
namespace detail {
void
temporary_buffer::
append(string_view s)
{
grow(s.size());
unchecked_append(s);
}
void
temporary_buffer::
append(string_view s1, string_view s2)
{
grow(s1.size() + s2.size());
unchecked_append(s1);
unchecked_append(s2);
}
void
temporary_buffer::
unchecked_append(string_view s)
{
auto n = s.size();
std::memcpy(&data_[size_], s.data(), n);
size_ += n;
}
void
temporary_buffer::
grow(std::size_t n)
{
if (capacity_ - size_ >= n)
return;
auto const capacity = (n + size_) * 2u;
BOOST_ASSERT(! detail::sum_exceeds(
n, size_, capacity));
char* const p = new char[capacity];
std::memcpy(p, data_, size_);
deallocate(boost::exchange(data_, p));
capacity_ = capacity;
}
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,80 @@
//
// 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 <boost/asio/async_result.hpp>
#include <boost/type_traits/make_void.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>()...))
{
};
/** @} */
template<class CompletionToken, class Signature, class = void>
struct is_completion_token_for : std::false_type
{
};
struct any_initiation
{
template<class...AnyArgs>
void operator()(AnyArgs&&...);
};
template<class CompletionToken, class R, class...Args>
struct is_completion_token_for<
CompletionToken, R(Args...), boost::void_t<decltype(
boost::asio::async_initiate<CompletionToken, R(Args...)>(
any_initiation(), std::declval<CompletionToken&>())
)>> : std::true_type
{
};
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,267 @@
//
// 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_OSTREAM_HPP
#define BOOST_BEAST_DETAIL_OSTREAM_HPP
#include <boost/beast/core/buffers_prefix.hpp>
#include <boost/beast/core/buffers_range.hpp>
#include <boost/throw_exception.hpp>
#include <boost/asio/buffer.hpp>
#include <memory>
#include <ostream>
#include <streambuf>
#include <type_traits>
#include <utility>
namespace boost {
namespace beast {
namespace detail {
struct basic_streambuf_movable_helper :
std::basic_streambuf<char, std::char_traits<char>>
{
basic_streambuf_movable_helper(
basic_streambuf_movable_helper&&) = default;
};
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> final
: public std::basic_streambuf<CharT, Traits>
{
using int_type = typename
std::basic_streambuf<CharT, Traits>::int_type;
using traits_type = typename
std::basic_streambuf<CharT, Traits>::traits_type;
DynamicBuffer& b_;
public:
ostream_buffer(ostream_buffer&&) = default;
ostream_buffer(ostream_buffer const&) = delete;
~ostream_buffer() noexcept
{
sync();
}
explicit
ostream_buffer(DynamicBuffer& b)
: b_(b)
{
b_.prepare(0);
}
int_type
overflow(int_type ch) override
{
BOOST_ASSERT(! Traits::eq_int_type(
ch, Traits::eof()));
sync();
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));
BOOST_ASSERT(b_.capacity() > b_.size());
return this->sputc(
Traits::to_char_type(ch));
}
int
sync() override
{
b_.commit(
(this->pptr() - this->pbase()) *
sizeof(CharT));
this->setp(nullptr, nullptr);
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
template<class DynamicBuffer, class CharT, class Traits>
class ostream_buffer
<DynamicBuffer, CharT, Traits, false>
: public std::basic_streambuf<CharT, Traits>
{
using int_type = typename
std::basic_streambuf<CharT, Traits>::int_type;
using traits_type = typename
std::basic_streambuf<CharT, Traits>::traits_type;
DynamicBuffer& b_;
public:
ostream_buffer(ostream_buffer&&) = delete;
ostream_buffer(ostream_buffer const&) = delete;
~ostream_buffer() noexcept
{
sync();
}
explicit
ostream_buffer(DynamicBuffer& b)
: b_(b)
{
}
int_type
overflow(int_type ch) override
{
BOOST_ASSERT(! Traits::eq_int_type(
ch, Traits::eof()));
sync();
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));
BOOST_ASSERT(b_.capacity() > b_.size());
return this->sputc(
Traits::to_char_type(ch));
}
int
sync() override
{
b_.commit(
(this->pptr() - this->pbase()) *
sizeof(CharT));
this->setp(nullptr, nullptr);
return 0;
}
};
//------------------------------------------------------------------------------
template<class DynamicBuffer,
class CharT, class Traits, bool isMovable>
class ostream_helper;
template<class DynamicBuffer, class CharT, class Traits>
class ostream_helper<
DynamicBuffer, CharT, Traits, true>
: public std::basic_ostream<CharT, Traits>
{
ostream_buffer<
DynamicBuffer, CharT, Traits, true> osb_;
public:
explicit
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& 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)
: std::basic_ostream<CharT, Traits>(&osb_)
, osb_(std::move(other.osb_))
{
}
// This work-around is for libstdc++ versions that
// don't have a movable std::basic_streambuf
template<class T>
class ostream_helper_base
{
protected:
std::unique_ptr<T> member;
ostream_helper_base(
ostream_helper_base&&) = default;
explicit
ostream_helper_base(T* t)
: member(t)
{
}
};
template<class DynamicBuffer, class CharT, class Traits>
class ostream_helper<
DynamicBuffer, CharT, Traits, false>
: private ostream_helper_base<ostream_buffer<
DynamicBuffer, CharT, Traits, false>>
, public std::basic_ostream<CharT, Traits>
{
public:
explicit
ostream_helper(DynamicBuffer& b)
: ostream_helper_base<ostream_buffer<
DynamicBuffer, CharT, Traits, false>>(
new ostream_buffer<DynamicBuffer,
CharT, Traits, false>(b))
, std::basic_ostream<CharT, Traits>(
this->member.get())
{
}
ostream_helper(ostream_helper&& other)
: ostream_helper_base<ostream_buffer<
DynamicBuffer, CharT, Traits, false>>(
std::move(other))
, std::basic_ostream<CharT, Traits>(
this->member.get())
{
}
};
} // detail
} // beast
} // boost
#endif

View File

@@ -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

View File

@@ -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,
BOOST_BEAST_ASYNC_TPARAM2 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

View File

@@ -0,0 +1,108 @@
//
// 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/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

View File

@@ -0,0 +1,38 @@
//
// 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_base : net::execution_context::service
{
static net::execution_context::id const id;
explicit
service_base(net::execution_context& ctx)
: net::execution_context::service(ctx)
{
}
};
template<class T>
net::execution_context::id const service_base<T>::id;
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,75 @@
//
// 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_HPP
#define BOOST_BEAST_DETAIL_SHA1_HPP
#include <boost/beast/core/detail/config.hpp>
#include <cstdint>
#include <cstddef>
// 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 {
static std::size_t constexpr BLOCK_INTS = 16;
static std::size_t constexpr BLOCK_BYTES = 64;
static std::size_t constexpr DIGEST_BYTES = 20;
} // sha1
struct sha1_context
{
static unsigned int constexpr block_size = sha1::BLOCK_BYTES;
static unsigned int constexpr digest_size = 20;
std::size_t buflen;
std::size_t blocks;
std::uint32_t digest[5];
std::uint8_t buf[block_size];
};
BOOST_BEAST_DECL
void
init(sha1_context& ctx) noexcept;
BOOST_BEAST_DECL
void
update(
sha1_context& ctx,
void const* message,
std::size_t size) noexcept;
BOOST_BEAST_DECL
void
finish(
sha1_context& ctx,
void* digest) noexcept;
} // detail
} // beast
} // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/core/detail/sha1.ipp>
#endif
#endif

View File

@@ -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

View File

@@ -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

View File

@@ -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_STATIC_OSTREAM_HPP
#define BOOST_BEAST_DETAIL_STATIC_OSTREAM_HPP
#include <locale>
#include <ostream>
#include <streambuf>
namespace boost {
namespace beast {
namespace detail {
// http://www.mr-edd.co.uk/blog/beginners_guide_streambuf
class static_ostream_buffer
: public std::basic_streambuf<char>
{
using CharT = char;
using Traits = std::char_traits<CharT>;
using int_type = typename
std::basic_streambuf<CharT, Traits>::int_type;
using traits_type = typename
std::basic_streambuf<CharT, Traits>::traits_type;
char* data_;
std::size_t size_;
std::size_t len_ = 0;
std::string s_;
public:
static_ostream_buffer(static_ostream_buffer&&) = delete;
static_ostream_buffer(static_ostream_buffer const&) = delete;
static_ostream_buffer(char* data, std::size_t size)
: data_(data)
, size_(size)
{
this->setp(data_, data_ + size - 1);
}
~static_ostream_buffer() noexcept
{
}
string_view
str() const
{
if(! s_.empty())
return {s_.data(), len_};
return {data_, len_};
}
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;
}
private:
void
prepare()
{
static auto const growth_factor = 1.5;
if(len_ < size_ - 1)
{
this->setp(
data_ + len_, data_ + size_ - 2);
return;
}
if(s_.empty())
{
s_.resize(static_cast<std::size_t>(
growth_factor * len_));
Traits::copy(&s_[0], data_, len_);
}
else
{
s_.resize(static_cast<std::size_t>(
growth_factor * len_));
}
this->setp(&s_[len_], &s_[len_] +
s_.size() - len_ - 1);
}
void
flush(int extra = 0)
{
len_ += static_cast<std::size_t>(
this->pptr() - this->pbase() + extra);
}
};
class static_ostream : public std::basic_ostream<char>
{
static_ostream_buffer osb_;
public:
static_ostream(char* data, std::size_t size)
: std::basic_ostream<char>(&this->osb_)
, osb_(data, size)
{
imbue(std::locale::classic());
}
string_view
str() const
{
return osb_.str();
}
};
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,133 @@
//
// 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_STRING_HPP
#define BOOST_BEAST_DETAIL_STATIC_STRING_HPP
#include <boost/beast/core/string.hpp>
#include <boost/assert.hpp>
#include <iterator>
#include <type_traits>
namespace boost {
namespace beast {
namespace detail {
// Because k-ballo said so
template<class T>
using is_input_iterator =
std::integral_constant<bool,
! std::is_integral<T>::value>;
template<class CharT, class Traits>
int
lexicographical_compare(
CharT const* s1, std::size_t n1,
CharT const* s2, std::size_t n2)
{
if(n1 < n2)
return Traits::compare(
s1, s2, n1) <= 0 ? -1 : 1;
if(n1 > n2)
return Traits::compare(
s1, s2, n2) >= 0 ? 1 : -1;
return Traits::compare(s1, s2, n1);
}
template<class CharT, class Traits>
int
lexicographical_compare(
basic_string_view<CharT, Traits> s1,
CharT const* s2, std::size_t n2)
{
return detail::lexicographical_compare<
CharT, Traits>(s1.data(), s1.size(), s2, n2);
}
template<class CharT, class Traits>
int
lexicographical_compare(
basic_string_view<CharT, Traits> s1,
basic_string_view<CharT, Traits> s2)
{
return detail::lexicographical_compare<CharT, Traits>(
s1.data(), s1.size(), s2.data(), s2.size());
}
// Maximum number of characters in the decimal
// representation of a binary number. This includes
// the potential minus sign.
//
inline
std::size_t constexpr
max_digits(std::size_t bytes)
{
return static_cast<std::size_t>(
bytes * 2.41) + 1 + 1;
}
template<class CharT, class Integer, class Traits>
CharT*
raw_to_string(
CharT* buf, Integer x, std::true_type)
{
if(x == 0)
{
Traits::assign(*--buf, '0');
return buf;
}
if(x < 0)
{
x = -x;
for(;x > 0; x /= 10)
Traits::assign(*--buf ,
"0123456789"[x % 10]);
Traits::assign(*--buf, '-');
return buf;
}
for(;x > 0; x /= 10)
Traits::assign(*--buf ,
"0123456789"[x % 10]);
return buf;
}
template<class CharT, class Integer, class Traits>
CharT*
raw_to_string(
CharT* buf, Integer x, std::false_type)
{
if(x == 0)
{
*--buf = '0';
return buf;
}
for(;x > 0; x /= 10)
Traits::assign(*--buf ,
"0123456789"[x % 10]);
return buf;
}
template<
class CharT,
class Integer,
class Traits = std::char_traits<CharT>>
CharT*
raw_to_string(CharT* last, std::size_t size, Integer i)
{
boost::ignore_unused(size);
BOOST_ASSERT(size >= max_digits(sizeof(Integer)));
return raw_to_string<CharT, Integer, Traits>(
last, i, std::is_signed<Integer>{});
}
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,138 @@
//
// 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_ = nullptr;
bool clear_ = true;
public:
~pending_guard()
{
if(clear_ && b_)
*b_ = false;
}
pending_guard()
: b_(nullptr)
, clear_(true)
{
}
explicit
pending_guard(bool& b)
: b_(&b)
{
// If this assert goes off, it means you are attempting
// to issue two of the same asynchronous I/O operation
// at the same time, without waiting for the first one
// to complete. For example, attempting two simultaneous
// calls to async_read_some. Only one pending call of
// each I/O type (read and write) is permitted.
//
BOOST_ASSERT(! *b_);
*b_ = true;
}
pending_guard(
pending_guard&& other) noexcept
: b_(other.b_)
, clear_(boost::exchange(
other.clear_, false))
{
}
void assign(bool& b)
{
BOOST_ASSERT(!b_);
BOOST_ASSERT(clear_);
b_ = &b;
// If this assert goes off, it means you are attempting
// to issue two of the same asynchronous I/O operation
// at the same time, without waiting for the first one
// to complete. For example, attempting two simultaneous
// calls to async_read_some. Only one pending call of
// each I/O type (read and write) is permitted.
//
BOOST_ASSERT(! *b_);
*b_ = true;
}
void
reset()
{
BOOST_ASSERT(clear_);
if (b_)
*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

View File

@@ -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

View File

@@ -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_DETAIL_STRING_HPP
#define BOOST_BEAST_DETAIL_STRING_HPP
#include <boost/beast/core/string_type.hpp>
namespace boost {
namespace beast {
namespace detail {
// Pulling in the UDL directly breaks in some places on MSVC,
// so introduce a namespace for this purprose.
namespace string_literals {
inline
string_view
operator"" _sv(char const* p, std::size_t n)
{
return string_view{p, n};
}
} // string_literals
inline
char
ascii_tolower(char c)
{
return ((static_cast<unsigned>(c) - 65U) < 26) ?
c + 'a' - 'A' : c;
}
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,83 @@
//
// Copyright (c) 2019 Damian Jarek(damian.jarek93@gmail.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_TEMPORARY_BUFFER_HPP
#define BOOST_BEAST_DETAIL_TEMPORARY_BUFFER_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/string.hpp>
#include <memory>
namespace boost {
namespace beast {
namespace detail {
struct temporary_buffer
{
temporary_buffer() = default;
temporary_buffer(temporary_buffer const&) = delete;
temporary_buffer& operator=(temporary_buffer const&) = delete;
~temporary_buffer() noexcept
{
deallocate(data_);
}
BOOST_BEAST_DECL
void
append(string_view s);
BOOST_BEAST_DECL
void
append(string_view s1, string_view s2);
string_view
view() const noexcept
{
return {data_, size_};
}
bool
empty() const noexcept
{
return size_ == 0;
}
private:
BOOST_BEAST_DECL
void
unchecked_append(string_view s);
BOOST_BEAST_DECL
void
grow(std::size_t n);
void
deallocate(char* data) noexcept
{
if (data != buffer_)
delete[] data;
}
char buffer_[4096];
char* data_ = buffer_;
std::size_t capacity_ = sizeof(buffer_);
std::size_t size_ = 0;
};
} // detail
} // beast
} // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/core/detail/impl/temporary_buffer.ipp>
#endif
#endif

View File

@@ -0,0 +1,116 @@
//
// Copyright (c) 2016-2019 Damian 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

View File

@@ -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_TYPE_TRAITS_HPP
#define BOOST_BEAST_DETAIL_TYPE_TRAITS_HPP
#include <boost/type_traits/make_void.hpp>
#include <type_traits>
#include <new>
namespace boost {
namespace beast {
namespace detail {
template<class U>
std::size_t constexpr
max_sizeof()
{
return sizeof(U);
}
template<class U0, class U1, class... Us>
std::size_t constexpr
max_sizeof()
{
return
max_sizeof<U0>() > max_sizeof<U1, Us...>() ?
max_sizeof<U0>() : max_sizeof<U1, Us...>();
}
template<class U>
std::size_t constexpr
max_alignof()
{
return alignof(U);
}
template<class U0, class U1, class... Us>
std::size_t constexpr
max_alignof()
{
return
max_alignof<U0>() > max_alignof<U1, Us...>() ?
max_alignof<U0>() : max_alignof<U1, Us...>();
}
// (since C++17)
template<class... Ts>
using make_void = boost::make_void<Ts...>;
template<class... Ts>
using void_t = boost::void_t<Ts...>;
// (since C++11) missing from g++4.8
template<std::size_t Len, class... Ts>
struct aligned_union
{
static
std::size_t constexpr alignment_value =
max_alignof<Ts...>();
using type = typename std::aligned_storage<
(Len > max_sizeof<Ts...>()) ? Len : (max_sizeof<Ts...>()),
alignment_value>::type;
};
template<std::size_t Len, class... Ts>
using aligned_union_t =
typename aligned_union<Len, Ts...>::type;
//------------------------------------------------------------------------------
// for span
template<class T, class E, class = void>
struct is_contiguous_container: std::false_type {};
template<class T, class E>
struct is_contiguous_container<T, E, void_t<
decltype(
std::declval<std::size_t&>() = std::declval<T const&>().size(),
std::declval<E*&>() = std::declval<T&>().data()),
typename std::enable_if<
std::is_same<
typename std::remove_cv<E>::type,
typename std::remove_cv<
typename std::remove_pointer<
decltype(std::declval<T&>().data())
>::type
>::type
>::value
>::type>>: std::true_type
{};
template <class T, class U>
T launder_cast(U* u)
{
#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606
return std::launder(reinterpret_cast<T>(u));
#elif defined(BOOST_GCC) && BOOST_GCC_VERSION > 80000
return __builtin_launder(reinterpret_cast<T>(u));
#else
return reinterpret_cast<T>(u);
#endif
}
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,235 @@
//
// 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_VARIANT_HPP
#define BOOST_BEAST_DETAIL_VARIANT_HPP
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/assert.hpp>
#include <boost/mp11/algorithm.hpp>
namespace boost {
namespace beast {
namespace detail {
// This simple variant gets the job done without
// causing too much trouble with template depth:
//
// * Always allows an empty state I==0
// * emplace() and get() support 1-based indexes only
// * Basic exception guarantee
// * Max 255 types
//
template<class... TN>
class variant
{
detail::aligned_union_t<1, TN...> buf_;
unsigned char i_ = 0;
struct destroy
{
variant& self;
void operator()(mp11::mp_size_t<0>)
{
}
template<class I>
void operator()(I) noexcept
{
using T =
mp11::mp_at_c<variant, I::value - 1>;
detail::launder_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(
*detail::launder_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(std::move(
*detail::launder_cast<T*>(&other.buf_)));
detail::launder_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
*detail::launder_cast<T const*>(&self.buf_) ==
*detail::launder_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()
{
destruct();
}
bool
operator==(variant const& other) const
{
if(i_ != other.i_)
return false;
return mp11::mp_with_index<
sizeof...(TN) + 1>(
i_, equals{*this, other});
}
// 0 = empty
unsigned char
index() const
{
return i_;
}
// moved-from object becomes empty
variant(variant&& other) noexcept
{
move_construct(other);
}
variant(variant const& other)
{
copy_construct(other);
}
// moved-from object becomes empty
variant& operator=(variant&& other)
{
if(this != &other)
{
destruct();
move_construct(other);
}
return *this;
}
variant& operator=(variant const& other)
{
if(this != &other)
{
destruct();
copy_construct(other);
}
return *this;
}
template<std::size_t I, class... Args>
void
emplace(Args&&... args) noexcept
{
destruct();
::new(&buf_) mp11::mp_at_c<variant, I - 1>(
std::forward<Args>(args)...);
i_ = I;
}
template<std::size_t I>
mp11::mp_at_c<variant, I - 1>&
get()
{
BOOST_ASSERT(i_ == I);
return *detail::launder_cast<
mp11::mp_at_c<variant, I - 1>*>(&buf_);
}
template<std::size_t I>
mp11::mp_at_c<variant, I - 1> const&
get() const
{
BOOST_ASSERT(i_ == I);
return *detail::launder_cast<
mp11::mp_at_c<variant, I - 1> const*>(&buf_);
}
void
reset()
{
destruct();
}
};
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,79 @@
//
// Copyright (c) 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_VARINT_HPP
#define BOOST_BEAST_DETAIL_VARINT_HPP
#include <boost/static_assert.hpp>
#include <cstdlib>
#include <iterator>
#include <type_traits>
namespace boost {
namespace beast {
namespace detail {
// https://developers.google.com/protocol-buffers/docs/encoding#varints
inline
std::size_t
varint_size(std::size_t value)
{
std::size_t n = 1;
while(value > 127)
{
++n;
value /= 128;
}
return n;
}
template<class FwdIt>
std::size_t
varint_read(FwdIt& first)
{
using value_type = typename
std::iterator_traits<FwdIt>::value_type;
BOOST_STATIC_ASSERT(
std::is_integral<value_type>::value &&
sizeof(value_type) == 1);
std::size_t value = 0;
std::size_t factor = 1;
while((*first & 0x80) != 0)
{
value += (*first++ & 0x7f) * factor;
factor *= 128;
}
value += *first++ * factor;
return value;
}
template<class FwdIt>
void
varint_write(FwdIt& first, std::size_t value)
{
using value_type = typename
std::iterator_traits<FwdIt>::value_type;
BOOST_STATIC_ASSERT(
std::is_integral<value_type>::value &&
sizeof(value_type) == 1);
while(value > 127)
{
*first++ = static_cast<value_type>(
0x80 | value);
value /= 128;
}
*first++ = static_cast<value_type>(value);
}
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,82 @@
//
// Copyright (c) 2019 Mika Fischer (mika.fischer@zoopnet.de)
//
// 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_WIN32_UNICODE_PATH_HPP
#define BOOST_BEAST_CORE_DETAIL_WIN32_UNICODE_PATH_HPP
#ifdef _WIN32
#include <boost/config.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/winapi/character_code_conversion.hpp>
#include <boost/winapi/file_management.hpp>
#include <boost/winapi/get_last_error.hpp>
#include <array>
#include <vector>
namespace boost {
namespace beast {
namespace detail {
class win32_unicode_path
{
using WCHAR_ = boost::winapi::WCHAR_;
public:
win32_unicode_path(const char* utf8_path, error_code& ec) {
int ret = mb2wide(utf8_path, static_buf_.data(),
static_buf_.size());
if (ret == 0)
{
int sz = mb2wide(utf8_path, nullptr, 0);
if (sz == 0)
{
ec.assign(boost::winapi::GetLastError(),
system_category());
return;
}
dynamic_buf_.resize(sz);
int ret2 = mb2wide(utf8_path,
dynamic_buf_.data(),
dynamic_buf_.size());
if (ret2 == 0)
{
ec.assign(boost::winapi::GetLastError(),
system_category());
return;
}
}
}
WCHAR_ const* c_str() const noexcept
{
return dynamic_buf_.empty()
? static_buf_.data()
: dynamic_buf_.data();
}
private:
int mb2wide(const char* utf8_path, WCHAR_* buf, size_t sz)
{
return boost::winapi::MultiByteToWideChar(
boost::winapi::CP_UTF8_,
boost::winapi::MB_ERR_INVALID_CHARS_,
utf8_path, -1,
buf, static_cast<int>(sz));
}
std::array<WCHAR_, boost::winapi::MAX_PATH_> static_buf_;
std::vector<WCHAR_> dynamic_buf_;
};
} // detail
} // beast
} // boost
#endif
#endif

View File

@@ -0,0 +1,93 @@
#ifndef BOOST_BEAST_CORE_DETAIL_WORK_GUARD_HPP
#define BOOST_BEAST_CORE_DETAIL_WORK_GUARD_HPP
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/execution.hpp>
#include <boost/optional.hpp>
namespace boost {
namespace beast {
namespace detail {
template<class Executor, class Enable = void>
struct select_work_guard;
template<class Executor>
using select_work_guard_t = typename
select_work_guard<Executor>::type;
#if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
template<class Executor>
struct select_work_guard
<
Executor,
typename std::enable_if
<
net::is_executor<Executor>::value
>::type
>
{
using type = net::executor_work_guard<Executor>;
};
#endif
template<class Executor>
struct execution_work_guard
{
using executor_type = decltype(
net::prefer(std::declval<Executor const&>(),
net::execution::outstanding_work.tracked));
execution_work_guard(Executor const& exec)
: ex_(net::prefer(exec, net::execution::outstanding_work.tracked))
{
}
executor_type
get_executor() const noexcept
{
BOOST_ASSERT(ex_.has_value());
return *ex_;
}
void reset() noexcept
{
ex_.reset();
}
private:
boost::optional<executor_type> ex_;
};
template<class Executor>
struct select_work_guard
<
Executor,
typename std::enable_if
<
net::execution::is_executor<Executor>::value
#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
|| net::is_executor<Executor>::value
#else
&& !net::is_executor<Executor>::value
#endif
>::type
>
{
using type = execution_work_guard<Executor>;
};
template<class Executor>
select_work_guard_t<Executor>
make_work_guard(Executor const& exec) noexcept
{
return select_work_guard_t<Executor>(exec);
}
}
}
}
#endif // BOOST_BEAST_CORE_DETAIL_WORK_GUARD_HPP

View File

@@ -0,0 +1,671 @@
//
// 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 =
net::default_completion_token_t<beast::executor_type<AsyncReadStream>>
>
#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 = net::default_completion_token_t<
beast::executor_type<AsyncReadStream>>{}) ->
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
{
// This macro facilitates asynchrnous handler tracking and
// debugging when the preprocessor macro
// BOOST_ASIO_CUSTOM_HANDLER_TRACKING is defined.
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"async_detect_ssl"));
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
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"async_detect_ssl"));
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

View File

@@ -0,0 +1,88 @@
//
// 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_ERROR_HPP
#define BOOST_BEAST_ERROR_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp>
namespace boost {
namespace beast {
/// The type of error code used by the library
using error_code = boost::system::error_code;
/// The type of system error thrown by the library
using system_error = boost::system::system_error;
/// The type of error category used by the library
using error_category = boost::system::error_category;
/// A function to return the generic error category used by the library
#if BOOST_BEAST_DOXYGEN
error_category const&
generic_category();
#else
using boost::system::generic_category;
#endif
/// A function to return the system error category used by the library
#if BOOST_BEAST_DOXYGEN
error_category const&
system_category();
#else
using boost::system::system_category;
#endif
/// The type of error condition used by the library
using error_condition = boost::system::error_condition;
/// The set of constants used for cross-platform error codes
#if BOOST_BEAST_DOXYGEN
enum errc{};
#else
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

View File

@@ -0,0 +1,44 @@
//
// 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)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_FILE_HPP
#define BOOST_BEAST_CORE_FILE_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/file_base.hpp>
#include <boost/beast/core/file_posix.hpp>
#include <boost/beast/core/file_stdio.hpp>
#include <boost/beast/core/file_win32.hpp>
namespace boost {
namespace beast {
/** An implementation of File.
This alias is set to the best available implementation
of <em>File</em> given the platform and build settings.
*/
#if BOOST_BEAST_DOXYGEN
struct file : file_stdio
{
};
#else
#if BOOST_BEAST_USE_WIN32_FILE
using file = file_win32;
#elif BOOST_BEAST_USE_POSIX_FILE
using file = file_posix;
#else
using file = file_stdio;
#endif
#endif
} // beast
} // boost
#endif

View File

@@ -0,0 +1,164 @@
//
// 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)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_FILE_BASE_HPP
#define BOOST_BEAST_CORE_FILE_BASE_HPP
#include <boost/beast/core/detail/config.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 <em>File</em> concept.
@see file_stdio
*/
enum class file_mode
{
/// Random read-only access to an existing file
read,
/// Sequential read-only access to an existing file
scan,
/** Random reading and writing to a new or truncated file
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 reading and writing to a new file only
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 write-only access to existing file
If the file does not exist, an error is generated.
*/
write_existing,
/** Appending to a new or truncated file
The current file position shall be set to the end of
the file prior to each write.
@li If the file does not exist, it is created.
@li If the file exists, it is truncated to
zero size upon opening.
*/
append,
/** Appending to an existing file
The current file position shall be set to the end of
the file prior to each write.
If the file does not exist, an error is generated.
*/
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
#endif

View File

@@ -0,0 +1,193 @@
//
// 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)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_FILE_POSIX_HPP
#define BOOST_BEAST_CORE_FILE_POSIX_HPP
#include <boost/beast/core/detail/config.hpp>
#if ! defined(BOOST_BEAST_NO_POSIX_FILE)
# if ! defined(__APPLE__) && ! defined(__linux__)
# define BOOST_BEAST_NO_POSIX_FILE
# endif
#endif
#if ! defined(BOOST_BEAST_USE_POSIX_FILE)
# if ! defined(BOOST_BEAST_NO_POSIX_FILE)
# define BOOST_BEAST_USE_POSIX_FILE 1
# else
# define BOOST_BEAST_USE_POSIX_FILE 0
# endif
#endif
#if BOOST_BEAST_USE_POSIX_FILE
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/file_base.hpp>
#include <cstdint>
namespace boost {
namespace beast {
/** An implementation of File for POSIX systems.
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.
This is platform-specific.
*/
using native_handle_type = int;
/** Destructor
If the file is open it is first closed.
*/
BOOST_BEAST_DECL
~file_posix();
/** Constructor
There is no open file initially.
*/
file_posix() = default;
/** Constructor
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.
native_handle_type
native_handle() const
{
return fd_;
}
/** Set the native handle associated with the file.
If the file is open it is first closed.
@param fd The native file handle to assign.
*/
BOOST_BEAST_DECL
void
native_handle(native_handle_type fd);
/// Returns `true` if the file is open
bool
is_open() const
{
return fd_ != -1;
}
/** Close the file if open
@param ec Set to the error, if any occurred.
*/
BOOST_BEAST_DECL
void
close(error_code& ec);
/** Open a file at the given path with the specified mode
@param path The utf-8 encoded path to the file
@param mode The file mode to use
@param ec Set to the error, if any occurred
*/
BOOST_BEAST_DECL
void
open(char const* path, file_mode mode, error_code& ec);
/** Return the size of the open file
@param ec Set to the error, if any occurred
@return The size in bytes
*/
BOOST_BEAST_DECL
std::uint64_t
size(error_code& ec) const;
/** Return the current position in the open file
@param ec Set to the error, if any occurred
@return The offset in bytes from the beginning of the file
*/
BOOST_BEAST_DECL
std::uint64_t
pos(error_code& ec) const;
/** Adjust the current position in the open file
@param offset The offset in bytes from the beginning of the file
@param ec Set to the error, if any occurred
*/
BOOST_BEAST_DECL
void
seek(std::uint64_t offset, error_code& ec);
/** Read from the open file
@param buffer The buffer for storing the result of the read
@param n The number of bytes to read
@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;
/** Write to the open file
@param buffer The buffer holding the data to write
@param n The number of bytes to write
@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);
};
} // beast
} // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/core/impl/file_posix.ipp>
#endif
#endif
#endif

View File

@@ -0,0 +1,171 @@
//
// 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)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_FILE_STDIO_HPP
#define BOOST_BEAST_CORE_FILE_STDIO_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/file_base.hpp>
#include <cstdio>
#include <cstdint>
namespace boost {
namespace beast {
/** An implementation of File which uses cstdio.
This class implements a file using the interfaces present
in the C++ Standard Library, in `<stdio>`.
*/
class file_stdio
{
std::FILE* f_ = nullptr;
public:
/** The type of the underlying file handle.
This is platform-specific.
*/
using native_handle_type = std::FILE*;
/** Destructor
If the file is open it is first closed.
*/
BOOST_BEAST_DECL
~file_stdio();
/** Constructor
There is no open file initially.
*/
file_stdio() = default;
/** Constructor
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.
std::FILE*
native_handle() const
{
return f_;
}
/** Set the native handle associated with the file.
If the file is open it is first closed.
@param f The native file handle to assign.
*/
BOOST_BEAST_DECL
void
native_handle(std::FILE* f);
/// Returns `true` if the file is open
bool
is_open() const
{
return f_ != nullptr;
}
/** Close the file if open
@param ec Set to the error, if any occurred.
*/
BOOST_BEAST_DECL
void
close(error_code& ec);
/** Open a file at the given path with the specified mode
@param path The utf-8 encoded path to the file
@param mode The file mode to use
@param ec Set to the error, if any occurred
*/
BOOST_BEAST_DECL
void
open(char const* path, file_mode mode, error_code& ec);
/** Return the size of the open file
@param ec Set to the error, if any occurred
@return The size in bytes
*/
BOOST_BEAST_DECL
std::uint64_t
size(error_code& ec) const;
/** Return the current position in the open file
@param ec Set to the error, if any occurred
@return The offset in bytes from the beginning of the file
*/
BOOST_BEAST_DECL
std::uint64_t
pos(error_code& ec) const;
/** Adjust the current position in the open file
@param offset The offset in bytes from the beginning of the file
@param ec Set to the error, if any occurred
*/
BOOST_BEAST_DECL
void
seek(std::uint64_t offset, error_code& ec);
/** Read from the open file
@param buffer The buffer for storing the result of the read
@param n The number of bytes to read
@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;
/** Write to the open file
@param buffer The buffer holding the data to write
@param n The number of bytes to write
@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);
};
} // beast
} // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/core/impl/file_stdio.ipp>
#endif
#endif

View File

@@ -0,0 +1,190 @@
//
// 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)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_FILE_WIN32_HPP
#define BOOST_BEAST_CORE_FILE_WIN32_HPP
#include <boost/beast/core/detail/config.hpp>
#if ! defined(BOOST_BEAST_USE_WIN32_FILE)
# ifdef BOOST_MSVC
# define BOOST_BEAST_USE_WIN32_FILE 1
# else
# define BOOST_BEAST_USE_WIN32_FILE 0
# endif
#endif
#if BOOST_BEAST_USE_WIN32_FILE
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/file_base.hpp>
#include <boost/winapi/basic_types.hpp>
#include <boost/winapi/handles.hpp>
#include <cstdio>
#include <cstdint>
namespace boost {
namespace beast {
/** An implementation of File for Win32.
This class implements a <em>File</em> using Win32 native interfaces.
*/
class file_win32
{
boost::winapi::HANDLE_ h_ =
boost::winapi::INVALID_HANDLE_VALUE_;
public:
/** The type of the underlying file handle.
This is platform-specific.
*/
#if BOOST_BEAST_DOXYGEN
using native_handle_type = HANDLE;
#else
using native_handle_type = boost::winapi::HANDLE_;
#endif
/** Destructor
If the file is open it is first closed.
*/
BOOST_BEAST_DECL
~file_win32();
/** Constructor
There is no open file initially.
*/
file_win32() = default;
/** Constructor
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.
native_handle_type
native_handle()
{
return h_;
}
/** Set the native handle associated with the file.
If the file is open it is first closed.
@param h The native file handle to assign.
*/
BOOST_BEAST_DECL
void
native_handle(native_handle_type h);
/// Returns `true` if the file is open
bool
is_open() const
{
return h_ != boost::winapi::INVALID_HANDLE_VALUE_;
}
/** Close the file if open
@param ec Set to the error, if any occurred.
*/
BOOST_BEAST_DECL
void
close(error_code& ec);
/** Open a file at the given path with the specified mode
@param path The utf-8 encoded path to the file
@param mode The file mode to use
@param ec Set to the error, if any occurred
*/
BOOST_BEAST_DECL
void
open(char const* path, file_mode mode, error_code& ec);
/** Return the size of the open file
@param ec Set to the error, if any occurred
@return The size in bytes
*/
BOOST_BEAST_DECL
std::uint64_t
size(error_code& ec) const;
/** Return the current position in the open file
@param ec Set to the error, if any occurred
@return The offset in bytes from the beginning of the file
*/
BOOST_BEAST_DECL
std::uint64_t
pos(error_code& ec);
/** Adjust the current position in the open file
@param offset The offset in bytes from the beginning of the file
@param ec Set to the error, if any occurred
*/
BOOST_BEAST_DECL
void
seek(std::uint64_t offset, error_code& ec);
/** Read from the open file
@param buffer The buffer for storing the result of the read
@param n The number of bytes to read
@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);
/** Write to the open file
@param buffer The buffer holding the data to write
@param n The number of bytes to write
@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);
};
} // beast
} // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/core/impl/file_win32.ipp>
#endif
#endif
#endif

View File

@@ -0,0 +1,530 @@
//
// 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_FLAT_BUFFER_HPP
#define BOOST_BEAST_FLAT_BUFFER_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/allocator.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/core/empty_value.hpp>
#include <limits>
#include <memory>
#include <type_traits>
namespace boost {
namespace beast {
/** A dynamic buffer providing buffer sequences 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.
@note This class is designed for use with algorithms that
take dynamic buffers as parameters, and are optimized
for the case where the input sequence or output sequence
is stored in a single contiguous buffer.
*/
template<class Allocator>
class basic_flat_buffer
#if ! BOOST_BEAST_DOXYGEN
: private boost::empty_value<
typename detail::allocator_traits<Allocator>::
template rebind_alloc<char>>
#endif
{
template<class OtherAlloc>
friend class basic_flat_buffer;
using base_alloc_type = typename
detail::allocator_traits<Allocator>::
template rebind_alloc<char>;
static bool constexpr default_nothrow =
std::is_nothrow_default_constructible<Allocator>::value;
using alloc_traits =
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
std::size_t
dist(char const* first, char const* last) noexcept
{
return static_cast<std::size_t>(last - first);
}
char* begin_;
char* in_;
char* out_;
char* last_;
char* end_;
std::size_t max_;
public:
/// The type of allocator used.
using allocator_type = Allocator;
/// Destructor
~basic_flat_buffer();
/** Constructor
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() noexcept(default_nothrow);
/** Constructor
After construction, @ref capacity will return zero, and
@ref max_size will return the specified value of `limit`.
@param limit The desired maximum size.
*/
explicit
basic_flat_buffer(
std::size_t limit) noexcept(default_nothrow);
/** Constructor
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_flat_buffer(Allocator const& alloc) noexcept;
/** Constructor
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.
@esafe
No-throw guarantee.
*/
basic_flat_buffer(
std::size_t limit,
Allocator const& alloc) noexcept;
/** Move Constructor
The container is constructed with the contents of `other`
using move semantics. The maximum size will be the same
as the moved-from object.
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) noexcept;
/** 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.
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 moved-from object will have zero capacity, zero readable
bytes, and zero writable bytes.
@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&& other,
Allocator const& alloc);
/** 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);
/** 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 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,
Allocator const& alloc);
/** 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)
noexcept(default_nothrow);
/** 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 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);
/** 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.
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&
operator=(basic_flat_buffer&& other) noexcept;
/** 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 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
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 allocator used.
allocator_type
get_allocator() const
{
return this->get();
}
/** 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);
/** Request the removal of unused capacity.
This function attempts to reduce @ref capacity()
to @ref size(), which may not succeed.
@esafe
No-throw guarantee.
*/
void
shrink_to_fit() noexcept;
/** 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 writable bytes.
using mutable_buffers_type = net::mutable_buffer;
/// Returns the number of readable bytes.
std::size_t
size() const noexcept
{
return dist(in_, out_);
}
/// Return the maximum number of bytes, both readable and writable, that can ever be held.
std::size_t
max_size() const noexcept
{
return max_;
}
/// 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 dist(begin_, end_);
}
/// Returns a constant buffer sequence representing the readable bytes
const_buffers_type
data() const noexcept
{
return {in_, dist(in_, out_)};
}
/// 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_buffers_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. Memory may be
reallocated as needed.
All buffers sequences previously obtained using
@ref data or @ref prepare become invalid.
@param n The desired number of bytes in the returned buffer
sequence.
@throws std::length_error if `size() + n` exceeds either
`max_size()` or the allocator's maximum allocation 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 buffers sequences previously obtained using
@ref data or @ref prepare become invalid.
@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
{
out_ += (std::min)(n, dist(out_, last_));
}
/** 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 become invalid.
@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;
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.hpp>
#endif

View File

@@ -0,0 +1,317 @@
//
// 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_FLAT_STATIC_BUFFER_HPP
#define BOOST_BEAST_FLAT_STATIC_BUFFER_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/asio/buffer.hpp>
#include <algorithm>
#include <cstddef>
#include <cstring>
namespace boost {
namespace beast {
/** A dynamic buffer using a fixed size internal 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.
@li Buffer sequences representing the readable and writable
bytes, returned by @ref data and @ref prepare, will have
length one.
@li Ownership of the underlying storage belongs to the
derived class.
@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
{
char* begin_;
char* in_;
char* out_;
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;
public:
/** Constructor
This creates a dynamic buffer using the provided storage area.
@param p A pointer to valid storage of at least `n` bytes.
@param n The number of valid bytes pointed to by `p`.
*/
flat_static_buffer_base(
void* p, std::size_t n) noexcept
{
reset(p, n);
}
/** 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;
//--------------------------------------------------------------------------
/// The ConstBufferSequence used to represent the readable bytes.
using const_buffers_type = net::const_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 noexcept
{
return out_ - in_;
}
/// Return the maximum number of bytes, both readable and writable, that can ever be held.
std::size_t
max_size() const noexcept
{
return dist(begin_, end_);
}
/// 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
{
return {in_, dist(in_, out_)};
}
/// 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_buffers_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.
All buffers sequences previously obtained using
@ref data or @ref prepare are invalidated.
@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.
*/
BOOST_BEAST_DECL
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 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.
@esafe
No-throw guarantee.
*/
void
commit(std::size_t n) noexcept
{
out_ += (std::min<std::size_t>)(n, last_ - out_);
}
/** 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) noexcept;
protected:
/** Constructor
The buffer will be in an undefined state. It is necessary
for the derived class to call @ref reset with a pointer
and size in order to initialize the object.
*/
flat_static_buffer_base() = default;
/** Reset the pointed-to buffer.
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 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) noexcept;
private:
static
std::size_t
dist(char const* first, char const* last) noexcept
{
return static_cast<std::size_t>(last - first);
}
};
//------------------------------------------------------------------------------
/** 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.
This implements a dynamic buffer using no memory allocations.
@tparam N The number of bytes in the internal buffer.
@note To reduce the number of template instantiations when passing
objects of this type in a deduced context, the signature of the
receiving function should use @ref flat_static_buffer_base instead.
@see flat_static_buffer_base
*/
template<std::size_t N>
class flat_static_buffer : public flat_static_buffer_base
{
char buf_[N];
public:
/// Constructor
flat_static_buffer(flat_static_buffer const&);
/// Constructor
flat_static_buffer()
: flat_static_buffer_base(buf_, N)
{
}
/// Assignment
flat_static_buffer& operator=(flat_static_buffer const&);
/// Returns the @ref flat_static_buffer_base portion of this object
flat_static_buffer_base&
base()
{
return *this;
}
/// Returns the @ref flat_static_buffer_base portion of this object
flat_static_buffer_base const&
base() const
{
return *this;
}
/// Return the maximum sum of the input and output sequence sizes.
std::size_t constexpr
max_size() const
{
return N;
}
/// Return the maximum sum of input and output sizes that can be held without an allocation.
std::size_t constexpr
capacity() const
{
return N;
}
};
} // 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

View File

@@ -0,0 +1,351 @@
//
// 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,
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler =
net::default_completion_token_t<executor_type>>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler =
net::default_completion_token_t<executor_type>{});
/** 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,
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler =
net::default_completion_token_t<executor_type>>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
async_write_some(
ConstBufferSequence const& buffers,
WriteHandler&& handler =
net::default_completion_token_t<executor_type>{});
};
} // beast
} // boost
#include <boost/beast/core/impl/flat_stream.hpp>
#endif

View File

@@ -0,0 +1,155 @@
//
// 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());
auto* p = this;
p->~allocate_stable_state();
a.deallocate(p, 1);
}
};
} // detail
template<
class Handler,
class Executor1,
class Allocator,
class Function>
boost::asio::asio_handler_invoke_is_deprecated
asio_handler_invoke(
Function&& f,
async_base<Handler, Executor1, Allocator>* p)
{
using boost::asio::asio_handler_invoke;
return asio_handler_invoke(f,
p->get_legacy_handler_pointer());
}
template<
class Handler,
class Executor1,
class Allocator>
boost::asio::asio_handler_allocate_is_deprecated
asio_handler_allocate(
std::size_t size,
async_base<Handler, Executor1, Allocator>* p)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(size,
p->get_legacy_handler_pointer());
}
template<
class Handler,
class Executor1,
class Allocator>
boost::asio::asio_handler_deallocate_is_deprecated
asio_handler_deallocate(
void* mem, std::size_t size,
async_base<Handler, Executor1, Allocator>* p)
{
using boost::asio::asio_handler_deallocate;
return 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 boost::asio::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 state = detail::allocate_stable_state<
State, allocator_type>;
using A = typename detail::allocator_traits<
allocator_type>::template rebind_alloc<state>;
struct deleter
{
allocator_type alloc;
state* ptr;
~deleter()
{
if(ptr)
{
A a(alloc);
a.deallocate(ptr, 1);
}
}
};
A a(base.get_allocator());
deleter d{base.get_allocator(), a.allocate(1)};
::new(static_cast<void*>(d.ptr))
state(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

File diff suppressed because it is too large Load Diff

View File

@@ -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/is_invocable.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, BOOST_BEAST_ASYNC_TPARAM2 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_completion_token_for<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, BOOST_BEAST_ASYNC_TPARAM2 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

View File

@@ -0,0 +1,707 @@
//
// 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_ADAPTOR_HPP
#define BOOST_BEAST_IMPL_BUFFERS_ADAPTOR_HPP
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/beast/core/buffers_adaptor.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>
template<bool isMutable>
class buffers_adaptor<MutableBufferSequence>::subrange
{
public:
using value_type = typename std::conditional<
isMutable,
net::mutable_buffer,
net::const_buffer>::type;
struct iterator;
// construct from two iterators plus optionally subrange definition
subrange(
iter_type first, // iterator to first buffer in storage
iter_type last, // iterator to last buffer in storage
std::size_t pos = 0, // the offset in bytes from the beginning of the storage
std::size_t n = // the total length of the subrange
(std::numeric_limits<std::size_t>::max)());
#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
subrange(
subrange const& other)
: first_(other.first_)
, last_(other.last_)
, first_offset_(other.first_offset_)
, last_size_(other.last_size_)
{
}
subrange& operator=(
subrange const& other)
{
first_ = other.first_;
last_ = other.last_;
first_offset_ = other.first_offset_;
last_size_ = other.last_size_;
return *this;
}
#else
subrange(
subrange const&) = default;
subrange& operator=(
subrange const&) = default;
#endif
// allow conversion from mutable to const
template<bool isMutable_ = isMutable, typename
std::enable_if<!isMutable_>::type * = nullptr>
subrange(subrange<true> const &other)
: first_(other.first_)
, last_(other.last_)
, first_offset_(other.first_offset_)
, last_size_(other.last_size_)
{
}
iterator
begin() const;
iterator
end() const;
private:
friend subrange<!isMutable>;
void
adjust(
std::size_t pos,
std::size_t n);
private:
// points to the first buffer in the sequence
iter_type first_;
// Points to one past the end of the underlying buffer sequence
iter_type last_;
// The initial offset into the first buffer
std::size_t first_offset_;
// how many bytes in the penultimate buffer are used (if any)
std::size_t last_size_;
};
#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
# pragma warning (pop)
#endif
//------------------------------------------------------------------------------
template<class MutableBufferSequence>
template<bool isMutable>
struct buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator
{
using iterator_category = std::bidirectional_iterator_tag;
using value_type = typename
buffers_adaptor<MutableBufferSequence>::
template subrange<isMutable>::
value_type;
using reference = value_type&;
using pointer = value_type*;
using difference_type = std::ptrdiff_t;
iterator(
subrange<isMutable> const *parent,
iter_type it);
iterator();
value_type
operator*() const;
pointer
operator->() const = delete;
iterator &
operator++();
iterator
operator++(int);
iterator &
operator--();
iterator
operator--(int);
bool
operator==(iterator const &b) const;
bool
operator!=(iterator const &b) const;
private:
subrange<isMutable> const *parent_;
iter_type it_;
};
//------------------------------------------------------------------------------
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
end_impl() const ->
iter_type
{
return out_ == end_ ? end_ : std::next(out_);
}
template<class MutableBufferSequence>
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_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>
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_))
{
}
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
operator=(buffers_adaptor const& other) ->
buffers_adaptor&
{
if(this == &other)
return *this;
auto const nbegin = std::distance<iter_type>(
net::buffer_sequence_begin(other.bs_),
other.begin_);
auto const nout = std::distance<iter_type>(
net::buffer_sequence_begin(other.bs_),
other.out_);
auto const nend = std::distance<iter_type>(
net::buffer_sequence_begin(other.bs_),
other.end_);
bs_ = other.bs_;
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_;
out_pos_ = other.out_pos_;
out_end_ = other.out_end_;
return *this;
}
//
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
data() const noexcept ->
const_buffers_type
{
return const_buffers_type(
begin_, end_,
in_pos_, in_size_);
}
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
data() noexcept ->
mutable_buffers_type
{
return mutable_buffers_type(
begin_, end_,
in_pos_, in_size_);
}
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
prepare(std::size_t n) ->
mutable_buffers_type
{
auto prepared = n;
end_ = out_;
if(end_ != net::buffer_sequence_end(bs_))
{
auto size = buffer_bytes(*end_) - out_pos_;
if(n > size)
{
n -= size;
while(++end_ !=
net::buffer_sequence_end(bs_))
{
size = buffer_bytes(*end_);
if(n < size)
{
out_end_ = n;
n = 0;
++end_;
break;
}
n -= size;
out_end_ = size;
}
}
else
{
++end_;
out_end_ = out_pos_ + n;
n = 0;
}
}
if(n > 0)
BOOST_THROW_EXCEPTION(std::length_error{
"buffers_adaptor too long"});
return mutable_buffers_type(out_, end_, out_pos_, prepared);
}
template<class MutableBufferSequence>
void
buffers_adaptor<MutableBufferSequence>::
commit(std::size_t n) noexcept
{
if(out_ == end_)
return;
auto const last = std::prev(end_);
while(out_ != last)
{
auto const avail =
buffer_bytes(*out_) - out_pos_;
if(n < avail)
{
out_pos_ += n;
in_size_ += n;
return;
}
++out_;
n -= avail;
out_pos_ = 0;
in_size_ += avail;
}
n = std::min<std::size_t>(
n, out_end_ - out_pos_);
out_pos_ += n;
in_size_ += n;
if(out_pos_ == buffer_bytes(*out_))
{
++out_;
out_pos_ = 0;
out_end_ = 0;
}
}
template<class MutableBufferSequence>
void
buffers_adaptor<MutableBufferSequence>::
consume(std::size_t n) noexcept
{
while(begin_ != out_)
{
auto const avail =
buffer_bytes(*begin_) - in_pos_;
if(n < avail)
{
in_size_ -= n;
in_pos_ += n;
return;
}
n -= avail;
in_size_ -= avail;
in_pos_ = 0;
++begin_;
}
auto const avail = out_pos_ - in_pos_;
if(n < avail)
{
in_size_ -= n;
in_pos_ += n;
}
else
{
in_size_ -= avail;
in_pos_ = out_pos_;
}
}
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
make_subrange(std::size_t pos, std::size_t n) ->
subrange<true>
{
return subrange<true>(
begin_, net::buffer_sequence_end(bs_),
in_pos_ + pos, n);
}
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
make_subrange(std::size_t pos, std::size_t n) const ->
subrange<false>
{
return subrange<false>(
begin_, net::buffer_sequence_end(bs_),
in_pos_ + pos, n);
}
// -------------------------------------------------------------------------
// subrange
template<class MutableBufferSequence>
template<bool isMutable>
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
subrange(
iter_type first, // iterator to first buffer in storage
iter_type last, // iterator to last buffer in storage
std::size_t pos, // the offset in bytes from the beginning of the storage
std::size_t n) // the total length of the subrange
: first_(first)
, last_(last)
, first_offset_(0)
, last_size_((std::numeric_limits<std::size_t>::max)())
{
adjust(pos, n);
}
template<class MutableBufferSequence>
template<bool isMutable>
void
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
adjust(
std::size_t pos,
std::size_t n)
{
if (n == 0)
last_ = first_;
if (first_ == last_)
{
first_offset_ = 0;
last_size_ = 0;
return;
}
auto is_last = [this](iter_type iter) {
return std::next(iter) == last_;
};
pos += first_offset_;
while (pos)
{
auto adjust = (std::min)(pos, first_->size());
if (adjust >= first_->size())
{
++first_;
first_offset_ = 0;
pos -= adjust;
}
else
{
first_offset_ = adjust;
pos = 0;
break;
}
}
auto current = first_;
auto max_elem = current->size() - first_offset_;
if (is_last(current))
{
// both first and last element
last_size_ = (std::min)(max_elem, n);
last_ = std::next(current);
return;
}
else if (max_elem >= n)
{
last_ = std::next(current);
last_size_ = n;
}
else
{
n -= max_elem;
++current;
}
for (;;)
{
max_elem = current->size();
if (is_last(current))
{
last_size_ = (std::min)(n, last_size_);
return;
}
else if (max_elem < n)
{
n -= max_elem;
++current;
}
else
{
last_size_ = n;
last_ = std::next(current);
return;
}
}
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
begin() const ->
iterator
{
return iterator(this, first_);
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
end() const ->
iterator
{
return iterator(this, last_);
}
// -------------------------------------------------------------------------
// buffers_adaptor::subrange::iterator
template<class MutableBufferSequence>
template<bool isMutable>
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
iterator()
: parent_(nullptr)
, it_()
{
}
template<class MutableBufferSequence>
template<bool isMutable>
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
iterator(subrange<isMutable> const *parent,
iter_type it)
: parent_(parent)
, it_(it)
{
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator*() const ->
value_type
{
value_type result = *it_;
if (it_ == parent_->first_)
result += parent_->first_offset_;
if (std::next(it_) == parent_->last_)
{
result = value_type(
result.data(),
(std::min)(
parent_->last_size_,
result.size()));
}
return result;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator++() ->
iterator &
{
++it_;
return *this;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator++(int) ->
iterator
{
auto result = *this;
++it_;
return result;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator--() ->
iterator &
{
--it_;
return *this;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator--(int) ->
iterator
{
auto result = *this;
--it_;
return result;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator==(iterator const &b) const ->
bool
{
return it_ == b.it_;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator!=(iterator const &b) const ->
bool
{
return !(*this == b);
}
} // beast
} // boost
#endif

View File

@@ -0,0 +1,470 @@
//
// 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/variant.hpp>
#include <boost/asio/buffer.hpp>
#include <cstdint>
#include <iterator>
#include <new>
#include <stdexcept>
#include <utility>
namespace boost {
namespace beast {
template<class Buffer>
class buffers_cat_view<Buffer>
{
Buffer buffer_;
public:
using value_type = buffers_type<Buffer>;
using const_iterator = buffers_iterator_type<Buffer>;
explicit
buffers_cat_view(Buffer const& buffer)
: buffer_(buffer)
{
}
const_iterator
begin() const
{
return net::buffer_sequence_begin(buffer_);
}
const_iterator
end() const
{
return net::buffer_sequence_end(buffer_);
}
};
#if defined(_MSC_VER) && ! defined(__clang__)
# define BOOST_BEAST_UNREACHABLE() __assume(false)
# define BOOST_BEAST_UNREACHABLE_RETURN(v) return v
#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

View File

@@ -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

View File

@@ -0,0 +1,225 @@
//
// 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_SUFFIX_HPP
#define BOOST_BEAST_IMPL_BUFFERS_SUFFIX_HPP
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/type_traits.hpp>
#include <algorithm>
#include <cstdint>
#include <iterator>
#include <type_traits>
#include <utility>
namespace boost {
namespace beast {
template<class Buffers>
class buffers_suffix<Buffers>::const_iterator
{
friend class buffers_suffix<Buffers>;
using iter_type = buffers_iterator_type<Buffers>;
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,
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;
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
{
if(it_ == b_->begin_)
return value_type(*it_) + b_->skip_;
return value_type(*it_);
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
const_iterator(
buffers_suffix const& b,
iter_type it)
: it_(it)
, b_(&b)
{
}
};
//------------------------------------------------------------------------------
template<class Buffers>
buffers_suffix<Buffers>::
buffers_suffix()
: begin_(net::buffer_sequence_begin(bs_))
{
}
template<class Buffers>
buffers_suffix<Buffers>::
buffers_suffix(buffers_suffix const& other)
: buffers_suffix(other,
std::distance<iter_type>(
net::buffer_sequence_begin(
other.bs_), other.begin_))
{
}
template<class Buffers>
buffers_suffix<Buffers>::
buffers_suffix(Buffers const& bs)
: bs_(bs)
, begin_(net::buffer_sequence_begin(bs_))
{
static_assert(
net::is_const_buffer_sequence<Buffers>::value ||
net::is_mutable_buffer_sequence<Buffers>::value,
"BufferSequence type requirements not met");
}
template<class Buffers>
template<class... Args>
buffers_suffix<Buffers>::
buffers_suffix(boost::in_place_init_t, Args&&... args)
: bs_(std::forward<Args>(args)...)
, begin_(net::buffer_sequence_begin(bs_))
{
static_assert(sizeof...(Args) > 0,
"Missing constructor arguments");
static_assert(
std::is_constructible<Buffers, Args...>::value,
"Buffers not constructible from arguments");
}
template<class Buffers>
auto
buffers_suffix<Buffers>::
operator=(buffers_suffix const& other) ->
buffers_suffix&
{
auto const dist = std::distance<iter_type>(
net::buffer_sequence_begin(other.bs_),
other.begin_);
bs_ = other.bs_;
begin_ = std::next(
net::buffer_sequence_begin(bs_), dist);
skip_ = other.skip_;
return *this;
}
template<class Buffers>
auto
buffers_suffix<Buffers>::
begin() const ->
const_iterator
{
return const_iterator{*this, begin_};
}
template<class Buffers>
auto
buffers_suffix<Buffers>::
end() const ->
const_iterator
{
return const_iterator{*this,
net::buffer_sequence_end(bs_)};
}
template<class Buffers>
void
buffers_suffix<Buffers>::
consume(std::size_t amount)
{
auto const end =
net::buffer_sequence_end(bs_);
for(;amount > 0 && begin_ != end; ++begin_)
{
auto const len =
buffer_bytes(*begin_) - skip_;
if(amount < len)
{
skip_ += amount;
break;
}
amount -= len;
skip_ = 0;
}
}
} // beast
} // boost
#endif

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,331 @@
//
// 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)
//
// Official repository: https://github.com/boostorg/beast
//
#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))
# define BOOST_BEAST_NO_POSIX_FADVISE
# endif
#endif
#if ! defined(BOOST_BEAST_USE_POSIX_FADVISE)
# if ! defined(BOOST_BEAST_NO_POSIX_FADVISE)
# define BOOST_BEAST_USE_POSIX_FADVISE 1
# else
# define BOOST_BEAST_USE_POSIX_FADVISE 0
# endif
#endif
namespace boost {
namespace beast {
int
file_posix::
native_close(native_handle_type& fd)
{
/* 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) != 0)
ev = errno;
fd = -1;
}
return ev;
}
file_posix::
~file_posix()
{
native_close(fd_);
}
file_posix::
file_posix(file_posix&& other)
: fd_(boost::exchange(other.fd_, -1))
{
}
file_posix&
file_posix::
operator=(file_posix&& other)
{
if(&other == this)
return *this;
native_close(fd_);
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
void
file_posix::
native_handle(native_handle_type fd)
{
native_close(fd_);
fd_ = fd;
}
void
file_posix::
close(error_code& ec)
{
auto const ev = native_close(fd_);
if(ev)
ec.assign(ev, system_category());
else
ec = {};
}
void
file_posix::
open(char const* path, file_mode mode, error_code& ec)
{
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;
#endif
switch(mode)
{
default:
case file_mode::read:
f = O_RDONLY;
#if BOOST_BEAST_USE_POSIX_FADVISE
advise = POSIX_FADV_RANDOM;
#endif
break;
case file_mode::scan:
f = O_RDONLY;
#if BOOST_BEAST_USE_POSIX_FADVISE
advise = POSIX_FADV_SEQUENTIAL;
#endif
break;
case file_mode::write:
f = O_RDWR | O_CREAT | O_TRUNC;
#if BOOST_BEAST_USE_POSIX_FADVISE
advise = POSIX_FADV_RANDOM;
#endif
break;
case file_mode::write_new:
f = O_RDWR | O_CREAT | O_EXCL;
#if BOOST_BEAST_USE_POSIX_FADVISE
advise = POSIX_FADV_RANDOM;
#endif
break;
case file_mode::write_existing:
f = O_RDWR | O_EXCL;
#if BOOST_BEAST_USE_POSIX_FADVISE
advise = POSIX_FADV_RANDOM;
#endif
break;
case file_mode::append:
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_WRONLY | O_APPEND;
#if BOOST_BEAST_USE_POSIX_FADVISE
advise = POSIX_FADV_SEQUENTIAL;
#endif
break;
}
for(;;)
{
fd_ = ::open(path, f, 0644);
if(fd_ != -1)
break;
auto const ev = errno;
if(ev != EINTR)
{
ec.assign(ev, system_category());
return;
}
}
#if BOOST_BEAST_USE_POSIX_FADVISE
if(::posix_fadvise(fd_, 0, 0, advise))
{
auto const ev = errno;
native_close(fd_);
ec.assign(ev, system_category());
return;
}
#endif
ec = {};
}
std::uint64_t
file_posix::
size(error_code& ec) const
{
if(fd_ == -1)
{
ec = make_error_code(errc::bad_file_descriptor);
return 0;
}
struct stat st;
if(::fstat(fd_, &st) != 0)
{
ec.assign(errno, system_category());
return 0;
}
ec = {};
return st.st_size;
}
std::uint64_t
file_posix::
pos(error_code& ec) const
{
if(fd_ == -1)
{
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, system_category());
return 0;
}
ec = {};
return result;
}
void
file_posix::
seek(std::uint64_t offset, error_code& ec)
{
if(fd_ == -1)
{
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, system_category());
return;
}
ec = {};
}
std::size_t
file_posix::
read(void* buffer, std::size_t n, error_code& ec) const
{
if(fd_ == -1)
{
ec = make_error_code(errc::bad_file_descriptor);
return 0;
}
std::size_t nread = 0;
while(n > 0)
{
auto const amount = static_cast<ssize_t>((std::min)(
n, static_cast<std::size_t>(SSIZE_MAX)));
auto const result = ::read(fd_, buffer, amount);
if(result == -1)
{
auto const ev = errno;
if(ev == EINTR)
continue;
ec.assign(ev, system_category());
return nread;
}
if(result == 0)
{
// short read
return nread;
}
n -= result;
nread += result;
buffer = static_cast<char*>(buffer) + result;
}
return nread;
}
std::size_t
file_posix::
write(void const* buffer, std::size_t n, error_code& ec)
{
if(fd_ == -1)
{
ec = make_error_code(errc::bad_file_descriptor);
return 0;
}
std::size_t nwritten = 0;
while(n > 0)
{
auto const amount = static_cast<ssize_t>((std::min)(
n, static_cast<std::size_t>(SSIZE_MAX)));
auto const result = ::write(fd_, buffer, amount);
if(result == -1)
{
auto const ev = errno;
if(ev == EINTR)
continue;
ec.assign(ev, system_category());
return nwritten;
}
n -= result;
nwritten += result;
buffer = static_cast<char const*>(buffer) + result;
}
return nwritten;
}
} // beast
} // boost
#endif
#endif

View File

@@ -0,0 +1,325 @@
//
// 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)
//
// Official repository: https://github.com/boostorg/beast
//
#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/beast/core/detail/win32_unicode_path.hpp>
#include <boost/config/workaround.hpp>
#include <boost/core/exchange.hpp>
#include <limits>
namespace boost {
namespace beast {
file_stdio::
~file_stdio()
{
if(f_)
fclose(f_);
}
file_stdio::
file_stdio(file_stdio&& other)
: f_(boost::exchange(other.f_, nullptr))
{
}
file_stdio&
file_stdio::
operator=(file_stdio&& other)
{
if(&other == this)
return *this;
if(f_)
fclose(f_);
f_ = other.f_;
other.f_ = nullptr;
return *this;
}
void
file_stdio::
native_handle(std::FILE* f)
{
if(f_)
fclose(f_);
f_ = f;
}
void
file_stdio::
close(error_code& ec)
{
if(f_)
{
int failed = fclose(f_);
f_ = nullptr;
if(failed)
{
ec.assign(errno, generic_category());
return;
}
}
ec = {};
}
void
file_stdio::
open(char const* path, file_mode mode, error_code& ec)
{
if(f_)
{
fclose(f_);
f_ = nullptr;
}
ec = {};
#ifdef BOOST_MSVC
boost::winapi::WCHAR_ const* s;
detail::win32_unicode_path unicode_path(path, ec);
if (ec)
return;
#else
char const* s;
#endif
switch(mode)
{
default:
case file_mode::read:
#ifdef BOOST_MSVC
s = L"rb";
#else
s = "rb";
#endif
break;
case file_mode::scan:
#ifdef BOOST_MSVC
s = L"rbS";
#else
s = "rb";
#endif
break;
case file_mode::write:
#ifdef BOOST_MSVC
s = L"wb+";
#else
s = "wb+";
#endif
break;
case file_mode::write_new:
{
#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
std::FILE* f0;
auto const ev = ::_wfopen_s(&f0, unicode_path.c_str(), L"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 = L"wb";
#elif defined(BOOST_MSVC)
s = L"wbx";
#else
s = "wbx";
#endif
break;
}
case file_mode::write_existing:
#ifdef BOOST_MSVC
s = L"rb+";
#else
s = "rb+";
#endif
break;
case file_mode::append:
#ifdef BOOST_MSVC
s = L"ab";
#else
s = "ab";
#endif
break;
case file_mode::append_existing:
{
#ifdef BOOST_MSVC
std::FILE* f0;
auto const ev =
::_wfopen_s(&f0, unicode_path.c_str(), L"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);
#ifdef BOOST_MSVC
s = L"ab";
#else
s = "ab";
#endif
break;
}
}
#ifdef BOOST_MSVC
auto const ev = ::_wfopen_s(&f_, unicode_path.c_str(), s);
if(ev)
{
f_ = nullptr;
ec.assign(ev, generic_category());
return;
}
#else
f_ = std::fopen(path, s);
if(! f_)
{
ec.assign(errno, generic_category());
return;
}
#endif
}
std::uint64_t
file_stdio::
size(error_code& ec) const
{
if(! f_)
{
ec = make_error_code(errc::bad_file_descriptor);
return 0;
}
long pos = std::ftell(f_);
if(pos == -1L)
{
ec.assign(errno, generic_category());
return 0;
}
int result = std::fseek(f_, 0, SEEK_END);
if(result != 0)
{
ec.assign(errno, generic_category());
return 0;
}
long size = std::ftell(f_);
if(size == -1L)
{
ec.assign(errno, generic_category());
std::fseek(f_, pos, SEEK_SET);
return 0;
}
result = std::fseek(f_, pos, SEEK_SET);
if(result != 0)
ec.assign(errno, generic_category());
else
ec = {};
return size;
}
std::uint64_t
file_stdio::
pos(error_code& ec) const
{
if(! f_)
{
ec = make_error_code(errc::bad_file_descriptor);
return 0;
}
long pos = std::ftell(f_);
if(pos == -1L)
{
ec.assign(errno, generic_category());
return 0;
}
ec = {};
return pos;
}
void
file_stdio::
seek(std::uint64_t offset, error_code& ec)
{
if(! f_)
{
ec = make_error_code(errc::bad_file_descriptor);
return;
}
if(offset > static_cast<std::uint64_t>((std::numeric_limits<long>::max)()))
{
ec = make_error_code(errc::invalid_seek);
return;
}
int result = std::fseek(f_,
static_cast<long>(offset), SEEK_SET);
if(result != 0)
ec.assign(errno, generic_category());
else
ec = {};
}
std::size_t
file_stdio::
read(void* buffer, std::size_t n, error_code& ec) const
{
if(! f_)
{
ec = make_error_code(errc::bad_file_descriptor);
return 0;
}
auto nread = std::fread(buffer, 1, n, f_);
if(std::ferror(f_))
{
ec.assign(errno, generic_category());
return 0;
}
return nread;
}
std::size_t
file_stdio::
write(void const* buffer, std::size_t n, error_code& ec)
{
if(! f_)
{
ec = make_error_code(errc::bad_file_descriptor);
return 0;
}
auto nwritten = std::fwrite(buffer, 1, n, f_);
if(std::ferror(f_))
{
ec.assign(errno, generic_category());
return 0;
}
return nwritten;
}
} // beast
} // boost
#endif

Some files were not shown because too many files have changed in this diff Show More