update boost

This commit is contained in:
2023-11-24 12:56:13 -06:00
parent cfc99971af
commit 19d727037a
9260 changed files with 849256 additions and 299957 deletions

View File

@@ -42,8 +42,9 @@ struct allocate_stable_state final
allocate_stable_state>;
A a(this->get());
detail::allocator_traits<A>::destroy(a, this);
detail::allocator_traits<A>::deallocate(a, this, 1);
auto* p = this;
p->~allocate_stable_state();
a.deallocate(p, 1);
}
};
@@ -54,12 +55,13 @@ template<
class Executor1,
class Allocator,
class Function>
void asio_handler_invoke(
boost::asio::asio_handler_invoke_is_deprecated
asio_handler_invoke(
Function&& f,
async_base<Handler, Executor1, Allocator>* p)
{
using net::asio_handler_invoke;
asio_handler_invoke(f,
using boost::asio::asio_handler_invoke;
return asio_handler_invoke(f,
p->get_legacy_handler_pointer());
}
@@ -67,12 +69,12 @@ template<
class Handler,
class Executor1,
class Allocator>
void*
boost::asio::asio_handler_allocate_is_deprecated
asio_handler_allocate(
std::size_t size,
async_base<Handler, Executor1, Allocator>* p)
{
using net::asio_handler_allocate;
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(size,
p->get_legacy_handler_pointer());
}
@@ -81,13 +83,13 @@ template<
class Handler,
class Executor1,
class Allocator>
void
boost::asio::asio_handler_deallocate_is_deprecated
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,
using boost::asio::asio_handler_deallocate;
return asio_handler_deallocate(mem, size,
p->get_legacy_handler_pointer());
}
@@ -99,7 +101,7 @@ bool
asio_handler_is_continuation(
async_base<Handler, Executor1, Allocator>* p)
{
using net::asio_handler_is_continuation;
using boost::asio::asio_handler_is_continuation;
return asio_handler_is_continuation(
p->get_legacy_handler_pointer());
}
@@ -118,33 +120,30 @@ allocate_stable(
{
using allocator_type = typename stable_async_base<
Handler, Executor1, Allocator>::allocator_type;
using state = detail::allocate_stable_state<
State, allocator_type>;
using A = typename detail::allocator_traits<
allocator_type>::template rebind_alloc<
detail::allocate_stable_state<
State, allocator_type>>;
allocator_type>::template rebind_alloc<state>;
struct deleter
{
allocator_type alloc;
detail::allocate_stable_state<
State, allocator_type>* ptr;
state* ptr;
~deleter()
{
if(ptr)
{
A a(alloc);
detail::allocator_traits<A>::deallocate(a, ptr, 1);
a.deallocate(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)...);
deleter d{base.get_allocator(), a.allocate(1)};
::new(static_cast<void*>(d.ptr))
state(d.alloc, std::forward<Args>(args)...);
d.ptr->next_ = base.list_;
base.list_ = d.ptr;
return boost::exchange(d.ptr, nullptr)->value;

View File

@@ -13,9 +13,7 @@
#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>
@@ -145,28 +143,43 @@ template<class Protocol, class Executor, class RatePolicy>
void
basic_stream<Protocol, Executor, RatePolicy>::
impl_type::
close()
close() noexcept
{
socket.close();
{
error_code ec;
socket.close(ec);
}
#if !defined(BOOST_NO_EXCEPTIONS)
try
{
timer.cancel();
}
catch(...)
{
}
#else
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();
#endif
}
//------------------------------------------------------------------------------
template<class Protocol, class Executor, class RatePolicy>
template<class Executor2>
struct basic_stream<Protocol, Executor, RatePolicy>::
timeout_handler
{
using executor_type = Executor2;
op_state& state;
boost::weak_ptr<impl_type> wp;
tick_type tick;
executor_type ex;
executor_type get_executor() const noexcept
{
return ex;
}
void
operator()(error_code ec)
@@ -211,63 +224,35 @@ class transfer_op
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());
if (isRead)
return impl_->read;
else
return impl_->write;
}
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);
if (isRead)
return rate_policy_access::
available_read_bytes(impl_->policy());
else
return rate_policy_access::
available_write_bytes(impl_->policy());
}
void
transfer_bytes(std::size_t n)
{
transfer_bytes(n, is_read{});
if (isRead)
rate_policy_access::
transfer_read_bytes(impl_->policy(), n);
else
rate_policy_access::
transfer_write_bytes(impl_->policy(), n);
}
void
@@ -288,6 +273,8 @@ class transfer_op
std::move(*this));
}
static bool never_pending_;
public:
template<class Handler_>
transfer_op(
@@ -297,10 +284,26 @@ public:
: async_base<Handler, Executor>(
std::forward<Handler_>(h), s.get_executor())
, impl_(s.impl_)
, pg_(state().pending)
, pg_()
, b_(b)
{
(*this)({});
this->set_allowed_cancellation(net::cancellation_type::all);
if (buffer_bytes(b_) == 0 && state().pending)
{
// Workaround:
// Corner case discovered in https://github.com/boostorg/beast/issues/2065
// Enclosing SSL stream wishes to complete a 0-length write early by
// executing a 0-length read against the underlying stream.
// This can occur even if an existing async_read is in progress.
// In this specific case, we will complete the async op with no error
// in order to prevent assertions and/or internal corruption of the basic_stream
this->complete(false, error_code(), 0);
}
else
{
pg_.assign(state().pending);
(*this)({});
}
}
void
@@ -315,27 +318,39 @@ public:
{
// make sure we perform the no-op
BOOST_ASIO_CORO_YIELD
async_perform(0, is_read{});
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
(isRead ? "basic_stream::async_read_some"
: "basic_stream::async_write_some")));
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;
BOOST_BEAST_ASSIGN_EC(ec, beast::error::timeout);
}
goto upcall;
}
// if a timeout is active, wait on the timer
if(state().timer.expiry() != never())
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
(isRead ? "basic_stream::async_read_some"
: "basic_stream::async_write_some")));
state().timer.async_wait(
net::bind_executor(
this->get_executor(),
timeout_handler{
state(),
impl_,
state().tick
}));
timeout_handler<decltype(this->get_executor())>{
state(),
impl_,
state().tick,
this->get_executor()});
}
// check rate limit, maybe wait
std::size_t amount;
@@ -344,7 +359,14 @@ public:
{
++impl_->waiting;
BOOST_ASIO_CORO_YIELD
impl_->timer.async_wait(std::move(*this));
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
(isRead ? "basic_stream::async_read_some"
: "basic_stream::async_write_some")));
impl_->timer.async_wait(std::move(*this));
}
if(ec)
{
// socket was closed, or a timeout
@@ -354,7 +376,7 @@ public:
if(state().timeout)
{
// yes, socket already closed
ec = beast::error::timeout;
BOOST_BEAST_ASSIGN_EC(ec, beast::error::timeout);
state().timeout = false;
}
goto upcall;
@@ -368,7 +390,14 @@ public:
}
BOOST_ASIO_CORO_YIELD
async_perform(amount, is_read{});
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
(isRead ? "basic_stream::async_read_some"
: "basic_stream::async_write_some")));
async_perform(amount, is_read{});
}
if(state().timer.expiry() != never())
{
@@ -383,7 +412,7 @@ public:
if(state().timeout)
{
// yes, socket already closed
ec = beast::error::timeout;
BOOST_BEAST_ASSIGN_EC(ec, beast::error::timeout);
state().timeout = false;
}
}
@@ -428,14 +457,24 @@ public:
, pg0_(impl_->read.pending)
, pg1_(impl_->write.pending)
{
this->set_allowed_cancellation(net::cancellation_type::all);
if(state().timer.expiry() != stream_base::never())
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"basic_stream::async_connect"));
impl_->write.timer.async_wait(
net::bind_executor(
this->get_executor(),
timeout_handler{
state(),
impl_,
state().tick}));
timeout_handler<decltype(this->get_executor())>{
state(),
impl_,
state().tick,
this->get_executor()});
}
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"basic_stream::async_connect"));
impl_->socket.async_connect(
ep, std::move(*this));
@@ -456,14 +495,24 @@ public:
, pg0_(impl_->read.pending)
, pg1_(impl_->write.pending)
{
this->set_allowed_cancellation(net::cancellation_type::all);
if(state().timer.expiry() != stream_base::never())
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"basic_stream::async_connect"));
impl_->write.timer.async_wait(
net::bind_executor(
this->get_executor(),
timeout_handler{
state(),
impl_,
state().tick}));
timeout_handler<decltype(this->get_executor())>{
state(),
impl_,
state().tick,
this->get_executor()});
}
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"basic_stream::async_connect"));
net::async_connect(impl_->socket,
eps, cond, std::move(*this));
@@ -484,14 +533,24 @@ public:
, pg0_(impl_->read.pending)
, pg1_(impl_->write.pending)
{
this->set_allowed_cancellation(net::cancellation_type::all);
if(state().timer.expiry() != stream_base::never())
{
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"basic_stream::async_connect"));
impl_->write.timer.async_wait(
net::bind_executor(
this->get_executor(),
timeout_handler{
state(),
impl_,
state().tick}));
timeout_handler<decltype(this->get_executor())>{
state(),
impl_,
state().tick,
this->get_executor()});
}
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"basic_stream::async_connect"));
net::async_connect(impl_->socket,
begin, end, cond, std::move(*this));
@@ -515,7 +574,7 @@ public:
if(state().timeout)
{
// yes, socket already closed
ec = beast::error::timeout;
BOOST_BEAST_ASSIGN_EC(ec, beast::error::timeout);
state().timeout = false;
}
}
@@ -705,7 +764,20 @@ basic_stream(basic_stream&& other)
: impl_(boost::make_shared<impl_type>(
std::move(*other.impl_)))
{
// VFALCO I'm not sure this implementation is correct...
// Explainer: Asio's sockets provide the guarantee that a moved-from socket
// will be in a state as-if newly created. i.e.:
// * having the same (valid) executor
// * the socket shall not be open
// We provide the same guarantee by moving the impl rather than the pointer
// controlling its lifetime.
}
template<class Protocol, class Executor, class RatePolicy>
template<class Executor_>
basic_stream<Protocol, Executor, RatePolicy>::
basic_stream(basic_stream<Protocol, Executor_, RatePolicy> && other)
: impl_(boost::make_shared<impl_type>(std::false_type{}, std::move(other.impl_->socket)))
{
}
//------------------------------------------------------------------------------
@@ -723,7 +795,7 @@ release_socket() ->
template<class Protocol, class Executor, class RatePolicy>
void
basic_stream<Protocol, Executor, RatePolicy>::
expires_after(std::chrono::nanoseconds expiry_time)
expires_after(net::steady_timer::duration expiry_time)
{
// If assert goes off, it means that there are
// already read or write (or connect) operations
@@ -800,7 +872,7 @@ close()
//------------------------------------------------------------------------------
template<class Protocol, class Executor, class RatePolicy>
template<class ConnectHandler>
template<BOOST_BEAST_ASYNC_TPARAM1 ConnectHandler>
BOOST_BEAST_ASYNC_RESULT1(ConnectHandler)
basic_stream<Protocol, Executor, RatePolicy>::
async_connect(
@@ -819,7 +891,7 @@ async_connect(
template<class Protocol, class Executor, class RatePolicy>
template<
class EndpointSequence,
class RangeConnectHandler,
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, typename Protocol::endpoint)) RangeConnectHandler,
class>
BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,void(error_code, typename Protocol::endpoint))
basic_stream<Protocol, Executor, RatePolicy>::
@@ -841,7 +913,7 @@ template<class Protocol, class Executor, class RatePolicy>
template<
class EndpointSequence,
class ConnectCondition,
class RangeConnectHandler,
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, typename Protocol::endpoint)) RangeConnectHandler,
class>
BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,void (error_code, typename Protocol::endpoint))
basic_stream<Protocol, Executor, RatePolicy>::
@@ -863,7 +935,7 @@ async_connect(
template<class Protocol, class Executor, class RatePolicy>
template<
class Iterator,
class IteratorConnectHandler>
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, Iterator)) IteratorConnectHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,void (error_code, Iterator))
basic_stream<Protocol, Executor, RatePolicy>::
async_connect(
@@ -884,7 +956,7 @@ template<class Protocol, class Executor, class RatePolicy>
template<
class Iterator,
class ConnectCondition,
class IteratorConnectHandler>
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, Iterator)) IteratorConnectHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,void (error_code, Iterator))
basic_stream<Protocol, Executor, RatePolicy>::
async_connect(
@@ -905,7 +977,7 @@ async_connect(
//------------------------------------------------------------------------------
template<class Protocol, class Executor, class RatePolicy>
template<class MutableBufferSequence, class ReadHandler>
template<class MutableBufferSequence, BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
basic_stream<Protocol, Executor, RatePolicy>::
async_read_some(
@@ -925,7 +997,7 @@ async_read_some(
}
template<class Protocol, class Executor, class RatePolicy>
template<class ConstBufferSequence, class WriteHandler>
template<class ConstBufferSequence, BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
basic_stream<Protocol, Executor, RatePolicy>::
async_write_some(

View File

@@ -15,7 +15,7 @@
#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/beast/core/detail/is_invocable.hpp>
#include <boost/asio/post.hpp>
#include <boost/throw_exception.hpp>
@@ -110,7 +110,7 @@ struct run_read_op
operator()(
ReadHandler&& h,
buffered_read_stream* s,
Buffers const& b)
Buffers const* b)
{
// If you get an error on the following line it means
// that your handler does not meet the documented type
@@ -124,7 +124,7 @@ struct run_read_op
read_op<
Buffers,
typename std::decay<ReadHandler>::type>(
std::forward<ReadHandler>(h), *s, b);
std::forward<ReadHandler>(h), *s, *b);
}
};
@@ -141,7 +141,7 @@ buffered_read_stream(Args&&... args)
}
template<class Stream, class DynamicBuffer>
template<class ConstBufferSequence, class WriteHandler>
template<class ConstBufferSequence, BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
buffered_read_stream<Stream, DynamicBuffer>::
async_write_some(
@@ -153,7 +153,7 @@ async_write_some(
static_assert(net::is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence type requirements not met");
static_assert(detail::is_invocable<WriteHandler,
static_assert(detail::is_completion_token_for<WriteHandler,
void(error_code, std::size_t)>::value,
"WriteHandler type requirements not met");
return next_layer_.async_write_some(buffers,
@@ -212,7 +212,7 @@ read_some(MutableBufferSequence const& buffers,
}
template<class Stream, class DynamicBuffer>
template<class MutableBufferSequence, class ReadHandler>
template<class MutableBufferSequence, BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
buffered_read_stream<Stream, DynamicBuffer>::
async_read_some(
@@ -233,7 +233,7 @@ async_read_some(
typename ops::run_read_op{},
handler,
this,
buffers);
&buffers);
}
} // beast

View File

@@ -11,7 +11,7 @@
#define BOOST_BEAST_IMPL_BUFFERS_ADAPTOR_HPP
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/beast/core/buffers_adaptor.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/config/workaround.hpp>
#include <boost/throw_exception.hpp>
@@ -35,71 +35,88 @@ namespace beast {
template<class MutableBufferSequence>
template<bool isMutable>
class buffers_adaptor<MutableBufferSequence>::
readable_bytes
class buffers_adaptor<MutableBufferSequence>::subrange
{
buffers_adaptor const* b_;
public:
using value_type = typename
std::conditional<isMutable,
net::mutable_buffer,
net::const_buffer>::type;
using value_type = typename std::conditional<
isMutable,
net::mutable_buffer,
net::const_buffer>::type;
class const_iterator;
struct iterator;
readable_bytes() = delete;
// construct from two iterators plus optionally subrange definition
subrange(
iter_type first, // iterator to first buffer in storage
iter_type last, // iterator to last buffer in storage
std::size_t pos = 0, // the offset in bytes from the beginning of the storage
std::size_t n = // the total length of the subrange
(std::numeric_limits<std::size_t>::max)());
#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
readable_bytes(
readable_bytes const& other)
: b_(other.b_)
subrange(
subrange const& other)
: first_(other.first_)
, last_(other.last_)
, first_offset_(other.first_offset_)
, last_size_(other.last_size_)
{
}
readable_bytes& operator=(
readable_bytes const& other)
subrange& operator=(
subrange const& other)
{
b_ = other.b_;
first_ = other.first_;
last_ = other.last_;
first_offset_ = other.first_offset_;
last_size_ = other.last_size_;
return *this;
}
#else
readable_bytes(
readable_bytes const&) = default;
readable_bytes& operator=(
readable_bytes const&) = default;
subrange(
subrange const&) = default;
subrange& operator=(
subrange 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_)
// allow conversion from mutable to const
template<bool isMutable_ = isMutable, typename
std::enable_if<!isMutable_>::type * = nullptr>
subrange(subrange<true> const &other)
: first_(other.first_)
, last_(other.last_)
, first_offset_(other.first_offset_)
, last_size_(other.last_size_)
{
}
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
iterator
begin() const;
const_iterator
iterator
end() const;
private:
friend class buffers_adaptor;
readable_bytes(buffers_adaptor const& b)
: b_(&b)
{
}
friend subrange<!isMutable>;
void
adjust(
std::size_t pos,
std::size_t n);
private:
// points to the first buffer in the sequence
iter_type first_;
// Points to one past the end of the underlying buffer sequence
iter_type last_;
// The initial offset into the first buffer
std::size_t first_offset_;
// how many bytes in the penultimate buffer are used (if any)
std::size_t last_size_;
};
#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
@@ -110,257 +127,55 @@ private:
template<class MutableBufferSequence>
template<bool isMutable>
class buffers_adaptor<MutableBufferSequence>::
readable_bytes<isMutable>::
const_iterator
struct buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator
{
iter_type it_{};
buffers_adaptor const* b_ = nullptr;
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = typename
std::conditional<isMutable,
net::mutable_buffer,
net::const_buffer>::type;
using pointer = value_type const*;
using reference = value_type;
buffers_adaptor<MutableBufferSequence>::
template subrange<isMutable>::
value_type;
using reference = value_type&;
using pointer = 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;
iterator(
subrange<isMutable> const *parent,
iter_type it);
bool
operator==(const_iterator const& other) const
{
return b_ == other.b_ && it_ == other.it_;
}
iterator();
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);
}
value_type
operator*() const;
pointer
operator->() const = delete;
const_iterator&
operator++()
{
++it_;
return *this;
}
iterator &
operator++();
const_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
iterator
operator++(int);
const_iterator&
operator--()
{
--it_;
return *this;
}
iterator &
operator--();
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;
iterator
operator--(int);
bool
operator==(const_iterator const& other) const
{
return b_ == other.b_ && it_ == other.it_;
}
operator==(iterator const &b) const;
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;
}
operator!=(iterator const &b) const;
private:
friend class mutable_buffers_type;
const_iterator(buffers_adaptor const& b,
iter_type iter)
: it_(iter)
, b_(&b)
{
}
subrange<isMutable> const *parent_;
iter_type it_;
};
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>
@@ -380,9 +195,9 @@ buffers_adaptor(
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))
, 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_)
@@ -480,16 +295,20 @@ buffers_adaptor<MutableBufferSequence>::
data() const noexcept ->
const_buffers_type
{
return const_buffers_type{*this};
return const_buffers_type(
begin_, end_,
in_pos_, in_size_);
}
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
data() noexcept ->
mutable_data_type
mutable_buffers_type
{
return mutable_data_type{*this};
return mutable_buffers_type(
begin_, end_,
in_pos_, in_size_);
}
template<class MutableBufferSequence>
@@ -498,6 +317,7 @@ buffers_adaptor<MutableBufferSequence>::
prepare(std::size_t n) ->
mutable_buffers_type
{
auto prepared = n;
end_ = out_;
if(end_ != net::buffer_sequence_end(bs_))
{
@@ -530,7 +350,7 @@ prepare(std::size_t n) ->
if(n > 0)
BOOST_THROW_EXCEPTION(std::length_error{
"buffers_adaptor too long"});
return mutable_buffers_type{*this};
return mutable_buffers_type(out_, end_, out_pos_, prepared);
}
template<class MutableBufferSequence>
@@ -602,6 +422,285 @@ consume(std::size_t n) noexcept
}
}
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
make_subrange(std::size_t pos, std::size_t n) ->
subrange<true>
{
return subrange<true>(
begin_, net::buffer_sequence_end(bs_),
in_pos_ + pos, n);
}
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
make_subrange(std::size_t pos, std::size_t n) const ->
subrange<false>
{
return subrange<false>(
begin_, net::buffer_sequence_end(bs_),
in_pos_ + pos, n);
}
// -------------------------------------------------------------------------
// subrange
template<class MutableBufferSequence>
template<bool isMutable>
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
subrange(
iter_type first, // iterator to first buffer in storage
iter_type last, // iterator to last buffer in storage
std::size_t pos, // the offset in bytes from the beginning of the storage
std::size_t n) // the total length of the subrange
: first_(first)
, last_(last)
, first_offset_(0)
, last_size_((std::numeric_limits<std::size_t>::max)())
{
adjust(pos, n);
}
template<class MutableBufferSequence>
template<bool isMutable>
void
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
adjust(
std::size_t pos,
std::size_t n)
{
if (n == 0)
last_ = first_;
if (first_ == last_)
{
first_offset_ = 0;
last_size_ = 0;
return;
}
auto is_last = [this](iter_type iter) {
return std::next(iter) == last_;
};
pos += first_offset_;
while (pos)
{
auto adjust = (std::min)(pos, first_->size());
if (adjust >= first_->size())
{
++first_;
first_offset_ = 0;
pos -= adjust;
}
else
{
first_offset_ = adjust;
pos = 0;
break;
}
}
auto current = first_;
auto max_elem = current->size() - first_offset_;
if (is_last(current))
{
// both first and last element
last_size_ = (std::min)(max_elem, n);
last_ = std::next(current);
return;
}
else if (max_elem >= n)
{
last_ = std::next(current);
last_size_ = n;
}
else
{
n -= max_elem;
++current;
}
for (;;)
{
max_elem = current->size();
if (is_last(current))
{
last_size_ = (std::min)(n, last_size_);
return;
}
else if (max_elem < n)
{
n -= max_elem;
++current;
}
else
{
last_size_ = n;
last_ = std::next(current);
return;
}
}
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
begin() const ->
iterator
{
return iterator(this, first_);
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
end() const ->
iterator
{
return iterator(this, last_);
}
// -------------------------------------------------------------------------
// buffers_adaptor::subrange::iterator
template<class MutableBufferSequence>
template<bool isMutable>
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
iterator()
: parent_(nullptr)
, it_()
{
}
template<class MutableBufferSequence>
template<bool isMutable>
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
iterator(subrange<isMutable> const *parent,
iter_type it)
: parent_(parent)
, it_(it)
{
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator*() const ->
value_type
{
value_type result = *it_;
if (it_ == parent_->first_)
result += parent_->first_offset_;
if (std::next(it_) == parent_->last_)
{
result = value_type(
result.data(),
(std::min)(
parent_->last_size_,
result.size()));
}
return result;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator++() ->
iterator &
{
++it_;
return *this;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator++(int) ->
iterator
{
auto result = *this;
++it_;
return result;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator--() ->
iterator &
{
--it_;
return *this;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator--(int) ->
iterator
{
auto result = *this;
--it_;
return result;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator==(iterator const &b) const ->
bool
{
return it_ == b.it_;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator!=(iterator const &b) const ->
bool
{
return !(*this == b);
}
} // beast
} // boost

View File

@@ -11,7 +11,6 @@
#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>
@@ -23,9 +22,37 @@
namespace boost {
namespace beast {
template<class Buffer>
class buffers_cat_view<Buffer>
{
Buffer buffer_;
public:
using value_type = buffers_type<Buffer>;
using const_iterator = buffers_iterator_type<Buffer>;
explicit
buffers_cat_view(Buffer const& buffer)
: buffer_(buffer)
{
}
const_iterator
begin() const
{
return net::buffer_sequence_begin(buffer_);
}
const_iterator
end() const
{
return net::buffer_sequence_end(buffer_);
}
};
#if defined(_MSC_VER) && ! defined(__clang__)
# define BOOST_BEAST_UNREACHABLE() __assume(false)
# define BOOST_BEAST_UNREACHABLE_RETURN(v) __assume(false)
# define BOOST_BEAST_UNREACHABLE_RETURN(v) return v
#else
# define BOOST_BEAST_UNREACHABLE() __builtin_unreachable()
# define BOOST_BEAST_UNREACHABLE_RETURN(v) \
@@ -35,30 +62,18 @@ namespace beast {
#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
@@ -70,11 +85,11 @@ struct buffers_cat_view_iterator_base
{
char unused = 0; // make g++8 happy
net::mutable_buffer
BOOST_NORETURN net::mutable_buffer
operator*() const
{
BOOST_BEAST_LOGIC_ERROR_RETURN({},
"Dereferencing a one-past-the-end iterator");
BOOST_BEAST_LOGIC_ERROR(
"Dereferencing a one-past-the-end iterator");
}
operator bool() const noexcept
@@ -158,10 +173,10 @@ private:
{
const_iterator const& self;
reference
BOOST_NORETURN reference
operator()(mp11::mp_size_t<0>)
{
BOOST_BEAST_LOGIC_ERROR_RETURN({},
BOOST_BEAST_LOGIC_ERROR(
"Dereferencing a default-constructed iterator");
}

View File

@@ -0,0 +1,173 @@
//
// Copyright (c) 2022 Seth Heeren (sgheeren at gmail dot com)
//
// Distributed under the 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_BUFFERS_GENERATOR_HPP
#define BOOST_BEAST_CORE_IMPL_BUFFERS_GENERATOR_HPP
#include <boost/beast/core/buffers_generator.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/compose.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/beast/core/stream_traits.hpp>
#include <boost/throw_exception.hpp>
#include <type_traits>
namespace boost {
namespace beast {
namespace detail {
template <
class AsyncWriteStream,
class BuffersGenerator>
struct write_buffers_generator_op
: boost::asio::coroutine
{
write_buffers_generator_op(
AsyncWriteStream& s, BuffersGenerator g)
: s_(s)
, g_(std::move(g))
{
}
template<class Self>
void operator()(
Self& self, error_code ec = {}, std::size_t n = 0)
{
BOOST_ASIO_CORO_REENTER(*this)
{
while(! g_.is_done())
{
BOOST_ASIO_CORO_YIELD
{
auto cb = g_.prepare(ec);
if(ec)
goto complete;
s_.async_write_some(
cb, std::move(self));
}
if(ec)
goto complete;
g_.consume(n);
total_ += n;
}
complete:
self.complete(ec, total_);
}
}
private:
AsyncWriteStream& s_;
BuffersGenerator g_;
std::size_t total_ = 0;
};
} // detail
template<
class SyncWriteStream,
class BuffersGenerator,
typename std::enable_if< //
is_buffers_generator<typename std::decay<
BuffersGenerator>::type>::value>::type* /*= nullptr*/
>
size_t
write(
SyncWriteStream& stream,
BuffersGenerator&& generator,
beast::error_code& ec)
{
static_assert(
is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream type requirements not met");
ec.clear();
size_t total = 0;
while(! generator.is_done())
{
auto cb = generator.prepare(ec);
if(ec)
break;
size_t n = net::write(stream, cb, ec);
if(ec)
break;
generator.consume(n);
total += n;
}
return total;
}
//----------------------------------------------------------
template<
class SyncWriteStream,
class BuffersGenerator,
typename std::enable_if<is_buffers_generator<
typename std::decay<BuffersGenerator>::type>::value>::
type* /*= nullptr*/
>
std::size_t
write(SyncWriteStream& stream, BuffersGenerator&& generator)
{
static_assert(
is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream type requirements not met");
beast::error_code ec;
std::size_t n = write(
stream, std::forward<BuffersGenerator>(generator), ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ ec });
return n;
}
//----------------------------------------------------------
template<
class AsyncWriteStream,
class BuffersGenerator,
BOOST_BEAST_ASYNC_TPARAM2 CompletionToken,
typename std::enable_if<is_buffers_generator<
BuffersGenerator>::value>::type* /*= nullptr*/
>
BOOST_BEAST_ASYNC_RESULT2(CompletionToken)
async_write(
AsyncWriteStream& stream,
BuffersGenerator generator,
CompletionToken&& token)
{
static_assert(
beast::is_async_write_stream<AsyncWriteStream>::value,
"AsyncWriteStream type requirements not met");
return net::async_compose< //
CompletionToken,
void(error_code, std::size_t)>(
detail::write_buffers_generator_op<
AsyncWriteStream,
BuffersGenerator>{ stream, std::move(generator) },
token,
stream);
}
} // namespace beast
} // namespace boost
#endif

View File

@@ -26,6 +26,9 @@ public:
return "boost.beast";
}
error_codes() : error_category(0x002f6e94401c6e8bu) {}
BOOST_BEAST_DECL
std::string
message(int ev) const override
@@ -62,6 +65,9 @@ public:
return "boost.beast";
}
error_conditions() : error_category(0x3dd0b0ce843c5b10u) {}
BOOST_BEAST_DECL
std::string
message(int cv) const override

View File

@@ -165,14 +165,14 @@ open(char const* path, file_mode mode, error_code& ec)
break;
case file_mode::append:
f = O_WRONLY | O_CREAT | O_TRUNC;
f = O_WRONLY | O_CREAT | O_APPEND;
#if BOOST_BEAST_USE_POSIX_FADVISE
advise = POSIX_FADV_SEQUENTIAL;
#endif
break;
case file_mode::append_existing:
f = O_WRONLY;
f = O_WRONLY | O_APPEND;
#if BOOST_BEAST_USE_POSIX_FADVISE
advise = POSIX_FADV_SEQUENTIAL;
#endif
@@ -270,8 +270,12 @@ read(void* buffer, std::size_t n, error_code& ec) const
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)));
// <limits> not required to define SSIZE_MAX so we avoid it
constexpr auto ssmax =
static_cast<std::size_t>((std::numeric_limits<
decltype(::read(fd_, buffer, n))>::max)());
auto const amount = (std::min)(
n, ssmax);
auto const result = ::read(fd_, buffer, amount);
if(result == -1)
{
@@ -305,8 +309,12 @@ write(void const* buffer, std::size_t n, error_code& ec)
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)));
// <limits> not required to define SSIZE_MAX so we avoid it
constexpr auto ssmax =
static_cast<std::size_t>((std::numeric_limits<
decltype(::write(fd_, buffer, n))>::max)());
auto const amount = (std::min)(
n, ssmax);
auto const result = ::write(fd_, buffer, amount);
if(result == -1)
{

View File

@@ -11,6 +11,7 @@
#define BOOST_BEAST_CORE_IMPL_FILE_STDIO_IPP
#include <boost/beast/core/file_stdio.hpp>
#include <boost/beast/core/detail/win32_unicode_path.hpp>
#include <boost/config/workaround.hpp>
#include <boost/core/exchange.hpp>
#include <limits>
@@ -46,7 +47,7 @@ operator=(file_stdio&& other)
void
file_stdio::
native_handle(FILE* f)
native_handle(std::FILE* f)
{
if(f_)
fclose(f_);
@@ -79,31 +80,50 @@ open(char const* path, file_mode mode, error_code& ec)
fclose(f_);
f_ = nullptr;
}
ec = {};
#if defined(BOOST_MSVC) || defined(_MSVC_STL_VERSION)
boost::winapi::WCHAR_ const* s;
detail::win32_unicode_path unicode_path(path, ec);
if (ec)
return;
#else
char const* s;
#endif
switch(mode)
{
default:
case file_mode::read:
#if defined(BOOST_MSVC) || defined(_MSVC_STL_VERSION)
s = L"rb";
#else
s = "rb";
#endif
break;
case file_mode::scan:
#ifdef BOOST_MSVC
s = "rbS";
#if defined(BOOST_MSVC) || defined(_MSVC_STL_VERSION)
s = L"rbS";
#else
s = "rb";
#endif
break;
case file_mode::write:
#if defined(BOOST_MSVC) || defined(_MSVC_STL_VERSION)
s = L"wb+";
#else
s = "wb+";
#endif
break;
case file_mode::write_new:
{
#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
FILE* f0;
auto const ev = ::fopen_s(&f0, path, "rb");
#if defined(BOOST_MSVC) || defined(_MSVC_STL_VERSION)
# if (defined(BOOST_MSVC) && BOOST_MSVC >= 1910) || (defined(_MSVC_STL_VERSION) && _MSVC_STL_VERSION >= 141)
s = L"wbx";
# else
std::FILE* f0;
auto const ev = ::_wfopen_s(&f0, unicode_path.c_str(), L"rb");
if(! ev)
{
std::fclose(f0);
@@ -116,28 +136,36 @@ open(char const* path, file_mode mode, error_code& ec)
ec.assign(ev, generic_category());
return;
}
s = "wb";
s = L"wb";
# endif
#else
s = "wbx";
#endif
break;
}
case file_mode::write_existing:
#if defined(BOOST_MSVC) || defined(_MSVC_STL_VERSION)
s = L"rb+";
#else
s = "rb+";
#endif
break;
case file_mode::append:
#if defined(BOOST_MSVC) || defined(_MSVC_STL_VERSION)
s = L"ab";
#else
s = "ab";
#endif
break;
case file_mode::append_existing:
{
#ifdef BOOST_MSVC
FILE* f0;
#if defined(BOOST_MSVC) || defined(_MSVC_STL_VERSION)
std::FILE* f0;
auto const ev =
::fopen_s(&f0, path, "rb+");
::_wfopen_s(&f0, unicode_path.c_str(), L"rb+");
if(ev)
{
ec.assign(ev, generic_category());
@@ -153,13 +181,17 @@ open(char const* path, file_mode mode, error_code& ec)
}
#endif
std::fclose(f0);
#if defined(BOOST_MSVC) || defined(_MSVC_STL_VERSION)
s = L"ab";
#else
s = "ab";
#endif
break;
}
}
#ifdef BOOST_MSVC
auto const ev = ::fopen_s(&f_, path, s);
#if defined(BOOST_MSVC) || defined(_MSVC_STL_VERSION)
auto const ev = ::_wfopen_s(&f_, unicode_path.c_str(), s);
if(ev)
{
f_ = nullptr;
@@ -174,7 +206,6 @@ open(char const* path, file_mode mode, error_code& ec)
return;
}
#endif
ec = {};
}
std::uint64_t
@@ -241,7 +272,7 @@ seek(std::uint64_t offset, error_code& ec)
ec = make_error_code(errc::bad_file_descriptor);
return;
}
if(offset > (std::numeric_limits<long>::max)())
if(offset > static_cast<std::uint64_t>((std::numeric_limits<long>::max)()))
{
ec = make_error_code(errc::invalid_seek);
return;

View File

@@ -14,10 +14,10 @@
#if BOOST_BEAST_USE_WIN32_FILE
#include <boost/beast/core/detail/win32_unicode_path.hpp>
#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>
@@ -175,7 +175,7 @@ open(char const* path, file_mode mode, error_code& ec)
desired_access = boost::winapi::GENERIC_READ_ |
boost::winapi::GENERIC_WRITE_;
creation_disposition = boost::winapi::CREATE_ALWAYS_;
creation_disposition = boost::winapi::OPEN_ALWAYS_;
flags_and_attributes = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
break;
@@ -186,19 +186,40 @@ open(char const* path, file_mode mode, error_code& ec)
flags_and_attributes = 0x08000000; // FILE_FLAG_SEQUENTIAL_SCAN
break;
}
h_ = ::CreateFileA(
path,
detail::win32_unicode_path unicode_path(path, ec);
if (ec)
return;
h_ = ::CreateFileW(
unicode_path.c_str(),
desired_access,
share_mode,
NULL,
creation_disposition,
flags_and_attributes,
NULL);
if(h_ == boost::winapi::INVALID_HANDLE_VALUE_)
if (h_ == boost::winapi::INVALID_HANDLE_VALUE_)
{
ec.assign(boost::winapi::GetLastError(),
system_category());
else
ec = {};
return;
}
if (mode == file_mode::append ||
mode == file_mode::append_existing)
{
boost::winapi::LARGE_INTEGER_ in;
in.QuadPart = 0;
if (!detail::set_file_pointer_ex(h_, in, 0,
boost::winapi::FILE_END_))
{
ec.assign(boost::winapi::GetLastError(),
system_category());
boost::winapi::CloseHandle(h_);
h_ = boost::winapi::INVALID_HANDLE_VALUE_;
return;
}
}
ec = {};
}
std::uint64_t

View File

@@ -124,8 +124,6 @@ basic_flat_buffer(
end_ = nullptr;
max_ = other.max_;
copy_from(other);
other.clear();
other.shrink_to_fit();
return;
}
@@ -262,17 +260,32 @@ reserve(std::size_t n)
template<class Allocator>
void
basic_flat_buffer<Allocator>::
shrink_to_fit()
shrink_to_fit() noexcept
{
auto const len = size();
if(len == capacity())
return;
char* p;
if(len > 0)
{
BOOST_ASSERT(begin_);
BOOST_ASSERT(in_);
p = alloc(len);
#ifndef BOOST_NO_EXCEPTIONS
try
{
#endif
p = alloc(len);
#ifndef BOOST_NO_EXCEPTIONS
}
catch(std::exception const&)
{
// request could not be fulfilled,
// squelch the exception
return;
}
#endif
std::memcpy(p, in_, len);
}
else
@@ -403,7 +416,7 @@ copy_from(
if(begin_)
{
BOOST_ASSERT(other.begin_);
std::memcpy(begin_, other.begin_, n);
std::memcpy(begin_, other.in_, n);
}
}
@@ -436,8 +449,6 @@ move_assign(basic_flat_buffer& other, std::false_type)
if(this->get() != other.get())
{
copy_from(other);
other.clear();
other.shrink_to_fit();
}
else
{

View File

@@ -16,7 +16,6 @@
#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 {
@@ -30,7 +29,6 @@ template<class Handler>
class write_op
: public async_base<Handler,
beast::executor_type<flat_stream>>
, public net::coroutine
{
public:
template<
@@ -53,6 +51,11 @@ public:
s.buffer_.commit(net::buffer_copy(
s.buffer_.prepare(result.size),
b, result.size));
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"flat_stream::async_write_some"));
s.stream_.async_write_some(
s.buffer_.data(), std::move(*this));
}
@@ -60,6 +63,11 @@ public:
{
s.buffer_.clear();
s.buffer_.shrink_to_fit();
BOOST_ASIO_HANDLER_LOCATION((
__FILE__, __LINE__,
"flat_stream::async_write_some"));
s.stream_.async_write_some(
beast::buffers_prefix(
result.size, b), std::move(*this));
@@ -146,7 +154,7 @@ read_some(MutableBufferSequence const& buffers, error_code& ec)
template<class NextLayer>
template<
class MutableBufferSequence,
class ReadHandler>
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
flat_stream<NextLayer>::
async_read_some(
@@ -227,7 +235,7 @@ write_some(ConstBufferSequence const& buffers, error_code& ec)
template<class NextLayer>
template<
class ConstBufferSequence,
class WriteHandler>
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
flat_stream<NextLayer>::
async_write_some(

View File

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

View File

@@ -11,13 +11,13 @@
#define BOOST_BEAST_IMPL_MULTI_BUFFER_HPP
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/beast/core/detail/type_traits.hpp>
#include <boost/config/workaround.hpp>
#include <boost/core/exchange.hpp>
#include <boost/assert.hpp>
#include <boost/throw_exception.hpp>
#include <algorithm>
#include <exception>
#include <iterator>
#include <sstream>
#include <string>
#include <type_traits>
@@ -88,41 +88,6 @@ namespace beast {
in_pos_ out_pos_ == 0
out_end_ == 0
*/
//------------------------------------------------------------------------------
template<class Allocator>
class basic_multi_buffer<Allocator>::element
: public boost::intrusive::list_base_hook<
boost::intrusive::link_mode<
boost::intrusive::normal_link>>
{
using size_type = typename
detail::allocator_traits<Allocator>::size_type;
size_type const size_;
public:
element(element const&) = delete;
explicit
element(size_type n) noexcept
: size_(n)
{
}
size_type
size() const noexcept
{
return size_;
}
char*
data() const noexcept
{
return const_cast<char*>(
reinterpret_cast<char const*>(this + 1));
}
};
//------------------------------------------------------------------------------
@@ -134,17 +99,155 @@ public:
template<class Allocator>
template<bool isMutable>
class basic_multi_buffer<Allocator>::readable_bytes
class basic_multi_buffer<Allocator>::subrange
{
basic_multi_buffer const* b_;
const_iter begin_;
const_iter end_;
size_type begin_pos_; // offset in begin_
size_type last_pos_; // offset in std::prev(end_)
friend class basic_multi_buffer;
explicit
readable_bytes(
basic_multi_buffer const& b) noexcept
subrange(
basic_multi_buffer const& b,
size_type pos,
size_type n) noexcept
: b_(&b)
{
auto const set_empty = [&]
{
begin_ = b_->list_.end();
end_ = b_->list_.end();
begin_pos_ = 0;
last_pos_ = 0;
};
// VFALCO Handle this trivial case of
// pos larger than total size, otherwise
// the addition to pos can overflow.
//if(pos >= b_->in_size_)
// skip unused prefix
pos = pos + b_->in_pos_;
// iterate the buffers
auto it = b_->list_.begin();
// is the list empty?
if(it == b_->list_.end())
{
set_empty();
return;
}
// is the requested size zero?
if(n == 0)
{
set_empty();
return;
}
// get last buffer and its size
auto const last =
std::prev(b_->list_.end());
auto const last_end =
[&]
{
if(b_->out_end_ == 0)
return last->size();
return b_->out_end_;
}();
// only one buffer in list?
if(it == last)
{
if(pos >= last_end)
{
set_empty();
return;
}
begin_ = it;
begin_pos_ = pos;
end_ = std::next(it);
if(n > last_end - pos)
last_pos_ = last_end;
else
last_pos_ = pos + n;
return;
}
for(;;)
{
// is pos in this buffer?
if(pos < it->size())
{
begin_ = it;
begin_pos_ = pos;
// does this buffer satisfy n?
auto const avail =
it->size() - pos;
if(n <= avail)
{
end_ = ++it;
last_pos_ = pos + n;
return;
}
n -= avail;
++it;
break;
}
pos -= it->size();
++it;
// did we reach the last buffer?
if(it == last)
{
// is pos past the end?
if(pos >= last_end)
{
set_empty();
return;
}
// satisfy the request
begin_ = it;
begin_pos_ = pos;
end_ = std::next(it);
if(n < last_end - pos)
last_pos_ = pos + n;
else
last_pos_ = last_end;
return;
}
}
// find pos+n
for(;;)
{
if(it == last)
{
end_ = ++it;
if(n >= last_end)
last_pos_ = last_end;
else
last_pos_ = n;
return;
}
if(n <= it->size())
{
end_ = ++it;
last_pos_ = n;
return;
}
n -= it->size();
++it;
}
}
public:
@@ -156,39 +259,55 @@ public:
class const_iterator;
readable_bytes() = delete;
subrange() = delete;
#if BOOST_WORKAROUND(BOOST_MSVC, < 1910)
readable_bytes(readable_bytes const& other)
subrange(subrange const& other)
: b_(other.b_)
, begin_(other.begin_)
, end_(other.end_)
, begin_pos_(other.begin_pos_)
, last_pos_(other.last_pos_)
{
}
readable_bytes& operator=(readable_bytes const& other)
subrange& operator=(subrange const& other)
{
b_ = other.b_;
begin_ = other.begin_;
end_ = other.end_;
begin_pos_ = other.begin_pos_;
last_pos_ = other.last_pos_;
return *this;
}
#else
readable_bytes(readable_bytes const&) = default;
readable_bytes& operator=(readable_bytes const&) = default;
subrange(subrange const&) = default;
subrange& operator=(subrange const&) = default;
#endif
template<
bool isMutable_ = isMutable,
class = typename std::enable_if<! isMutable_>::type>
readable_bytes(
readable_bytes<true> const& other) noexcept
subrange(
subrange<true> const& other) noexcept
: b_(other.b_)
{
, begin_(other.begin_)
, end_(other.end_)
, begin_pos_(other.begin_pos_)
, last_pos_(other.last_pos_)
{
}
template<
bool isMutable_ = isMutable,
class = typename std::enable_if<! isMutable_>::type>
readable_bytes& operator=(
readable_bytes<true> const& other) noexcept
subrange& operator=(
subrange<true> const& other) noexcept
{
b_ = other.b_;
begin_ = other.begin_;
end_ = other.end_;
begin_pos_ = other.begin_pos_;
last_pos_ = other.last_pos_;
return *this;
}
@@ -212,15 +331,25 @@ template<class Allocator>
template<bool isMutable>
class
basic_multi_buffer<Allocator>::
readable_bytes<isMutable>::
subrange<isMutable>::
const_iterator
{
basic_multi_buffer const* b_ = nullptr;
friend class subrange;
subrange const* sr_ = nullptr;
typename list_type::const_iterator it_;
const_iterator(
subrange const& sr, typename
list_type::const_iterator const& it) noexcept
: sr_(&sr)
, it_(it)
{
}
public:
using value_type =
typename readable_bytes::value_type;
typename subrange::value_type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
@@ -233,22 +362,16 @@ public:
const_iterator& operator=(
const_iterator const& other) = default;
const_iterator(
basic_multi_buffer const& b, typename
list_type::const_iterator const& it) noexcept
: b_(&b)
, it_(it)
bool
operator==(
const_iterator const& other) const noexcept
{
return sr_ == other.sr_ && it_ == other.it_;
}
bool
operator==(const_iterator const& other) const noexcept
{
return b_ == other.b_ && it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const noexcept
operator!=(
const_iterator const& other) const noexcept
{
return !(*this == other);
}
@@ -256,125 +379,17 @@ public:
reference
operator*() const noexcept
{
auto const& e = *it_;
return value_type{e.data(),
(b_->out_ == b_->list_.end() ||
&e != &*b_->out_) ? e.size() : b_->out_pos_} +
(&e == &*b_->list_.begin() ? b_->in_pos_ : 0);
}
pointer
operator->() const = delete;
const_iterator&
operator++() noexcept
{
++it_;
return *this;
}
const_iterator
operator++(int) noexcept
{
auto temp = *this;
++(*this);
return temp;
}
const_iterator&
operator--() noexcept
{
--it_;
return *this;
}
const_iterator
operator--(int) noexcept
{
auto temp = *this;
--(*this);
return temp;
}
};
//------------------------------------------------------------------------------
template<class Allocator>
class basic_multi_buffer<Allocator>::mutable_buffers_type
{
basic_multi_buffer const* b_;
friend class basic_multi_buffer;
explicit
mutable_buffers_type(
basic_multi_buffer const& b) noexcept
: b_(&b)
{
}
public:
using value_type = net::mutable_buffer;
class const_iterator;
mutable_buffers_type() = delete;
mutable_buffers_type(mutable_buffers_type const&) = default;
mutable_buffers_type& operator=(mutable_buffers_type const&) = default;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
};
//------------------------------------------------------------------------------
template<class Allocator>
class basic_multi_buffer<Allocator>::mutable_buffers_type::const_iterator
{
basic_multi_buffer const* b_ = nullptr;
typename list_type::const_iterator it_;
public:
using value_type = typename
mutable_buffers_type::value_type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
const_iterator() = default;
const_iterator(const_iterator const& other) = default;
const_iterator& operator=(const_iterator const& other) = default;
const_iterator(
basic_multi_buffer const& b,
typename list_type::const_iterator const& it) noexcept
: b_(&b)
, it_(it)
{
}
bool
operator==(const_iterator const& other) const noexcept
{
return b_ == other.b_ && it_ == other.it_;
}
bool
operator!=(const_iterator const& other) const noexcept
{
return !(*this == other);
}
reference
operator*() const noexcept
{
auto const& e = *it_;
return value_type{e.data(),
&e == &*std::prev(b_->list_.end()) ?
b_->out_end_ : e.size()} +
(&e == &*b_->out_ ? b_->out_pos_ : 0);
value_type result;
BOOST_ASSERT(sr_->last_pos_ != 0);
if(it_ == std::prev(sr_->end_))
result = {
it_->data(), sr_->last_pos_ };
else
result = {
it_->data(), it_->size() };
if(it_ == sr_->begin_)
result += sr_->begin_pos_;
return result;
}
pointer
@@ -417,44 +432,24 @@ template<class Allocator>
template<bool isMutable>
auto
basic_multi_buffer<Allocator>::
readable_bytes<isMutable>::
subrange<isMutable>::
begin() const noexcept ->
const_iterator
{
return const_iterator{*b_, b_->list_.begin()};
return const_iterator(
*this, begin_);
}
template<class Allocator>
template<bool isMutable>
auto
basic_multi_buffer<Allocator>::
readable_bytes<isMutable>::
subrange<isMutable>::
end() const noexcept ->
const_iterator
{
return const_iterator{*b_, b_->out_ ==
b_->list_.end() ? b_->list_.end() :
std::next(b_->out_)};
}
template<class Allocator>
auto
basic_multi_buffer<Allocator>::
mutable_buffers_type::
begin() const noexcept ->
const_iterator
{
return const_iterator{*b_, b_->out_};
}
template<class Allocator>
auto
basic_multi_buffer<Allocator>::
mutable_buffers_type::
end() const noexcept ->
const_iterator
{
return const_iterator{*b_, b_->list_.end()};
return const_iterator(
*this, end_);
}
//------------------------------------------------------------------------------
@@ -487,7 +482,7 @@ template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(
Allocator const& alloc) noexcept
: boost::empty_value<base_alloc_type>(
: boost::empty_value<Allocator>(
boost::empty_init_t(), alloc)
, max_(alloc_traits::max_size(this->get()))
, out_(list_.end())
@@ -499,8 +494,8 @@ basic_multi_buffer<Allocator>::
basic_multi_buffer(
std::size_t limit,
Allocator const& alloc) noexcept
: boost::empty_value<
base_alloc_type>(boost::empty_init_t(), alloc)
: boost::empty_value<Allocator>(
boost::empty_init_t(), alloc)
, max_(limit)
, out_(list_.end())
{
@@ -510,7 +505,7 @@ template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(
basic_multi_buffer&& other) noexcept
: boost::empty_value<base_alloc_type>(
: boost::empty_value<Allocator>(
boost::empty_init_t(), std::move(other.get()))
, max_(other.max_)
, in_size_(boost::exchange(other.in_size_, 0))
@@ -529,17 +524,15 @@ template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(
basic_multi_buffer&& other,
Allocator const& alloc)
: boost::empty_value<
base_alloc_type>(boost::empty_init_t(), alloc)
Allocator const& alloc)
: boost::empty_value<Allocator>(
boost::empty_init_t(), alloc)
, max_(other.max_)
{
if(this->get() != other.get())
{
out_ = list_.end();
copy_from(other);
other.clear();
other.shrink_to_fit();
return;
}
@@ -562,7 +555,7 @@ template<class Allocator>
basic_multi_buffer<Allocator>::
basic_multi_buffer(
basic_multi_buffer const& other)
: boost::empty_value<base_alloc_type>(
: boost::empty_value<Allocator>(
boost::empty_init_t(), alloc_traits::
select_on_container_copy_construction(
other.get()))
@@ -577,7 +570,7 @@ basic_multi_buffer<Allocator>::
basic_multi_buffer(
basic_multi_buffer const& other,
Allocator const& alloc)
: boost::empty_value<base_alloc_type>(
: boost::empty_value<Allocator>(
boost::empty_init_t(), alloc)
, max_(other.max_)
, out_(list_.end())
@@ -601,8 +594,8 @@ basic_multi_buffer<Allocator>::
basic_multi_buffer(
basic_multi_buffer<OtherAlloc> const& other,
allocator_type const& alloc)
: boost::empty_value<
base_alloc_type>(boost::empty_init_t(), alloc)
: boost::empty_value<Allocator>(
boost::empty_init_t(), alloc)
, max_(other.max_)
, out_(list_.end())
{
@@ -669,16 +662,18 @@ basic_multi_buffer<Allocator>::
data() const noexcept ->
const_buffers_type
{
return const_buffers_type(*this);
return const_buffers_type(
*this, 0, in_size_);
}
template<class Allocator>
auto
basic_multi_buffer<Allocator>::
data() noexcept ->
mutable_data_type
mutable_buffers_type
{
return mutable_data_type(*this);
return mutable_buffers_type(
*this, 0, in_size_);
}
template<class Allocator>
@@ -784,7 +779,7 @@ shrink_to_fit()
};
// partial last buffer
if(list_.size() > 1 && out_ != list_.end())
if(out_ != list_.begin() && out_ != list_.end())
{
BOOST_ASSERT(out_ ==
list_.iterator_to(list_.back()));
@@ -820,7 +815,8 @@ shrink_to_fit()
}
else
{
BOOST_ASSERT(list_.size() == 1);
BOOST_ASSERT(out_ ==
list_.iterator_to(list_.back()));
BOOST_ASSERT(out_pos_ > in_pos_);
auto const n = out_pos_ - in_pos_;
auto& e = alloc(n);
@@ -856,6 +852,7 @@ basic_multi_buffer<Allocator>::
prepare(size_type n) ->
mutable_buffers_type
{
auto const n0 = n;
if(in_size_ > max_ || n > (max_ - in_size_))
BOOST_THROW_EXCEPTION(std::length_error{
"basic_multi_buffer too long"});
@@ -916,15 +913,17 @@ prepare(size_type n) ->
destroy(reuse);
if(n > 0)
{
static auto const growth_factor = 2.0f;
std::size_t const growth_factor = 2;
std::size_t altn = in_size_ * growth_factor;
// Overflow detection:
if(in_size_ > altn)
altn = (std::numeric_limits<std::size_t>::max)();
else
altn = (std::max<std::size_t>)(512, altn);
auto const size =
(std::min<std::size_t>)(
max_ - total,
(std::max<std::size_t>)({
static_cast<std::size_t>(
in_size_ * growth_factor - in_size_),
512,
n}));
(std::max<std::size_t>)(n, altn));
auto& e = alloc(size);
list_.push_back(e);
if(out_ == list_.end())
@@ -935,7 +934,12 @@ prepare(size_type n) ->
#endif
}
}
return mutable_buffers_type(*this);
auto const result =
mutable_buffers_type(
*this, in_size_, n0);
BOOST_ASSERT(
net::buffer_size(result) == n0);
return result;
}
template<class Allocator>
@@ -1012,10 +1016,7 @@ consume(size_type n) noexcept
in_pos_ = 0;
auto& e = list_.front();
list_.erase(list_.iterator_to(e));
auto const len = sizeof(e) + e.size();
e.~element();
alloc_traits::deallocate(this->get(),
reinterpret_cast<char*>(&e), len);
destroy(e);
#if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK
debug_check();
#endif
@@ -1099,8 +1100,6 @@ move_assign(basic_multi_buffer& other, std::false_type)
if(this->get() != other.get())
{
copy_from(other);
other.clear();
other.shrink_to_fit();
}
else
{
@@ -1202,24 +1201,19 @@ destroy(list_type& list) noexcept
destroy(*it++);
}
template<class Allocator>
void
basic_multi_buffer<Allocator>::
destroy(const_iter it)
{
auto& e = list_.erase(it);
destroy(e);
}
template<class Allocator>
void
basic_multi_buffer<Allocator>::
destroy(element& e)
{
auto const len = sizeof(e) + e.size();
auto a = rebind_type{this->get()};
auto const n =
(sizeof(element) + e.size() +
sizeof(align_type) - 1) /
sizeof(align_type);
e.~element();
alloc_traits::deallocate(this->get(),
reinterpret_cast<char*>(&e), len);
alloc_traits::deallocate(a,
reinterpret_cast<align_type*>(&e), n);
}
template<class Allocator>
@@ -1231,9 +1225,11 @@ alloc(std::size_t size) ->
if(size > alloc_traits::max_size(this->get()))
BOOST_THROW_EXCEPTION(std::length_error(
"A basic_multi_buffer exceeded the allocator's maximum size"));
return *::new(alloc_traits::allocate(
this->get(),
sizeof(element) + size)) element(size);
auto a = rebind_type{this->get()};
auto const p = alloc_traits::allocate(a,
(sizeof(element) + size + sizeof(align_type) - 1) /
sizeof(align_type));
return *(::new(p) element(size));
}
template<class Allocator>

View File

@@ -46,7 +46,6 @@ read_size(DynamicBuffer& buffer,
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());

View File

@@ -12,7 +12,9 @@
#include <boost/beast/core/detail/allocator.hpp>
#include <boost/asio/associated_allocator.hpp>
#include <boost/asio/associated_cancellation_slot.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <boost/assert.hpp>
#include <boost/core/empty_value.hpp>
@@ -28,9 +30,10 @@ class saved_handler::base
{
protected:
~base() = default;
saved_handler * owner_;
public:
base() = default;
base(saved_handler * owner) : owner_(owner){}
void set_owner(saved_handler * new_owner) { owner_ = new_owner;}
virtual void destroy() = 0;
virtual void invoke() = 0;
};
@@ -63,14 +66,32 @@ class saved_handler::impl final : public base
};
ebo_pair v_;
#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
typename std::decay<decltype(net::prefer(std::declval<
net::associated_executor_t<Handler>>(),
net::execution::outstanding_work.tracked))>::type
wg2_;
#else // defined(BOOST_ASIO_NO_TS_EXECUTORS)
net::executor_work_guard<
net::associated_executor_t<Handler>> wg2_;
#endif // defined(BOOST_ASIO_NO_TS_EXECUTORS)
net::cancellation_slot slot_{net::get_associated_cancellation_slot(v_.h)};
public:
template<class Handler_>
impl(alloc_type const& a, Handler_&& h)
: v_(a, std::forward<Handler_>(h))
impl(alloc_type const& a, Handler_&& h,
saved_handler * owner)
: base(owner), v_(a, std::forward<Handler_>(h))
#if defined(BOOST_ASIO_NO_TS_EXECUTORS)
, wg2_(net::prefer(
net::get_associated_executor(v_.h),
net::execution::outstanding_work.tracked))
#else // defined(BOOST_ASIO_NO_TS_EXECUTORS)
, wg2_(net::get_associated_executor(v_.h))
#endif // defined(BOOST_ASIO_NO_TS_EXECUTORS)
{
}
~impl()
{
}
@@ -78,6 +99,7 @@ public:
destroy() override
{
auto v = std::move(v_);
slot_.clear();
alloc_traits::destroy(v.get(), this);
alloc_traits::deallocate(v.get(), this, 1);
}
@@ -85,11 +107,22 @@ public:
void
invoke() override
{
slot_.clear();
auto v = std::move(v_);
alloc_traits::destroy(v.get(), this);
alloc_traits::deallocate(v.get(), this, 1);
v.h();
}
void self_complete()
{
slot_.clear();
owner_->p_ = nullptr;
auto v = std::move(v_);
alloc_traits::destroy(v.get(), this);
alloc_traits::deallocate(v.get(), this, 1);
v.h(net::error::operation_aborted);
}
};
//------------------------------------------------------------------------------
@@ -97,7 +130,8 @@ public:
template<class Handler, class Allocator>
void
saved_handler::
emplace(Handler&& handler, Allocator const& alloc)
emplace(Handler&& handler, Allocator const& alloc,
net::cancellation_type cancel_type)
{
// Can't delete a handler before invoking
BOOST_ASSERT(! has_value());
@@ -127,22 +161,47 @@ emplace(Handler&& handler, Allocator const& alloc)
alloc_traits::deallocate(a, p, 1);
}
};
auto cancel_slot = net::get_associated_cancellation_slot(handler);
storage s(alloc);
alloc_traits::construct(s.a, s.p,
s.a, std::forward<Handler>(handler));
p_ = boost::exchange(s.p, nullptr);
s.a, std::forward<Handler>(handler), this);
auto tmp = boost::exchange(s.p, nullptr);
p_ = tmp;
if (cancel_slot.is_connected())
{
struct cancel_op
{
impl<Handler, Allocator>* p;
net::cancellation_type accepted_ct;
cancel_op(impl<Handler, Allocator>* p,
net::cancellation_type accepted_ct)
: p(p), accepted_ct(accepted_ct) {}
void operator()(net::cancellation_type ct)
{
if ((ct & accepted_ct) != net::cancellation_type::none)
p->self_complete();
}
};
cancel_slot.template emplace<cancel_op>(tmp, cancel_type);
}
}
template<class Handler>
void
saved_handler::
emplace(Handler&& handler)
emplace(Handler&& handler, net::cancellation_type cancel_type)
{
// Can't delete a handler before invoking
BOOST_ASSERT(! has_value());
emplace(
std::forward<Handler>(handler),
net::get_associated_allocator(handler));
net::get_associated_allocator(handler),
cancel_type);
}
} // beast

View File

@@ -27,6 +27,7 @@ saved_handler::
saved_handler(saved_handler&& other) noexcept
: p_(boost::exchange(other.p_, nullptr))
{
p_->set_owner(this);
}
saved_handler&
@@ -36,6 +37,7 @@ operator=(saved_handler&& other) noexcept
// Can't delete a handler before invoking
BOOST_ASSERT(! has_value());
p_ = boost::exchange(other.p_, nullptr);
p_->set_owner(this);
return *this;
}

View File

@@ -10,12 +10,7 @@
#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 {

View File

@@ -11,12 +11,8 @@
#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 {
@@ -60,7 +56,7 @@ data() const noexcept ->
auto
static_buffer_base::
data() noexcept ->
mutable_data_type
mutable_buffers_type
{
if(in_off_ + in_size_ <= capacity_)
return {

View File

@@ -1,642 +0,0 @@
//
// 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,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_IMPL_STRING_IPP
#define BOOST_BEAST_IMPL_STRING_IPP
#include <boost/beast/core/string.hpp>
#include <boost/beast/core/detail/string.hpp>
#include <algorithm>
namespace boost {
namespace beast {
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( detail::ascii_tolower(a) !=
detail::ascii_tolower(b))
return false;
a = *p1++;
b = *p2++;
}
while(n--);
return true;
}
bool
iless::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);
}
);
}
} // beast
} // boost
#endif