add boost on mac

This commit is contained in:
Bassem Girgis
2019-08-10 16:38:17 -05:00
parent 861b918727
commit be945cb63b
14105 changed files with 2714968 additions and 0 deletions

View File

@@ -0,0 +1,711 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_ASYNC_BASE_HPP
#define BOOST_BEAST_CORE_ASYNC_BASE_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/detail/allocator.hpp>
#include <boost/beast/core/detail/async_base.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/asio/post.hpp>
#include <boost/core/exchange.hpp>
#include <boost/core/empty_value.hpp>
#include <utility>
namespace boost {
namespace beast {
/** Base class to assist writing composed operations.
A function object submitted to intermediate initiating functions during
a composed operation may derive from this type to inherit all of the
boilerplate to forward the executor, allocator, and legacy customization
points associated with the completion handler invoked at the end of the
composed operation.
The composed operation must be typical; that is, associated with one
executor of an I/O object, and invoking a caller-provided completion
handler when the operation is finished. Classes derived from
@ref async_base will acquire these properties:
@li Ownership of the final completion handler provided upon construction.
@li If the final handler has an associated allocator, this allocator will
be propagated to the composed operation subclass. Otherwise, the
associated allocator will be the type specified in the allocator
template parameter, or the default of `std::allocator<void>` if the
parameter is omitted.
@li If the final handler has an associated executor, then it will be used
as the executor associated with the composed operation. Otherwise,
the specified `Executor1` will be the type of executor associated
with the composed operation.
@li An instance of `net::executor_work_guard` for the instance of `Executor1`
shall be maintained until either the final handler is invoked, or the
operation base is destroyed, whichever comes first.
@li Calls to the legacy customization points
`asio_handler_invoke`,
`asio_handler_allocate`,
`asio_handler_deallocate`, and
`asio_handler_is_continuation`,
which use argument-dependent lookup, will be forwarded to the
legacy customization points associated with the handler.
@par Example
The following code demonstrates how @ref async_base may be be used to
assist authoring an asynchronous initiating function, by providing all of
the boilerplate to manage the final completion handler in a way that
maintains the allocator and executor associations:
@code
// Asynchronously read into a buffer until the buffer is full, or an error occurs
template<class AsyncReadStream, class ReadHandler>
typename net::async_result<ReadHandler, void(error_code, std::size_t)>::return_type
async_read(AsyncReadStream& stream, net::mutable_buffer buffer, ReadHandler&& handler)
{
using handler_type = BOOST_ASIO_HANDLER_TYPE(ReadHandler, void(error_code, std::size_t));
using base_type = async_base<handler_type, typename AsyncReadStream::executor_type>;
struct op : base_type
{
AsyncReadStream& stream_;
net::mutable_buffer buffer_;
std::size_t total_bytes_transferred_;
op(
AsyncReadStream& stream,
net::mutable_buffer buffer,
handler_type& handler)
: base_type(std::move(handler), stream.get_executor())
, stream_(stream)
, buffer_(buffer)
, total_bytes_transferred_(0)
{
(*this)({}, 0, false); // start the operation
}
void operator()(error_code ec, std::size_t bytes_transferred, bool is_continuation = true)
{
// Adjust the count of bytes and advance our buffer
total_bytes_transferred_ += bytes_transferred;
buffer_ = buffer_ + bytes_transferred;
// Keep reading until buffer is full or an error occurs
if(! ec && buffer_.size() > 0)
return stream_.async_read_some(buffer_, std::move(*this));
// Call the completion handler with the result. If `is_continuation` is
// false, which happens on the first time through this function, then
// `net::post` will be used to call the completion handler, otherwise
// the completion handler will be invoked directly.
this->invoke(is_continuation, ec, total_bytes_transferred_);
}
};
net::async_completion<ReadHandler, void(error_code, std::size_t)> init{handler};
op(stream, buffer, init.completion_handler);
return init.result.get();
}
@endcode
Data members of composed operations implemented as completion handlers
do not have stable addresses, as the composed operation object is move
constructed upon each call to an initiating function. For most operations
this is not a problem. For complex operations requiring stable temporary
storage, the class @ref stable_async_base is provided which offers
additional functionality:
@li The free function @ref allocate_stable may be used to allocate
one or more temporary objects associated with the composed operation.
@li Memory for stable temporary objects is allocated using the allocator
associated with the composed operation.
@li Stable temporary objects are automatically destroyed, and the memory
freed using the associated allocator, either before the final completion
handler is invoked (a Networking requirement) or when the composed operation
is destroyed, whichever occurs first.
@par Temporary Storage Example
The following example demonstrates how a composed operation may store a
temporary object.
@code
@endcode
@tparam Handler The type of the completion handler to store.
This type must meet the requirements of <em>CompletionHandler</em>.
@tparam Executor1 The type of the executor used when the handler has no
associated executor. An instance of this type must be provided upon
construction. The implementation will maintain an executor work guard
and a copy of this instance.
@tparam Allocator The allocator type to use if the handler does not
have an associated allocator. If this parameter is omitted, then
`std::allocator<void>` will be used. If the specified allocator is
not default constructible, an instance of the type must be provided
upon construction.
@see stable_async_base
*/
template<
class Handler,
class Executor1,
class Allocator = std::allocator<void>
>
class async_base
#if ! BOOST_BEAST_DOXYGEN
: private boost::empty_value<Allocator>
#endif
{
static_assert(
net::is_executor<Executor1>::value,
"Executor type requirements not met");
Handler h_;
net::executor_work_guard<Executor1> wg1_;
virtual
void
before_invoke_hook()
{
}
public:
/** Constructor
@param handler The final completion handler.
The type of this object must meet the requirements of <em>CompletionHandler</em>.
The implementation takes ownership of the handler by performing a decay-copy.
@param ex1 The executor associated with the implied I/O object
target of the operation. The implementation shall maintain an
executor work guard for the lifetime of the operation, or until
the final completion handler is invoked, whichever is shorter.
@param alloc The allocator to be associated with objects
derived from this class. If `Allocator` is default-constructible,
this parameter is optional and may be omitted.
*/
#if BOOST_BEAST_DOXYGEN
template<class Handler_>
async_base(
Handler&& handler,
Executor1 const& ex1,
Allocator const& alloc = Allocator());
#else
template<
class Handler_,
class = typename std::enable_if<
! std::is_same<typename
std::decay<Handler_>::type,
async_base
>::value>::type
>
async_base(
Handler_&& handler,
Executor1 const& ex1)
: h_(std::forward<Handler_>(handler))
, wg1_(ex1)
{
}
template<class Handler_>
async_base(
Handler_&& handler,
Executor1 const& ex1,
Allocator const& alloc)
: boost::empty_value<Allocator>(
boost::empty_init_t{}, alloc)
, h_(std::forward<Handler_>(handler))
, wg1_(ex1)
{
}
#endif
/// Move Constructor
async_base(async_base&& other) = default;
/** The type of allocator associated with this object.
If a class derived from @ref async_base is a completion
handler, then the associated allocator of the derived class will
be this type.
*/
using allocator_type =
net::associated_allocator_t<Handler, Allocator>;
/** The type of executor associated with this object.
If a class derived from @ref async_base is a completion
handler, then the associated executor of the derived class will
be this type.
*/
using executor_type =
net::associated_executor_t<Handler, Executor1>;
/** Returns the allocator associated with this object.
If a class derived from @ref async_base is a completion
handler, then the object returned from this function will be used
as the associated allocator of the derived class.
*/
allocator_type
get_allocator() const noexcept
{
return net::get_associated_allocator(h_,
boost::empty_value<Allocator>::get());
}
/** Returns the executor associated with this object.
If a class derived from @ref async_base is a completion
handler, then the object returned from this function will be used
as the associated executor of the derived class.
*/
executor_type
get_executor() const noexcept
{
return net::get_associated_executor(
h_, wg1_.get_executor());
}
/// Returns the handler associated with this object
Handler const&
handler() const noexcept
{
return h_;
}
/** Returns ownership of the handler associated with this object
This function is used to transfer ownership of the handler to
the caller, by move-construction. After the move, the only
valid operations on the base object are move construction and
destruction.
*/
Handler
release_handler()
{
return std::move(h_);
}
/** Invoke the final completion handler, maybe using post.
This invokes the final completion handler with the specified
arguments forwarded. It is undefined to call either of
@ref complete or @ref complete_now more than once.
Any temporary objects allocated with @ref beast::allocate_stable will
be automatically destroyed before the final completion handler
is invoked.
@param is_continuation If this value is `false`, then the
handler will be submitted to the executor using `net::post`.
Otherwise the handler will be invoked as if by calling
@ref complete_now.
@param args A list of optional parameters to invoke the handler
with. The completion handler must be invocable with the parameter
list, or else a compilation error will result.
*/
template<class... Args>
void
complete(bool is_continuation, Args&&... args)
{
this->before_invoke_hook();
if(! is_continuation)
{
auto const ex = get_executor();
net::post(net::bind_executor(
ex,
beast::bind_front_handler(
std::move(h_),
std::forward<Args>(args)...)));
wg1_.reset();
}
else
{
wg1_.reset();
h_(std::forward<Args>(args)...);
}
}
/** Invoke the final completion handler.
This invokes the final completion handler with the specified
arguments forwarded. It is undefined to call either of
@ref complete or @ref complete_now more than once.
Any temporary objects allocated with @ref beast::allocate_stable will
be automatically destroyed before the final completion handler
is invoked.
@param args A list of optional parameters to invoke the handler
with. The completion handler must be invocable with the parameter
list, or else a compilation error will result.
*/
template<class... Args>
void
complete_now(Args&&... args)
{
this->before_invoke_hook();
wg1_.reset();
h_(std::forward<Args>(args)...);
}
#if ! BOOST_BEAST_DOXYGEN
Handler*
get_legacy_handler_pointer() noexcept
{
return std::addressof(h_);
}
#endif
};
//------------------------------------------------------------------------------
/** Base class to provide completion handler boilerplate for composed operations.
A function object submitted to intermediate initiating functions during
a composed operation may derive from this type to inherit all of the
boilerplate to forward the executor, allocator, and legacy customization
points associated with the completion handler invoked at the end of the
composed operation.
The composed operation must be typical; that is, associated with one
executor of an I/O object, and invoking a caller-provided completion
handler when the operation is finished. Classes derived from
@ref async_base will acquire these properties:
@li Ownership of the final completion handler provided upon construction.
@li If the final handler has an associated allocator, this allocator will
be propagated to the composed operation subclass. Otherwise, the
associated allocator will be the type specified in the allocator
template parameter, or the default of `std::allocator<void>` if the
parameter is omitted.
@li If the final handler has an associated executor, then it will be used
as the executor associated with the composed operation. Otherwise,
the specified `Executor1` will be the type of executor associated
with the composed operation.
@li An instance of `net::executor_work_guard` for the instance of `Executor1`
shall be maintained until either the final handler is invoked, or the
operation base is destroyed, whichever comes first.
@li Calls to the legacy customization points
`asio_handler_invoke`,
`asio_handler_allocate`,
`asio_handler_deallocate`, and
`asio_handler_is_continuation`,
which use argument-dependent lookup, will be forwarded to the
legacy customization points associated with the handler.
Data members of composed operations implemented as completion handlers
do not have stable addresses, as the composed operation object is move
constructed upon each call to an initiating function. For most operations
this is not a problem. For complex operations requiring stable temporary
storage, the class @ref stable_async_base is provided which offers
additional functionality:
@li The free function @ref beast::allocate_stable may be used to allocate
one or more temporary objects associated with the composed operation.
@li Memory for stable temporary objects is allocated using the allocator
associated with the composed operation.
@li Stable temporary objects are automatically destroyed, and the memory
freed using the associated allocator, either before the final completion
handler is invoked (a Networking requirement) or when the composed operation
is destroyed, whichever occurs first.
@par Example
The following code demonstrates how @ref stable_async_base may be be used to
assist authoring an asynchronous initiating function, by providing all of
the boilerplate to manage the final completion handler in a way that maintains
the allocator and executor associations. Furthermore, the operation shown
allocates temporary memory using @ref beast::allocate_stable for the timer and
message, whose addresses must not change between intermediate operations:
@code
// Asynchronously send a message multiple times, once per second
template <class AsyncWriteStream, class T, class WriteHandler>
auto async_write_messages(
AsyncWriteStream& stream,
T const& message,
std::size_t repeat_count,
WriteHandler&& handler) ->
typename net::async_result<
typename std::decay<WriteHandler>::type,
void(error_code)>::return_type
{
using handler_type = typename net::async_completion<WriteHandler, void(error_code)>::completion_handler_type;
using base_type = stable_async_base<handler_type, typename AsyncWriteStream::executor_type>;
struct op : base_type, boost::asio::coroutine
{
// This object must have a stable address
struct temporary_data
{
// Although std::string is in theory movable, most implementations
// use a "small buffer optimization" which means that we might
// be submitting a buffer to the write operation and then
// moving the string, invalidating the buffer. To prevent
// undefined behavior we store the string object itself at
// a stable location.
std::string const message;
net::steady_timer timer;
temporary_data(std::string message_, net::io_context& ctx)
: message(std::move(message_))
, timer(ctx)
{
}
};
AsyncWriteStream& stream_;
std::size_t repeats_;
temporary_data& data_;
op(AsyncWriteStream& stream, std::size_t repeats, std::string message, handler_type& handler)
: base_type(std::move(handler), stream.get_executor())
, stream_(stream)
, repeats_(repeats)
, data_(allocate_stable<temporary_data>(*this, std::move(message), stream.get_executor().context()))
{
(*this)(); // start the operation
}
// Including this file provides the keywords for macro-based coroutines
#include <boost/asio/yield.hpp>
void operator()(error_code ec = {}, std::size_t = 0)
{
reenter(*this)
{
// If repeats starts at 0 then we must complete immediately. But
// we can't call the final handler from inside the initiating
// function, so we post our intermediate handler first. We use
// net::async_write with an empty buffer instead of calling
// net::post to avoid an extra function template instantiation, to
// keep compile times lower and make the resulting executable smaller.
yield net::async_write(stream_, net::const_buffer{}, std::move(*this));
while(! ec && repeats_-- > 0)
{
// Send the string. We construct a `const_buffer` here to guarantee
// that we do not create an additional function template instantation
// of net::async_write, since we already instantiated it above for
// net::const_buffer.
yield net::async_write(stream_,
net::const_buffer(net::buffer(data_.message)), std::move(*this));
if(ec)
break;
// Set the timer and wait
data_.timer.expires_after(std::chrono::seconds(1));
yield data_.timer.async_wait(std::move(*this));
}
}
// The base class destroys the temporary data automatically,
// before invoking the final completion handler
this->complete_now(ec);
}
// Including this file undefines the macros for the coroutines
#include <boost/asio/unyield.hpp>
};
net::async_completion<WriteHandler, void(error_code)> completion(handler);
std::ostringstream os;
os << message;
op(stream, repeat_count, os.str(), completion.completion_handler);
return completion.result.get();
}
@endcode
@tparam Handler The type of the completion handler to store.
This type must meet the requirements of <em>CompletionHandler</em>.
@tparam Executor1 The type of the executor used when the handler has no
associated executor. An instance of this type must be provided upon
construction. The implementation will maintain an executor work guard
and a copy of this instance.
@tparam Allocator The allocator type to use if the handler does not
have an associated allocator. If this parameter is omitted, then
`std::allocator<void>` will be used. If the specified allocator is
not default constructible, an instance of the type must be provided
upon construction.
@see allocate_stable, async_base
*/
template<
class Handler,
class Executor1,
class Allocator = std::allocator<void>
>
class stable_async_base
: public async_base<
Handler, Executor1, Allocator>
{
detail::stable_base* list_ = nullptr;
void
before_invoke_hook() override
{
detail::stable_base::destroy_list(list_);
}
public:
/** Constructor
@param handler The final completion handler.
The type of this object must meet the requirements of <em>CompletionHandler</em>.
The implementation takes ownership of the handler by performing a decay-copy.
@param ex1 The executor associated with the implied I/O object
target of the operation. The implementation shall maintain an
executor work guard for the lifetime of the operation, or until
the final completion handler is invoked, whichever is shorter.
@param alloc The allocator to be associated with objects
derived from this class. If `Allocator` is default-constructible,
this parameter is optional and may be omitted.
*/
#if BOOST_BEAST_DOXYGEN
template<class Handler>
stable_async_base(
Handler&& handler,
Executor1 const& ex1,
Allocator const& alloc = Allocator());
#else
template<
class Handler_,
class = typename std::enable_if<
! std::is_same<typename
std::decay<Handler_>::type,
stable_async_base
>::value>::type
>
stable_async_base(
Handler_&& handler,
Executor1 const& ex1)
: async_base<
Handler, Executor1, Allocator>(
std::forward<Handler_>(handler), ex1)
{
}
template<class Handler_>
stable_async_base(
Handler_&& handler,
Executor1 const& ex1,
Allocator const& alloc)
: async_base<
Handler, Executor1, Allocator>(
std::forward<Handler_>(handler), ex1, alloc)
{
}
#endif
/// Move Constructor
stable_async_base(stable_async_base&& other)
: async_base<Handler, Executor1, Allocator>(
std::move(other))
, list_(boost::exchange(other.list_, nullptr))
{
}
/** Destructor
If the completion handler was not invoked, then any
state objects allocated with @ref allocate_stable will
be destroyed here.
*/
~stable_async_base()
{
detail::stable_base::destroy_list(list_);
}
/** Allocate a temporary object to hold operation state.
The object will be destroyed just before the completion
handler is invoked, or when the operation base is destroyed.
*/
template<
class State,
class Handler_,
class Executor1_,
class Allocator_,
class... Args>
friend
State&
allocate_stable(
stable_async_base<
Handler_, Executor1_, Allocator_>& base,
Args&&... args);
};
/** Allocate a temporary object to hold stable asynchronous operation state.
The object will be destroyed just before the completion
handler is invoked, or when the base is destroyed.
@tparam State The type of object to allocate.
@param base The helper to allocate from.
@param args An optional list of parameters to forward to the
constructor of the object being allocated.
@see stable_async_base
*/
template<
class State,
class Handler,
class Executor1,
class Allocator,
class... Args>
State&
allocate_stable(
stable_async_base<
Handler, Executor1, Allocator>& base,
Args&&... args);
} // beast
} // boost
#include <boost/beast/core/impl/async_base.hpp>
#endif

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 namespace net;
buffer_bytes(b);
@endcode
In addition this handles types which are convertible to
`net::const_buffer`; these are not handled by `net::buffer_size`.
@param buffers The buffer or buffer sequence to calculate the size of.
@return The total number of bytes in the buffer or sequence.
*/
#if BOOST_BEAST_DOXYGEN
template<class BufferSequence>
std::size_t
buffer_bytes(BufferSequence const& buffers);
#else
BOOST_BEAST_INLINE_VARIABLE(buffer_bytes, detail::buffer_bytes_impl)
#endif
} // beast
} // boost
#endif

View File

@@ -0,0 +1,349 @@
//
// 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, class ReadHandler>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
async_read_some(MutableBufferSequence const& buffers,
ReadHandler&& handler);
/** Write some data to the stream.
This function is used to write data 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, class WriteHandler>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
async_write_some(ConstBufferSequence const& buffers,
WriteHandler&& handler);
};
} // beast
} // boost
#include <boost/beast/core/impl/buffered_read_stream.hpp>
#endif

View File

@@ -0,0 +1,35 @@
//
// 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_ADAPTER_HPP
#define BOOST_BEAST_BUFFERS_ADAPTER_HPP
#include <boost/beast/core/detail/config.hpp>
#ifdef BOOST_BEAST_ALLOW_DEPRECATED
#include <boost/beast/core/buffers_adaptor.hpp>
namespace boost {
namespace beast {
template<class MutableBufferSequence>
using buffers_adapter = buffers_adaptor<MutableBufferSequence>;
} // beast
} // boost
#else
// The new filename is <boost/beast/core/buffers_adaptor.hpp>
#error The file <boost/beast/core/buffers_adapter.hpp> is deprecated, define BOOST_BEAST_ALLOW_DEPRECATED to disable this error
#endif
#endif

View File

@@ -0,0 +1,226 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_BUFFERS_ADAPTOR_HPP
#define BOOST_BEAST_BUFFERS_ADAPTOR_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/optional.hpp>
#include <type_traits>
namespace boost {
namespace beast {
/** Adapts a <em>MutableBufferSequence</em> into a <em>DynamicBuffer</em>.
This class wraps a <em>MutableBufferSequence</em> to meet the requirements
of <em>DynamicBuffer</em>. Upon construction the input and output sequences
are empty. A copy of the mutable buffer sequence object is stored; however,
ownership of the underlying memory is not transferred. The caller is
responsible for making sure that referenced memory remains valid
for the duration of any operations.
The size of the mutable buffer sequence determines the maximum
number of bytes which may be prepared and committed.
@tparam MutableBufferSequence The type of mutable buffer sequence to adapt.
*/
template<class MutableBufferSequence>
class buffers_adaptor
{
static_assert(net::is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence type requirements not met");
using iter_type =
buffers_iterator_type<MutableBufferSequence>;
template<bool>
class readable_bytes;
MutableBufferSequence bs_;
iter_type begin_;
iter_type out_;
iter_type end_;
std::size_t max_size_;
std::size_t in_pos_ = 0; // offset in *begin_
std::size_t in_size_ = 0; // size of input sequence
std::size_t out_pos_ = 0; // offset in *out_
std::size_t out_end_ = 0; // output end offset
iter_type end_impl() const;
buffers_adaptor(
buffers_adaptor const& other,
std::size_t nbegin,
std::size_t nout,
std::size_t nend);
public:
/// The type of the underlying mutable buffer sequence
using value_type = MutableBufferSequence;
/** Construct a buffers adaptor.
@param buffers The mutable buffer sequence to wrap. A copy of
the object will be made, but ownership of the memory is not
transferred.
*/
explicit
buffers_adaptor(MutableBufferSequence const& buffers);
/** Constructor
This constructs the buffer adaptor in-place from
a list of arguments.
@param args Arguments forwarded to the buffers constructor.
*/
template<class... Args>
explicit
buffers_adaptor(boost::in_place_init_t, Args&&... args);
/// Copy Constructor
buffers_adaptor(buffers_adaptor const& other);
/// Copy Assignment
buffers_adaptor& operator=(buffers_adaptor const&);
/// Returns the original mutable buffer sequence
value_type const&
value() const
{
return bs_;
}
//--------------------------------------------------------------------------
#if BOOST_BEAST_DOXYGEN
/// The ConstBufferSequence used to represent the readable bytes.
using const_buffers_type = __implementation_defined__;
/// The MutableBufferSequence used to represent the readable bytes.
using mutable_data_type = __implementation_defined__;
/// The MutableBufferSequence used to represent the writable bytes.
using mutable_buffers_type = __implementation_defined__;
#else
using const_buffers_type = readable_bytes<false>;
using mutable_data_type = readable_bytes<true>;
class mutable_buffers_type;
#endif
/// Returns the number of readable bytes.
std::size_t
size() const noexcept
{
return in_size_;
}
/// Return the maximum number of bytes, both readable and writable, that can ever be held.
std::size_t
max_size() const noexcept
{
return max_size_;
}
/// Return the maximum number of bytes, both readable and writable, that can be held without requiring an allocation.
std::size_t
capacity() const noexcept
{
return max_size_;
}
/// Returns a constant buffer sequence representing the readable bytes
const_buffers_type
data() const noexcept;
/// Returns a constant buffer sequence representing the readable bytes
const_buffers_type
cdata() const noexcept
{
return data();
}
/// Returns a mutable buffer sequence representing the readable bytes.
mutable_data_type
data() noexcept;
/** Returns a mutable buffer sequence representing writable bytes.
Returns a mutable buffer sequence representing the writable
bytes containing exactly `n` bytes of storage. This function
does not allocate memory. Instead, the storage comes from
the underlying mutable buffer sequence.
All buffer sequences previously obtained using @ref prepare are
invalidated. Buffer sequences previously obtained using @ref data
remain valid.
@param n The desired number of bytes in the returned buffer
sequence.
@throws std::length_error if `size() + n` exceeds `max_size()`.
@esafe
Strong guarantee.
*/
mutable_buffers_type
prepare(std::size_t n);
/** Append writable bytes to the readable bytes.
Appends n bytes from the start of the writable bytes to the
end of the readable bytes. The remainder of the writable bytes
are discarded. If n is greater than the number of writable
bytes, all writable bytes are appended to the readable bytes.
All buffer sequences previously obtained using @ref prepare are
invalidated. Buffer sequences previously obtained using @ref data
remain valid.
@param n The number of bytes to append. If this number
is greater than the number of writable bytes, all
writable bytes are appended.
@esafe
No-throw guarantee.
*/
void
commit(std::size_t n) noexcept;
/** Remove bytes from beginning of the readable bytes.
Removes n bytes from the beginning of the readable bytes.
All buffers sequences previously obtained using
@ref data or @ref prepare are invalidated.
@param n The number of bytes to remove. If this number
is greater than the number of readable bytes, all
readable bytes are removed.
@esafe
No-throw guarantee.
*/
void
consume(std::size_t n) noexcept;
};
} // beast
} // boost
#include <boost/beast/core/impl/buffers_adaptor.hpp>
#endif

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_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 2 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 B2, class... Bn>
buffers_cat_view<B1, B2, Bn...>
buffers_cat(B1 const& b1, B2 const& b2, Bn const&... bn)
#endif
{
static_assert(
is_const_buffer_sequence<B1, B2, Bn...>::value,
"BufferSequence type requirements not met");
return buffers_cat_view<B1, B2, Bn...>{b1, b2, bn...};
}
} // beast
} // boost
#include <boost/beast/core/impl/buffers_cat.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.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,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_BASE64_HPP
#define BOOST_BEAST_DETAIL_BASE64_HPP
#include <boost/beast/core/string.hpp>
#include <cctype>
#include <string>
#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
BOOST_BEAST_DECL
std::string
base64_encode(std::uint8_t const* data, std::size_t len);
BOOST_BEAST_DECL
std::string
base64_encode(string_view s);
template<class = void>
std::string
base64_decode(string_view data)
{
std::string dest;
dest.resize(base64::decoded_size(data.size()));
auto const result = base64::decode(
&dest[0], data.data(), data.size());
dest.resize(result.first);
return dest;
}
} // detail
} // beast
} // boost
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/core/detail/base64.ipp>
#endif
#endif

View File

@@ -0,0 +1,220 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
/*
Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
Copyright notice:
base64.cpp and base64.h
Copyright (C) 2004-2008 Rene Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
Rene Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
#ifndef BOOST_BEAST_DETAIL_BASE64_IPP
#define BOOST_BEAST_DETAIL_BASE64_IPP
#include <boost/beast/core/detail/base64.hpp>
#include <boost/beast/core/string.hpp>
#include <cctype>
#include <string>
#include <utility>
namespace boost {
namespace beast {
namespace detail {
namespace base64 {
char const*
get_alphabet()
{
static char constexpr tab[] = {
"ABCDEFGHIJKLMNOP"
"QRSTUVWXYZabcdef"
"ghijklmnopqrstuv"
"wxyz0123456789+/"
};
return &tab[0];
}
signed char const*
get_inverse()
{
static signed char constexpr tab[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48-63
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255
};
return &tab[0];
}
/** Encode a series of octets as a padded, base64 string.
The resulting string will not be null terminated.
@par Requires
The memory pointed to by `out` points to valid memory
of at least `encoded_size(len)` bytes.
@return The number of characters written to `out`. This
will exclude any null termination.
*/
std::size_t
encode(void* dest, void const* src, std::size_t len)
{
char* out = static_cast<char*>(dest);
char const* in = static_cast<char const*>(src);
auto const tab = base64::get_alphabet();
for(auto n = len / 3; n--;)
{
*out++ = tab[ (in[0] & 0xfc) >> 2];
*out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
*out++ = tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)];
*out++ = tab[ in[2] & 0x3f];
in += 3;
}
switch(len % 3)
{
case 2:
*out++ = tab[ (in[0] & 0xfc) >> 2];
*out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
*out++ = tab[ (in[1] & 0x0f) << 2];
*out++ = '=';
break;
case 1:
*out++ = tab[ (in[0] & 0xfc) >> 2];
*out++ = tab[((in[0] & 0x03) << 4)];
*out++ = '=';
*out++ = '=';
break;
case 0:
break;
}
return out - static_cast<char*>(dest);
}
/** Decode a padded base64 string into a series of octets.
@par Requires
The memory pointed to by `out` points to valid memory
of at least `decoded_size(len)` bytes.
@return The number of octets written to `out`, and
the number of characters read from the input string,
expressed as a pair.
*/
std::pair<std::size_t, std::size_t>
decode(void* dest, char const* src, std::size_t len)
{
char* out = static_cast<char*>(dest);
auto in = reinterpret_cast<unsigned char const*>(src);
unsigned char c3[3], c4[4];
int i = 0;
int j = 0;
auto const inverse = base64::get_inverse();
while(len-- && *in != '=')
{
auto const v = inverse[*in];
if(v == -1)
break;
++in;
c4[i] = v;
if(++i == 4)
{
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
for(i = 0; i < 3; i++)
*out++ = c3[i];
i = 0;
}
}
if(i)
{
c3[0] = ( c4[0] << 2) + ((c4[1] & 0x30) >> 4);
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
for(j = 0; j < i - 1; j++)
*out++ = c3[j];
}
return {out - static_cast<char*>(dest),
in - reinterpret_cast<unsigned char const*>(src)};
}
} // base64
std::string
base64_encode(
std::uint8_t const* data,
std::size_t len)
{
std::string dest;
dest.resize(base64::encoded_size(len));
dest.resize(base64::encode(&dest[0], data, len));
return dest;
}
std::string
base64_encode(string_view s)
{
return base64_encode (reinterpret_cast <
std::uint8_t const*> (s.data()), s.size());
}
} // detail
} // beast
} // boost
#endif

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,123 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_DETAIL_BIND_DEFAULT_EXECUTOR_HPP
#define BOOST_BEAST_CORE_DETAIL_BIND_DEFAULT_EXECUTOR_HPP
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/executor.hpp>
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/core/empty_value.hpp>
#include <utility>
namespace boost {
namespace beast {
namespace detail {
template<class Handler, class Executor>
class bind_default_executor_wrapper
: private boost::empty_value<Executor>
{
Handler h_;
public:
template<class Handler_>
bind_default_executor_wrapper(
Handler_&& h,
Executor const& ex)
: boost::empty_value<Executor>(
boost::empty_init_t{}, ex)
, h_(std::forward<Handler_>(h))
{
}
template<class... Args>
void
operator()(Args&&... args)
{
h_(std::forward<Args>(args)...);
}
using allocator_type =
net::associated_allocator_t<Handler>;
allocator_type
get_allocator() const noexcept
{
return net::get_associated_allocator(h_);
}
using executor_type =
net::associated_executor_t<Handler, Executor>;
executor_type
get_executor() const noexcept
{
return net::get_associated_executor(
h_, this->get());
}
template<class Function>
void
asio_handler_invoke(Function&& f,
bind_default_executor_wrapper* p)
{
net::dispatch(p->get_executor(), std::move(f));
}
friend
void* asio_handler_allocate(
std::size_t size, bind_default_executor_wrapper* p)
{
using net::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(p->h_));
}
friend
void asio_handler_deallocate(
void* mem, std::size_t size,
bind_default_executor_wrapper* p)
{
using net::asio_handler_deallocate;
asio_handler_deallocate(mem, size,
std::addressof(p->h_));
}
friend
bool asio_handler_is_continuation(
bind_default_executor_wrapper* p)
{
using net::asio_handler_is_continuation;
return asio_handler_is_continuation(
std::addressof(p->h_));
}
};
template<class Executor, class Handler>
auto
bind_default_executor(Executor const& ex, Handler&& h) ->
bind_default_executor_wrapper<
typename std::decay<Handler>::type,
Executor>
{
return bind_default_executor_wrapper<
typename std::decay<Handler>::type,
Executor>(std::forward<Handler>(h), ex);
}
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,410 @@
//
// 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
void asio_handler_invoke(
Function&& f, bind_wrapper* op)
{
using net::asio_handler_invoke;
asio_handler_invoke(f, std::addressof(op->h_));
}
friend
bool asio_handler_is_continuation(
bind_wrapper* op)
{
using net::asio_handler_is_continuation;
return asio_handler_is_continuation(
std::addressof(op->h_));
}
friend
void* asio_handler_allocate(
std::size_t size, bind_wrapper* op)
{
using net::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->h_));
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, bind_wrapper* op)
{
using net::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(op->h_));
}
};
template<class Handler, class... Args>
class bind_back_wrapper;
template<class Handler, class... Args>
class bind_front_wrapper;
//------------------------------------------------------------------------------
//
// bind_front
//
//------------------------------------------------------------------------------
template<class Handler, class... Args>
class bind_front_wrapper
{
Handler h_;
detail::tuple<Args...> args_;
template<class T, class Executor>
friend struct net::associated_executor;
template<class T, class Allocator>
friend struct net::associated_allocator;
template<std::size_t... I, class... Ts>
void
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
void asio_handler_invoke(
Function&& f, bind_front_wrapper* op)
{
using net::asio_handler_invoke;
asio_handler_invoke(f, std::addressof(op->h_));
}
friend
bool asio_handler_is_continuation(
bind_front_wrapper* op)
{
using net::asio_handler_is_continuation;
return asio_handler_is_continuation(
std::addressof(op->h_));
}
friend
void* asio_handler_allocate(
std::size_t size, bind_front_wrapper* op)
{
using net::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->h_));
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, bind_front_wrapper* op)
{
using net::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(op->h_));
}
};
} // detail
} // beast
} // boost
//------------------------------------------------------------------------------
namespace boost {
namespace asio {
template<class Handler, class... Args, class Executor>
struct associated_executor<
beast::detail::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,83 @@
//
// 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/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,142 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_DETAIL_BUFFERS_RANGE_ADAPTOR_HPP
#define BOOST_BEAST_DETAIL_BUFFERS_RANGE_ADAPTOR_HPP
#include <boost/beast/core/buffer_traits.hpp>
#include <iterator>
#include <type_traits>
namespace boost {
namespace beast {
namespace detail {
template<class BufferSequence>
class buffers_range_adaptor
{
BufferSequence b_;
public:
#if BOOST_BEAST_DOXYGEN
using value_type = __see_below__;
#else
using value_type = buffers_type<BufferSequence>;
#endif
class const_iterator
{
friend class buffers_range_adaptor;
using iter_type =
buffers_iterator_type<BufferSequence>;
iter_type it_{};
buffers_range_adaptor const* b_ = nullptr;
const_iterator(
buffers_range_adaptor const& b,
iter_type const& it)
: it_(it)
, b_(&b)
{
}
public:
using value_type = typename
buffers_range_adaptor::value_type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
bool
operator==(const_iterator const& other) const
{
return b_ == other.b_ && it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return *it_;
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
};
buffers_range_adaptor(
buffers_range_adaptor const&) = default;
buffers_range_adaptor& operator=(
buffers_range_adaptor const&) = default;
explicit
buffers_range_adaptor(BufferSequence const& b)
: b_(b)
{
}
const_iterator
begin() const noexcept
{
return {*this, net::buffer_sequence_begin(b_)};
}
const_iterator
end() const noexcept
{
return {*this, net::buffer_sequence_end(b_)};
}
};
} // detail
} // beast
} // boost
#endif

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,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,96 @@
//
// 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_RESULT_TYPE(type, void(::boost::beast::error_code))
#endif
#ifndef BOOST_BEAST_ASYNC_RESULT2
#define BOOST_BEAST_ASYNC_RESULT2(type) \
BOOST_ASIO_INITFN_RESULT_TYPE(type, void(::boost::beast::error_code, std::size_t))
#endif
#endif

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,107 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_DETAIL_GET_IO_CONTEXT_HPP
#define BOOST_BEAST_DETAIL_GET_IO_CONTEXT_HPP
#include <boost/beast/core/stream_traits.hpp>
#include <boost/asio/executor.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/strand.hpp>
#include <memory>
#include <type_traits>
namespace boost {
namespace beast {
namespace detail {
//------------------------------------------------------------------------------
inline
net::io_context*
get_io_context(net::io_context& ioc)
{
return std::addressof(ioc);
}
inline
net::io_context*
get_io_context(net::io_context::executor_type const& ex)
{
return std::addressof(ex.context());
}
inline
net::io_context*
get_io_context(net::strand<
net::io_context::executor_type> const& ex)
{
return std::addressof(
ex.get_inner_executor().context());
}
template<class Executor>
net::io_context*
get_io_context(net::strand<Executor> const& ex)
{
return get_io_context(ex.get_inner_executor());
}
template<
class T,
class = typename std::enable_if<
std::is_same<T, net::executor>::value>::type>
net::io_context*
get_io_context(T const& ex)
{
auto p = ex.template target<typename
net::io_context::executor_type>();
if(! p)
return nullptr;
return std::addressof(p->context());
}
inline
net::io_context*
get_io_context(...)
{
return nullptr;
}
//------------------------------------------------------------------------------
template<class T>
net::io_context*
get_io_context_impl(T& t, std::true_type)
{
return get_io_context(
t.get_executor());
}
template<class T>
net::io_context*
get_io_context_impl(T const&, std::false_type)
{
return nullptr;
}
// Returns the io_context*, or nullptr, for any object.
template<class T>
net::io_context*
get_io_context(T& t)
{
return get_io_context_impl(t,
has_get_executor<T>{});
}
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,260 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_DETAIL_IMPL_READ_HPP
#define BOOST_BEAST_DETAIL_IMPL_READ_HPP
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/async_base.hpp>
#include <boost/beast/core/flat_static_buffer.hpp>
#include <boost/asio/basic_stream_socket.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/throw_exception.hpp>
namespace boost {
namespace beast {
namespace detail {
// The number of bytes in the stack buffer when using non-blocking.
static std::size_t constexpr default_max_stack_buffer = 16384;
//------------------------------------------------------------------------------
struct dynamic_read_ops
{
// read into a dynamic buffer until the
// condition is met or an error occurs
template<
class Stream,
class DynamicBuffer,
class Condition,
class Handler>
class read_op
: public net::coroutine
, public async_base<
Handler, beast::executor_type<Stream>>
{
Stream& s_;
DynamicBuffer& b_;
Condition cond_;
error_code ec_;
std::size_t total_ = 0;
public:
read_op(read_op&&) = default;
template<class Handler_, class Condition_>
read_op(
Handler_&& h,
Stream& s,
DynamicBuffer& b,
Condition_&& cond)
: async_base<Handler,
beast::executor_type<Stream>>(
std::forward<Handler_>(h),
s.get_executor())
, s_(s)
, b_(b)
, cond_(std::forward<Condition_>(cond))
{
(*this)({}, 0, false);
}
void
operator()(
error_code ec,
std::size_t bytes_transferred,
bool cont = true)
{
std::size_t max_size;
std::size_t max_prepare;
BOOST_ASIO_CORO_REENTER(*this)
{
for(;;)
{
max_size = cond_(ec, total_, b_);
max_prepare = std::min<std::size_t>(
std::max<std::size_t>(
512, b_.capacity() - b_.size()),
std::min<std::size_t>(
max_size, b_.max_size() - b_.size()));
if(max_prepare == 0)
break;
BOOST_ASIO_CORO_YIELD
s_.async_read_some(
b_.prepare(max_prepare), std::move(*this));
b_.commit(bytes_transferred);
total_ += bytes_transferred;
}
if(! cont)
{
// run this handler "as-if" using net::post
// to reduce template instantiations
ec_ = ec;
BOOST_ASIO_CORO_YIELD
s_.async_read_some(
b_.prepare(0), std::move(*this));
ec = ec_;
}
this->complete_now(ec, total_);
}
}
};
//------------------------------------------------------------------------------
struct run_read_op
{
template<
class AsyncReadStream,
class DynamicBuffer,
class Condition,
class ReadHandler>
void
operator()(
ReadHandler&& h,
AsyncReadStream* s,
DynamicBuffer* b,
Condition&& c)
{
// If you get an error on the following line it means
// that your handler does not meet the documented type
// requirements for the handler.
static_assert(
beast::detail::is_invocable<ReadHandler,
void(error_code, std::size_t)>::value,
"ReadHandler type requirements not met");
read_op<
AsyncReadStream,
DynamicBuffer,
typename std::decay<Condition>::type,
typename std::decay<ReadHandler>::type>(
std::forward<ReadHandler>(h),
*s,
*b,
std::forward<Condition>(c));
}
};
};
//------------------------------------------------------------------------------
template<
class SyncReadStream,
class DynamicBuffer,
class CompletionCondition,
class>
std::size_t
read(
SyncReadStream& stream,
DynamicBuffer& buffer,
CompletionCondition cond)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream type requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer type requirements not met");
static_assert(
detail::is_invocable<CompletionCondition,
void(error_code&, std::size_t, DynamicBuffer&)>::value,
"CompletionCondition type requirements not met");
error_code ec;
auto const bytes_transferred = detail::read(
stream, buffer, std::move(cond), ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return bytes_transferred;
}
template<
class SyncReadStream,
class DynamicBuffer,
class CompletionCondition,
class>
std::size_t
read(
SyncReadStream& stream,
DynamicBuffer& buffer,
CompletionCondition cond,
error_code& ec)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream type requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer type requirements not met");
static_assert(
detail::is_invocable<CompletionCondition,
void(error_code&, std::size_t, DynamicBuffer&)>::value,
"CompletionCondition type requirements not met");
ec = {};
std::size_t total = 0;
std::size_t max_size;
std::size_t max_prepare;
for(;;)
{
max_size = cond(ec, total, buffer);
max_prepare = std::min<std::size_t>(
std::max<std::size_t>(
512, buffer.capacity() - buffer.size()),
std::min<std::size_t>(
max_size, buffer.max_size() - buffer.size()));
if(max_prepare == 0)
break;
std::size_t const bytes_transferred =
stream.read_some(buffer.prepare(max_prepare), ec);
buffer.commit(bytes_transferred);
total += bytes_transferred;
}
return total;
}
template<
class AsyncReadStream,
class DynamicBuffer,
class CompletionCondition,
class ReadHandler,
class>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
async_read(
AsyncReadStream& stream,
DynamicBuffer& buffer,
CompletionCondition&& cond,
ReadHandler&& handler)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream type requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer type requirements not met");
static_assert(
detail::is_invocable<CompletionCondition,
void(error_code&, std::size_t, DynamicBuffer&)>::value,
"CompletionCondition type requirements not met");
return net::async_initiate<
ReadHandler,
void(error_code, std::size_t)>(
typename dynamic_read_ops::run_read_op{},
handler,
&stream,
&buffer,
std::forward<CompletionCondition>(cond));
}
} // detail
} // beast
} // boost
#endif

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_DETAIL_IS_INVOCABLE_HPP
#define BOOST_BEAST_DETAIL_IS_INVOCABLE_HPP
#include <type_traits>
#include <utility>
namespace boost {
namespace beast {
namespace detail {
template<class R, class C, class ...A>
auto
is_invocable_test(C&& c, int, A&& ...a)
-> decltype(std::is_convertible<
decltype(c(std::forward<A>(a)...)), R>::value ||
std::is_same<R, void>::value,
std::true_type());
template<class R, class C, class ...A>
std::false_type
is_invocable_test(C&& c, long, A&& ...a);
/** Metafunction returns `true` if F callable as R(A...)
Example:
@code
is_invocable<T, void(std::string)>::value
@endcode
*/
/** @{ */
template<class C, class F>
struct is_invocable : std::false_type
{
};
template<class C, class R, class ...A>
struct is_invocable<C, R(A...)>
: decltype(is_invocable_test<R>(
std::declval<C>(), 1, std::declval<A>()...))
{
};
/** @} */
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,265 @@
//
// 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/beast/core/detail/type_traits.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>
: 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)
{
}
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));
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));
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,
class ReadHandler
#if ! BOOST_BEAST_DOXYGEN
, class = typename std::enable_if<
is_async_read_stream<AsyncReadStream>::value &&
net::is_dynamic_buffer<DynamicBuffer>::value &&
detail::is_invocable<CompletionCondition,
void(error_code&, std::size_t, DynamicBuffer&)>::value
>::type
#endif
>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
async_read(
AsyncReadStream& stream,
DynamicBuffer& buffer,
CompletionCondition&& completion_condition,
ReadHandler&& handler);
} // detail
} // beast
} // boost
#include <boost/beast/core/detail/impl/read.hpp>
#endif

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_REMAP_POST_TO_DEFER_HPP
#define BOOST_BEAST_DETAIL_REMAP_POST_TO_DEFER_HPP
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/is_executor.hpp>
#include <boost/core/empty_value.hpp>
#include <type_traits>
#include <utility>
namespace boost {
namespace beast {
namespace detail {
template<class Executor>
class remap_post_to_defer
: private boost::empty_value<Executor>
{
BOOST_STATIC_ASSERT(
net::is_executor<Executor>::value);
Executor const&
ex() const noexcept
{
return this->get();
}
public:
remap_post_to_defer(
remap_post_to_defer&&) = default;
remap_post_to_defer(
remap_post_to_defer const&) = default;
explicit
remap_post_to_defer(
Executor const& ex)
: boost::empty_value<Executor>(
boost::empty_init_t{}, ex)
{
}
bool
operator==(
remap_post_to_defer const& other) const noexcept
{
return ex() == other.ex();
}
bool
operator!=(
remap_post_to_defer const& other) const noexcept
{
return ex() != other.ex();
}
decltype(std::declval<Executor const&>().context())
context() const noexcept
{
return ex().context();
}
void
on_work_started() const noexcept
{
ex().on_work_started();
}
void
on_work_finished() const noexcept
{
ex().on_work_finished();
}
template<class F, class A>
void
dispatch(F&& f, A const& a) const
{
ex().dispatch(std::forward<F>(f), a);
}
template<class F, class A>
void
post(F&& f, A const& a) const
{
ex().defer(std::forward<F>(f), a);
}
template<class F, class A>
void
defer(F&& f, A const& a) const
{
ex().defer(std::forward<F>(f), a);
}
};
} // detail
} // beast
} // boost
#endif

View File

@@ -0,0 +1,43 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_DETAIL_SERVICE_BASE_HPP
#define BOOST_BEAST_DETAIL_SERVICE_BASE_HPP
#include <boost/asio/execution_context.hpp>
namespace boost {
namespace beast {
namespace detail {
template<class T>
struct service_id : net::execution_context::id
{
};
template<class T>
struct service_base : net::execution_context::service
{
static service_id<T> id;
explicit
service_base(net::execution_context& ctx)
: net::execution_context::service(ctx)
{
}
};
template<class T>
service_id<T> service_base<T>::id;
} // detail
} // beast
} // boost
#endif

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_DETAIL_SHA1_HPP
#define BOOST_BEAST_DETAIL_SHA1_HPP
#include <boost/beast/core/detail/config.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 {
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,107 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_DETAIL_STREAM_BASE_HPP
#define BOOST_BEAST_CORE_DETAIL_STREAM_BASE_HPP
#include <boost/asio/steady_timer.hpp>
#include <boost/assert.hpp>
#include <boost/core/exchange.hpp>
#include <chrono>
#include <cstdint>
#include <utility>
namespace boost {
namespace beast {
namespace detail {
struct any_endpoint
{
template<class Error, class Endpoint>
bool
operator()(
Error const&, Endpoint const&) const noexcept
{
return true;
}
};
struct stream_base
{
using clock_type = std::chrono::steady_clock;
using time_point = typename
std::chrono::steady_clock::time_point;
using tick_type = std::uint64_t;
struct op_state
{
net::steady_timer timer; // for timing out
tick_type tick = 0; // counts waits
bool pending = false; // if op is pending
bool timeout = false; // if timed out
template<class... Args>
explicit
op_state(Args&&... args)
: timer(std::forward<Args>(args)...)
{
}
};
class pending_guard
{
bool& b_;
bool clear_ = true;
public:
~pending_guard()
{
if(clear_)
b_ = false;
}
explicit
pending_guard(bool& b)
: b_(b)
{
BOOST_ASSERT(! b_);
b_ = true;
}
pending_guard(
pending_guard&& other) noexcept
: b_(other.b_)
, clear_(boost::exchange(
other.clear_, false))
{
}
void
reset()
{
BOOST_ASSERT(clear_);
b_ = false;
clear_ = false;
}
};
static time_point never() noexcept
{
return (time_point::max)();
}
static std::size_t constexpr no_limit =
(std::numeric_limits<std::size_t>::max)();
};
} // detail
} // beast
} // boost
#endif

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,116 @@
//
// Copyright (c) 2016-2019Damian Jarek (damian dot jarek93 at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_DETAIL_TUPLE_HPP
#define BOOST_BEAST_DETAIL_TUPLE_HPP
#include <boost/mp11/integer_sequence.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/type_traits/remove_cv.hpp>
#include <boost/type_traits/copy_cv.hpp>
#include <cstdlib>
#include <utility>
namespace boost {
namespace beast {
namespace detail {
template<std::size_t I, class T>
struct tuple_element_impl
{
T t;
tuple_element_impl(T const& t_)
: t(t_)
{
}
tuple_element_impl(T&& t_)
: t(std::move(t_))
{
}
};
template<std::size_t I, class T>
struct tuple_element_impl<I, T&>
{
T& t;
tuple_element_impl(T& t_)
: t(t_)
{
}
};
template<class... Ts>
struct tuple_impl;
template<class... Ts, std::size_t... Is>
struct tuple_impl<
boost::mp11::index_sequence<Is...>, Ts...>
: tuple_element_impl<Is, Ts>...
{
template<class... Us>
explicit tuple_impl(Us&&... us)
: tuple_element_impl<Is, Ts>(
std::forward<Us>(us))...
{
}
};
template<class... Ts>
struct tuple : tuple_impl<
boost::mp11::index_sequence_for<Ts...>, Ts...>
{
template<class... Us>
explicit tuple(Us&&... us)
: tuple_impl<
boost::mp11::index_sequence_for<Ts...>, Ts...>{
std::forward<Us>(us)...}
{
}
};
template<std::size_t I, class T>
T&
get(tuple_element_impl<I, T>& te)
{
return te.t;
}
template<std::size_t I, class T>
T const&
get(tuple_element_impl<I, T> const& te)
{
return te.t;
}
template<std::size_t I, class T>
T&&
get(tuple_element_impl<I, T>&& te)
{
return std::move(te.t);
}
template<std::size_t I, class T>
T&
get(tuple_element_impl<I, T&>&& te)
{
return te.t;
}
template <std::size_t I, class T>
using tuple_element = typename boost::copy_cv<
mp11::mp_at_c<typename remove_cv<T>::type, I>, T>::type;
} // detail
} // beast
} // boost
#endif

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_DETAIL_TYPE_TRAITS_HPP
#define BOOST_BEAST_DETAIL_TYPE_TRAITS_HPP
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/detail/is_invocable.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/mp11/function.hpp>
#include <boost/type_traits/make_void.hpp>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <string>
#include <utility>
namespace boost {
namespace beast {
namespace detail {
// variadic min
template<class T>
T constexpr min(T t)
{
return t;
}
template<class T, class...Tn>
T constexpr min(T t0, T t1, Tn... tn)
{
return (t0 < t1) ?
(detail::min)(t0, tn...) :
(detail::min)(t1, tn...);
}
template<class U>
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;
//------------------------------------------------------------------------------
template<class T>
void
accept_rv(T){}
//------------------------------------------------------------------------------
// 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
{};
} // 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>;
reinterpret_cast<T&>(self.buf_).~T();
}
};
struct copy
{
variant& self;
variant const& other;
void operator()(mp11::mp_size_t<0>)
{
}
template<class I>
void operator()(I)
{
using T =
mp11::mp_at_c<variant, I::value - 1>;
::new(&self.buf_) T(
reinterpret_cast<T const&>(other.buf_));
self.i_ = I::value;
}
};
struct move
{
variant& self;
variant& other;
void operator()(mp11::mp_size_t<0>)
{
}
template<class I>
void operator()(I)
{
using T =
mp11::mp_at_c<variant, I::value - 1>;
::new(&self.buf_) T(
reinterpret_cast<T&&>(other.buf_));
reinterpret_cast<T&>(other.buf_).~T();
self.i_ = I::value;
}
};
struct equals
{
variant const& self;
variant const& other;
bool operator()(mp11::mp_size_t<0>)
{
return true;
}
template<class I>
bool operator()(I)
{
using T =
mp11::mp_at_c<variant, I::value - 1>;
return
reinterpret_cast<T const&>(self.buf_) ==
reinterpret_cast<T const&>(other.buf_);
}
};
void destruct()
{
mp11::mp_with_index<
sizeof...(TN) + 1>(
i_, destroy{*this});
i_ = 0;
}
void copy_construct(variant const& other)
{
mp11::mp_with_index<
sizeof...(TN) + 1>(
other.i_, copy{*this, other});
}
void move_construct(variant& other)
{
mp11::mp_with_index<
sizeof...(TN) + 1>(
other.i_, move{*this, other});
other.i_ = 0;
}
public:
variant() = default;
~variant()
{
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 *reinterpret_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 *reinterpret_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,651 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_DETECT_SSL_HPP
#define BOOST_BEAST_CORE_DETECT_SSL_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/async_base.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/read_size.hpp>
#include <boost/beast/core/stream_traits.hpp>
#include <boost/logic/tribool.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/coroutine.hpp>
#include <type_traits>
namespace boost {
namespace beast {
//------------------------------------------------------------------------------
//
// Example: Detect TLS client_hello
//
// This is an example and also a public interface. It implements
// an algorithm for determining if a "TLS client_hello" message
// is received. It can be used to implement a listening port that
// can handle both plain and TLS encrypted connections.
//
//------------------------------------------------------------------------------
//[example_core_detect_ssl_1
// By convention, the "detail" namespace means "not-public."
// Identifiers in a detail namespace are not visible in the documentation,
// and users should not directly use those identifiers in programs, otherwise
// their program may break in the future.
//
// Using a detail namespace gives the library writer the freedom to change
// the interface or behavior later, and maintain backward-compatibility.
namespace detail {
/** Return `true` if the buffer contains a TLS Protocol client_hello message.
This function analyzes the bytes at the beginning of the buffer
and compares it to a valid client_hello message. This is the
message required to be sent by a client at the beginning of
any TLS (encrypted communication) session, including when
resuming a session.
The return value will be:
@li `true` if the contents of the buffer unambiguously define
contain a client_hello message,
@li `false` if the contents of the buffer cannot possibly
be a valid client_hello message, or
@li `boost::indeterminate` if the buffer contains an
insufficient number of bytes to determine the result. In
this case the caller should read more data from the relevant
stream, append it to the buffers, and call this function again.
@param buffers The buffer sequence to inspect.
This type must meet the requirements of <em>ConstBufferSequence</em>.
@return `boost::tribool` indicating whether the buffer contains
a TLS client handshake, does not contain a handshake, or needs
additional bytes to determine an outcome.
@see
<a href="https://tools.ietf.org/html/rfc2246#section-7.4">7.4. Handshake protocol</a>
(RFC2246: The TLS Protocol)
*/
template <class ConstBufferSequence>
boost::tribool
is_tls_client_hello (ConstBufferSequence const& buffers);
} // detail
//]
//[example_core_detect_ssl_2
namespace detail {
template <class ConstBufferSequence>
boost::tribool
is_tls_client_hello (ConstBufferSequence const& buffers)
{
// Make sure buffers meets the requirements
static_assert(
net::is_const_buffer_sequence<ConstBufferSequence>::value,
"ConstBufferSequence type requirements not met");
/*
The first message on a TLS connection must be the client_hello,
which is a type of handshake record, and it cannot be compressed
or encrypted. A plaintext record has this format:
0 byte record_type // 0x16 = handshake
1 byte major // major protocol version
2 byte minor // minor protocol version
3-4 uint16 length // size of the payload
5 byte handshake_type // 0x01 = client_hello
6 uint24 length // size of the ClientHello
9 byte major // major protocol version
10 byte minor // minor protocol version
11 uint32 gmt_unix_time
15 byte random_bytes[28]
...
*/
// Flatten the input buffers into a single contiguous range
// of bytes on the stack to make it easier to work with the data.
unsigned char buf[9];
auto const n = net::buffer_copy(
net::mutable_buffer(buf, sizeof(buf)), buffers);
// Can't do much without any bytes
if(n < 1)
return boost::indeterminate;
// Require the first byte to be 0x16, indicating a TLS handshake record
if(buf[0] != 0x16)
return false;
// We need at least 5 bytes to know the record payload size
if(n < 5)
return boost::indeterminate;
// Calculate the record payload size
std::uint32_t const length = (buf[3] << 8) + buf[4];
// A ClientHello message payload is at least 34 bytes.
// There can be multiple handshake messages in the same record.
if(length < 34)
return false;
// We need at least 6 bytes to know the handshake type
if(n < 6)
return boost::indeterminate;
// The handshake_type must be 0x01 == client_hello
if(buf[5] != 0x01)
return false;
// We need at least 9 bytes to know the payload size
if(n < 9)
return boost::indeterminate;
// Calculate the message payload size
std::uint32_t const size =
(buf[6] << 16) + (buf[7] << 8) + buf[8];
// The message payload can't be bigger than the enclosing record
if(size + 4 > length)
return false;
// This can only be a TLS client_hello message
return true;
}
} // detail
//]
//[example_core_detect_ssl_3
/** Detect a TLS client handshake on a stream.
This function reads from a stream to determine if a client
handshake message is being received.
The call blocks until one of the following is true:
@li A TLS client opening handshake is detected,
@li The received data is invalid for a TLS client handshake, or
@li An error occurs.
The algorithm, known as a <em>composed operation</em>, is implemented
in terms of calls to the next layer's `read_some` function.
Bytes read from the stream will be stored in the passed dynamic
buffer, which may be used to perform the TLS handshake if the
detector returns true, or be otherwise consumed by the caller based
on the expected protocol.
@param stream The stream to read from. This type must meet the
requirements of <em>SyncReadStream</em>.
@param buffer The dynamic buffer to use. This type must meet the
requirements of <em>DynamicBuffer</em>.
@param ec Set to the error if any occurred.
@return `true` if the buffer contains a TLS client handshake and
no error occurred, otherwise `false`.
*/
template<
class SyncReadStream,
class DynamicBuffer>
bool
detect_ssl(
SyncReadStream& stream,
DynamicBuffer& buffer,
error_code& ec)
{
namespace beast = boost::beast;
// Make sure arguments meet the requirements
static_assert(
is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream type requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer type requirements not met");
// Loop until an error occurs or we get a definitive answer
for(;;)
{
// There could already be data in the buffer
// so we do this first, before reading from the stream.
auto const result = detail::is_tls_client_hello(buffer.data());
// If we got an answer, return it
if(! boost::indeterminate(result))
{
// A definite answer is a success
ec = {};
return static_cast<bool>(result);
}
// Try to fill our buffer by reading from the stream.
// The function read_size calculates a reasonable size for the
// amount to read next, using existing capacity if possible to
// avoid allocating memory, up to the limit of 1536 bytes which
// is the size of a normal TCP frame.
std::size_t const bytes_transferred = stream.read_some(
buffer.prepare(beast::read_size(buffer, 1536)), ec);
// Commit what we read into the buffer's input area.
buffer.commit(bytes_transferred);
// Check for an error
if(ec)
break;
}
// error
return false;
}
//]
//[example_core_detect_ssl_4
/** Detect a TLS/SSL handshake asynchronously on a stream.
This function reads asynchronously from a stream to determine
if a client handshake message is being received.
This call always returns immediately. The asynchronous operation
will continue until one of the following conditions is true:
@li A TLS client opening handshake is detected,
@li The received data is invalid for a TLS client handshake, or
@li An error occurs.
The algorithm, known as a <em>composed asynchronous operation</em>,
is implemented in terms of calls to the next layer's `async_read_some`
function. The program must ensure that no other calls to
`async_read_some` are performed until this operation completes.
Bytes read from the stream will be stored in the passed dynamic
buffer, which may be used to perform the TLS handshake if the
detector returns true, or be otherwise consumed by the caller based
on the expected protocol.
@param stream The stream to read from. This type must meet the
requirements of <em>AsyncReadStream</em>.
@param buffer The dynamic buffer to use. This type must meet the
requirements of <em>DynamicBuffer</em>.
@param token The completion token used to determine the method
used to provide the result of the asynchronous operation. If
this is a completion handler, the implementation takes ownership
of the handler by performing a decay-copy, and the equivalent
function signature of the handler must be:
@code
void handler(
error_code const& error, // Set to the error, if any
bool result // The result of the detector
);
@endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `net::post`.
*/
template<
class AsyncReadStream,
class DynamicBuffer,
class CompletionToken>
#if BOOST_BEAST_DOXYGEN
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, bool))
#else
auto
#endif
async_detect_ssl(
AsyncReadStream& stream,
DynamicBuffer& buffer,
CompletionToken&& token) ->
typename net::async_result<
typename std::decay<CompletionToken>::type, /*< `async_result` customizes the return value based on the completion token >*/
void(error_code, bool)>::return_type; /*< This is the signature for the completion handler >*/
//]
//[example_core_detect_ssl_5
// These implementation details don't need to be public
namespace detail {
// The composed operation object
template<
class DetectHandler,
class AsyncReadStream,
class DynamicBuffer>
class detect_ssl_op;
// This is a function object which `net::async_initiate` can use to launch
// our composed operation. This is a relatively new feature in networking
// which allows the asynchronous operation to be "lazily" executed (meaning
// that it is launched later). Users don't need to worry about this, but
// authors of composed operations need to write it this way to get the
// very best performance, for example when using Coroutines TS (`co_await`).
struct run_detect_ssl_op
{
// The implementation of `net::async_initiate` captures the
// arguments of the initiating function, and then calls this
// function object later with the captured arguments in order
// to launch the composed operation. All we need to do here
// is take those arguments and construct our composed operation
// object.
//
// `async_initiate` takes care of transforming the completion
// token into the "real handler" which must have the correct
// signature, in this case `void(error_code, boost::tri_bool)`.
template<
class DetectHandler,
class AsyncReadStream,
class DynamicBuffer>
void operator()(
DetectHandler&& h,
AsyncReadStream* s, // references are passed as pointers
DynamicBuffer& b)
{
detect_ssl_op<
typename std::decay<DetectHandler>::type,
AsyncReadStream,
DynamicBuffer>(
std::forward<DetectHandler>(h), *s, b);
}
};
} // detail
//]
//[example_core_detect_ssl_6
// Here is the implementation of the asynchronous initiation function
template<
class AsyncReadStream,
class DynamicBuffer,
class CompletionToken>
#if BOOST_BEAST_DOXYGEN
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, bool))
#else
auto
#endif
async_detect_ssl(
AsyncReadStream& stream,
DynamicBuffer& buffer,
CompletionToken&& token)
-> typename net::async_result<
typename std::decay<CompletionToken>::type,
void(error_code, bool)>::return_type
{
// Make sure arguments meet the type requirements
static_assert(
is_async_read_stream<AsyncReadStream>::value,
"SyncReadStream type requirements not met");
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer type requirements not met");
// The function `net::async_initate` uses customization points
// to allow one asynchronous initiating function to work with
// all sorts of notification systems, such as callbacks but also
// fibers, futures, coroutines, and user-defined types.
//
// It works by capturing all of the arguments using perfect
// forwarding, and then depending on the specialization of
// `net::async_result` for the type of `CompletionToken`,
// the `initiation` object will be invoked with the saved
// parameters and the actual completion handler. Our
// initiating object is `run_detect_ssl_op`.
//
// Non-const references need to be passed as pointers,
// since we don't want a decay-copy.
return net::async_initiate<
CompletionToken,
void(error_code, bool)>(
detail::run_detect_ssl_op{},
token,
&stream, // pass the reference by pointer
buffer);
}
//]
//[example_core_detect_ssl_7
namespace detail {
// Read from a stream, calling is_tls_client_hello on the data
// data to determine if the TLS client handshake is present.
//
// This will be implemented using Asio's "stackless coroutines"
// which are based on macros forming a switch statement. The
// operation is derived from `coroutine` for this reason.
//
// The library type `async_base` takes care of all of the
// boilerplate for writing composed operations, including:
//
// * Storing the user's completion handler
// * Maintaining the work guard for the handler's associated executor
// * Propagating the associated allocator of the handler
// * Propagating the associated executor of the handler
// * Deallocating temporary storage before invoking the handler
// * Posting the handler to the executor on an immediate completion
//
// `async_base` needs to know the type of the handler, as well
// as the executor of the I/O object being used. The metafunction
// `executor_type` returns the type of executor used by an
// I/O object.
//
template<
class DetectHandler,
class AsyncReadStream,
class DynamicBuffer>
class detect_ssl_op
: public boost::asio::coroutine
, public async_base<
DetectHandler, executor_type<AsyncReadStream>>
{
// This composed operation has trivial state,
// so it is just kept inside the class and can
// be cheaply copied as needed by the implementation.
AsyncReadStream& stream_;
// The callers buffer is used to hold all received data
DynamicBuffer& buffer_;
// We're going to need this in case we have to post the handler
error_code ec_;
boost::tribool result_ = false;
public:
// Completion handlers must be MoveConstructible.
detect_ssl_op(detect_ssl_op&&) = default;
// Construct the operation. The handler is deduced through
// the template type `DetectHandler_`, this lets the same constructor
// work properly for both lvalues and rvalues.
//
template<class DetectHandler_>
detect_ssl_op(
DetectHandler_&& handler,
AsyncReadStream& stream,
DynamicBuffer& buffer)
: beast::async_base<
DetectHandler_,
beast::executor_type<AsyncReadStream>>(
std::forward<DetectHandler_>(handler),
stream.get_executor())
, stream_(stream)
, buffer_(buffer)
{
// This starts the operation. We pass `false` to tell the
// algorithm that it needs to use net::post if it wants to
// complete immediately. This is required by Networking,
// as initiating functions are not allowed to invoke the
// completion handler on the caller's thread before
// returning.
(*this)({}, 0, false);
}
// Our main entry point. This will get called as our
// intermediate operations complete. Definition below.
//
// The parameter `cont` indicates if we are being called subsequently
// from the original invocation
//
void operator()(
error_code ec,
std::size_t bytes_transferred,
bool cont = true);
};
} // detail
//]
//[example_core_detect_ssl_8
namespace detail {
// This example uses the Asio's stackless "fauxroutines", implemented
// using a macro-based solution. It makes the code easier to write and
// easier to read. This include file defines the necessary macros and types.
#include <boost/asio/yield.hpp>
// detect_ssl_op is callable with the signature void(error_code, bytes_transferred),
// allowing `*this` to be used as a ReadHandler
//
template<
class AsyncStream,
class DynamicBuffer,
class Handler>
void
detect_ssl_op<AsyncStream, DynamicBuffer, Handler>::
operator()(error_code ec, std::size_t bytes_transferred, bool cont)
{
namespace beast = boost::beast;
// This introduces the scope of the stackless coroutine
reenter(*this)
{
// Loop until an error occurs or we get a definitive answer
for(;;)
{
// There could already be a hello in the buffer so check first
result_ = is_tls_client_hello(buffer_.data());
// If we got an answer, then the operation is complete
if(! boost::indeterminate(result_))
break;
// Try to fill our buffer by reading from the stream.
// The function read_size calculates a reasonable size for the
// amount to read next, using existing capacity if possible to
// avoid allocating memory, up to the limit of 1536 bytes which
// is the size of a normal TCP frame.
//
// `async_read_some` expects a ReadHandler as the completion
// handler. The signature of a read handler is void(error_code, size_t),
// and this function matches that signature (the `cont` parameter has
// a default of true). We pass `std::move(*this)` as the completion
// handler for the read operation. This transfers ownership of this
// entire state machine back into the `async_read_some` operation.
// Care must be taken with this idiom, to ensure that parameters
// passed to the initiating function which could be invalidated
// by the move, are first moved to the stack before calling the
// initiating function.
yield stream_.async_read_some(buffer_.prepare(
read_size(buffer_, 1536)), std::move(*this));
// Commit what we read into the buffer's input area.
buffer_.commit(bytes_transferred);
// Check for an error
if(ec)
break;
}
// If `cont` is true, the handler will be invoked directly.
//
// Otherwise, the handler cannot be invoked directly, because
// initiating functions are not allowed to call the handler
// before returning. Instead, the handler must be posted to
// the I/O context. We issue a zero-byte read using the same
// type of buffers used in the ordinary read above, to prevent
// the compiler from creating an extra instantiation of the
// function template. This reduces compile times and the size
// of the program executable.
if(! cont)
{
// Save the error, otherwise it will be overwritten with
// a successful error code when this read completes
// immediately.
ec_ = ec;
// Zero-byte reads and writes are guaranteed to complete
// immediately with succcess. The type of buffers and the
// type of handler passed here need to exactly match the types
// used in the call to async_read_some above, to avoid
// instantiating another version of the function template.
yield stream_.async_read_some(buffer_.prepare(0), std::move(*this));
// Restore the saved error code
ec = ec_;
}
// Invoke the final handler.
// At this point, we are guaranteed that the original initiating
// function is no longer on our stack frame.
this->complete_now(ec, static_cast<bool>(result_));
}
}
// Including this file undefines the macros used by the stackless fauxroutines.
#include <boost/asio/unyield.hpp>
} // detail
//]
} // beast
} // boost
#endif

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
{
FILE* f_ = nullptr;
public:
/** The type of the underlying file handle.
This is platform-specific.
*/
using native_handle_type = 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.
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(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,533 @@
//
// 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);
/** Reallocate the buffer to fit the readable bytes exactly.
Buffer sequences previously obtained using @ref data or
@ref prepare become invalid.
@esafe
Strong guarantee.
*/
void
shrink_to_fit();
/** Set the size of the readable and writable bytes to zero.
This clears the buffer without changing capacity.
Buffer sequences previously obtained using @ref data or
@ref prepare become invalid.
@esafe
No-throw guarantee.
*/
void
clear() noexcept;
/// Exchange two dynamic buffers
template<class Alloc>
friend
void
swap(
basic_flat_buffer<Alloc>&,
basic_flat_buffer<Alloc>&);
//--------------------------------------------------------------------------
/// The ConstBufferSequence used to represent the readable bytes.
using const_buffers_type = net::const_buffer;
/// The MutableBufferSequence used to represent the readable bytes.
using mutable_data_type = net::mutable_buffer;
/// The MutableBufferSequence used to represent the writable bytes.
using mutable_buffers_type = net::mutable_buffer;
/// Returns the number of readable bytes.
std::size_t
size() const 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_data_type
data() noexcept
{
return {in_, dist(in_, out_)};
}
/** Returns a mutable buffer sequence representing writable bytes.
Returns a mutable buffer sequence representing the writable
bytes containing exactly `n` bytes of storage. 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,337 @@
//
// 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;
#ifdef BOOST_BEAST_ALLOW_DEPRECATED
/// Change the number of readable and writable bytes to zero.
void
reset() noexcept
{
clear();
}
#elif ! BOOST_BEAST_DOXYGEN
template<std::size_t I = 0>
void
reset() noexcept
{
static_assert(I != 0,
BOOST_BEAST_DEPRECATION_STRING);
}
#endif
//--------------------------------------------------------------------------
/// The ConstBufferSequence used to represent the readable bytes.
using const_buffers_type = net::const_buffer;
/// The MutableBufferSequence used to represent the readable bytes.
using mutable_data_type = net::mutable_buffer;
/// The MutableBufferSequence used to represent the writable bytes.
using mutable_buffers_type = net::mutable_buffer;
/// Returns the number of readable bytes.
std::size_t
size() const 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_data_type
data() noexcept
{
return {in_, dist(in_, out_)};
}
/** Returns a mutable buffer sequence representing writable bytes.
Returns a mutable buffer sequence representing the writable
bytes containing exactly `n` bytes of storage.
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,347 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_FLAT_STREAM_HPP
#define BOOST_BEAST_CORE_FLAT_STREAM_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/beast/core/stream_traits.hpp>
#include <boost/beast/core/detail/flat_stream.hpp>
#include <boost/asio/async_result.hpp>
#include <cstdlib>
#include <utility>
namespace boost {
namespace beast {
/** Stream wrapper to improve write performance.
This wrapper flattens writes for buffer sequences having length
greater than 1 and total size below a predefined amount, using
a dynamic memory allocation. It is primarily designed to overcome
a performance limitation of the current version of `net::ssl::stream`,
which does not use OpenSSL's scatter/gather interface for its
low-level read some and write some operations.
It is normally not necessary to use this class directly if you
are already using @ref ssl_stream. The following examples shows
how to use this class with the ssl stream that comes with
networking:
@par Example
To use the @ref flat_stream template with SSL streams, declare
a variable of the correct type. Parameters passed to the constructor
will be forwarded to the next layer's constructor:
@code
flat_stream<net::ssl::stream<ip::tcp::socket>> fs{ioc, ctx};
@endcode
Alternatively you can write
@code
ssl::stream<ip::tcp::socket> ss{ioc, ctx};
flat_stream<net::ssl::stream<ip::tcp::socket>&> fs{ss};
@endcode
The resulting stream may be passed to any stream algorithms which
operate on synchronous or asynchronous read or write streams,
examples include:
@li `net::read`, `net::async_read`
@li `net::write`, `net::async_write`
@li `net::read_until`, `net::async_read_until`
The stream may also be used as a template parameter in other
stream wrappers, such as for websocket:
@code
websocket::stream<flat_stream<net::ssl::stream<ip::tcp::socket>>> ws{ioc, ctx};
@endcode
@tparam NextLayer The type representing the next layer, to which
data will be read and written during operations. For synchronous
operations, the type must support the @b SyncStream concept. For
asynchronous operations, the type must support the @b AsyncStream
concept. This type will usually be some variation of
`net::ssl::stream`.
@par Concepts
@li SyncStream
@li AsyncStream
@see
@li https://github.com/boostorg/asio/issues/100
@li https://github.com/boostorg/beast/issues/1108
@li https://stackoverflow.com/questions/38198638/openssl-ssl-write-from-multiple-buffers-ssl-writev
@li https://stackoverflow.com/questions/50026167/performance-drop-on-port-from-beast-1-0-0-b66-to-boost-1-67-0-beast
*/
template<class NextLayer>
class flat_stream
#if ! BOOST_BEAST_DOXYGEN
: private detail::flat_stream_base
#endif
{
NextLayer stream_;
flat_buffer buffer_;
BOOST_STATIC_ASSERT(has_get_executor<NextLayer>::value);
struct ops;
template<class ConstBufferSequence>
std::size_t
stack_write_some(
std::size_t size,
ConstBufferSequence const& buffers,
error_code& ec);
public:
/// The type of the next layer.
using next_layer_type =
typename std::remove_reference<NextLayer>::type;
/// The type of the executor associated with the object.
using executor_type = beast::executor_type<next_layer_type>;
flat_stream(flat_stream&&) = default;
flat_stream(flat_stream const&) = default;
flat_stream& operator=(flat_stream&&) = default;
flat_stream& operator=(flat_stream const&) = default;
/** Destructor
The treatment of pending operations will be the same as that
of the next layer.
*/
~flat_stream() = default;
/** Constructor
Arguments, if any, are forwarded to the next layer's constructor.
*/
template<class... Args>
explicit
flat_stream(Args&&... args);
//--------------------------------------------------------------------------
/** Get the executor associated with the object.
This function may be used to obtain the executor object that the
stream uses to dispatch handlers for asynchronous operations.
@return A copy of the executor that stream will use to dispatch handlers.
*/
executor_type
get_executor() noexcept
{
return stream_.get_executor();
}
/** Get a reference to the next layer
This function returns a reference to the next layer
in a stack of stream layers.
@return A reference to the next layer in the stack of
stream layers.
*/
next_layer_type&
next_layer() noexcept
{
return stream_;
}
/** Get a reference to the next layer
This function returns a reference to the next layer in a
stack of stream layers.
@return A reference to the next layer in the stack of
stream layers.
*/
next_layer_type const&
next_layer() const noexcept
{
return stream_;
}
//--------------------------------------------------------------------------
/** Read some data from the stream.
This function is used to read data from the stream. The function call will
block until one or more bytes of data has been read successfully, or until
an error occurs.
@param buffers The buffers into which the data will be read.
@returns The number of bytes read.
@throws boost::system::system_error Thrown on failure.
@note The `read_some` operation may not read all of the requested number of
bytes. Consider using the function `net::read` if you need to ensure
that the requested amount of data is read before the blocking operation
completes.
*/
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers);
/** Read some data from the stream.
This function is used to read data from the stream. The function call will
block until one or more bytes of data has been read successfully, or until
an error occurs.
@param buffers The buffers into which the data will be read.
@param ec Set to indicate what error occurred, if any.
@returns The number of bytes read.
@note The `read_some` operation may not read all of the requested number of
bytes. Consider using the function `net::read` if you need to ensure
that the requested amount of data is read before the blocking operation
completes.
*/
template<class MutableBufferSequence>
std::size_t
read_some(
MutableBufferSequence const& buffers,
error_code& ec);
/** Start an asynchronous read.
This function is used to asynchronously read one or more bytes of data from
the stream. The function call always returns immediately.
@param buffers The buffers into which the data will be read. Although the
buffers object may be copied as necessary, ownership of the underlying
buffers is retained by the caller, which must guarantee that they remain
valid until the handler is called.
@param handler The completion handler to invoke when the operation
completes. The implementation takes ownership of the handler by
performing a decay-copy. The equivalent function signature of
the handler must be:
@code
void handler(
error_code const& error, // Result of operation.
std::size_t bytes_transferred // Number of bytes read.
);
@endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `net::post`.
@note The `read_some` operation may not read all of the requested number of
bytes. Consider using the function `net::async_read` if you need
to ensure that the requested amount of data is read before the asynchronous
operation completes.
*/
template<
class MutableBufferSequence,
class ReadHandler>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler);
/** Write some data to the stream.
This function is used to write data on the stream. The function call will
block until one or more bytes of data has been written successfully, or
until an error occurs.
@param buffers The data to be written.
@returns The number of bytes written.
@throws boost::system::system_error Thrown on failure.
@note The `write_some` operation may not transmit all of the data to the
peer. Consider using the function `net::write` if you need to
ensure that all data is written before the blocking operation completes.
*/
template<class ConstBufferSequence>
std::size_t
write_some(ConstBufferSequence const& buffers);
/** Write some data to the stream.
This function is used to write data on the stream. The function call will
block until one or more bytes of data has been written successfully, or
until an error occurs.
@param buffers The data to be written.
@param ec Set to indicate what error occurred, if any.
@returns The number of bytes written.
@note The `write_some` operation may not transmit all of the data to the
peer. Consider using the function `net::write` if you need to
ensure that all data is written before the blocking operation completes.
*/
template<class ConstBufferSequence>
std::size_t
write_some(
ConstBufferSequence const& buffers,
error_code& ec);
/** Start an asynchronous write.
This function is used to asynchronously write one or more bytes of data to
the stream. The function call always returns immediately.
@param buffers The data to be written to the stream. Although the buffers
object may be copied as necessary, ownership of the underlying buffers is
retained by the caller, which must guarantee that they remain valid until
the handler is called.
@param handler The completion handler to invoke when the operation
completes. The implementation takes ownership of the handler by
performing a decay-copy. The equivalent function signature of
the handler must be:
@code
void handler(
error_code const& ec, // Result of operation.
std::size_t bytes_transferred // Number of bytes written.
);
@endcode
Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a
manner equivalent to using `net::post`.
@note The `async_write_some` operation may not transmit all of the data to
the peer. Consider using the function `net::async_write` if you need
to ensure that all data is written before the asynchronous operation completes.
*/
template<
class ConstBufferSequence,
class WriteHandler>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
async_write_some(
ConstBufferSequence const& buffers,
WriteHandler&& handler);
};
} // beast
} // boost
#include <boost/beast/core/impl/flat_stream.hpp>
#endif

View File

@@ -0,0 +1,229 @@
//
// 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_HANDLER_PTR_HPP
#define BOOST_BEAST_HANDLER_PTR_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/allocator.hpp>
#include <boost/assert.hpp>
#include <boost/config/pragma_message.hpp>
#include <type_traits>
#include <utility>
#ifndef BOOST_BEAST_DOXYGEN
BOOST_PRAGMA_MESSAGE("<boost/beast/core/handler_ptr.hpp> is DEPRECATED and will be removed in a future release.")
namespace boost {
namespace beast {
/** A smart pointer container with associated completion handler.
This is a smart pointer that retains unique ownership of an
object through a pointer. Memory is managed using the allocator
associated with a completion handler stored in the object. The
managed object is destroyed and its memory deallocated when one
of the following occurs:
@li The function @ref invoke is called.
@li The function @ref release_handler is called.
@li The container is destroyed.
Objects of this type are used in the implementation of composed
operations with states that are expensive or impossible to move.
This container manages that non-trivial state on behalf of the
composed operation.
@par Thread Safety
@e Distinct @e objects: Safe.@n
@e Shared @e objects: Unsafe.
@tparam T The type of the owned object. Must be noexcept destructible.
@tparam Handler The type of the completion handler.
*/
template<class T, class Handler>
class handler_ptr
{
#ifndef BOOST_BEAST_ALLOW_DEPRECATED
static_assert(sizeof(T) == 0,
BOOST_BEAST_DEPRECATION_STRING);
#endif
T* t_ = nullptr;
union
{
Handler h_;
};
void clear();
public:
/// The type of element stored
using element_type = T;
/// The type of handler stored
using handler_type = Handler;
/// Default constructor (deleted).
handler_ptr() = delete;
/// Copy assignment (deleted).
handler_ptr& operator=(handler_ptr const&) = delete;
/// Move assignment (deleted).
handler_ptr& operator=(handler_ptr &&) = delete;
/** Destructor
If `*this` owns an object the object is destroyed and
the memory deallocated using the allocator associated
with the handler.
*/
~handler_ptr();
/** Move constructor.
When this call returns, the moved-from container
will have no owned object.
*/
handler_ptr(handler_ptr&& other);
/// Copy constructor (deleted).
handler_ptr(handler_ptr const& other) = delete;
/** Constructor
This creates a new container with an owned object of
type `T`. The allocator associated with the handler will
be used to allocate memory for the owned object. The
constructor for the owned object will be called with the
following equivalent signature:
@code
T::T(Handler const&, Args&&...)
@endcode
@esafe
Strong guarantee.
@param handler The handler to associate with the owned object.
The implementation takes ownership of the handler by performing a decay-copy.
@param args Optional arguments forwarded to
the owned object's constructor.
*/
template<class DeducedHandler, class... Args>
explicit
handler_ptr(DeducedHandler&& handler, Args&&... args);
/// Return a reference to the handler
handler_type const&
handler() const noexcept
{
return h_;
}
/// Return a reference to the handler
handler_type&
handler() noexcept
{
return h_;
}
/// Return `true` if `*this` owns an object
bool
has_value() const noexcept
{
return t_ != nullptr;
}
/** Return a pointer to the owned object.
@par Preconditions:
`has_value() == true`
*/
T*
get() const
{
BOOST_ASSERT(t_);
return t_;
}
/** Return a reference to the owned object.
@par Preconditions:
`has_value() == true`
*/
T&
operator*() const
{
BOOST_ASSERT(t_);
return *t_;
}
/// Return a pointer to the owned object.
T*
operator->() const
{
BOOST_ASSERT(t_);
return t_;
}
/** Returns ownership of the handler
Before this function returns, the owned object is
destroyed, satisfying the deallocation-before-invocation
Asio guarantee.
@return The released handler.
@par Preconditions:
`has_value() == true`
@par Postconditions:
`has_value() == false`
*/
handler_type
release_handler();
/** Invoke the handler in the owned object.
This function invokes the handler in the owned object
with a forwarded argument list. Before the invocation,
the owned object is destroyed, satisfying the
deallocation-before-invocation Asio guarantee.
@par Preconditions:
`has_value() == true`
@par Postconditions:
`has_value() == false`
@note Care must be taken when the arguments are themselves
stored in the owned object. Such arguments must first be
moved to the stack or elsewhere, and then passed, or else
undefined behavior will result.
*/
template<class... Args>
void
invoke(Args&&... args);
};
} // beast
} // boost
#include <boost/beast/core/impl/handler_ptr.hpp>
#endif
#endif

View File

@@ -0,0 +1,156 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_IMPL_ASYNC_BASE_HPP
#define BOOST_BEAST_CORE_IMPL_ASYNC_BASE_HPP
#include <boost/core/exchange.hpp>
namespace boost {
namespace beast {
namespace detail {
template<class State, class Allocator>
struct allocate_stable_state final
: stable_base
, boost::empty_value<Allocator>
{
State value;
template<class... Args>
explicit
allocate_stable_state(
Allocator const& alloc,
Args&&... args)
: boost::empty_value<Allocator>(
boost::empty_init_t{}, alloc)
, value{std::forward<Args>(args)...}
{
}
void destroy() override
{
using A = typename allocator_traits<
Allocator>::template rebind_alloc<
allocate_stable_state>;
A a(this->get());
detail::allocator_traits<A>::destroy(a, this);
detail::allocator_traits<A>::deallocate(a, this, 1);
}
};
} // detail
template<
class Handler,
class Executor1,
class Allocator,
class Function>
void asio_handler_invoke(
Function&& f,
async_base<Handler, Executor1, Allocator>* p)
{
using net::asio_handler_invoke;
asio_handler_invoke(f,
p->get_legacy_handler_pointer());
}
template<
class Handler,
class Executor1,
class Allocator>
void*
asio_handler_allocate(
std::size_t size,
async_base<Handler, Executor1, Allocator>* p)
{
using net::asio_handler_allocate;
return asio_handler_allocate(size,
p->get_legacy_handler_pointer());
}
template<
class Handler,
class Executor1,
class Allocator>
void
asio_handler_deallocate(
void* mem, std::size_t size,
async_base<Handler, Executor1, Allocator>* p)
{
using net::asio_handler_deallocate;
asio_handler_deallocate(mem, size,
p->get_legacy_handler_pointer());
}
template<
class Handler,
class Executor1,
class Allocator>
bool
asio_handler_is_continuation(
async_base<Handler, Executor1, Allocator>* p)
{
using net::asio_handler_is_continuation;
return asio_handler_is_continuation(
p->get_legacy_handler_pointer());
}
template<
class State,
class Handler,
class Executor1,
class Allocator,
class... Args>
State&
allocate_stable(
stable_async_base<
Handler, Executor1, Allocator>& base,
Args&&... args)
{
using allocator_type = typename stable_async_base<
Handler, Executor1, Allocator>::allocator_type;
using A = typename detail::allocator_traits<
allocator_type>::template rebind_alloc<
detail::allocate_stable_state<
State, allocator_type>>;
struct deleter
{
allocator_type alloc;
detail::allocate_stable_state<
State, allocator_type>* ptr;
~deleter()
{
if(ptr)
{
A a(alloc);
detail::allocator_traits<A>::deallocate(a, ptr, 1);
}
}
};
A a(base.get_allocator());
deleter d{base.get_allocator(), nullptr};
d.ptr = detail::allocator_traits<A>::allocate(a, 1);
detail::allocator_traits<A>::construct(a, d.ptr,
d.alloc, std::forward<Args>(args)...);
d.ptr->next_ = base.list_;
base.list_ = d.ptr;
return boost::exchange(d.ptr, nullptr)->value;
}
} // beast
} // boost
#endif

View File

@@ -0,0 +1,995 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_IMPL_BASIC_STREAM_HPP
#define BOOST_BEAST_CORE_IMPL_BASIC_STREAM_HPP
#include <boost/beast/core/async_base.hpp>
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/beast/core/buffers_prefix.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/beast/websocket/teardown.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/assert.hpp>
#include <boost/make_shared.hpp>
#include <boost/core/exchange.hpp>
#include <cstdlib>
#include <type_traits>
#include <utility>
namespace boost {
namespace beast {
//------------------------------------------------------------------------------
template<class Protocol, class Executor, class RatePolicy>
template<class... Args>
basic_stream<Protocol, Executor, RatePolicy>::
impl_type::
impl_type(std::false_type, Args&&... args)
: socket(std::forward<Args>(args)...)
, read(ex())
, write(ex())
, timer(ex())
{
reset();
}
template<class Protocol, class Executor, class RatePolicy>
template<class RatePolicy_, class... Args>
basic_stream<Protocol, Executor, RatePolicy>::
impl_type::
impl_type(std::true_type,
RatePolicy_&& policy, Args&&... args)
: boost::empty_value<RatePolicy>(
boost::empty_init_t{},
std::forward<RatePolicy_>(policy))
, socket(std::forward<Args>(args)...)
, read(ex())
, write(ex())
, timer(ex())
{
reset();
}
template<class Protocol, class Executor, class RatePolicy>
template<class Executor2>
void
basic_stream<Protocol, Executor, RatePolicy>::
impl_type::
on_timer(Executor2 const& ex2)
{
BOOST_ASSERT(waiting > 0);
// the last waiter starts the new slice
if(--waiting > 0)
return;
// update the expiration time
BOOST_VERIFY(timer.expires_after(
std::chrono::seconds(1)) == 0);
rate_policy_access::on_timer(policy());
struct handler : boost::empty_value<Executor2>
{
boost::weak_ptr<impl_type> wp;
using executor_type = Executor2;
executor_type
get_executor() const noexcept
{
return this->get();
}
handler(
Executor2 const& ex2,
boost::shared_ptr<impl_type> const& sp)
: boost::empty_value<Executor2>(
boost::empty_init_t{}, ex2)
, wp(sp)
{
}
void
operator()(error_code ec)
{
auto sp = wp.lock();
if(! sp)
return;
if(ec == net::error::operation_aborted)
return;
BOOST_ASSERT(! ec);
if(ec)
return;
sp->on_timer(this->get());
}
};
// wait on the timer again
++waiting;
timer.async_wait(handler(ex2, this->shared_from_this()));
}
template<class Protocol, class Executor, class RatePolicy>
void
basic_stream<Protocol, Executor, RatePolicy>::
impl_type::
reset()
{
// If assert goes off, it means that there are
// already read or write (or connect) operations
// outstanding, so there is nothing to apply
// the expiration time to!
//
BOOST_ASSERT(! read.pending || ! write.pending);
if(! read.pending)
BOOST_VERIFY(
read.timer.expires_at(never()) == 0);
if(! write.pending)
BOOST_VERIFY(
write.timer.expires_at(never()) == 0);
}
template<class Protocol, class Executor, class RatePolicy>
void
basic_stream<Protocol, Executor, RatePolicy>::
impl_type::
close()
{
socket.close();
timer.cancel();
// have to let the read/write ops cancel the timer,
// otherwise we will get error::timeout on close when
// we actually want net::error::operation_aborted.
//
//read.timer.cancel();
//write.timer.cancel();
}
//------------------------------------------------------------------------------
template<class Protocol, class Executor, class RatePolicy>
struct basic_stream<Protocol, Executor, RatePolicy>::
timeout_handler
{
op_state& state;
boost::weak_ptr<impl_type> wp;
tick_type tick;
void
operator()(error_code ec)
{
// timer canceled
if(ec == net::error::operation_aborted)
return;
BOOST_ASSERT(! ec);
auto sp = wp.lock();
// stream destroyed
if(! sp)
return;
// stale timer
if(tick < state.tick)
return;
BOOST_ASSERT(tick == state.tick);
// timeout
BOOST_ASSERT(! state.timeout);
sp->close();
state.timeout = true;
}
};
//------------------------------------------------------------------------------
template<class Protocol, class Executor, class RatePolicy>
struct basic_stream<Protocol, Executor, RatePolicy>::ops
{
template<bool isRead, class Buffers, class Handler>
class transfer_op
: public async_base<Handler, Executor>
, public boost::asio::coroutine
{
boost::shared_ptr<impl_type> impl_;
pending_guard pg_;
Buffers b_;
using is_read = std::integral_constant<bool, isRead>;
op_state&
state(std::true_type)
{
return impl_->read;
}
op_state&
state(std::false_type)
{
return impl_->write;
}
op_state&
state()
{
return state(
std::integral_constant<bool, isRead>{});
}
std::size_t
available_bytes(std::true_type)
{
return rate_policy_access::
available_read_bytes(impl_->policy());
}
std::size_t
available_bytes(std::false_type)
{
return rate_policy_access::
available_write_bytes(impl_->policy());
}
std::size_t
available_bytes()
{
return available_bytes(is_read{});
}
void
transfer_bytes(std::size_t n, std::true_type)
{
rate_policy_access::
transfer_read_bytes(impl_->policy(), n);
}
void
transfer_bytes(std::size_t n, std::false_type)
{
rate_policy_access::
transfer_write_bytes(impl_->policy(), n);
}
void
transfer_bytes(std::size_t n)
{
transfer_bytes(n, is_read{});
}
void
async_perform(
std::size_t amount, std::true_type)
{
impl_->socket.async_read_some(
beast::buffers_prefix(amount, b_),
std::move(*this));
}
void
async_perform(
std::size_t amount, std::false_type)
{
impl_->socket.async_write_some(
beast::buffers_prefix(amount, b_),
std::move(*this));
}
public:
template<class Handler_>
transfer_op(
Handler_&& h,
basic_stream& s,
Buffers const& b)
: async_base<Handler, Executor>(
std::forward<Handler_>(h), s.get_executor())
, impl_(s.impl_)
, pg_(state().pending)
, b_(b)
{
(*this)({});
}
void
operator()(
error_code ec,
std::size_t bytes_transferred = 0)
{
BOOST_ASIO_CORO_REENTER(*this)
{
// handle empty buffers
if(detail::buffers_empty(b_))
{
// make sure we perform the no-op
BOOST_ASIO_CORO_YIELD
async_perform(0, is_read{});
// apply the timeout manually, otherwise
// behavior varies across platforms.
if(state().timer.expiry() <= clock_type::now())
{
impl_->close();
ec = beast::error::timeout;
}
goto upcall;
}
// if a timeout is active, wait on the timer
if(state().timer.expiry() != never())
state().timer.async_wait(
net::bind_executor(
this->get_executor(),
timeout_handler{
state(),
impl_,
state().tick
}));
// check rate limit, maybe wait
std::size_t amount;
amount = available_bytes();
if(amount == 0)
{
++impl_->waiting;
BOOST_ASIO_CORO_YIELD
impl_->timer.async_wait(std::move(*this));
if(ec)
{
// socket was closed, or a timeout
BOOST_ASSERT(ec ==
net::error::operation_aborted);
// timeout handler invoked?
if(state().timeout)
{
// yes, socket already closed
ec = beast::error::timeout;
state().timeout = false;
}
goto upcall;
}
impl_->on_timer(this->get_executor());
// Allow at least one byte, otherwise
// bytes_transferred could be 0.
amount = std::max<std::size_t>(
available_bytes(), 1);
}
BOOST_ASIO_CORO_YIELD
async_perform(amount, is_read{});
if(state().timer.expiry() != never())
{
++state().tick;
// try cancelling timer
auto const n =
state().timer.cancel();
if(n == 0)
{
// timeout handler invoked?
if(state().timeout)
{
// yes, socket already closed
ec = beast::error::timeout;
state().timeout = false;
}
}
else
{
BOOST_ASSERT(n == 1);
BOOST_ASSERT(! state().timeout);
}
}
upcall:
pg_.reset();
transfer_bytes(bytes_transferred);
this->complete_now(ec, bytes_transferred);
}
}
};
template<class Handler>
class connect_op
: public async_base<Handler, Executor>
{
boost::shared_ptr<impl_type> impl_;
pending_guard pg0_;
pending_guard pg1_;
op_state&
state() noexcept
{
return impl_->write;
}
public:
template<class Handler_>
connect_op(
Handler_&& h,
basic_stream& s,
endpoint_type ep)
: async_base<Handler, Executor>(
std::forward<Handler_>(h), s.get_executor())
, impl_(s.impl_)
, pg0_(impl_->read.pending)
, pg1_(impl_->write.pending)
{
if(state().timer.expiry() != stream_base::never())
impl_->write.timer.async_wait(
net::bind_executor(
this->get_executor(),
timeout_handler{
state(),
impl_,
state().tick}));
impl_->socket.async_connect(
ep, std::move(*this));
// *this is now moved-from
}
template<
class Endpoints, class Condition,
class Handler_>
connect_op(
Handler_&& h,
basic_stream& s,
Endpoints const& eps,
Condition const& cond)
: async_base<Handler, Executor>(
std::forward<Handler_>(h), s.get_executor())
, impl_(s.impl_)
, pg0_(impl_->read.pending)
, pg1_(impl_->write.pending)
{
if(state().timer.expiry() != stream_base::never())
impl_->write.timer.async_wait(
net::bind_executor(
this->get_executor(),
timeout_handler{
state(),
impl_,
state().tick}));
net::async_connect(impl_->socket,
eps, cond, std::move(*this));
// *this is now moved-from
}
template<
class Iterator, class Condition,
class Handler_>
connect_op(
Handler_&& h,
basic_stream& s,
Iterator begin, Iterator end,
Condition const& cond)
: async_base<Handler, Executor>(
std::forward<Handler_>(h), s.get_executor())
, impl_(s.impl_)
, pg0_(impl_->read.pending)
, pg1_(impl_->write.pending)
{
if(state().timer.expiry() != stream_base::never())
impl_->write.timer.async_wait(
net::bind_executor(
this->get_executor(),
timeout_handler{
state(),
impl_,
state().tick}));
net::async_connect(impl_->socket,
begin, end, cond, std::move(*this));
// *this is now moved-from
}
template<class... Args>
void
operator()(error_code ec, Args&&... args)
{
if(state().timer.expiry() != stream_base::never())
{
++state().tick;
// try cancelling timer
auto const n =
impl_->write.timer.cancel();
if(n == 0)
{
// timeout handler invoked?
if(state().timeout)
{
// yes, socket already closed
ec = beast::error::timeout;
state().timeout = false;
}
}
else
{
BOOST_ASSERT(n == 1);
BOOST_ASSERT(! state().timeout);
}
}
pg0_.reset();
pg1_.reset();
this->complete_now(ec, std::forward<Args>(args)...);
}
};
struct run_read_op
{
template<class ReadHandler, class Buffers>
void
operator()(
ReadHandler&& h,
basic_stream* s,
Buffers const& b)
{
// If you get an error on the following line it means
// that your handler does not meet the documented type
// requirements for the handler.
static_assert(
detail::is_invocable<ReadHandler,
void(error_code, std::size_t)>::value,
"ReadHandler type requirements not met");
transfer_op<
true,
Buffers,
typename std::decay<ReadHandler>::type>(
std::forward<ReadHandler>(h), *s, b);
}
};
struct run_write_op
{
template<class WriteHandler, class Buffers>
void
operator()(
WriteHandler&& h,
basic_stream* s,
Buffers const& b)
{
// If you get an error on the following line it means
// that your handler does not meet the documented type
// requirements for the handler.
static_assert(
detail::is_invocable<WriteHandler,
void(error_code, std::size_t)>::value,
"WriteHandler type requirements not met");
transfer_op<
false,
Buffers,
typename std::decay<WriteHandler>::type>(
std::forward<WriteHandler>(h), *s, b);
}
};
struct run_connect_op
{
template<class ConnectHandler>
void
operator()(
ConnectHandler&& h,
basic_stream* s,
endpoint_type const& ep)
{
// If you get an error on the following line it means
// that your handler does not meet the documented type
// requirements for the handler.
static_assert(
detail::is_invocable<ConnectHandler,
void(error_code)>::value,
"ConnectHandler type requirements not met");
connect_op<typename std::decay<ConnectHandler>::type>(
std::forward<ConnectHandler>(h), *s, ep);
}
};
struct run_connect_range_op
{
template<
class RangeConnectHandler,
class EndpointSequence,
class Condition>
void
operator()(
RangeConnectHandler&& h,
basic_stream* s,
EndpointSequence const& eps,
Condition const& cond)
{
// If you get an error on the following line it means
// that your handler does not meet the documented type
// requirements for the handler.
static_assert(
detail::is_invocable<RangeConnectHandler,
void(error_code, typename Protocol::endpoint)>::value,
"RangeConnectHandler type requirements not met");
connect_op<typename std::decay<RangeConnectHandler>::type>(
std::forward<RangeConnectHandler>(h), *s, eps, cond);
}
};
struct run_connect_iter_op
{
template<
class IteratorConnectHandler,
class Iterator,
class Condition>
void
operator()(
IteratorConnectHandler&& h,
basic_stream* s,
Iterator begin, Iterator end,
Condition const& cond)
{
// If you get an error on the following line it means
// that your handler does not meet the documented type
// requirements for the handler.
static_assert(
detail::is_invocable<IteratorConnectHandler,
void(error_code, Iterator)>::value,
"IteratorConnectHandler type requirements not met");
connect_op<typename std::decay<IteratorConnectHandler>::type>(
std::forward<IteratorConnectHandler>(h), *s, begin, end, cond);
}
};
};
//------------------------------------------------------------------------------
template<class Protocol, class Executor, class RatePolicy>
basic_stream<Protocol, Executor, RatePolicy>::
~basic_stream()
{
// the shared object can outlive *this,
// cancel any operations so the shared
// object is destroyed as soon as possible.
impl_->close();
}
template<class Protocol, class Executor, class RatePolicy>
template<class Arg0, class... Args, class>
basic_stream<Protocol, Executor, RatePolicy>::
basic_stream(Arg0&& arg0, Args&&... args)
: impl_(boost::make_shared<impl_type>(
std::false_type{},
std::forward<Arg0>(arg0),
std::forward<Args>(args)...))
{
}
template<class Protocol, class Executor, class RatePolicy>
template<class RatePolicy_, class Arg0, class... Args, class>
basic_stream<Protocol, Executor, RatePolicy>::
basic_stream(
RatePolicy_&& policy, Arg0&& arg0, Args&&... args)
: impl_(boost::make_shared<impl_type>(
std::true_type{},
std::forward<RatePolicy_>(policy),
std::forward<Arg0>(arg0),
std::forward<Args>(args)...))
{
}
template<class Protocol, class Executor, class RatePolicy>
basic_stream<Protocol, Executor, RatePolicy>::
basic_stream(basic_stream&& other)
: impl_(boost::make_shared<impl_type>(
std::move(*other.impl_)))
{
// VFALCO I'm not sure this implementation is correct...
}
//------------------------------------------------------------------------------
template<class Protocol, class Executor, class RatePolicy>
auto
basic_stream<Protocol, Executor, RatePolicy>::
release_socket() ->
socket_type
{
this->cancel();
return std::move(impl_->socket);
}
template<class Protocol, class Executor, class RatePolicy>
void
basic_stream<Protocol, Executor, RatePolicy>::
expires_after(std::chrono::nanoseconds expiry_time)
{
// If assert goes off, it means that there are
// already read or write (or connect) operations
// outstanding, so there is nothing to apply
// the expiration time to!
//
BOOST_ASSERT(
! impl_->read.pending ||
! impl_->write.pending);
if(! impl_->read.pending)
BOOST_VERIFY(
impl_->read.timer.expires_after(
expiry_time) == 0);
if(! impl_->write.pending)
BOOST_VERIFY(
impl_->write.timer.expires_after(
expiry_time) == 0);
}
template<class Protocol, class Executor, class RatePolicy>
void
basic_stream<Protocol, Executor, RatePolicy>::
expires_at(
net::steady_timer::time_point expiry_time)
{
// If assert goes off, it means that there are
// already read or write (or connect) operations
// outstanding, so there is nothing to apply
// the expiration time to!
//
BOOST_ASSERT(
! impl_->read.pending ||
! impl_->write.pending);
if(! impl_->read.pending)
BOOST_VERIFY(
impl_->read.timer.expires_at(
expiry_time) == 0);
if(! impl_->write.pending)
BOOST_VERIFY(
impl_->write.timer.expires_at(
expiry_time) == 0);
}
template<class Protocol, class Executor, class RatePolicy>
void
basic_stream<Protocol, Executor, RatePolicy>::
expires_never()
{
impl_->reset();
}
template<class Protocol, class Executor, class RatePolicy>
void
basic_stream<Protocol, Executor, RatePolicy>::
cancel()
{
error_code ec;
impl_->socket.cancel(ec);
impl_->timer.cancel();
}
template<class Protocol, class Executor, class RatePolicy>
void
basic_stream<Protocol, Executor, RatePolicy>::
close()
{
impl_->close();
}
//------------------------------------------------------------------------------
template<class Protocol, class Executor, class RatePolicy>
template<class ConnectHandler>
BOOST_BEAST_ASYNC_RESULT1(ConnectHandler)
basic_stream<Protocol, Executor, RatePolicy>::
async_connect(
endpoint_type const& ep,
ConnectHandler&& handler)
{
return net::async_initiate<
ConnectHandler,
void(error_code)>(
typename ops::run_connect_op{},
handler,
this,
ep);
}
template<class Protocol, class Executor, class RatePolicy>
template<
class EndpointSequence,
class RangeConnectHandler,
class>
BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,void(error_code, typename Protocol::endpoint))
basic_stream<Protocol, Executor, RatePolicy>::
async_connect(
EndpointSequence const& endpoints,
RangeConnectHandler&& handler)
{
return net::async_initiate<
RangeConnectHandler,
void(error_code, typename Protocol::endpoint)>(
typename ops::run_connect_range_op{},
handler,
this,
endpoints,
detail::any_endpoint{});
}
template<class Protocol, class Executor, class RatePolicy>
template<
class EndpointSequence,
class ConnectCondition,
class RangeConnectHandler,
class>
BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,void (error_code, typename Protocol::endpoint))
basic_stream<Protocol, Executor, RatePolicy>::
async_connect(
EndpointSequence const& endpoints,
ConnectCondition connect_condition,
RangeConnectHandler&& handler)
{
return net::async_initiate<
RangeConnectHandler,
void(error_code, typename Protocol::endpoint)>(
typename ops::run_connect_range_op{},
handler,
this,
endpoints,
connect_condition);
}
template<class Protocol, class Executor, class RatePolicy>
template<
class Iterator,
class IteratorConnectHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,void (error_code, Iterator))
basic_stream<Protocol, Executor, RatePolicy>::
async_connect(
Iterator begin, Iterator end,
IteratorConnectHandler&& handler)
{
return net::async_initiate<
IteratorConnectHandler,
void(error_code, Iterator)>(
typename ops::run_connect_iter_op{},
handler,
this,
begin, end,
detail::any_endpoint{});
}
template<class Protocol, class Executor, class RatePolicy>
template<
class Iterator,
class ConnectCondition,
class IteratorConnectHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,void (error_code, Iterator))
basic_stream<Protocol, Executor, RatePolicy>::
async_connect(
Iterator begin, Iterator end,
ConnectCondition connect_condition,
IteratorConnectHandler&& handler)
{
return net::async_initiate<
IteratorConnectHandler,
void(error_code, Iterator)>(
typename ops::run_connect_iter_op{},
handler,
this,
begin, end,
connect_condition);
}
//------------------------------------------------------------------------------
template<class Protocol, class Executor, class RatePolicy>
template<class MutableBufferSequence, class ReadHandler>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
basic_stream<Protocol, Executor, RatePolicy>::
async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
static_assert(net::is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence type requirements not met");
return net::async_initiate<
ReadHandler,
void(error_code, std::size_t)>(
typename ops::run_read_op{},
handler,
this,
buffers);
}
template<class Protocol, class Executor, class RatePolicy>
template<class ConstBufferSequence, class WriteHandler>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
basic_stream<Protocol, Executor, RatePolicy>::
async_write_some(
ConstBufferSequence const& buffers,
WriteHandler&& handler)
{
static_assert(net::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence type requirements not met");
return net::async_initiate<
WriteHandler,
void(error_code, std::size_t)>(
typename ops::run_write_op{},
handler,
this,
buffers);
}
//------------------------------------------------------------------------------
//
// Customization points
//
#if ! BOOST_BEAST_DOXYGEN
template<
class Protocol, class Executor, class RatePolicy>
void
beast_close_socket(
basic_stream<Protocol, Executor, RatePolicy>& stream)
{
error_code ec;
stream.socket().close(ec);
}
template<
class Protocol, class Executor, class RatePolicy>
void
teardown(
role_type role,
basic_stream<Protocol, Executor, RatePolicy>& stream,
error_code& ec)
{
using beast::websocket::teardown;
teardown(role, stream.socket(), ec);
}
template<
class Protocol, class Executor, class RatePolicy,
class TeardownHandler>
void
async_teardown(
role_type role,
basic_stream<Protocol, Executor, RatePolicy>& stream,
TeardownHandler&& handler)
{
using beast::websocket::async_teardown;
async_teardown(role, stream.socket(),
std::forward<TeardownHandler>(handler));
}
#endif
} // beast
} // boost
#endif

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/type_traits.hpp>
#include <boost/asio/post.hpp>
#include <boost/throw_exception.hpp>
namespace boost {
namespace beast {
template<class Stream, class DynamicBuffer>
struct buffered_read_stream<Stream, DynamicBuffer>::ops
{
template<class MutableBufferSequence, class Handler>
class read_op
: public async_base<Handler,
beast::executor_type<buffered_read_stream>>
{
buffered_read_stream& s_;
MutableBufferSequence b_;
int step_ = 0;
public:
read_op(read_op&&) = default;
read_op(read_op const&) = delete;
template<class Handler_>
read_op(
Handler_&& h,
buffered_read_stream& s,
MutableBufferSequence const& b)
: async_base<
Handler, beast::executor_type<buffered_read_stream>>(
std::forward<Handler_>(h), s.get_executor())
, s_(s)
, b_(b)
{
(*this)({}, 0);
}
void
operator()(
error_code ec,
std::size_t bytes_transferred)
{
// VFALCO TODO Rewrite this using reenter/yield
switch(step_)
{
case 0:
if(s_.buffer_.size() == 0)
{
if(s_.capacity_ == 0)
{
// read (unbuffered)
step_ = 1;
return s_.next_layer_.async_read_some(
b_, std::move(*this));
}
// read
step_ = 2;
return s_.next_layer_.async_read_some(
s_.buffer_.prepare(read_size(
s_.buffer_, s_.capacity_)),
std::move(*this));
}
step_ = 3;
return net::post(
s_.get_executor(),
beast::bind_front_handler(
std::move(*this), ec, 0));
case 1:
// upcall
break;
case 2:
s_.buffer_.commit(bytes_transferred);
BOOST_FALLTHROUGH;
case 3:
bytes_transferred =
net::buffer_copy(b_, s_.buffer_.data());
s_.buffer_.consume(bytes_transferred);
break;
}
this->complete_now(ec, bytes_transferred);
}
};
struct run_read_op
{
template<class ReadHandler, class Buffers>
void
operator()(
ReadHandler&& h,
buffered_read_stream* s,
Buffers const& b)
{
// If you get an error on the following line it means
// that your handler does not meet the documented type
// requirements for the handler.
static_assert(
beast::detail::is_invocable<ReadHandler,
void(error_code, std::size_t)>::value,
"ReadHandler type requirements not met");
read_op<
Buffers,
typename std::decay<ReadHandler>::type>(
std::forward<ReadHandler>(h), *s, b);
}
};
};
//------------------------------------------------------------------------------
template<class Stream, class DynamicBuffer>
template<class... Args>
buffered_read_stream<Stream, DynamicBuffer>::
buffered_read_stream(Args&&... args)
: next_layer_(std::forward<Args>(args)...)
{
}
template<class Stream, class DynamicBuffer>
template<class ConstBufferSequence, class WriteHandler>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
buffered_read_stream<Stream, DynamicBuffer>::
async_write_some(
ConstBufferSequence const& buffers,
WriteHandler&& handler)
{
static_assert(is_async_write_stream<next_layer_type>::value,
"AsyncWriteStream type requirements not met");
static_assert(net::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence type requirements not met");
static_assert(detail::is_invocable<WriteHandler,
void(error_code, std::size_t)>::value,
"WriteHandler type requirements not met");
return next_layer_.async_write_some(buffers,
std::forward<WriteHandler>(handler));
}
template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence>
std::size_t
buffered_read_stream<Stream, DynamicBuffer>::
read_some(
MutableBufferSequence const& buffers)
{
static_assert(is_sync_read_stream<next_layer_type>::value,
"SyncReadStream type requirements not met");
static_assert(net::is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence type requirements not met");
error_code ec;
auto n = read_some(buffers, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
return n;
}
template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence>
std::size_t
buffered_read_stream<Stream, DynamicBuffer>::
read_some(MutableBufferSequence const& buffers,
error_code& ec)
{
static_assert(is_sync_read_stream<next_layer_type>::value,
"SyncReadStream type requirements not met");
static_assert(net::is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence type requirements not met");
if(buffer_.size() == 0)
{
if(capacity_ == 0)
return next_layer_.read_some(buffers, ec);
buffer_.commit(next_layer_.read_some(
buffer_.prepare(read_size(buffer_,
capacity_)), ec));
if(ec)
return 0;
}
else
{
ec = {};
}
auto bytes_transferred =
net::buffer_copy(buffers, buffer_.data());
buffer_.consume(bytes_transferred);
return bytes_transferred;
}
template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence, class ReadHandler>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
buffered_read_stream<Stream, DynamicBuffer>::
async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
static_assert(is_async_read_stream<next_layer_type>::value,
"AsyncReadStream type requirements not met");
static_assert(net::is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence type requirements not met");
if(buffer_.size() == 0 && capacity_ == 0)
return next_layer_.async_read_some(buffers,
std::forward<ReadHandler>(handler));
return net::async_initiate<
ReadHandler,
void(error_code, std::size_t)>(
typename ops::run_read_op{},
handler,
this,
buffers);
}
} // beast
} // boost
#endif

View File

@@ -0,0 +1,608 @@
//
// 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/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/config/workaround.hpp>
#include <boost/throw_exception.hpp>
#include <algorithm>
#include <cstring>
#include <iterator>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace boost {
namespace beast {
//------------------------------------------------------------------------------
#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
# pragma warning (push)
# pragma warning (disable: 4521) // multiple copy constructors specified
# pragma warning (disable: 4522) // multiple assignment operators specified
#endif
template<class MutableBufferSequence>
template<bool isMutable>
class buffers_adaptor<MutableBufferSequence>::
readable_bytes
{
buffers_adaptor const* b_;
public:
using value_type = typename
std::conditional<isMutable,
net::mutable_buffer,
net::const_buffer>::type;
class const_iterator;
readable_bytes() = delete;
#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
readable_bytes(
readable_bytes const& other)
: b_(other.b_)
{
}
readable_bytes& operator=(
readable_bytes const& other)
{
b_ = other.b_;
return *this;
}
#else
readable_bytes(
readable_bytes const&) = default;
readable_bytes& operator=(
readable_bytes const&) = default;
#endif
template<bool isMutable_ = isMutable, class =
typename std::enable_if<! isMutable_>::type>
readable_bytes(
readable_bytes<true> const& other) noexcept
: b_(other.b_)
{
}
template<bool isMutable_ = isMutable, class =
typename std::enable_if<! isMutable_>::type>
readable_bytes& operator=(
readable_bytes<true> const& other) noexcept
{
b_ = other.b_;
return *this;
}
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class buffers_adaptor;
readable_bytes(buffers_adaptor const& b)
: b_(&b)
{
}
};
#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
# pragma warning (pop)
#endif
//------------------------------------------------------------------------------
template<class MutableBufferSequence>
template<bool isMutable>
class buffers_adaptor<MutableBufferSequence>::
readable_bytes<isMutable>::
const_iterator
{
iter_type it_{};
buffers_adaptor const* b_ = nullptr;
public:
using value_type = typename
std::conditional<isMutable,
net::mutable_buffer,
net::const_buffer>::type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
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 const b = *it_;
return value_type{b.data(),
(b_->out_ == net::buffer_sequence_end(b_->bs_) ||
it_ != b_->out_) ? b.size() : b_->out_pos_} +
(it_ == b_->begin_ ? b_->in_pos_ : 0);
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class readable_bytes;
const_iterator(
buffers_adaptor const& b,
iter_type iter)
: it_(iter)
, b_(&b)
{
}
};
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
readable_bytes<isMutable>::
begin() const ->
const_iterator
{
return const_iterator{*b_, b_->begin_};
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
readable_bytes<isMutable>::
readable_bytes::end() const ->
const_iterator
{
return const_iterator{*b_, b_->end_impl()};
}
//------------------------------------------------------------------------------
template<class MutableBufferSequence>
class buffers_adaptor<MutableBufferSequence>::
mutable_buffers_type
{
buffers_adaptor const* b_;
public:
using value_type = net::mutable_buffer;
class const_iterator;
mutable_buffers_type() = delete;
mutable_buffers_type(
mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(
mutable_buffers_type const&) = default;
const_iterator
begin() const;
const_iterator
end() const;
private:
friend class buffers_adaptor;
mutable_buffers_type(
buffers_adaptor const& b)
: b_(&b)
{
}
};
template<class MutableBufferSequence>
class buffers_adaptor<MutableBufferSequence>::
mutable_buffers_type::const_iterator
{
iter_type it_{};
buffers_adaptor const* b_ = nullptr;
public:
using value_type = net::mutable_buffer;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator 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 const b = *it_;
return value_type{b.data(),
it_ == std::prev(b_->end_) ?
b_->out_end_ : b.size()} +
(it_ == b_->out_ ? b_->out_pos_ : 0);
}
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--()
{
--it_;
return *this;
}
const_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
private:
friend class mutable_buffers_type;
const_iterator(buffers_adaptor const& b,
iter_type iter)
: it_(iter)
, b_(&b)
{
}
};
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
mutable_buffers_type::
begin() const ->
const_iterator
{
return const_iterator{*b_, b_->out_};
}
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
mutable_buffers_type::
end() const ->
const_iterator
{
return const_iterator{*b_, b_->end_};
}
//------------------------------------------------------------------------------
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{*this};
}
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
data() noexcept ->
mutable_data_type
{
return mutable_data_type{*this};
}
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
prepare(std::size_t n) ->
mutable_buffers_type
{
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{*this};
}
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_;
}
}
} // beast
} // boost
#endif

View File

@@ -0,0 +1,443 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_IMPL_BUFFERS_CAT_HPP
#define BOOST_BEAST_IMPL_BUFFERS_CAT_HPP
#include <boost/beast/core/detail/tuple.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/beast/core/detail/variant.hpp>
#include <boost/asio/buffer.hpp>
#include <cstdint>
#include <iterator>
#include <new>
#include <stdexcept>
#include <utility>
namespace boost {
namespace beast {
#if defined(_MSC_VER) && ! defined(__clang__)
# define BOOST_BEAST_UNREACHABLE() __assume(false)
# define BOOST_BEAST_UNREACHABLE_RETURN(v) __assume(false)
#else
# define BOOST_BEAST_UNREACHABLE() __builtin_unreachable()
# define BOOST_BEAST_UNREACHABLE_RETURN(v) \
do { __builtin_unreachable(); return v; } while(false)
#endif
#ifdef BOOST_BEAST_TESTS
#define BOOST_BEAST_LOGIC_ERROR(s) \
do { \
BOOST_THROW_EXCEPTION(std::logic_error((s))); \
BOOST_BEAST_UNREACHABLE(); \
} while(false)
#define BOOST_BEAST_LOGIC_ERROR_RETURN(v, s) \
do { \
BOOST_THROW_EXCEPTION(std::logic_error(s)); \
BOOST_BEAST_UNREACHABLE_RETURN(v); \
} while(false)
#else
#define BOOST_BEAST_LOGIC_ERROR(s) \
do { \
BOOST_ASSERT_MSG(false, s); \
BOOST_BEAST_UNREACHABLE(); \
} while(false)
#define BOOST_BEAST_LOGIC_ERROR_RETURN(v, s) \
do { \
BOOST_ASSERT_MSG(false, (s)); \
BOOST_BEAST_UNREACHABLE_RETURN(v); \
} while(false)
#endif
namespace detail {
struct buffers_cat_view_iterator_base
{
struct past_end
{
char unused = 0; // make g++8 happy
net::mutable_buffer
operator*() const
{
BOOST_BEAST_LOGIC_ERROR_RETURN({},
"Dereferencing a one-past-the-end iterator");
}
operator bool() const noexcept
{
return true;
}
};
};
} // detail
template<class... Bn>
class buffers_cat_view<Bn...>::const_iterator
: private detail::buffers_cat_view_iterator_base
{
// VFALCO The logic to skip empty sequences fails
// if there is just one buffer in the list.
static_assert(sizeof...(Bn) >= 2,
"A minimum of two sequences are required");
detail::tuple<Bn...> const* bn_ = nullptr;
detail::variant<
buffers_iterator_type<Bn>..., past_end> it_{};
friend class buffers_cat_view<Bn...>;
template<std::size_t I>
using C = std::integral_constant<std::size_t, I>;
public:
using value_type = typename
buffers_cat_view<Bn...>::value_type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(
const_iterator const& other) = default;
bool
operator==(const_iterator const& other) const;
bool
operator!=(const_iterator const& other) const
{
return ! (*this == other);
}
reference
operator*() const;
pointer
operator->() const = delete;
const_iterator&
operator++();
const_iterator
operator++(int);
const_iterator&
operator--();
const_iterator
operator--(int);
private:
const_iterator(
detail::tuple<Bn...> const& bn,
std::true_type);
const_iterator(
detail::tuple<Bn...> const& bn,
std::false_type);
struct dereference
{
const_iterator const& self;
reference
operator()(mp11::mp_size_t<0>)
{
BOOST_BEAST_LOGIC_ERROR_RETURN({},
"Dereferencing a default-constructed iterator");
}
template<class I>
reference operator()(I)
{
return *self.it_.template get<I::value>();
}
};
struct increment
{
const_iterator& self;
void
operator()(mp11::mp_size_t<0>)
{
BOOST_BEAST_LOGIC_ERROR(
"Incrementing a default-constructed iterator");
}
template<std::size_t I>
void
operator()(mp11::mp_size_t<I>)
{
++self.it_.template get<I>();
next(mp11::mp_size_t<I>{});
}
template<std::size_t I>
void
next(mp11::mp_size_t<I>)
{
auto& it = self.it_.template get<I>();
for(;;)
{
if (it == net::buffer_sequence_end(
detail::get<I-1>(*self.bn_)))
break;
if(net::const_buffer(*it).size() > 0)
return;
++it;
}
self.it_.template emplace<I+1>(
net::buffer_sequence_begin(
detail::get<I>(*self.bn_)));
next(mp11::mp_size_t<I+1>{});
}
void
operator()(mp11::mp_size_t<sizeof...(Bn)>)
{
auto constexpr I = sizeof...(Bn);
++self.it_.template get<I>();
next(mp11::mp_size_t<I>{});
}
void
next(mp11::mp_size_t<sizeof...(Bn)>)
{
auto constexpr I = sizeof...(Bn);
auto& it = self.it_.template get<I>();
for(;;)
{
if (it == net::buffer_sequence_end(
detail::get<I-1>(*self.bn_)))
break;
if(net::const_buffer(*it).size() > 0)
return;
++it;
}
// end
self.it_.template emplace<I+1>();
}
void
operator()(mp11::mp_size_t<sizeof...(Bn)+1>)
{
BOOST_BEAST_LOGIC_ERROR(
"Incrementing a one-past-the-end iterator");
}
};
struct decrement
{
const_iterator& self;
void
operator()(mp11::mp_size_t<0>)
{
BOOST_BEAST_LOGIC_ERROR(
"Decrementing a default-constructed iterator");
}
void
operator()(mp11::mp_size_t<1>)
{
auto constexpr I = 1;
auto& it = self.it_.template get<I>();
for(;;)
{
if(it == net::buffer_sequence_begin(
detail::get<I-1>(*self.bn_)))
{
BOOST_BEAST_LOGIC_ERROR(
"Decrementing an iterator to the beginning");
}
--it;
if(net::const_buffer(*it).size() > 0)
return;
}
}
template<std::size_t I>
void
operator()(mp11::mp_size_t<I>)
{
auto& it = self.it_.template get<I>();
for(;;)
{
if(it == net::buffer_sequence_begin(
detail::get<I-1>(*self.bn_)))
break;
--it;
if(net::const_buffer(*it).size() > 0)
return;
}
self.it_.template emplace<I-1>(
net::buffer_sequence_end(
detail::get<I-2>(*self.bn_)));
(*this)(mp11::mp_size_t<I-1>{});
}
void
operator()(mp11::mp_size_t<sizeof...(Bn)+1>)
{
auto constexpr I = sizeof...(Bn)+1;
self.it_.template emplace<I-1>(
net::buffer_sequence_end(
detail::get<I-2>(*self.bn_)));
(*this)(mp11::mp_size_t<I-1>{});
}
};
};
//------------------------------------------------------------------------------
template<class... Bn>
buffers_cat_view<Bn...>::
const_iterator::
const_iterator(
detail::tuple<Bn...> const& bn,
std::true_type)
: bn_(&bn)
{
// one past the end
it_.template emplace<sizeof...(Bn)+1>();
}
template<class... Bn>
buffers_cat_view<Bn...>::
const_iterator::
const_iterator(
detail::tuple<Bn...> const& bn,
std::false_type)
: bn_(&bn)
{
it_.template emplace<1>(
net::buffer_sequence_begin(
detail::get<0>(*bn_)));
increment{*this}.next(
mp11::mp_size_t<1>{});
}
template<class... Bn>
bool
buffers_cat_view<Bn...>::
const_iterator::
operator==(const_iterator const& other) const
{
return bn_ == other.bn_ && it_ == other.it_;
}
template<class... Bn>
auto
buffers_cat_view<Bn...>::
const_iterator::
operator*() const ->
reference
{
return mp11::mp_with_index<
sizeof...(Bn) + 2>(
it_.index(),
dereference{*this});
}
template<class... Bn>
auto
buffers_cat_view<Bn...>::
const_iterator::
operator++() ->
const_iterator&
{
mp11::mp_with_index<
sizeof...(Bn) + 2>(
it_.index(),
increment{*this});
return *this;
}
template<class... Bn>
auto
buffers_cat_view<Bn...>::
const_iterator::
operator++(int) ->
const_iterator
{
auto temp = *this;
++(*this);
return temp;
}
template<class... Bn>
auto
buffers_cat_view<Bn...>::
const_iterator::
operator--() ->
const_iterator&
{
mp11::mp_with_index<
sizeof...(Bn) + 2>(
it_.index(),
decrement{*this});
return *this;
}
template<class... Bn>
auto
buffers_cat_view<Bn...>::
const_iterator::
operator--(int) ->
const_iterator
{
auto temp = *this;
--(*this);
return temp;
}
//------------------------------------------------------------------------------
template<class... Bn>
buffers_cat_view<Bn...>::
buffers_cat_view(Bn const&... bn)
: bn_(bn...)
{
}
template<class... Bn>
auto
buffers_cat_view<Bn...>::begin() const ->
const_iterator
{
return const_iterator{bn_, std::false_type{}};
}
template<class... Bn>
auto
buffers_cat_view<Bn...>::end() const->
const_iterator
{
return const_iterator{bn_, std::true_type{}};
}
} // beast
} // boost
#endif

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;
#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,296 @@
//
// 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/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(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;
}
char const* s;
switch(mode)
{
default:
case file_mode::read:
s = "rb";
break;
case file_mode::scan:
#ifdef BOOST_MSVC
s = "rbS";
#else
s = "rb";
#endif
break;
case file_mode::write:
s = "wb+";
break;
case file_mode::write_new:
{
#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
FILE* f0;
auto const ev = ::fopen_s(&f0, path, "rb");
if(! ev)
{
std::fclose(f0);
ec = make_error_code(errc::file_exists);
return;
}
else if(ev !=
errc::no_such_file_or_directory)
{
ec.assign(ev, generic_category());
return;
}
s = "wb";
#else
s = "wbx";
#endif
break;
}
case file_mode::write_existing:
s = "rb+";
break;
case file_mode::append:
s = "ab";
break;
case file_mode::append_existing:
{
#ifdef BOOST_MSVC
FILE* f0;
auto const ev =
::fopen_s(&f0, path, "rb+");
if(ev)
{
ec.assign(ev, generic_category());
return;
}
#else
auto const f0 =
std::fopen(path, "rb+");
if(! f0)
{
ec.assign(errno, generic_category());
return;
}
#endif
std::fclose(f0);
s = "ab";
break;
}
}
#ifdef BOOST_MSVC
auto const ev = ::fopen_s(&f_, path, s);
if(ev)
{
f_ = nullptr;
ec.assign(ev, generic_category());
return;
}
#else
f_ = std::fopen(path, s);
if(! f_)
{
ec.assign(errno, generic_category());
return;
}
#endif
ec = {};
}
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 > (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

View File

@@ -0,0 +1,353 @@
//
// 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_WIN32_IPP
#define BOOST_BEAST_CORE_IMPL_FILE_WIN32_IPP
#include <boost/beast/core/file_win32.hpp>
#if BOOST_BEAST_USE_WIN32_FILE
#include <boost/core/exchange.hpp>
#include <boost/winapi/access_rights.hpp>
#include <boost/winapi/error_codes.hpp>
#include <boost/winapi/file_management.hpp>
#include <boost/winapi/get_last_error.hpp>
#include <limits>
#include <utility>
namespace boost {
namespace beast {
namespace detail {
// VFALCO Can't seem to get boost/detail/winapi to work with
// this so use the non-Ex version for now.
BOOST_BEAST_DECL
boost::winapi::BOOL_
set_file_pointer_ex(
boost::winapi::HANDLE_ hFile,
boost::winapi::LARGE_INTEGER_ lpDistanceToMove,
boost::winapi::PLARGE_INTEGER_ lpNewFilePointer,
boost::winapi::DWORD_ dwMoveMethod)
{
auto dwHighPart = lpDistanceToMove.u.HighPart;
auto dwLowPart = boost::winapi::SetFilePointer(
hFile,
lpDistanceToMove.u.LowPart,
&dwHighPart,
dwMoveMethod);
if(dwLowPart == boost::winapi::INVALID_SET_FILE_POINTER_)
return 0;
if(lpNewFilePointer)
{
lpNewFilePointer->u.LowPart = dwLowPart;
lpNewFilePointer->u.HighPart = dwHighPart;
}
return 1;
}
} // detail
file_win32::
~file_win32()
{
if(h_ != boost::winapi::INVALID_HANDLE_VALUE_)
boost::winapi::CloseHandle(h_);
}
file_win32::
file_win32(file_win32&& other)
: h_(boost::exchange(other.h_,
boost::winapi::INVALID_HANDLE_VALUE_))
{
}
file_win32&
file_win32::
operator=(file_win32&& other)
{
if(&other == this)
return *this;
if(h_)
boost::winapi::CloseHandle(h_);
h_ = other.h_;
other.h_ = boost::winapi::INVALID_HANDLE_VALUE_;
return *this;
}
void
file_win32::
native_handle(native_handle_type h)
{
if(h_ != boost::winapi::INVALID_HANDLE_VALUE_)
boost::winapi::CloseHandle(h_);
h_ = h;
}
void
file_win32::
close(error_code& ec)
{
if(h_ != boost::winapi::INVALID_HANDLE_VALUE_)
{
if(! boost::winapi::CloseHandle(h_))
ec.assign(boost::winapi::GetLastError(),
system_category());
else
ec = {};
h_ = boost::winapi::INVALID_HANDLE_VALUE_;
}
else
{
ec = {};
}
}
void
file_win32::
open(char const* path, file_mode mode, error_code& ec)
{
if(h_ != boost::winapi::INVALID_HANDLE_VALUE_)
{
boost::winapi::CloseHandle(h_);
h_ = boost::winapi::INVALID_HANDLE_VALUE_;
}
boost::winapi::DWORD_ share_mode = 0;
boost::winapi::DWORD_ desired_access = 0;
boost::winapi::DWORD_ creation_disposition = 0;
boost::winapi::DWORD_ flags_and_attributes = 0;
/*
| When the file...
This argument: | Exists Does not exist
-------------------------+------------------------------------------------------
CREATE_ALWAYS | Truncates Creates
CREATE_NEW +-----------+ Fails Creates
OPEN_ALWAYS ===| does this |===> Opens Creates
OPEN_EXISTING +-----------+ Opens Fails
TRUNCATE_EXISTING | Truncates Fails
*/
switch(mode)
{
default:
case file_mode::read:
desired_access = boost::winapi::GENERIC_READ_;
share_mode = boost::winapi::FILE_SHARE_READ_;
creation_disposition = boost::winapi::OPEN_EXISTING_;
flags_and_attributes = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
break;
case file_mode::scan:
desired_access = boost::winapi::GENERIC_READ_;
share_mode = boost::winapi::FILE_SHARE_READ_;
creation_disposition = boost::winapi::OPEN_EXISTING_;
flags_and_attributes = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
break;
case file_mode::write:
desired_access = boost::winapi::GENERIC_READ_ |
boost::winapi::GENERIC_WRITE_;
creation_disposition = boost::winapi::CREATE_ALWAYS_;
flags_and_attributes = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
break;
case file_mode::write_new:
desired_access = boost::winapi::GENERIC_READ_ |
boost::winapi::GENERIC_WRITE_;
creation_disposition = boost::winapi::CREATE_NEW_;
flags_and_attributes = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
break;
case file_mode::write_existing:
desired_access = boost::winapi::GENERIC_READ_ |
boost::winapi::GENERIC_WRITE_;
creation_disposition = boost::winapi::OPEN_EXISTING_;
flags_and_attributes = 0x10000000; // FILE_FLAG_RANDOM_ACCESS
break;
case file_mode::append:
desired_access = boost::winapi::GENERIC_READ_ |
boost::winapi::GENERIC_WRITE_;
creation_disposition = boost::winapi::CREATE_ALWAYS_;
flags_and_attributes = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
break;
case file_mode::append_existing:
desired_access = boost::winapi::GENERIC_READ_ |
boost::winapi::GENERIC_WRITE_;
creation_disposition = boost::winapi::OPEN_EXISTING_;
flags_and_attributes = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
break;
}
h_ = ::CreateFileA(
path,
desired_access,
share_mode,
NULL,
creation_disposition,
flags_and_attributes,
NULL);
if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
ec.assign(boost::winapi::GetLastError(),
system_category());
else
ec = {};
}
std::uint64_t
file_win32::
size(error_code& ec) const
{
if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
{
ec = make_error_code(errc::bad_file_descriptor);
return 0;
}
boost::winapi::LARGE_INTEGER_ fileSize;
if(! boost::winapi::GetFileSizeEx(h_, &fileSize))
{
ec.assign(boost::winapi::GetLastError(),
system_category());
return 0;
}
ec = {};
return fileSize.QuadPart;
}
std::uint64_t
file_win32::
pos(error_code& ec)
{
if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
{
ec = make_error_code(errc::bad_file_descriptor);
return 0;
}
boost::winapi::LARGE_INTEGER_ in;
boost::winapi::LARGE_INTEGER_ out;
in.QuadPart = 0;
if(! detail::set_file_pointer_ex(h_, in, &out,
boost::winapi::FILE_CURRENT_))
{
ec.assign(boost::winapi::GetLastError(),
system_category());
return 0;
}
ec = {};
return out.QuadPart;
}
void
file_win32::
seek(std::uint64_t offset, error_code& ec)
{
if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
{
ec = make_error_code(errc::bad_file_descriptor);
return;
}
boost::winapi::LARGE_INTEGER_ in;
in.QuadPart = offset;
if(! detail::set_file_pointer_ex(h_, in, 0,
boost::winapi::FILE_BEGIN_))
{
ec.assign(boost::winapi::GetLastError(),
system_category());
return;
}
ec = {};
}
std::size_t
file_win32::
read(void* buffer, std::size_t n, error_code& ec)
{
if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
{
ec = make_error_code(errc::bad_file_descriptor);
return 0;
}
std::size_t nread = 0;
while(n > 0)
{
boost::winapi::DWORD_ amount;
if(n > (std::numeric_limits<
boost::winapi::DWORD_>::max)())
amount = (std::numeric_limits<
boost::winapi::DWORD_>::max)();
else
amount = static_cast<
boost::winapi::DWORD_>(n);
boost::winapi::DWORD_ bytesRead;
if(! ::ReadFile(h_, buffer, amount, &bytesRead, 0))
{
auto const dwError = boost::winapi::GetLastError();
if(dwError != boost::winapi::ERROR_HANDLE_EOF_)
ec.assign(dwError, system_category());
else
ec = {};
return nread;
}
if(bytesRead == 0)
return nread;
n -= bytesRead;
nread += bytesRead;
buffer = static_cast<char*>(buffer) + bytesRead;
}
ec = {};
return nread;
}
std::size_t
file_win32::
write(void const* buffer, std::size_t n, error_code& ec)
{
if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
{
ec = make_error_code(errc::bad_file_descriptor);
return 0;
}
std::size_t nwritten = 0;
while(n > 0)
{
boost::winapi::DWORD_ amount;
if(n > (std::numeric_limits<
boost::winapi::DWORD_>::max)())
amount = (std::numeric_limits<
boost::winapi::DWORD_>::max)();
else
amount = static_cast<
boost::winapi::DWORD_>(n);
boost::winapi::DWORD_ bytesWritten;
if(! ::WriteFile(h_, buffer, amount, &bytesWritten, 0))
{
auto const dwError = boost::winapi::GetLastError();
if(dwError != boost::winapi::ERROR_HANDLE_EOF_)
ec.assign(dwError, system_category());
else
ec = {};
return nwritten;
}
if(bytesWritten == 0)
return nwritten;
n -= bytesWritten;
nwritten += bytesWritten;
buffer = static_cast<char const*>(buffer) + bytesWritten;
}
ec = {};
return nwritten;
}
} // beast
} // boost
#endif
#endif

View File

@@ -0,0 +1,533 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_IMPL_FLAT_BUFFER_HPP
#define BOOST_BEAST_IMPL_FLAT_BUFFER_HPP
#include <boost/core/exchange.hpp>
#include <boost/assert.hpp>
#include <boost/throw_exception.hpp>
#include <memory>
#include <stdexcept>
namespace boost {
namespace beast {
/* Layout:
begin_ in_ out_ last_ end_
|<------->|<---------->|<---------->|<------->|
| readable | writable |
*/
template<class Allocator>
basic_flat_buffer<Allocator>::
~basic_flat_buffer()
{
if(! begin_)
return;
alloc_traits::deallocate(
this->get(), begin_, capacity());
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer() noexcept(default_nothrow)
: begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_(alloc_traits::max_size(
this->get()))
{
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer(
std::size_t limit) noexcept(default_nothrow)
: begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_(limit)
{
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer(Allocator const& alloc) noexcept
: boost::empty_value<base_alloc_type>(
boost::empty_init_t{}, alloc)
, begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_(alloc_traits::max_size(
this->get()))
{
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer(
std::size_t limit,
Allocator const& alloc) noexcept
: boost::empty_value<base_alloc_type>(
boost::empty_init_t{}, alloc)
, begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_(limit)
{
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer(basic_flat_buffer&& other) noexcept
: boost::empty_value<base_alloc_type>(
boost::empty_init_t{}, std::move(other.get()))
, begin_(boost::exchange(other.begin_, nullptr))
, in_(boost::exchange(other.in_, nullptr))
, out_(boost::exchange(other.out_, nullptr))
, last_(boost::exchange(other.last_, nullptr))
, end_(boost::exchange(other.end_, nullptr))
, max_(other.max_)
{
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer(
basic_flat_buffer&& other,
Allocator const& alloc)
: boost::empty_value<base_alloc_type>(
boost::empty_init_t{}, alloc)
{
if(this->get() != other.get())
{
begin_ = nullptr;
in_ = nullptr;
out_ = nullptr;
last_ = nullptr;
end_ = nullptr;
max_ = other.max_;
copy_from(other);
other.clear();
other.shrink_to_fit();
return;
}
begin_ = other.begin_;
in_ = other.in_;
out_ = other.out_;
last_ = other.out_; // invalidate
end_ = other.end_;
max_ = other.max_;
BOOST_ASSERT(
alloc_traits::max_size(this->get()) ==
alloc_traits::max_size(other.get()));
other.begin_ = nullptr;
other.in_ = nullptr;
other.out_ = nullptr;
other.last_ = nullptr;
other.end_ = nullptr;
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer(basic_flat_buffer const& other)
: boost::empty_value<base_alloc_type>(boost::empty_init_t{},
alloc_traits::select_on_container_copy_construction(
other.get()))
, begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_(other.max_)
{
copy_from(other);
}
template<class Allocator>
basic_flat_buffer<Allocator>::
basic_flat_buffer(
basic_flat_buffer const& other,
Allocator const& alloc)
: boost::empty_value<base_alloc_type>(
boost::empty_init_t{}, alloc)
, begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_(other.max_)
{
copy_from(other);
}
template<class Allocator>
template<class OtherAlloc>
basic_flat_buffer<Allocator>::
basic_flat_buffer(
basic_flat_buffer<OtherAlloc> const& other)
noexcept(default_nothrow)
: begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_(other.max_)
{
copy_from(other);
}
template<class Allocator>
template<class OtherAlloc>
basic_flat_buffer<Allocator>::
basic_flat_buffer(
basic_flat_buffer<OtherAlloc> const& other,
Allocator const& alloc)
: boost::empty_value<base_alloc_type>(
boost::empty_init_t{}, alloc)
, begin_(nullptr)
, in_(nullptr)
, out_(nullptr)
, last_(nullptr)
, end_(nullptr)
, max_(other.max_)
{
copy_from(other);
}
template<class Allocator>
auto
basic_flat_buffer<Allocator>::
operator=(basic_flat_buffer&& other) noexcept ->
basic_flat_buffer&
{
if(this == &other)
return *this;
move_assign(other, pocma{});
return *this;
}
template<class Allocator>
auto
basic_flat_buffer<Allocator>::
operator=(basic_flat_buffer const& other) ->
basic_flat_buffer&
{
if(this == &other)
return *this;
copy_assign(other, pocca{});
return *this;
}
template<class Allocator>
template<class OtherAlloc>
auto
basic_flat_buffer<Allocator>::
operator=(
basic_flat_buffer<OtherAlloc> const& other) ->
basic_flat_buffer&
{
copy_from(other);
return *this;
}
template<class Allocator>
void
basic_flat_buffer<Allocator>::
reserve(std::size_t n)
{
if(max_ < n)
max_ = n;
if(n > capacity())
prepare(n - size());
}
template<class Allocator>
void
basic_flat_buffer<Allocator>::
shrink_to_fit()
{
auto const len = size();
if(len == capacity())
return;
char* p;
if(len > 0)
{
BOOST_ASSERT(begin_);
BOOST_ASSERT(in_);
p = alloc(len);
std::memcpy(p, in_, len);
}
else
{
p = nullptr;
}
alloc_traits::deallocate(
this->get(), begin_, this->capacity());
begin_ = p;
in_ = begin_;
out_ = begin_ + len;
last_ = out_;
end_ = out_;
}
template<class Allocator>
void
basic_flat_buffer<Allocator>::
clear() noexcept
{
in_ = begin_;
out_ = begin_;
last_ = begin_;
}
//------------------------------------------------------------------------------
template<class Allocator>
auto
basic_flat_buffer<Allocator>::
prepare(std::size_t n) ->
mutable_buffers_type
{
auto const len = size();
if(len > max_ || n > (max_ - len))
BOOST_THROW_EXCEPTION(std::length_error{
"basic_flat_buffer too long"});
if(n <= dist(out_, end_))
{
// existing capacity is sufficient
last_ = out_ + n;
return{out_, n};
}
if(n <= capacity() - len)
{
// after a memmove,
// existing capacity is sufficient
if(len > 0)
{
BOOST_ASSERT(begin_);
BOOST_ASSERT(in_);
std::memmove(begin_, in_, len);
}
in_ = begin_;
out_ = in_ + len;
last_ = out_ + n;
return {out_, n};
}
// allocate a new buffer
auto const new_size = (std::min<std::size_t>)(
max_,
(std::max<std::size_t>)(2 * len, len + n));
auto const p = alloc(new_size);
if(begin_)
{
BOOST_ASSERT(p);
BOOST_ASSERT(in_);
std::memcpy(p, in_, len);
alloc_traits::deallocate(
this->get(), begin_, capacity());
}
begin_ = p;
in_ = begin_;
out_ = in_ + len;
last_ = out_ + n;
end_ = begin_ + new_size;
return {out_, n};
}
template<class Allocator>
void
basic_flat_buffer<Allocator>::
consume(std::size_t n) noexcept
{
if(n >= dist(in_, out_))
{
in_ = begin_;
out_ = begin_;
return;
}
in_ += n;
}
//------------------------------------------------------------------------------
template<class Allocator>
template<class OtherAlloc>
void
basic_flat_buffer<Allocator>::
copy_from(
basic_flat_buffer<OtherAlloc> const& other)
{
std::size_t const n = other.size();
if(n == 0 || n > capacity())
{
if(begin_ != nullptr)
{
alloc_traits::deallocate(
this->get(), begin_,
this->capacity());
begin_ = nullptr;
in_ = nullptr;
out_ = nullptr;
last_ = nullptr;
end_ = nullptr;
}
if(n == 0)
return;
begin_ = alloc(n);
in_ = begin_;
out_ = begin_ + n;
last_ = begin_ + n;
end_ = begin_ + n;
}
in_ = begin_;
out_ = begin_ + n;
last_ = begin_ + n;
if(begin_)
{
BOOST_ASSERT(other.begin_);
std::memcpy(begin_, other.begin_, n);
}
}
template<class Allocator>
void
basic_flat_buffer<Allocator>::
move_assign(basic_flat_buffer& other, std::true_type)
{
clear();
shrink_to_fit();
this->get() = std::move(other.get());
begin_ = other.begin_;
in_ = other.in_;
out_ = other.out_;
last_ = out_;
end_ = other.end_;
max_ = other.max_;
other.begin_ = nullptr;
other.in_ = nullptr;
other.out_ = nullptr;
other.last_ = nullptr;
other.end_ = nullptr;
}
template<class Allocator>
void
basic_flat_buffer<Allocator>::
move_assign(basic_flat_buffer& other, std::false_type)
{
if(this->get() != other.get())
{
copy_from(other);
other.clear();
other.shrink_to_fit();
}
else
{
move_assign(other, std::true_type{});
}
}
template<class Allocator>
void
basic_flat_buffer<Allocator>::
copy_assign(basic_flat_buffer const& other, std::true_type)
{
max_ = other.max_;
this->get() = other.get();
copy_from(other);
}
template<class Allocator>
void
basic_flat_buffer<Allocator>::
copy_assign(basic_flat_buffer const& other, std::false_type)
{
clear();
shrink_to_fit();
max_ = other.max_;
copy_from(other);
}
template<class Allocator>
void
basic_flat_buffer<Allocator>::
swap(basic_flat_buffer& other)
{
swap(other, typename
alloc_traits::propagate_on_container_swap{});
}
template<class Allocator>
void
basic_flat_buffer<Allocator>::
swap(basic_flat_buffer& other, std::true_type)
{
using std::swap;
swap(this->get(), other.get());
swap(max_, other.max_);
swap(begin_, other.begin_);
swap(in_, other.in_);
swap(out_, other.out_);
last_ = this->out_;
other.last_ = other.out_;
swap(end_, other.end_);
}
template<class Allocator>
void
basic_flat_buffer<Allocator>::
swap(basic_flat_buffer& other, std::false_type)
{
BOOST_ASSERT(this->get() == other.get());
using std::swap;
swap(max_, other.max_);
swap(begin_, other.begin_);
swap(in_, other.in_);
swap(out_, other.out_);
last_ = this->out_;
other.last_ = other.out_;
swap(end_, other.end_);
}
template<class Allocator>
void
swap(
basic_flat_buffer<Allocator>& lhs,
basic_flat_buffer<Allocator>& rhs)
{
lhs.swap(rhs);
}
template<class Allocator>
char*
basic_flat_buffer<Allocator>::
alloc(std::size_t n)
{
if(n > alloc_traits::max_size(this->get()))
BOOST_THROW_EXCEPTION(std::length_error(
"A basic_flat_buffer exceeded the allocator's maximum size"));
return alloc_traits::allocate(this->get(), n);
}
} // beast
} // boost
#endif

View File

@@ -0,0 +1,43 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_HPP
#define BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_HPP
namespace boost {
namespace beast {
template<std::size_t N>
flat_static_buffer<N>::
flat_static_buffer(
flat_static_buffer const& other)
: flat_static_buffer_base(buf_, N)
{
this->commit(net::buffer_copy(
this->prepare(other.size()), other.data()));
}
template<std::size_t N>
auto
flat_static_buffer<N>::
operator=(flat_static_buffer const& other) ->
flat_static_buffer<N>&
{
if(this == &other)
return *this;
this->consume(this->size());
this->commit(net::buffer_copy(
this->prepare(other.size()), other.data()));
return *this;
}
} // beast
} // boost
#endif

View File

@@ -0,0 +1,89 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_IPP
#define BOOST_BEAST_IMPL_FLAT_STATIC_BUFFER_IPP
#include <boost/beast/core/flat_static_buffer.hpp>
#include <boost/throw_exception.hpp>
#include <algorithm>
#include <cstring>
#include <iterator>
#include <memory>
#include <stdexcept>
namespace boost {
namespace beast {
/* Layout:
begin_ in_ out_ last_ end_
|<------->|<---------->|<---------->|<------->|
| readable | writable |
*/
void
flat_static_buffer_base::
clear() noexcept
{
in_ = begin_;
out_ = begin_;
last_ = begin_;
}
auto
flat_static_buffer_base::
prepare(std::size_t n) ->
mutable_buffers_type
{
if(n <= dist(out_, end_))
{
last_ = out_ + n;
return {out_, n};
}
auto const len = size();
if(n > capacity() - len)
BOOST_THROW_EXCEPTION(std::length_error{
"buffer overflow"});
if(len > 0)
std::memmove(begin_, in_, len);
in_ = begin_;
out_ = in_ + len;
last_ = out_ + n;
return {out_, n};
}
void
flat_static_buffer_base::
consume(std::size_t n) noexcept
{
if(n >= size())
{
in_ = begin_;
out_ = in_;
return;
}
in_ += n;
}
void
flat_static_buffer_base::
reset(void* p, std::size_t n) noexcept
{
begin_ = static_cast<char*>(p);
in_ = begin_;
out_ = begin_;
last_ = begin_;
end_ = begin_ + n;
}
} // beast
} // boost
#endif

View File

@@ -0,0 +1,276 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_IMPL_FLAT_STREAM_HPP
#define BOOST_BEAST_CORE_IMPL_FLAT_STREAM_HPP
#include <boost/beast/core/async_base.hpp>
#include <boost/beast/core/buffers_prefix.hpp>
#include <boost/beast/core/static_buffer.hpp>
#include <boost/beast/core/stream_traits.hpp>
#include <boost/beast/websocket/teardown.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/coroutine.hpp>
#include <memory>
namespace boost {
namespace beast {
template<class NextLayer>
struct flat_stream<NextLayer>::ops
{
template<class Handler>
class write_op
: public async_base<Handler,
beast::executor_type<flat_stream>>
, public net::coroutine
{
public:
template<
class ConstBufferSequence,
class Handler_>
write_op(
Handler_&& h,
flat_stream<NextLayer>& s,
ConstBufferSequence const& b)
: async_base<Handler,
beast::executor_type<flat_stream>>(
std::forward<Handler_>(h),
s.get_executor())
{
auto const result =
flatten(b, max_size);
if(result.flatten)
{
s.buffer_.clear();
s.buffer_.commit(net::buffer_copy(
s.buffer_.prepare(result.size),
b, result.size));
s.stream_.async_write_some(
s.buffer_.data(), std::move(*this));
}
else
{
s.buffer_.clear();
s.buffer_.shrink_to_fit();
s.stream_.async_write_some(
beast::buffers_prefix(
result.size, b), std::move(*this));
}
}
void
operator()(
boost::system::error_code ec,
std::size_t bytes_transferred)
{
this->complete_now(ec, bytes_transferred);
}
};
struct run_write_op
{
template<class WriteHandler, class Buffers>
void
operator()(
WriteHandler&& h,
flat_stream* s,
Buffers const& b)
{
// If you get an error on the following line it means
// that your handler does not meet the documented type
// requirements for the handler.
static_assert(
beast::detail::is_invocable<WriteHandler,
void(error_code, std::size_t)>::value,
"WriteHandler type requirements not met");
write_op<
typename std::decay<WriteHandler>::type>(
std::forward<WriteHandler>(h), *s, b);
}
};
};
//------------------------------------------------------------------------------
template<class NextLayer>
template<class... Args>
flat_stream<NextLayer>::
flat_stream(Args&&... args)
: stream_(std::forward<Args>(args)...)
{
}
template<class NextLayer>
template<class MutableBufferSequence>
std::size_t
flat_stream<NextLayer>::
read_some(MutableBufferSequence const& buffers)
{
static_assert(boost::beast::is_sync_read_stream<next_layer_type>::value,
"SyncReadStream type requirements not met");
static_assert(net::is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence type requirements not met");
error_code ec;
auto n = read_some(buffers, ec);
if(ec)
BOOST_THROW_EXCEPTION(boost::system::system_error{ec});
return n;
}
template<class NextLayer>
template<class MutableBufferSequence>
std::size_t
flat_stream<NextLayer>::
read_some(MutableBufferSequence const& buffers, error_code& ec)
{
static_assert(boost::beast::is_sync_read_stream<next_layer_type>::value,
"SyncReadStream type requirements not met");
static_assert(net::is_mutable_buffer_sequence<
MutableBufferSequence>::value,
"MutableBufferSequence type requirements not met");
return stream_.read_some(buffers, ec);
}
template<class NextLayer>
template<
class MutableBufferSequence,
class ReadHandler>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
flat_stream<NextLayer>::
async_read_some(
MutableBufferSequence const& buffers,
ReadHandler&& handler)
{
static_assert(boost::beast::is_async_read_stream<next_layer_type>::value,
"AsyncReadStream type requirements not met");
static_assert(net::is_mutable_buffer_sequence<
MutableBufferSequence >::value,
"MutableBufferSequence type requirements not met");
return stream_.async_read_some(
buffers, std::forward<ReadHandler>(handler));
}
template<class NextLayer>
template<class ConstBufferSequence>
std::size_t
flat_stream<NextLayer>::
write_some(ConstBufferSequence const& buffers)
{
static_assert(boost::beast::is_sync_write_stream<next_layer_type>::value,
"SyncWriteStream type requirements not met");
static_assert(net::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence type requirements not met");
error_code ec;
auto n = write_some(buffers, ec);
if(ec)
BOOST_THROW_EXCEPTION(boost::system::system_error{ec});
return n;
}
template<class NextLayer>
template<class ConstBufferSequence>
std::size_t
flat_stream<NextLayer>::
stack_write_some(
std::size_t size,
ConstBufferSequence const& buffers,
error_code& ec)
{
static_buffer<max_stack> b;
b.commit(net::buffer_copy(
b.prepare(size), buffers));
return stream_.write_some(b.data(), ec);
}
template<class NextLayer>
template<class ConstBufferSequence>
std::size_t
flat_stream<NextLayer>::
write_some(ConstBufferSequence const& buffers, error_code& ec)
{
static_assert(boost::beast::is_sync_write_stream<next_layer_type>::value,
"SyncWriteStream type requirements not met");
static_assert(net::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence type requirements not met");
auto const result = flatten(buffers, max_size);
if(result.flatten)
{
if(result.size <= max_stack)
return stack_write_some(result.size, buffers, ec);
buffer_.clear();
buffer_.commit(net::buffer_copy(
buffer_.prepare(result.size),
buffers));
return stream_.write_some(buffer_.data(), ec);
}
buffer_.clear();
buffer_.shrink_to_fit();
return stream_.write_some(
boost::beast::buffers_prefix(result.size, buffers), ec);
}
template<class NextLayer>
template<
class ConstBufferSequence,
class WriteHandler>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
flat_stream<NextLayer>::
async_write_some(
ConstBufferSequence const& buffers,
WriteHandler&& handler)
{
static_assert(boost::beast::is_async_write_stream<next_layer_type>::value,
"AsyncWriteStream type requirements not met");
static_assert(net::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence type requirements not met");
return net::async_initiate<
WriteHandler,
void(error_code, std::size_t)>(
typename ops::run_write_op{},
handler,
this,
buffers);
}
template<class NextLayer>
void
teardown(
boost::beast::role_type role,
flat_stream<NextLayer>& s,
error_code& ec)
{
using boost::beast::websocket::teardown;
teardown(role, s.next_layer(), ec);
}
template<class NextLayer, class TeardownHandler>
void
async_teardown(
boost::beast::role_type role,
flat_stream<NextLayer>& s,
TeardownHandler&& handler)
{
using boost::beast::websocket::async_teardown;
async_teardown(role, s.next_layer(), std::move(handler));
}
} // beast
} // boost
#endif

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_IMPL_HANDLER_PTR_HPP
#define BOOST_BEAST_IMPL_HANDLER_PTR_HPP
#include <boost/asio/associated_allocator.hpp>
#include <boost/assert.hpp>
#include <memory>
namespace boost {
namespace beast {
template<class T, class Handler>
void
handler_ptr<T, Handler>::
clear()
{
using A = typename detail::allocator_traits<
net::associated_allocator_t<
Handler>>::template rebind_alloc<T>;
using alloc_traits =
beast::detail::allocator_traits<A>;
A alloc(
net::get_associated_allocator(handler()));
alloc_traits::destroy(alloc, t_);
alloc_traits::deallocate(alloc, t_, 1);
t_ = nullptr;
}
template<class T, class Handler>
handler_ptr<T, Handler>::
~handler_ptr()
{
if(t_)
{
clear();
h_.~Handler();
}
}
template<class T, class Handler>
handler_ptr<T, Handler>::
handler_ptr(handler_ptr&& other)
: t_(other.t_)
{
if(other.t_)
{
::new(static_cast<void*>(std::addressof(h_)))
Handler(std::move(other.h_));
other.h_.~Handler();
other.t_ = nullptr;
}
}
template<class T, class Handler>
template<class DeducedHandler, class... Args>
handler_ptr<T, Handler>::
handler_ptr(DeducedHandler&& h, Args&&... args)
{
BOOST_STATIC_ASSERT(! std::is_array<T>::value);
using A = typename detail::allocator_traits<
net::associated_allocator_t<
Handler>>::template rebind_alloc<T>;
using alloc_traits =
beast::detail::allocator_traits<A>;
A alloc{net::get_associated_allocator(h)};
bool destroy = false;
auto deleter = [&alloc, &destroy](T* p)
{
if(destroy)
alloc_traits::destroy(alloc, p);
alloc_traits::deallocate(alloc, p, 1);
};
std::unique_ptr<T, decltype(deleter)> t{
alloc_traits::allocate(alloc, 1), deleter};
alloc_traits::construct(alloc, t.get(),
static_cast<DeducedHandler const&>(h),
std::forward<Args>(args)...);
destroy = true;
::new(static_cast<void*>(std::addressof(h_)))
Handler(std::forward<DeducedHandler>(h));
t_ = t.release();
}
template<class T, class Handler>
auto
handler_ptr<T, Handler>::
release_handler() ->
handler_type
{
BOOST_ASSERT(t_);
clear();
auto deleter = [](Handler* h)
{
h->~Handler();
};
std::unique_ptr<
Handler, decltype(deleter)> destroyer{
std::addressof(h_), deleter};
return std::move(h_);
}
template<class T, class Handler>
template<class... Args>
void
handler_ptr<T, Handler>::
invoke(Args&&... args)
{
BOOST_ASSERT(t_);
clear();
auto deleter = [](Handler* h)
{
boost::ignore_unused(h); // fix #1119
h->~Handler();
};
std::unique_ptr<
Handler, decltype(deleter)> destroyer{
std::addressof(h_), deleter};
h_(std::forward<Args>(args)...);
}
} // beast
} // boost
#endif

File diff suppressed because it is too large Load Diff

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_IMPL_READ_SIZE_HPP
#define BOOST_BEAST_IMPL_READ_SIZE_HPP
#include <boost/asio/buffer.hpp>
#include <boost/assert.hpp>
#include <stdexcept>
#include <type_traits>
namespace boost {
namespace beast {
namespace detail {
template<class T, class = void>
struct has_read_size_helper : std::false_type {};
template<class T>
struct has_read_size_helper<T, decltype(
read_size_helper(std::declval<T&>(), 512),
(void)0)> : std::true_type
{
};
template<class DynamicBuffer>
std::size_t
read_size(DynamicBuffer& buffer,
std::size_t max_size, std::true_type)
{
return read_size_helper(buffer, max_size);
}
template<class DynamicBuffer>
std::size_t
read_size(DynamicBuffer& buffer,
std::size_t max_size, std::false_type)
{
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer type requirements not met");
BOOST_ASSERT(max_size >= 1);
auto const size = buffer.size();
auto const limit = buffer.max_size() - size;
BOOST_ASSERT(size <= buffer.max_size());
return std::min<std::size_t>(
std::max<std::size_t>(512, buffer.capacity() - size),
std::min<std::size_t>(max_size, limit));
}
} // detail
template<class DynamicBuffer>
std::size_t
read_size(
DynamicBuffer& buffer, std::size_t max_size)
{
return detail::read_size(buffer, max_size,
detail::has_read_size_helper<DynamicBuffer>{});
}
template<class DynamicBuffer>
std::size_t
read_size_or_throw(
DynamicBuffer& buffer, std::size_t max_size)
{
auto const n = read_size(buffer, max_size);
if(n == 0)
BOOST_THROW_EXCEPTION(std::length_error{
"buffer overflow"});
return n;
}
} // beast
} // boost
#endif

View File

@@ -0,0 +1,151 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_IMPL_SAVED_HANDLER_HPP
#define BOOST_BEAST_CORE_IMPL_SAVED_HANDLER_HPP
#include <boost/beast/core/detail/allocator.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/assert.hpp>
#include <boost/core/empty_value.hpp>
#include <boost/core/exchange.hpp>
#include <utility>
namespace boost {
namespace beast {
//------------------------------------------------------------------------------
class saved_handler::base
{
protected:
~base() = default;
public:
base() = default;
virtual void destroy() = 0;
virtual void invoke() = 0;
};
//------------------------------------------------------------------------------
template<class Handler, class Alloc>
class saved_handler::impl final : public base
{
using alloc_type = typename
beast::detail::allocator_traits<
Alloc>::template rebind_alloc<impl>;
using alloc_traits =
beast::detail::allocator_traits<alloc_type>;
struct ebo_pair : boost::empty_value<alloc_type>
{
Handler h;
template<class Handler_>
ebo_pair(
alloc_type const& a,
Handler_&& h_)
: boost::empty_value<alloc_type>(
boost::empty_init_t{}, a)
, h(std::forward<Handler_>(h_))
{
}
};
ebo_pair v_;
net::executor_work_guard<
net::associated_executor_t<Handler>> wg2_;
public:
template<class Handler_>
impl(alloc_type const& a, Handler_&& h)
: v_(a, std::forward<Handler_>(h))
, wg2_(net::get_associated_executor(v_.h))
{
}
void
destroy() override
{
auto v = std::move(v_);
alloc_traits::destroy(v.get(), this);
alloc_traits::deallocate(v.get(), this, 1);
}
void
invoke() override
{
auto v = std::move(v_);
alloc_traits::destroy(v.get(), this);
alloc_traits::deallocate(v.get(), this, 1);
v.h();
}
};
//------------------------------------------------------------------------------
template<class Handler, class Allocator>
void
saved_handler::
emplace(Handler&& handler, Allocator const& alloc)
{
// Can't delete a handler before invoking
BOOST_ASSERT(! has_value());
using handler_type =
typename std::decay<Handler>::type;
using alloc_type = typename
detail::allocator_traits<Allocator>::
template rebind_alloc<impl<
handler_type, Allocator>>;
using alloc_traits =
beast::detail::allocator_traits<alloc_type>;
struct storage
{
alloc_type a;
impl<Handler, Allocator>* p;
explicit
storage(Allocator const& a_)
: a(a_)
, p(alloc_traits::allocate(a, 1))
{
}
~storage()
{
if(p)
alloc_traits::deallocate(a, p, 1);
}
};
storage s(alloc);
alloc_traits::construct(s.a, s.p,
s.a, std::forward<Handler>(handler));
p_ = boost::exchange(s.p, nullptr);
}
template<class Handler>
void
saved_handler::
emplace(Handler&& handler)
{
// Can't delete a handler before invoking
BOOST_ASSERT(! has_value());
emplace(
std::forward<Handler>(handler),
net::get_associated_allocator(handler));
}
} // beast
} // boost
#endif

View File

@@ -0,0 +1,76 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_IMPL_SAVED_HANDLER_IPP
#define BOOST_BEAST_CORE_IMPL_SAVED_HANDLER_IPP
#include <boost/beast/core/saved_handler.hpp>
#include <boost/core/exchange.hpp>
namespace boost {
namespace beast {
saved_handler::
~saved_handler()
{
if(p_)
p_->destroy();
}
saved_handler::
saved_handler(saved_handler&& other) noexcept
: p_(boost::exchange(other.p_, nullptr))
{
}
saved_handler&
saved_handler::
operator=(saved_handler&& other) noexcept
{
// Can't delete a handler before invoking
BOOST_ASSERT(! has_value());
p_ = boost::exchange(other.p_, nullptr);
return *this;
}
bool
saved_handler::
reset() noexcept
{
if(! p_)
return false;
boost::exchange(p_, nullptr)->destroy();
return true;
}
void
saved_handler::
invoke()
{
// Can't invoke without a value
BOOST_ASSERT(has_value());
boost::exchange(
p_, nullptr)->invoke();
}
bool
saved_handler::
maybe_invoke()
{
if(! p_)
return false;
boost::exchange(
p_, nullptr)->invoke();
return true;
}
} // beast
} // boost
#endif

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_IMPL_STATIC_BUFFER_HPP
#define BOOST_BEAST_IMPL_STATIC_BUFFER_HPP
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/throw_exception.hpp>
#include <algorithm>
#include <cstring>
#include <iterator>
#include <stdexcept>
namespace boost {
namespace beast {
template<std::size_t N>
static_buffer<N>::
static_buffer(static_buffer const& other) noexcept
: static_buffer_base(buf_, N)
{
this->commit(net::buffer_copy(
this->prepare(other.size()), other.data()));
}
template<std::size_t N>
auto
static_buffer<N>::
operator=(static_buffer const& other) noexcept ->
static_buffer<N>&
{
if(this == &other)
return *this;
this->consume(this->size());
this->commit(net::buffer_copy(
this->prepare(other.size()), other.data()));
return *this;
}
} // beast
} // boost
#endif

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_IMPL_STATIC_BUFFER_IPP
#define BOOST_BEAST_IMPL_STATIC_BUFFER_IPP
#include <boost/beast/core/static_buffer.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/throw_exception.hpp>
#include <algorithm>
#include <cstring>
#include <iterator>
#include <stdexcept>
namespace boost {
namespace beast {
static_buffer_base::
static_buffer_base(
void* p, std::size_t size) noexcept
: begin_(static_cast<char*>(p))
, capacity_(size)
{
}
void
static_buffer_base::
clear() noexcept
{
in_off_ = 0;
in_size_ = 0;
out_size_ = 0;
}
auto
static_buffer_base::
data() const noexcept ->
const_buffers_type
{
if(in_off_ + in_size_ <= capacity_)
return {
net::const_buffer{
begin_ + in_off_, in_size_},
net::const_buffer{
begin_, 0}};
return {
net::const_buffer{
begin_ + in_off_, capacity_ - in_off_},
net::const_buffer{
begin_, in_size_ - (capacity_ - in_off_)}};
}
auto
static_buffer_base::
data() noexcept ->
mutable_data_type
{
if(in_off_ + in_size_ <= capacity_)
return {
net::mutable_buffer{
begin_ + in_off_, in_size_},
net::mutable_buffer{
begin_, 0}};
return {
net::mutable_buffer{
begin_ + in_off_, capacity_ - in_off_},
net::mutable_buffer{
begin_, in_size_ - (capacity_ - in_off_)}};
}
auto
static_buffer_base::
prepare(std::size_t n) ->
mutable_buffers_type
{
using net::mutable_buffer;
if(n > capacity_ - in_size_)
BOOST_THROW_EXCEPTION(std::length_error{
"static_buffer overflow"});
out_size_ = n;
auto const out_off =
(in_off_ + in_size_) % capacity_;
if(out_off + out_size_ <= capacity_ )
return {
net::mutable_buffer{
begin_ + out_off, out_size_},
net::mutable_buffer{
begin_, 0}};
return {
net::mutable_buffer{
begin_ + out_off, capacity_ - out_off},
net::mutable_buffer{
begin_, out_size_ - (capacity_ - out_off)}};
}
void
static_buffer_base::
commit(std::size_t n) noexcept
{
in_size_ += (std::min)(n, out_size_);
out_size_ = 0;
}
void
static_buffer_base::
consume(std::size_t n) noexcept
{
if(n < in_size_)
{
in_off_ = (in_off_ + n) % capacity_;
in_size_ -= n;
}
else
{
// rewind the offset, so the next call to prepare
// can have a longer contiguous segment. this helps
// algorithms optimized for larger buffers.
in_off_ = 0;
in_size_ = 0;
}
}
} // beast
} // boost
#endif

View File

@@ -0,0 +1,642 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_IMPL_STATIC_STRING_HPP
#define BOOST_BEAST_IMPL_STATIC_STRING_HPP
#include <boost/beast/core/detail/static_string.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/throw_exception.hpp>
namespace boost {
namespace beast {
//
// (constructor)
//
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string()
{
n_ = 0;
term();
}
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string(size_type count, CharT ch)
{
assign(count, ch);
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
static_string<N, CharT, Traits>::
static_string(static_string<M, CharT, Traits> const& other,
size_type pos)
{
assign(other, pos);
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
static_string<N, CharT, Traits>::
static_string(static_string<M, CharT, Traits> const& other,
size_type pos, size_type count)
{
assign(other, pos, count);
}
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string(CharT const* s, size_type count)
{
assign(s, count);
}
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string(CharT const* s)
{
auto const count = Traits::length(s);
if(count > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"count > max_size()"});
n_ = count;
Traits::copy(&s_[0], s, n_ + 1);
}
template<std::size_t N, class CharT, class Traits>
template<class InputIt>
static_string<N, CharT, Traits>::
static_string(InputIt first, InputIt last)
{
assign(first, last);
}
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string(static_string const& s)
{
assign(s);
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
static_string<N, CharT, Traits>::
static_string(static_string<M, CharT, Traits> const& s)
{
assign(s);
}
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string(std::initializer_list<CharT> init)
{
assign(init.begin(), init.end());
}
template<std::size_t N, class CharT, class Traits>
static_string<N, CharT, Traits>::
static_string(string_view_type sv)
{
assign(sv);
}
template<std::size_t N, class CharT, class Traits>
template<class T, class>
static_string<N, CharT, Traits>::
static_string(T const& t, size_type pos, size_type n)
{
assign(t, pos, n);
}
//
// (assignment)
//
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
operator=(CharT const* s) ->
static_string&
{
auto const count = Traits::length(s);
if(count > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"count > max_size()"});
n_ = count;
Traits::copy(&s_[0], s, n_ + 1);
return *this;
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
assign(size_type count, CharT ch) ->
static_string&
{
if(count > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"count > max_size()"});
n_ = count;
Traits::assign(&s_[0], n_, ch);
term();
return *this;
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
assign(static_string const& str) ->
static_string&
{
n_ = str.n_;
auto const n = n_ + 1;
BOOST_BEAST_ASSUME(n != 0);
Traits::copy(&s_[0], &str.s_[0], n);
return *this;
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
auto
static_string<N, CharT, Traits>::
assign(static_string<M, CharT, Traits> const& str,
size_type pos, size_type count) ->
static_string&
{
auto const ss = str.substr(pos, count);
return assign(ss.data(), ss.size());
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
assign(CharT const* s, size_type count) ->
static_string&
{
if(count > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"count > max_size()"});
n_ = count;
Traits::copy(&s_[0], s, n_);
term();
return *this;
}
template<std::size_t N, class CharT, class Traits>
template<class InputIt>
auto
static_string<N, CharT, Traits>::
assign(InputIt first, InputIt last) ->
static_string&
{
std::size_t const n = std::distance(first, last);
if(n > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"n > max_size()"});
n_ = n;
for(auto it = &s_[0]; first != last; ++it, ++first)
Traits::assign(*it, *first);
term();
return *this;
}
template<std::size_t N, class CharT, class Traits>
template<class T>
auto
static_string<N, CharT, Traits>::
assign(T const& t, size_type pos, size_type count) ->
typename std::enable_if<std::is_convertible<T,
string_view_type>::value, static_string&>::type
{
auto const sv = string_view_type(t).substr(pos, count);
if(sv.size() > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"sv.size() > max_size()"});
n_ = sv.size();
Traits::copy(&s_[0], &sv[0], n_);
term();
return *this;
}
//
// Element access
//
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
at(size_type pos) ->
reference
{
if(pos >= size())
BOOST_THROW_EXCEPTION(std::out_of_range{
"pos >= size()"});
return s_[pos];
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
at(size_type pos) const ->
const_reference
{
if(pos >= size())
BOOST_THROW_EXCEPTION(std::out_of_range{
"pos >= size()"});
return s_[pos];
}
//
// Capacity
//
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
reserve(std::size_t n)
{
if(n > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"n > max_size()"});
}
//
// Operations
//
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
clear()
{
n_ = 0;
term();
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
insert(size_type index, size_type count, CharT ch) ->
static_string&
{
if(index > size())
BOOST_THROW_EXCEPTION(std::out_of_range{
"index > size()"});
insert(begin() + index, count, ch);
return *this;
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
insert(size_type index, CharT const* s, size_type count) ->
static_string&
{
if(index > size())
BOOST_THROW_EXCEPTION(std::out_of_range{
"index > size()"});
if(size() + count > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"size() + count > max_size()"});
Traits::move(
&s_[index + count], &s_[index], size() - index);
n_ += count;
Traits::copy(&s_[index], s, count);
term();
return *this;
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
auto
static_string<N, CharT, Traits>::
insert(size_type index,
static_string<M, CharT, Traits> const& str,
size_type index_str, size_type count) ->
static_string&
{
auto const ss = str.substr(index_str, count);
return insert(index, ss.data(), ss.size());
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
insert(const_iterator pos, size_type count, CharT ch) ->
iterator
{
if(size() + count > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"size() + count() > max_size()"});
auto const index = pos - &s_[0];
Traits::move(
&s_[index + count], &s_[index], size() - index);
n_ += count;
Traits::assign(&s_[index], count, ch);
term();
return &s_[index];
}
template<std::size_t N, class CharT, class Traits>
template<class InputIt>
auto
static_string<N, CharT, Traits>::
insert(const_iterator pos, InputIt first, InputIt last) ->
typename std::enable_if<
detail::is_input_iterator<InputIt>::value,
iterator>::type
{
std::size_t const count = std::distance(first, last);
if(size() + count > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"size() + count > max_size()"});
std::size_t const index = pos - begin();
Traits::move(
&s_[index + count], &s_[index], size() - index);
n_ += count;
for(auto it = begin() + index;
first != last; ++it, ++first)
Traits::assign(*it, *first);
term();
return begin() + index;
}
template<std::size_t N, class CharT, class Traits>
template<class T>
auto
static_string<N, CharT, Traits>::
insert(size_type index, const T& t,
size_type index_str, size_type count) ->
typename std::enable_if<std::is_convertible<
T const&, string_view_type>::value &&
! std::is_convertible<T const&, CharT const*>::value,
static_string&>::type
{
auto const str =
string_view_type(t).substr(index_str, count);
return insert(index, str.data(), str.size());
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
erase(size_type index, size_type count) ->
static_string&
{
if(index > size())
BOOST_THROW_EXCEPTION(std::out_of_range{
"index > size()"});
auto const n = (std::min)(count, size() - index);
Traits::move(
&s_[index], &s_[index + n], size() - (index + n) + 1);
n_ -= n;
return *this;
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
erase(const_iterator pos) ->
iterator
{
erase(pos - begin(), 1);
return begin() + (pos - begin());
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
erase(const_iterator first, const_iterator last) ->
iterator
{
erase(first - begin(),
std::distance(first, last));
return begin() + (first - begin());
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
push_back(CharT ch)
{
if(size() >= max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"size() >= max_size()"});
Traits::assign(s_[n_++], ch);
term();
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
auto
static_string<N, CharT, Traits>::
append(static_string<M, CharT, Traits> const& str,
size_type pos, size_type count) ->
static_string&
{
// Valid range is [0, size)
if(pos >= str.size())
BOOST_THROW_EXCEPTION(std::out_of_range{
"pos > str.size()"});
string_view_type const ss{&str.s_[pos],
(std::min)(count, str.size() - pos)};
insert(size(), ss.data(), ss.size());
return *this;
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
substr(size_type pos, size_type count) const ->
string_view_type
{
if(pos > size())
BOOST_THROW_EXCEPTION(std::out_of_range{
"pos > size()"});
return{&s_[pos], (std::min)(count, size() - pos)};
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
copy(CharT* dest, size_type count, size_type pos) const ->
size_type
{
auto const str = substr(pos, count);
Traits::copy(dest, str.data(), str.size());
return str.size();
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
resize(std::size_t n)
{
if(n > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"n > max_size()"});
if(n > n_)
Traits::assign(&s_[n_], n - n_, CharT{});
n_ = n;
term();
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
resize(std::size_t n, CharT c)
{
if(n > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"n > max_size()"});
if(n > n_)
Traits::assign(&s_[n_], n - n_, c);
n_ = n;
term();
}
template<std::size_t N, class CharT, class Traits>
void
static_string<N, CharT, Traits>::
swap(static_string& str)
{
static_string tmp(str);
str.n_ = n_;
Traits::copy(&str.s_[0], &s_[0], n_ + 1);
n_ = tmp.n_;
Traits::copy(&s_[0], &tmp.s_[0], n_ + 1);
}
template<std::size_t N, class CharT, class Traits>
template<std::size_t M>
void
static_string<N, CharT, Traits>::
swap(static_string<M, CharT, Traits>& str)
{
if(size() > str.max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"size() > str.max_size()"});
if(str.size() > max_size())
BOOST_THROW_EXCEPTION(std::length_error{
"str.size() > max_size()"});
static_string tmp(str);
str.n_ = n_;
Traits::copy(&str.s_[0], &s_[0], n_ + 1);
n_ = tmp.n_;
Traits::copy(&s_[0], &tmp.s_[0], n_ + 1);
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
assign_char(CharT ch, std::true_type) ->
static_string&
{
n_ = 1;
Traits::assign(s_[0], ch);
term();
return *this;
}
template<std::size_t N, class CharT, class Traits>
auto
static_string<N, CharT, Traits>::
assign_char(CharT, std::false_type) ->
static_string&
{
BOOST_THROW_EXCEPTION(std::length_error{
"max_size() == 0"});
}
namespace detail {
template<class Integer>
static_string<max_digits(sizeof(Integer))>
to_static_string(Integer x, std::true_type)
{
if(x == 0)
return {'0'};
static_string<detail::max_digits(
sizeof(Integer))> s;
if(x < 0)
{
x = -x;
char buf[max_digits(sizeof(x))];
char* p = buf;
for(;x > 0; x /= 10)
*p++ = "0123456789"[x % 10];
s.resize(1 + p - buf);
s[0] = '-';
auto d = &s[1];
while(p > buf)
*d++ = *--p;
}
else
{
char buf[max_digits(sizeof(x))];
char* p = buf;
for(;x > 0; x /= 10)
*p++ = "0123456789"[x % 10];
s.resize(p - buf);
auto d = &s[0];
while(p > buf)
*d++ = *--p;
}
return s;
}
template<class Integer>
static_string<max_digits(sizeof(Integer))>
to_static_string(Integer x, std::false_type)
{
if(x == 0)
return {'0'};
char buf[max_digits(sizeof(x))];
char* p = buf;
for(;x > 0; x /= 10)
*p++ = "0123456789"[x % 10];
static_string<detail::max_digits(
sizeof(Integer))> s;
s.resize(p - buf);
auto d = &s[0];
while(p > buf)
*d++ = *--p;
return s;
}
} // detail
template<class Integer, class>
static_string<detail::max_digits(sizeof(Integer))>
to_static_string(Integer x)
{
using CharT = char;
using Traits = std::char_traits<CharT>;
BOOST_STATIC_ASSERT(std::is_integral<Integer>::value);
char buf[detail::max_digits(sizeof(Integer))];
auto last = buf + sizeof(buf);
auto it = detail::raw_to_string<
CharT, Integer, Traits>(last, sizeof(buf), x);
static_string<detail::max_digits(sizeof(Integer))> s;
s.resize(static_cast<std::size_t>(last - it));
auto p = s.data();
while(it < last)
Traits::assign(*p++, *it++);
return s;
}
} // beast
} // boost
#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_IMPL_STRING_PARAM_HPP
#define BOOST_BEAST_IMPL_STRING_PARAM_HPP
namespace boost {
namespace beast {
template<class T>
typename std::enable_if<
std::is_integral<T>::value>::type
string_param::
print(T const& t)
{
auto const last = buf_ + sizeof(buf_);
auto const it = detail::raw_to_string<
char, T, std::char_traits<char>>(
last, sizeof(buf_), t);
sv_ = {it, static_cast<std::size_t>(
last - it)};
}
template<class T>
typename std::enable_if<
! std::is_integral<T>::value &&
! std::is_convertible<T, string_view>::value
>::type
string_param::
print(T const& t)
{
os_.emplace(buf_, sizeof(buf_));
*os_ << t;
os_->flush();
sv_ = os_->str();
}
inline
void
string_param::
print(string_view sv)
{
sv_ = sv;
}
template<class T>
typename std::enable_if<
std::is_integral<T>::value>::type
string_param::
print_1(T const& t)
{
char buf[detail::max_digits(sizeof(T))];
auto const last = buf + sizeof(buf);
auto const it = detail::raw_to_string<
char, T, std::char_traits<char>>(
last, sizeof(buf), t);
*os_ << string_view{it,
static_cast<std::size_t>(last - it)};
}
template<class T>
typename std::enable_if<
! std::is_integral<T>::value>::type
string_param::
print_1(T const& t)
{
*os_ << t;
}
template<class T0, class... TN>
void
string_param::
print_n(T0 const& t0, TN const&... tn)
{
print_1(t0);
print_n(tn...);
}
template<class T0, class T1, class... TN>
void
string_param::
print(T0 const& t0, T1 const& t1, TN const&... tn)
{
os_.emplace(buf_, sizeof(buf_));
print_1(t0);
print_1(t1);
print_n(tn...);
os_->flush();
sv_ = os_->str();
}
template<class... Args>
string_param::
string_param(Args const&... args)
{
print(args...);
}
} // beast
} // boost
#endif

View File

@@ -0,0 +1,107 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_MAKE_PRINTABLE_HPP
#define BOOST_BEAST_MAKE_PRINTABLE_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <ostream>
namespace boost {
namespace beast {
namespace detail {
template<class Buffers>
class make_printable_adaptor
{
Buffers b_;
public:
explicit
make_printable_adaptor(Buffers const& b)
: b_(b)
{
}
template<class B>
friend
std::ostream&
operator<<(std::ostream& os,
make_printable_adaptor<B> const& v);
};
template<class Buffers>
std::ostream&
operator<<(std::ostream& os,
make_printable_adaptor<Buffers> const& v)
{
for(
auto it = net::buffer_sequence_begin(v.b_),
end = net::buffer_sequence_end(v.b_);
it != end;
++it)
{
net::const_buffer cb = *it;
os.write(static_cast<char const*>(
cb.data()), cb.size());
}
return os;
}
} // detail
/** Helper to permit a buffer sequence to be printed to a std::ostream
This function is used to wrap a buffer sequence to allow it to
be interpreted as characters and written to a `std::ostream` such
as `std::cout`. No character translation is performed; unprintable
and null characters will be transferred as-is to the output stream.
@par Example
This function prints the size and contents of a buffer sequence
to standard output:
@code
template <class ConstBufferSequence>
void
print (ConstBufferSequence const& buffers)
{
std::cout <<
"Buffer size: " << buffer_bytes(buffers) << " bytes\n"
"Buffer data: '" << make_printable(buffers) << "'\n";
}
@endcode
@param buffers An object meeting the requirements of
<em>ConstBufferSequence</em> to be streamed. The implementation
will make a copy of this object. Ownership of the underlying
memory is not transferred, the application is still responsible
for managing its lifetime.
*/
template<class ConstBufferSequence>
#if BOOST_BEAST_DOXYGEN
__implementation_defined__
#else
detail::make_printable_adaptor<ConstBufferSequence>
#endif
make_printable(ConstBufferSequence const& buffers)
{
static_assert(net::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence type requirements not met");
return detail::make_printable_adaptor<
ConstBufferSequence>{buffers};
}
} // beast
} // boost
#endif

View File

@@ -0,0 +1,555 @@
//
// 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_MULTI_BUFFER_HPP
#define BOOST_BEAST_MULTI_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 <boost/intrusive/list.hpp>
#include <iterator>
#include <limits>
#include <memory>
#include <type_traits>
namespace boost {
namespace beast {
/** A dynamic buffer providing sequences of variable length.
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.
The implementation uses a sequence of one or more byte
arrays of varying sizes to represent the readable and
writable bytes. Additional byte array objects are
appended to the sequence to accommodate changes in the
desired size. The behavior and implementation of this
container is most similar to `std::deque`.
Objects of this type meet the requirements of <em>DynamicBuffer</em>
and have the following additional properties:
@li A mutable buffer sequence representing the readable
bytes is returned by @ref data when `this` is non-const.
@li Buffer sequences representing the readable and writable
bytes, returned by @ref data and @ref prepare, may have
length greater than one.
@li A configurable maximum size may be set upon construction
and adjusted afterwards. Calls to @ref prepare that would
exceed this size will throw `std::length_error`.
@li Sequences previously obtained using @ref data remain
valid after calls to @ref prepare or @ref commit.
@tparam Allocator The allocator to use for managing memory.
*/
template<class Allocator>
class basic_multi_buffer
#if ! BOOST_BEAST_DOXYGEN
: private boost::empty_value<
typename detail::allocator_traits<Allocator>::
template rebind_alloc<char>>
#endif
{
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;
// Storage for the list of buffers representing the input
// and output sequences. The allocation for each element
// contains `element` followed by raw storage bytes.
class element;
template<bool>
class readable_bytes;
using alloc_traits =
beast::detail::allocator_traits<base_alloc_type>;
using list_type = typename boost::intrusive::make_list<element,
boost::intrusive::constant_time_size<true>>::type;
using iter = typename list_type::iterator;
using const_iter = typename list_type::const_iterator;
using size_type = typename alloc_traits::size_type;
using pocma = typename
alloc_traits::propagate_on_container_move_assignment;
using pocca = typename
alloc_traits::propagate_on_container_copy_assignment;
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<iter>::iterator_category>::value,
"BidirectionalIterator type requirements not met");
static_assert(std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<const_iter>::iterator_category>::value,
"BidirectionalIterator type requirements not met");
std::size_t max_;
list_type list_; // list of allocated buffers
iter out_; // element that contains out_pos_
size_type in_size_ = 0; // size of the input sequence
size_type in_pos_ = 0; // input offset in list_.front()
size_type out_pos_ = 0; // output offset in *out_
size_type out_end_ = 0; // output end offset in list_.back()
public:
/// The type of allocator used.
using allocator_type = Allocator;
/// Destructor
~basic_multi_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_multi_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_multi_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_multi_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_multi_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_multi_buffer(basic_multi_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_multi_buffer(
basic_multi_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_multi_buffer(basic_multi_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_multi_buffer(basic_multi_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_multi_buffer(basic_multi_buffer<
OtherAlloc> const& other);
/** 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_multi_buffer(
basic_multi_buffer<OtherAlloc> const& other,
allocator_type 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.
*/
basic_multi_buffer&
operator=(basic_multi_buffer&& 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.
*/
basic_multi_buffer& operator=(
basic_multi_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_multi_buffer& operator=(
basic_multi_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 remain
valid, while buffer sequences previously obtained using
@ref prepare become invalid.
@param n The minimum number of byte for the new capacity.
If this value is greater than the maximum size, then the
maximum size will be adjusted upwards to this value.
@throws std::length_error if n is larger than the maximum
allocation size of the allocator.
@esafe
Strong guarantee.
*/
void
reserve(std::size_t n);
/** Reallocate the buffer to fit the readable bytes exactly.
Buffer sequences previously obtained using @ref data or
@ref prepare become invalid.
@esafe
Strong guarantee.
*/
void
shrink_to_fit();
/** Set the size of the readable and writable bytes to zero.
This clears the buffer without changing capacity.
Buffer sequences previously obtained using @ref data or
@ref prepare become invalid.
@esafe
No-throw guarantee.
*/
void
clear() noexcept;
/// Exchange two dynamic buffers
template<class Alloc>
friend
void
swap(
basic_multi_buffer<Alloc>& lhs,
basic_multi_buffer<Alloc>& rhs) noexcept;
//--------------------------------------------------------------------------
#if BOOST_BEAST_DOXYGEN
/// The ConstBufferSequence used to represent the readable bytes.
using const_buffers_type = __implementation_defined__;
/// The MutableBufferSequence used to represent the readable bytes.
using mutable_data_type = __implementation_defined__;
/// The MutableBufferSequence used to represent the writable bytes.
using mutable_buffers_type = __implementation_defined__;
#else
using const_buffers_type = readable_bytes<false>;
using mutable_data_type = readable_bytes<true>;
class mutable_buffers_type;
#endif
/// Returns the number of readable bytes.
size_type
size() const noexcept
{
return in_size_;
}
/// Return the maximum number of bytes, both readable and writable, that can ever be held.
size_type
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;
/** Returns a constant buffer sequence representing the readable bytes
@note The sequence may contain multiple contiguous memory regions.
*/
const_buffers_type
data() const noexcept;
/** Returns a constant buffer sequence representing the readable bytes
@note The sequence may contain multiple contiguous memory regions.
*/
const_buffers_type
cdata() const noexcept
{
return data();
}
/** Returns a mutable buffer sequence representing the readable bytes.
@note The sequence may contain multiple contiguous memory regions.
*/
mutable_data_type
data() noexcept;
/** Returns a mutable buffer sequence representing writable bytes.
Returns a mutable buffer sequence representing the writable
bytes containing exactly `n` bytes of storage. Memory may be
reallocated as needed.
All 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(size_type 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(size_type 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(size_type n) noexcept;
private:
template<class OtherAlloc>
friend class basic_multi_buffer;
template<class OtherAlloc>
void copy_from(basic_multi_buffer<OtherAlloc> const&);
void move_assign(basic_multi_buffer& other, std::false_type);
void move_assign(basic_multi_buffer& other, std::true_type) noexcept;
void copy_assign(basic_multi_buffer const& other, std::false_type);
void copy_assign(basic_multi_buffer const& other, std::true_type);
void swap(basic_multi_buffer&) noexcept;
void swap(basic_multi_buffer&, std::true_type) noexcept;
void swap(basic_multi_buffer&, std::false_type) noexcept;
void destroy(list_type& list) noexcept;
void destroy(const_iter it);
void destroy(element& e);
element& alloc(std::size_t size);
void debug_check() const;
};
/// A typical multi buffer
using multi_buffer = basic_multi_buffer<std::allocator<char>>;
} // beast
} // boost
#include <boost/beast/core/impl/multi_buffer.hpp>
#endif

View File

@@ -0,0 +1,90 @@
//
// 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_WRITE_OSTREAM_HPP
#define BOOST_BEAST_WRITE_OSTREAM_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/ostream.hpp>
#include <type_traits>
#include <streambuf>
#include <utility>
#ifdef BOOST_BEAST_ALLOW_DEPRECATED
#include <boost/beast/core/make_printable.hpp>
#endif
namespace boost {
namespace beast {
/** Return an output stream that formats values into a <em>DynamicBuffer</em>.
This function wraps the caller provided <em>DynamicBuffer</em> into
a `std::ostream` derived class, to allow `operator<<` stream style
formatting operations.
@par Example
@code
ostream(buffer) << "Hello, world!" << std::endl;
@endcode
@note Calling members of the underlying buffer before the output
stream is destroyed results in undefined behavior.
@param buffer An object meeting the requirements of <em>DynamicBuffer</em>
into which the formatted output will be placed.
@return An object derived from `std::ostream` which redirects output
The wrapped dynamic 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. The caller is
responsible for ensuring the dynamic buffer is not destroyed for the
lifetime of the output stream.
*/
template<class DynamicBuffer>
#if BOOST_BEAST_DOXYGEN
__implementation_defined__
#else
detail::ostream_helper<
DynamicBuffer, char, std::char_traits<char>,
detail::basic_streambuf_movable::value>
#endif
ostream(DynamicBuffer& buffer)
{
static_assert(
net::is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer type requirements not met");
return detail::ostream_helper<
DynamicBuffer, char, std::char_traits<char>,
detail::basic_streambuf_movable::value>{buffer};
}
//------------------------------------------------------------------------------
#ifdef BOOST_BEAST_ALLOW_DEPRECATED
template<class T>
detail::make_printable_adaptor<T>
buffers(T const& t)
{
return make_printable(t);
}
#else
template<class T>
void buffers(T const&)
{
static_assert(sizeof(T) == 0,
"The function buffers() is deprecated, use make_printable() instead, "
"or define BOOST_BEAST_ALLOW_DEPRECATED to silence this error.");
}
#endif
} // beast
} // boost
#endif

View File

@@ -0,0 +1,220 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_RATE_POLICY_HPP
#define BOOST_BEAST_CORE_RATE_POLICY_HPP
#include <boost/beast/core/detail/config.hpp>
#include <cstdint>
#include <limits>
namespace boost {
namespace beast {
/** Helper class to assist implementing a <em>RatePolicy</em>.
This class is used by the implementation to gain access to the
private members of a user-defined object meeting the requirements
of <em>RatePolicy</em>. To use it, simply declare it as a friend
in your class:
@par Example
@code
class custom_rate_policy
{
friend class beast::rate_policy_access;
...
@endcode
@par Concepts
@li <em>RatePolicy</em>
@see beast::basic_stream
*/
class rate_policy_access
{
private:
template<class, class, class>
friend class basic_stream;
template<class Policy>
static
std::size_t
available_read_bytes(Policy& policy)
{
return policy.available_read_bytes();
}
template<class Policy>
static
std::size_t
available_write_bytes(Policy& policy)
{
return policy.available_write_bytes();
}
template<class Policy>
static
void
transfer_read_bytes(
Policy& policy, std::size_t n)
{
return policy.transfer_read_bytes(n);
}
template<class Policy>
static
void
transfer_write_bytes(
Policy& policy, std::size_t n)
{
return policy.transfer_write_bytes(n);
}
template<class Policy>
static
void
on_timer(Policy& policy)
{
return policy.on_timer();
}
};
//------------------------------------------------------------------------------
/** A rate policy with unlimited throughput.
This rate policy object does not apply any rate limit.
@par Concepts
@li <em>RatePolicy</em>
@see beast::basic_stream, beast::tcp_stream
*/
class unlimited_rate_policy
{
friend class rate_policy_access;
static std::size_t constexpr all =
(std::numeric_limits<std::size_t>::max)();
std::size_t
available_read_bytes() const noexcept
{
return all;
}
std::size_t
available_write_bytes() const noexcept
{
return all;
}
void
transfer_read_bytes(std::size_t) const noexcept
{
}
void
transfer_write_bytes(std::size_t) const noexcept
{
}
void
on_timer() const noexcept
{
}
};
//------------------------------------------------------------------------------
/** A rate policy with simple, configurable limits on reads and writes.
This rate policy allows for simple individual limits on the amount
of bytes per second allowed for reads and writes.
@par Concepts
@li <em>RatePolicy</em>
@see beast::basic_stream
*/
class simple_rate_policy
{
friend class rate_policy_access;
static std::size_t constexpr all =
std::numeric_limits<std::size_t>::max();
std::size_t rd_remain_ = all;
std::size_t wr_remain_ = all;
std::size_t rd_limit_ = all;
std::size_t wr_limit_ = all;
std::size_t
available_read_bytes() const noexcept
{
return rd_remain_;
}
std::size_t
available_write_bytes() const noexcept
{
return wr_remain_;
}
void
transfer_read_bytes(std::size_t n) noexcept
{
if( rd_remain_ != all)
rd_remain_ =
(n < rd_remain_) ? rd_remain_ - n : 0;
}
void
transfer_write_bytes(std::size_t n) noexcept
{
if( wr_remain_ != all)
wr_remain_ =
(n < wr_remain_) ? wr_remain_ - n : 0;
}
void
on_timer() noexcept
{
rd_remain_ = rd_limit_;
wr_remain_ = wr_limit_;
}
public:
/// Set the limit of bytes per second to read
void
read_limit(std::size_t bytes_per_second) noexcept
{
rd_limit_ = bytes_per_second;
if( rd_remain_ > bytes_per_second)
rd_remain_ = bytes_per_second;
}
/// Set the limit of bytes per second to write
void
write_limit(std::size_t bytes_per_second) noexcept
{
wr_limit_ = bytes_per_second;
if( wr_remain_ > bytes_per_second)
wr_remain_ = bytes_per_second;
}
};
} // beast
} // boost
#endif

View File

@@ -0,0 +1,63 @@
//
// 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_READ_SIZE_HELPER_HPP
#define BOOST_BEAST_READ_SIZE_HELPER_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/throw_exception.hpp>
namespace boost {
namespace beast {
/** Returns a natural read size.
This function inspects the capacity, size, and maximum
size of the dynamic buffer. Then it computes a natural
read size given the passed-in upper limit. It favors
a read size that does not require a reallocation, subject
to a reasonable minimum to avoid tiny reads.
@param buffer The dynamic buffer to inspect.
@param max_size An upper limit on the returned value.
@note If the buffer is already at its maximum size, zero
is returned.
*/
template<class DynamicBuffer>
std::size_t
read_size(DynamicBuffer& buffer, std::size_t max_size);
/** Returns a natural read size or throw if the buffer is full.
This function inspects the capacity, size, and maximum
size of the dynamic buffer. Then it computes a natural
read size given the passed-in upper limit. It favors
a read size that does not require a reallocation, subject
to a reasonable minimum to avoid tiny reads.
@param buffer The dynamic buffer to inspect.
@param max_size An upper limit on the returned value.
@throws std::length_error if `max_size > 0` and the buffer
is full.
*/
template<class DynamicBuffer>
std::size_t
read_size_or_throw(DynamicBuffer& buffer,
std::size_t max_size);
} // beast
} // boost
#include <boost/beast/core/impl/read_size.hpp>
#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_ROLE_HPP
#define BOOST_BEAST_ROLE_HPP
#include <boost/beast/core/detail/config.hpp>
namespace boost {
namespace beast {
/** The role of local or remote peer.
Whether the endpoint is a client or server affects the
behavior of teardown.
The teardown behavior also depends on the type of the stream
being torn down.
The default implementation of teardown for regular
TCP/IP sockets is as follows:
@li In the client role, a TCP/IP shutdown is sent after
reading all remaining data on the connection.
@li In the server role, a TCP/IP shutdown is sent before
reading all remaining data on the connection.
When the next layer type is a `net::ssl::stream`,
the connection is closed by performing the SSL closing
handshake corresponding to the role type, client or server.
*/
enum class role_type
{
/// The stream is operating as a client.
client,
/// The stream is operating as a server.
server
};
} // beast
} // boost
#endif

View File

@@ -0,0 +1,137 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_SAVED_HANDLER_HPP
#define BOOST_BEAST_CORE_SAVED_HANDLER_HPP
#include <boost/beast/core/detail/config.hpp>
namespace boost {
namespace beast {
/** An invocable, nullary function object which holds a completion handler.
This container can hold a type-erased instance of any completion
handler, or it can be empty. When the container holds a value,
the implementation maintains an instance of `net::executor_work_guard`
for the handler's associated executor. Memory is dynamically allocated
to store the completion handler, and the allocator may optionally
be specified. Otherwise, the implementation uses the handler's
associated allocator.
*/
class saved_handler
{
class base;
template<class, class>
class impl;
base* p_ = nullptr;
public:
/// Default Constructor
saved_handler() = default;
/// Copy Constructor (deleted)
saved_handler(saved_handler const&) = delete;
/// Copy Assignment (deleted)
saved_handler& operator=(saved_handler const&) = delete;
/// Destructor
BOOST_BEAST_DECL
~saved_handler();
/// Move Constructor
BOOST_BEAST_DECL
saved_handler(saved_handler&& other) noexcept;
/// Move Assignment
BOOST_BEAST_DECL
saved_handler&
operator=(saved_handler&& other) noexcept;
/// Returns `true` if `*this` contains a completion handler.
bool
has_value() const noexcept
{
return p_ != nullptr;
}
/** Store a completion handler in the container.
Requires `this->has_value() == false`.
@param handler The completion handler to store.
The implementation takes ownership of the handler by performing a decay-copy.
@param alloc The allocator to use.
*/
template<class Handler, class Allocator>
void
emplace(Handler&& handler, Allocator const& alloc);
/** Store a completion handler in the container.
Requires `this->has_value() == false`. The
implementation will use the handler's associated
allocator to obtian storage.
@param handler The completion handler to store.
The implementation takes ownership of the handler by performing a decay-copy.
*/
template<class Handler>
void
emplace(Handler&& handler);
/** Discard the saved handler, if one exists.
If `*this` contains an object, it is destroyed.
@returns `true` if an object was destroyed.
*/
BOOST_BEAST_DECL
bool
reset() noexcept;
/** Unconditionally invoke the stored completion handler.
Requires `this->has_value() == true`. Any dynamic memory
used is deallocated before the stored completion handler
is invoked. The executor work guard is also reset before
the invocation.
*/
BOOST_BEAST_DECL
void
invoke();
/** Conditionally invoke the stored completion handler.
Invokes the stored completion handler if
`this->has_value() == true`, otherwise does nothing. Any
dynamic memory used is deallocated before the stored completion
handler is invoked. The executor work guard is also reset before
the invocation.
@return `true` if the invocation took place.
*/
BOOST_BEAST_DECL
bool
maybe_invoke();
};
} // beast
} // boost
#include <boost/beast/core/impl/saved_handler.hpp>
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/core/impl/saved_handler.ipp>
#endif
#endif

View File

@@ -0,0 +1,215 @@
//
// 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_SPAN_HPP
#define BOOST_BEAST_CORE_SPAN_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
#include <algorithm>
#include <iterator>
#include <string>
#include <type_traits>
namespace boost {
namespace beast {
/** A range of bytes expressed as a ContiguousContainer
This class implements a non-owning reference to a storage
area of a certain size and having an underlying integral
type with size of 1.
@tparam T The type pointed to by span iterators
*/
template<class T>
class span
{
T* data_ = nullptr;
std::size_t size_ = 0;
public:
/// The type of value, including cv qualifiers
using element_type = T;
/// The type of value of each span element
using value_type = typename std::remove_const<T>::type;
/// The type of integer used to index the span
using index_type = std::ptrdiff_t;
/// A pointer to a span element
using pointer = T*;
/// A reference to a span element
using reference = T&;
/// The iterator used by the container
using iterator = pointer;
/// The const pointer used by the container
using const_pointer = T const*;
/// The const reference used by the container
using const_reference = T const&;
/// The const iterator used by the container
using const_iterator = const_pointer;
/// Constructor
span() = default;
/// Constructor
span(span const&) = default;
/// Assignment
span& operator=(span const&) = default;
/** Constructor
@param data A pointer to the beginning of the range of elements
@param size The number of elements pointed to by `data`
*/
span(T* data, std::size_t size)
: data_(data), size_(size)
{
}
/** Constructor
@param container The container to construct from
*/
template<class ContiguousContainer
#if ! BOOST_BEAST_DOXYGEN
, class = typename std::enable_if<
detail::is_contiguous_container<
ContiguousContainer, T>::value>::type
#endif
>
explicit
span(ContiguousContainer&& container)
: data_(container.data())
, size_(container.size())
{
}
#if ! BOOST_BEAST_DOXYGEN
template<class CharT, class Traits, class Allocator>
explicit
span(std::basic_string<CharT, Traits, Allocator>& s)
: data_(&s[0])
, size_(s.size())
{
}
template<class CharT, class Traits, class Allocator>
explicit
span(std::basic_string<CharT, Traits, Allocator> const& s)
: data_(s.data())
, size_(s.size())
{
}
#endif
/** Assignment
@param container The container to assign from
*/
template<class ContiguousContainer>
#if BOOST_BEAST_DOXYGEN
span&
#else
typename std::enable_if<detail::is_contiguous_container<
ContiguousContainer, T>::value,
span&>::type
#endif
operator=(ContiguousContainer&& container)
{
data_ = container.data();
size_ = container.size();
return *this;
}
#if ! BOOST_BEAST_DOXYGEN
template<class CharT, class Traits, class Allocator>
span&
operator=(std::basic_string<
CharT, Traits, Allocator>& s)
{
data_ = &s[0];
size_ = s.size();
return *this;
}
template<class CharT, class Traits, class Allocator>
span&
operator=(std::basic_string<
CharT, Traits, Allocator> const& s)
{
data_ = s.data();
size_ = s.size();
return *this;
}
#endif
/// Returns `true` if the span is empty
bool
empty() const
{
return size_ == 0;
}
/// Returns a pointer to the beginning of the span
T*
data() const
{
return data_;
}
/// Returns the number of elements in the span
std::size_t
size() const
{
return size_;
}
/// Returns an iterator to the beginning of the span
const_iterator
begin() const
{
return data_;
}
/// Returns an iterator to the beginning of the span
const_iterator
cbegin() const
{
return data_;
}
/// Returns an iterator to one past the end of the span
const_iterator
end() const
{
return data_ + size_;
}
/// Returns an iterator to one past the end of the span
const_iterator
cend() const
{
return data_ + size_;
}
};
} // beast
} // boost
#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_STATIC_BUFFER_HPP
#define BOOST_BEAST_STATIC_BUFFER_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/buffers_pair.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/assert.hpp>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstring>
namespace boost {
namespace beast {
/** A dynamic buffer providing a fixed size, circular buffer.
A dynamic buffer encapsulates memory storage that may be
automatically resized as required, where the memory is
divided into two regions: readable bytes followed by
writable bytes. These memory regions are internal to
the dynamic buffer, but direct access to the elements
is provided to permit them to be efficiently used with
I/O operations.
Objects of this type meet the requirements of <em>DynamicBuffer</em>
and have the following additional properties:
@li A mutable buffer sequence representing the readable
bytes is returned by @ref data when `this` is non-const.
@li Buffer sequences representing the readable and writable
bytes, returned by @ref data and @ref prepare, may have
length up to two.
@li All operations execute in constant time.
@li Ownership of the underlying storage belongs to the
derived class.
@note Variables are usually declared using the template class
@ref static_buffer; however, to reduce the number of template
instantiations, objects should be passed `static_buffer_base&`.
@see static_buffer
*/
class static_buffer_base
{
char* begin_;
std::size_t in_off_ = 0;
std::size_t in_size_ = 0;
std::size_t out_size_ = 0;
std::size_t capacity_;
static_buffer_base(static_buffer_base const& other) = delete;
static_buffer_base& operator=(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 size The number of valid bytes pointed to by `p`.
*/
BOOST_BEAST_DECL
static_buffer_base(void* p, std::size_t size) noexcept;
/** Clear the readable and writable bytes to zero.
This function causes the readable and writable bytes
to become empty. The capacity is not changed.
Buffer sequences previously obtained using @ref data or
@ref prepare become invalid.
@esafe
No-throw guarantee.
*/
BOOST_BEAST_DECL
void
clear() noexcept;
//--------------------------------------------------------------------------
#if BOOST_BEAST_DOXYGEN
/// The ConstBufferSequence used to represent the readable bytes.
using const_buffers_type = __implementation_defined__;
/// The MutableBufferSequence used to represent the readable bytes.
using mutable_data_type = __implementation_defined__;
/// The MutableBufferSequence used to represent the writable bytes.
using mutable_buffers_type = __implementation_defined__;
#else
using const_buffers_type = detail::buffers_pair<false>;
using mutable_data_type = detail::buffers_pair<true>;
using mutable_buffers_type = detail::buffers_pair<true>;
#endif
/// Returns the number of readable bytes.
std::size_t
size() const 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 capacity_;
}
/// 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 capacity_;
}
/// Returns a constant buffer sequence representing the readable bytes
BOOST_BEAST_DECL
const_buffers_type
data() const noexcept;
/// Returns a constant buffer sequence representing the readable bytes
const_buffers_type
cdata() const noexcept
{
return data();
}
/// Returns a mutable buffer sequence representing the readable bytes
BOOST_BEAST_DECL
mutable_data_type
data() noexcept;
/** Returns a mutable buffer sequence representing writable bytes.
Returns a mutable buffer sequence representing the writable
bytes containing exactly `n` bytes of storage. Memory may be
reallocated as needed.
All buffers sequences previously obtained using
@ref data or @ref prepare are invalidated.
@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.
*/
BOOST_BEAST_DECL
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.
*/
BOOST_BEAST_DECL
void
consume(std::size_t n) noexcept;
};
//------------------------------------------------------------------------------
/** A dynamic buffer providing a fixed size, circular buffer.
A dynamic buffer encapsulates memory storage that may be
automatically resized as required, where the memory is
divided into two regions: readable bytes followed by
writable bytes. These memory regions are internal to
the dynamic buffer, but direct access to the elements
is provided to permit them to be efficiently used with
I/O operations.
Objects of this type meet the requirements of <em>DynamicBuffer</em>
and have the following additional properties:
@li A mutable buffer sequence representing the readable
bytes is returned by @ref data when `this` is non-const.
@li Buffer sequences representing the readable and writable
bytes, returned by @ref data and @ref prepare, may have
length up to two.
@li All operations execute in constant time.
@tparam N The number of bytes in the internal buffer.
@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 static_buffer_base instead.
@see static_buffer_base
*/
template<std::size_t N>
class static_buffer : public static_buffer_base
{
char buf_[N];
public:
/// Constructor
static_buffer() noexcept
: static_buffer_base(buf_, N)
{
}
/// Constructor
static_buffer(static_buffer const&) noexcept;
/// Assignment
static_buffer& operator=(static_buffer const&) noexcept;
/// Returns the @ref static_buffer_base portion of this object
static_buffer_base&
base() noexcept
{
return *this;
}
/// Returns the @ref static_buffer_base portion of this object
static_buffer_base const&
base() const noexcept
{
return *this;
}
/// Return the maximum sum of the input and output sequence sizes.
std::size_t constexpr
max_size() const noexcept
{
return N;
}
/// Return the maximum sum of input and output sizes that can be held without an allocation.
std::size_t constexpr
capacity() const noexcept
{
return N;
}
};
} // beast
} // boost
#include <boost/beast/core/impl/static_buffer.hpp>
#ifdef BOOST_BEAST_HEADER_ONLY
#include <boost/beast/core/impl/static_buffer.ipp>
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,544 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_STREAM_TRAITS_HPP
#define BOOST_BEAST_STREAM_TRAITS_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/static_const.hpp>
#include <boost/beast/core/detail/stream_traits.hpp>
#include <boost/asio/basic_socket.hpp>
namespace boost {
namespace beast {
/** A trait to determine the lowest layer type of a stack of stream layers.
If `t.next_layer()` is well-defined for an object `t` of type `T`,
then `lowest_layer_type<T>` will be an alias for
`lowest_layer_type<decltype(t.next_layer())>`,
otherwise it will be the type
`std::remove_reference<T>`.
@param T The type to determine the lowest layer type of.
@return The type of the lowest layer.
*/
template<class T>
#if BOOST_BEAST_DOXYGEN
using lowest_layer_type = __see_below__;
#else
using lowest_layer_type = detail::lowest_layer_type<T>;
#endif
/** Return the lowest layer in a stack of stream layers.
If `t.next_layer()` is well-defined, returns
`get_lowest_layer(t.next_layer())`. Otherwise, it returns `t`.
A stream layer is an object of class type which wraps another object through
composition, and meets some or all of the named requirements of the wrapped
type while optionally changing behavior. Examples of stream layers include
`net::ssl::stream` or @ref beast::websocket::stream. The owner of a stream
layer can interact directly with the wrapper, by passing it to stream
algorithms. Or, the owner can obtain a reference to the wrapped object by
calling `next_layer()` and accessing its members. This is necessary when it is
desired to access functionality in the next layer which is not available
in the wrapper. For example, @ref websocket::stream permits reading and
writing, but in order to establish the underlying connection, members
of the wrapped stream (such as `connect`) must be invoked directly.
Usually the last object in the chain of composition is the concrete socket
object (for example, a `net::basic_socket` or a class derived from it).
The function @ref get_lowest_layer exists to easily obtain the concrete
socket when it is desired to perform an action that is not prescribed by
a named requirement, such as changing a socket option, cancelling all
pending asynchronous I/O, or closing the socket (perhaps by using
@ref close_socket).
@par Example
@code
// Set non-blocking mode on a stack of stream
// layers with a regular socket at the lowest layer.
template <class Stream>
void set_non_blocking (Stream& stream)
{
error_code ec;
// A compile error here means your lowest layer is not the right type!
get_lowest_layer(stream).non_blocking(true, ec);
if(ec)
throw system_error{ec};
}
@endcode
@param t The layer in a stack of layered objects for which the lowest layer is returned.
@see close_socket, lowest_layer_type
*/
template<class T>
lowest_layer_type<T>&
get_lowest_layer(T& t) noexcept
{
return detail::get_lowest_layer_impl(
t, detail::has_next_layer<T>{});
}
//------------------------------------------------------------------------------
/** A trait to determine the return type of get_executor.
This type alias will be the type of values returned by
by calling member `get_exector` on an object of type `T&`.
@param T The type to query
@return The type of values returned from `get_executor`.
*/
// Workaround for ICE on gcc 4.8
#if BOOST_BEAST_DOXYGEN
template<class T>
using executor_type = __see_below__;
#elif BOOST_WORKAROUND(BOOST_GCC, < 40900)
template<class T>
using executor_type =
typename std::decay<T>::type::executor_type;
#else
template<class T>
using executor_type =
decltype(std::declval<T&>().get_executor());
#endif
/** Determine if `T` has the `get_executor` member function.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` has the member
function with the correct signature, else type will be `std::false_type`.
@par Example
Use with tag dispatching:
@code
template<class T>
void maybe_hello(T const& t, std::true_type)
{
net::post(
t.get_executor(),
[]
{
std::cout << "Hello, world!" << std::endl;
});
}
template<class T>
void maybe_hello(T const&, std::false_type)
{
// T does not have get_executor
}
template<class T>
void maybe_hello(T const& t)
{
maybe_hello(t, has_get_executor<T>{});
}
@endcode
Use with `static_assert`:
@code
struct stream
{
using executor_type = net::io_context::executor_type;
executor_type get_executor() noexcept;
};
static_assert(has_get_executor<stream>::value, "Missing get_executor member");
@endcode
*/
#if BOOST_BEAST_DOXYGEN
template<class T>
using has_get_executor = __see_below__;
#else
template<class T, class = void>
struct has_get_executor : std::false_type {};
template<class T>
struct has_get_executor<T, boost::void_t<decltype(
std::declval<T&>().get_executor())>> : std::true_type {};
#endif
//------------------------------------------------------------------------------
/** Determine if at type meets the requirements of <em>SyncReadStream</em>.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class SyncReadStream>
void f(SyncReadStream& stream)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream type requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class SyncReadStream>
typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type
f(SyncReadStream& stream);
@endcode
*/
#if BOOST_BEAST_DOXYGEN
template<class T>
using is_sync_read_stream = __see_below__;
#else
template<class T, class = void>
struct is_sync_read_stream : std::false_type {};
template<class T>
struct is_sync_read_stream<T, boost::void_t<decltype(
std::declval<std::size_t&>() = std::declval<T&>().read_some(
std::declval<detail::MutableBufferSequence>()),
std::declval<std::size_t&>() = std::declval<T&>().read_some(
std::declval<detail::MutableBufferSequence>(),
std::declval<boost::system::error_code&>())
)>> : std::true_type {};
#endif
/** Determine if `T` meets the requirements of <em>SyncWriteStream</em>.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class SyncReadStream>
void f(SyncReadStream& stream)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream type requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class SyncReadStream>
typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type
f(SyncReadStream& stream);
@endcode
*/
#if BOOST_BEAST_DOXYGEN
template<class T>
using is_sync_write_stream = __see_below__;
#else
template<class T, class = void>
struct is_sync_write_stream : std::false_type {};
template<class T>
struct is_sync_write_stream<T, boost::void_t<decltype(
(
std::declval<std::size_t&>() = std::declval<T&>().write_some(
std::declval<detail::ConstBufferSequence>()))
,std::declval<std::size_t&>() = std::declval<T&>().write_some(
std::declval<detail::ConstBufferSequence>(),
std::declval<boost::system::error_code&>())
)>> : std::true_type {};
#endif
/** Determine if `T` meets the requirements of @b SyncStream.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class SyncStream>
void f(SyncStream& stream)
{
static_assert(is_sync_stream<SyncStream>::value,
"SyncStream type requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class SyncStream>
typename std::enable_if<is_sync_stream<SyncStream>::value>::type
f(SyncStream& stream);
@endcode
*/
#if BOOST_BEAST_DOXYGEN
template<class T>
using is_sync_stream = __see_below__;
#else
template<class T>
using is_sync_stream = std::integral_constant<bool,
is_sync_read_stream<T>::value && is_sync_write_stream<T>::value>;
#endif
//------------------------------------------------------------------------------
/** Determine if `T` meets the requirements of <em>AsyncReadStream</em>.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class AsyncReadStream>
void f(AsyncReadStream& stream)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream type requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class AsyncReadStream>
typename std::enable_if<is_async_read_stream<AsyncReadStream>::value>::type
f(AsyncReadStream& stream);
@endcode
*/
#if BOOST_BEAST_DOXYGEN
template<class T>
using is_async_read_stream = __see_below__;
#else
template<class T, class = void>
struct is_async_read_stream : std::false_type {};
template<class T>
struct is_async_read_stream<T, boost::void_t<decltype(
std::declval<T&>().async_read_some(
std::declval<detail::MutableBufferSequence>(),
std::declval<detail::ReadHandler>())
)>> : std::integral_constant<bool,
has_get_executor<T>::value
> {};
#endif
/** Determine if `T` meets the requirements of <em>AsyncWriteStream</em>.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class AsyncWriteStream>
void f(AsyncWriteStream& stream)
{
static_assert(is_async_write_stream<AsyncWriteStream>::value,
"AsyncWriteStream type requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class AsyncWriteStream>
typename std::enable_if<is_async_write_stream<AsyncWriteStream>::value>::type
f(AsyncWriteStream& stream);
@endcode
*/
#if BOOST_BEAST_DOXYGEN
template<class T>
using is_async_write_stream = __see_below__;
#else
template<class T, class = void>
struct is_async_write_stream : std::false_type {};
template<class T>
struct is_async_write_stream<T, boost::void_t<decltype(
std::declval<T&>().async_write_some(
std::declval<detail::ConstBufferSequence>(),
std::declval<detail::WriteHandler>())
)>> : std::integral_constant<bool,
has_get_executor<T>::value
> {};
#endif
/** Determine if `T` meets the requirements of @b AsyncStream.
Metafunctions are used to perform compile time checking of template
types. This type will be `std::true_type` if `T` meets the requirements,
else the type will be `std::false_type`.
@par Example
Use with `static_assert`:
@code
template<class AsyncStream>
void f(AsyncStream& stream)
{
static_assert(is_async_stream<AsyncStream>::value,
"AsyncStream type requirements not met");
...
@endcode
Use with `std::enable_if` (SFINAE):
@code
template<class AsyncStream>
typename std::enable_if<is_async_stream<AsyncStream>::value>::type
f(AsyncStream& stream);
@endcode
*/
#if BOOST_BEAST_DOXYGEN
template<class T>
using is_async_stream = __see_below__;
#else
template<class T>
using is_async_stream = std::integral_constant<bool,
is_async_read_stream<T>::value && is_async_write_stream<T>::value>;
#endif
//------------------------------------------------------------------------------
/** Default socket close function.
This function is not meant to be called directly. Instead, it
is called automatically when using @ref close_socket. To enable
closure of user-defined types or classes derived from a particular
user-defined type, this function should be overloaded in the
corresponding namespace for the type in question.
@see close_socket
*/
template<
class Protocol,
class Executor>
void
beast_close_socket(
net::basic_socket<
Protocol, Executor>& sock)
{
boost::system::error_code ec;
sock.close(ec);
}
namespace detail {
struct close_socket_impl
{
template<class T>
void
operator()(T& t) const
{
using beast::beast_close_socket;
beast_close_socket(t);
}
};
} // detail
/** Close a socket or socket-like object.
This function attempts to close an object representing a socket.
In this context, a socket is an object for which an unqualified
call to the function `void beast_close_socket(Socket&)` is
well-defined. The function `beast_close_socket` is a
<em>customization point</em>, allowing user-defined types to
provide an algorithm for performing the close operation by
overloading this function for the type in question.
Since the customization point is a function call, the normal
rules for finding the correct overload are applied including
the rules for argument-dependent lookup ("ADL"). This permits
classes derived from a type for which a customization is provided
to inherit the customization point.
An overload for the networking class template `net::basic_socket`
is provided, which implements the close algorithm for all socket-like
objects (hence the name of this customization point). When used
in conjunction with @ref get_lowest_layer, a generic algorithm
operating on a layered stream can perform a closure of the underlying
socket without knowing the exact list of concrete types.
@par Example 1
The following generic function synchronously sends a message
on the stream, then closes the socket.
@code
template <class WriteStream>
void hello_and_close (WriteStream& stream)
{
net::write(stream, net::const_buffer("Hello, world!", 13));
close_socket(get_lowest_layer(stream));
}
@endcode
To enable closure of user defined types, it is necessary to provide
an overload of the function `beast_close_socket` for the type.
@par Example 2
The following code declares a user-defined type which contains a
private socket, and provides an overload of the customization
point which closes the private socket.
@code
class my_socket
{
net::ip::tcp::socket sock_;
public:
my_socket(net::io_context& ioc)
: sock_(ioc)
{
}
friend void beast_close_socket(my_socket& s)
{
error_code ec;
s.sock_.close(ec);
// ignore the error
}
};
@endcode
@param sock The socket to close. If the customization point is not
defined for the type of this object, or one of its base classes,
then a compiler error results.
@see beast_close_socket
*/
#if BOOST_BEAST_DOXYGEN
template<class Socket>
void
close_socket(Socket& sock);
#else
BOOST_BEAST_INLINE_VARIABLE(close_socket, detail::close_socket_impl)
#endif
} // beast
} // boost
#endif

View File

@@ -0,0 +1,148 @@
//
// 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_STRING_HPP
#define BOOST_BEAST_STRING_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/version.hpp>
#if defined(BOOST_BEAST_USE_STD_STRING_VIEW)
#include <string_view>
#else
#include <boost/utility/string_view.hpp>
#endif
#include <algorithm>
namespace boost {
namespace beast {
#if defined(BOOST_BEAST_USE_STD_STRING_VIEW)
/// The type of string view used by the library
using string_view = std::string_view;
/// The type of basic string view used by the library
template<class CharT, class Traits>
using basic_string_view =
std::basic_string_view<CharT, Traits>;
#else
/// The type of string view used by the library
using string_view = boost::string_view;
/// The type of basic string view used by the library
template<class CharT, class Traits>
using basic_string_view =
boost::basic_string_view<CharT, Traits>;
#endif
namespace detail {
inline
char
ascii_tolower(char c)
{
return ((static_cast<unsigned>(c) - 65U) < 26) ?
c + 'a' - 'A' : c;
}
template<class = void>
bool
iequals(
beast::string_view lhs,
beast::string_view rhs)
{
auto n = lhs.size();
if(rhs.size() != n)
return false;
auto p1 = lhs.data();
auto p2 = rhs.data();
char a, b;
// fast loop
while(n--)
{
a = *p1++;
b = *p2++;
if(a != b)
goto slow;
}
return true;
slow:
do
{
if(ascii_tolower(a) != ascii_tolower(b))
return false;
a = *p1++;
b = *p2++;
}
while(n--);
return true;
}
} // detail
/** Returns `true` if two strings are equal, using a case-insensitive comparison.
The case-comparison operation is defined only for low-ASCII characters.
@param lhs The string on the left side of the equality
@param rhs The string on the right side of the equality
*/
inline
bool
iequals(
beast::string_view lhs,
beast::string_view rhs)
{
return detail::iequals(lhs, rhs);
}
/** A case-insensitive less predicate for strings.
The case-comparison operation is defined only for low-ASCII characters.
*/
struct iless
{
bool
operator()(
string_view lhs,
string_view rhs) const
{
using std::begin;
using std::end;
return std::lexicographical_compare(
begin(lhs), end(lhs), begin(rhs), end(rhs),
[](char c1, char c2)
{
return detail::ascii_tolower(c1) < detail::ascii_tolower(c2);
}
);
}
};
/** A case-insensitive equality predicate for strings.
The case-comparison operation is defined only for low-ASCII characters.
*/
struct iequal
{
bool
operator()(
string_view lhs,
string_view rhs) const
{
return iequals(lhs, rhs);
}
};
} // beast
} // boost
#endif

View File

@@ -0,0 +1,130 @@
//
// 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_STRING_PARAM_HPP
#define BOOST_BEAST_STRING_PARAM_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/string.hpp>
#include <boost/beast/core/static_string.hpp>
#include <boost/beast/core/detail/static_ostream.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/optional.hpp>
namespace boost {
namespace beast {
/** A function parameter which efficiently converts to string.
This is used as a function parameter type to allow callers
notational convenience: objects other than strings may be
passed in contexts where a string is expected. The conversion
to string is made using `operator<<` to a non-dynamically
allocated static buffer if possible, else to a `std::string`
on overflow.
To use it, modify your function signature to accept
`string_param` and then extract the string inside the
function:
@code
void print(string_param s)
{
std::cout << s.str();
}
@endcode
*/
class string_param
{
string_view sv_;
char buf_[128];
boost::optional<detail::static_ostream> os_;
template<class T>
typename std::enable_if<
std::is_integral<T>::value>::type
print(T const&);
template<class T>
typename std::enable_if<
! std::is_integral<T>::value &&
! std::is_convertible<T, string_view>::value
>::type
print(T const&);
void
print(string_view);
template<class T>
typename std::enable_if<
std::is_integral<T>::value>::type
print_1(T const&);
template<class T>
typename std::enable_if<
! std::is_integral<T>::value>::type
print_1(T const&);
void
print_n()
{
}
template<class T0, class... TN>
void
print_n(T0 const&, TN const&...);
template<class T0, class T1, class... TN>
void
print(T0 const&, T1 const&, TN const&...);
public:
/// Copy constructor (disallowed)
string_param(string_param const&) = delete;
/// Copy assignment (disallowed)
string_param& operator=(string_param const&) = delete;
/** Constructor
This function constructs a string as if by concatenating
the result of streaming each argument in order into an
output stream. It is used as a notational convenience
at call sites which expect a parameter with the semantics
of a @ref string_view.
The implementation uses a small, internal static buffer
to avoid memory allocations especially for the case where
the list of arguments to be converted consists of a single
integral type.
@param args One or more arguments to convert
*/
template<class... Args>
string_param(Args const&... args);
/// Returns the contained string
string_view
str() const
{
return sv_;
}
/// Implicit conversion to @ref string_view
operator string_view const() const
{
return sv_;
}
};
} // beast
} // boost
#include <boost/beast/core/impl/string_param.hpp>
#endif

View File

@@ -0,0 +1,34 @@
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
#ifndef BOOST_BEAST_CORE_TCP_STREAM_HPP
#define BOOST_BEAST_CORE_TCP_STREAM_HPP
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/basic_stream.hpp>
#include <boost/beast/core/rate_policy.hpp>
#include <boost/asio/executor.hpp>
#include <boost/asio/ip/tcp.hpp>
namespace boost {
namespace beast {
/** A TCP/IP stream socket with timeouts and a polymorphic executor.
@see basic_stream
*/
using tcp_stream = basic_stream<
net::ip::tcp,
net::executor,
unlimited_rate_policy>;
} // beast
} // boost
#endif

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_TYPE_TRAITS_HPP
#define BOOST_BEAST_TYPE_TRAITS_HPP
#ifndef BOOST_BEAST_DOXYGEN
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/core/detail/is_invocable.hpp>
#include <boost/config/pragma_message.hpp>
#include <type_traits.hpp>
BOOST_PRAGMA_MESSAGE("<boost/beast/core/type_traits.hpp> is DEPRECATED and will be removed in a future release.")
namespace boost {
namespace beast {
/** Determine if `T` meets the requirements of <em>CompletionHandler</em>.
This trait checks whether a type meets the requirements for a completion
handler, and is also callable with the specified signature.
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
struct handler
{
void operator()(error_code&);
};
static_assert(is_completion_handler<handler, void(error_code&)>::value,
"Not a completion handler");
@endcode
*/
template<class T, class Signature>
#if BOOST_BEAST_DOXYGEN
using is_completion_handler = __see_below__
#else
using is_completion_handler = std::integral_constant<bool,
std::is_move_constructible<typename std::decay<T>::type>::value &&
detail::is_invocable<T, Signature>::value>;
#endif
} // beast
} // boost
#endif
#endif