mirror of
https://github.com/boostorg/mqtt5.git
synced 2025-07-29 20:17:37 +02:00
Downgrade C++ standard requirement to C++17
Summary: related to T13242 Reviewers: ivica Reviewed By: ivica Subscribers: miljen, iljazovic Differential Revision: https://repo.mireo.local/D26860
This commit is contained in:
@ -1,8 +1,10 @@
|
||||
#include <boost/asio/use_awaitable.hpp>
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
|
||||
#include <boost/asio/as_tuple.hpp>
|
||||
#include <boost/asio/co_spawn.hpp>
|
||||
#include <boost/asio/detached.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/use_awaitable.hpp>
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
@ -171,3 +173,5 @@ int main(int argc, char** argv) {
|
||||
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include <async_mqtt5.hpp>
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
|
||||
namespace asio = boost::asio;
|
||||
|
||||
asio::awaitable<void> client_publisher(asio::io_context& ioc) {
|
||||
@ -53,3 +55,5 @@ int main() {
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
#endif
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include <async_mqtt5.hpp>
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
|
||||
namespace asio = boost::asio;
|
||||
|
||||
asio::awaitable<void> client_receiver(asio::io_context& ioc) {
|
||||
@ -78,3 +80,5 @@ int main() {
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
#endif
|
||||
|
@ -4,6 +4,9 @@
|
||||
#include <boost/asio/any_completion_handler.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
|
||||
#include <boost/type_traits/is_detected.hpp>
|
||||
#include <boost/type_traits/is_detected_convertible.hpp>
|
||||
|
||||
#include <async_mqtt5/types.hpp>
|
||||
|
||||
namespace async_mqtt5 {
|
||||
@ -17,13 +20,23 @@ using auth_handler_type = asio::any_completion_handler<
|
||||
void (error_code ec, std::string auth_data)
|
||||
>;
|
||||
|
||||
template <typename T, typename ...Ts>
|
||||
using async_auth_sig = decltype(
|
||||
std::declval<T>().async_auth(std::declval<Ts>()...)
|
||||
);
|
||||
|
||||
template <typename T>
|
||||
concept is_authenticator = requires (T a) {
|
||||
{
|
||||
a.async_auth(auth_step_e {}, std::string {}, auth_handler_type {})
|
||||
} -> std::same_as<void>;
|
||||
{ a.method() } -> std::same_as<std::string_view>;
|
||||
};
|
||||
using method_sig = decltype(
|
||||
std::declval<T>().method()
|
||||
);
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_authenticator =
|
||||
boost::is_detected<
|
||||
async_auth_sig, T,
|
||||
auth_step_e, std::string, auth_handler_type
|
||||
>::value &&
|
||||
boost::is_detected_convertible_v<std::string_view, method_sig, T>;
|
||||
|
||||
class auth_fun_base {
|
||||
using auth_func = void(*)(
|
||||
@ -36,13 +49,17 @@ public:
|
||||
~auth_fun_base() = default;
|
||||
|
||||
void async_auth(
|
||||
auth_step_e step, std::string data, auth_handler_type auth_handler
|
||||
auth_step_e step, std::string data,
|
||||
auth_handler_type auth_handler
|
||||
) {
|
||||
_auth_func(step, std::move(data), std::move(auth_handler), this);
|
||||
}
|
||||
};
|
||||
|
||||
template <is_authenticator Authenticator>
|
||||
template <
|
||||
typename Authenticator,
|
||||
typename = std::enable_if_t<is_authenticator<Authenticator>>
|
||||
>
|
||||
class auth_fun : public auth_fun_base {
|
||||
Authenticator _authenticator;
|
||||
|
||||
@ -72,7 +89,10 @@ class any_authenticator {
|
||||
public:
|
||||
any_authenticator() = default;
|
||||
|
||||
template <detail::is_authenticator Authenticator>
|
||||
template <
|
||||
typename Authenticator,
|
||||
std::enable_if_t<detail::is_authenticator<Authenticator>, bool> = true
|
||||
>
|
||||
any_authenticator(Authenticator&& a) :
|
||||
_method(a.method()),
|
||||
_auth_fun(
|
||||
@ -94,7 +114,7 @@ public:
|
||||
using Signature = void (error_code, std::string);
|
||||
|
||||
auto initiation = [](
|
||||
auto handler, any_authenticator& self,
|
||||
auto handler, any_authenticator& self,
|
||||
auth_step_e step, std::string data
|
||||
) {
|
||||
self._auth_fun->async_auth(
|
||||
@ -110,4 +130,5 @@ public:
|
||||
|
||||
} // end namespace async_mqtt5
|
||||
|
||||
|
||||
#endif // !ASYNC_MQTT5_ANY_AUTHENTICATOR
|
||||
|
@ -114,7 +114,7 @@ public:
|
||||
bool is_locked() const noexcept {
|
||||
return _locked.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
|
||||
// Schedules mutex for lock operation and return immediately.
|
||||
// Calls given completion handler when mutex is locked.
|
||||
// It's the responsibility of the completion handler to unlock the mutex.
|
||||
@ -187,7 +187,8 @@ private:
|
||||
// or queues it for later execution otherwise. In both cases
|
||||
// the operation will be executed in a manner equivalent
|
||||
// to asio::post to avoid recursion.
|
||||
void execute_or_queue(auto handler) noexcept {
|
||||
template <typename Handler>
|
||||
void execute_or_queue(Handler&& handler) noexcept {
|
||||
std::unique_lock l { _thread_mutex };
|
||||
tracked_op h { std::move(handler), _ex };
|
||||
if (_locked.load(std::memory_order_relaxed)) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
#ifndef ASYNC_MQTT5_ASYNC_TRAITS_HPP
|
||||
#define ASYNC_MQTT5_ASYNC_TRAITS_HPP
|
||||
|
||||
#include <concepts>
|
||||
#include <type_traits>
|
||||
|
||||
#include <boost/asio/associated_executor.hpp>
|
||||
@ -10,15 +9,16 @@
|
||||
|
||||
#include <boost/beast/core/stream_traits.hpp>
|
||||
|
||||
#include <boost/type_traits/detected_or.hpp>
|
||||
#include <boost/type_traits/is_detected.hpp>
|
||||
#include <boost/type_traits/remove_cv_ref.hpp>
|
||||
|
||||
#include <async_mqtt5/types.hpp>
|
||||
|
||||
namespace async_mqtt5 {
|
||||
|
||||
namespace asio = boost::asio;
|
||||
|
||||
// TODO: move tls_handshake_type and assign_tls_sni to
|
||||
// separate header
|
||||
|
||||
template <typename StreamType>
|
||||
struct tls_handshake_type {};
|
||||
|
||||
@ -44,61 +44,94 @@ tracking_executor(const Handler& handler, const DfltExecutor& ex) {
|
||||
);
|
||||
}
|
||||
|
||||
template <typename T, typename ...Ts>
|
||||
using async_write_sig = decltype(
|
||||
std::declval<T&>().async_write(std::declval<Ts>()...)
|
||||
);
|
||||
|
||||
constexpr auto write_handler_t = [](error_code, size_t) {};
|
||||
|
||||
template <typename T, typename B>
|
||||
concept has_async_write = requires(T a) {
|
||||
a.async_write(
|
||||
std::declval<B>(),
|
||||
[](error_code, size_t) {}
|
||||
);
|
||||
};
|
||||
constexpr bool has_async_write = boost::is_detected<
|
||||
async_write_sig, T, B, decltype(write_handler_t)
|
||||
>::value;
|
||||
|
||||
template<typename T>
|
||||
concept has_tls_handshake = requires(T a) {
|
||||
a.async_handshake(
|
||||
typename T::handshake_type{},
|
||||
[](error_code) {}
|
||||
);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
concept has_ws_handshake = requires(T a) {
|
||||
a.async_handshake(
|
||||
std::declval<std::string_view>(),
|
||||
std::declval<std::string_view>(),
|
||||
[](error_code) {}
|
||||
);
|
||||
};
|
||||
constexpr auto handshake_handler_t = [](error_code) {};
|
||||
|
||||
template <typename T>
|
||||
concept has_tls_context = requires(T a) {
|
||||
a.tls_context();
|
||||
};
|
||||
using tls_handshake_t = typename T::handshake_type;
|
||||
|
||||
template <typename T>
|
||||
concept has_next_layer = requires(T a) {
|
||||
a.next_layer();
|
||||
};
|
||||
using tls_handshake_type_of = boost::detected_or_t<void, tls_handshake_t, T>;
|
||||
|
||||
template <typename T, typename ...Ts>
|
||||
using async_tls_handshake_sig = decltype(
|
||||
std::declval<T&>().async_handshake(std::declval<Ts>()...)
|
||||
);
|
||||
|
||||
template <typename T>
|
||||
constexpr bool has_tls_handshake = boost::is_detected<
|
||||
async_tls_handshake_sig, T, tls_handshake_type_of<T>,
|
||||
decltype(handshake_handler_t)
|
||||
>::value;
|
||||
|
||||
|
||||
template <typename T, typename ...Ts>
|
||||
using async_ws_handshake_sig = decltype(
|
||||
std::declval<T&>().async_handshake(std::declval<Ts>()...)
|
||||
);
|
||||
|
||||
template <typename T>
|
||||
constexpr bool has_ws_handshake = boost::is_detected<
|
||||
async_ws_handshake_sig, T,
|
||||
std::string_view, std::string_view,
|
||||
decltype(handshake_handler_t)
|
||||
>::value;
|
||||
|
||||
|
||||
template <typename T>
|
||||
using tls_context_sig = decltype(
|
||||
std::declval<T&>().tls_context()
|
||||
);
|
||||
|
||||
template <typename T>
|
||||
constexpr bool has_tls_context = boost::is_detected<
|
||||
tls_context_sig, T
|
||||
>::value;
|
||||
|
||||
|
||||
template <typename T>
|
||||
using next_layer_sig = decltype(
|
||||
std::declval<T&>().next_layer()
|
||||
);
|
||||
|
||||
template <typename T>
|
||||
constexpr bool has_next_layer = boost::is_detected<
|
||||
next_layer_sig, T
|
||||
>::value;
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct next_layer_type {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
requires has_next_layer<T>
|
||||
struct next_layer_type<T> {
|
||||
struct next_layer_type<
|
||||
T, std::enable_if_t<has_next_layer<T>>
|
||||
> {
|
||||
using type = typename std::remove_reference_t<T>::next_layer_type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
requires (!has_next_layer<T>)
|
||||
typename next_layer_type<T>::type& next_layer(T&& a) {
|
||||
typename next_layer_type<T, std::enable_if_t<!has_next_layer<T>>>::type&
|
||||
next_layer(T&& a) {
|
||||
return a;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires has_next_layer<T>
|
||||
typename next_layer_type<T>::type& next_layer(T&& a) {
|
||||
typename next_layer_type<T, std::enable_if_t<has_next_layer<T>>>::type&
|
||||
next_layer(T&& a) {
|
||||
return a.next_layer();
|
||||
}
|
||||
|
||||
@ -110,23 +143,27 @@ lowest_layer_type<S>& lowest_layer(S&& a) {
|
||||
return boost::beast::get_lowest_layer(std::forward<S>(a));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename T, typename Enable = void>
|
||||
struct has_tls_layer_impl : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
requires has_tls_handshake<T>
|
||||
struct has_tls_layer_impl<T> : std::true_type {};
|
||||
struct has_tls_layer_impl<
|
||||
T, std::enable_if_t<has_tls_handshake<T>>
|
||||
> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
requires (!has_tls_handshake<T> && has_next_layer<T>)
|
||||
struct has_tls_layer_impl<T> : has_tls_layer_impl<
|
||||
std::remove_cvref_t<decltype(std::declval<T&>().next_layer())>
|
||||
struct has_tls_layer_impl<
|
||||
T, std::enable_if_t<!has_tls_handshake<T> && has_next_layer<T>>
|
||||
> : has_tls_layer_impl<
|
||||
boost::remove_cv_ref_t<decltype(std::declval<T&>().next_layer())>
|
||||
> {};
|
||||
|
||||
template <typename T>
|
||||
concept has_tls_layer = has_tls_layer_impl<std::remove_cvref_t<T>>::value;
|
||||
constexpr bool has_tls_layer = has_tls_layer_impl<
|
||||
boost::remove_cv_ref_t<T>
|
||||
>::value;
|
||||
|
||||
|
||||
//TODO: move to appropriate place
|
||||
template <
|
||||
typename Stream,
|
||||
typename ConstBufferSequence,
|
||||
@ -135,7 +172,6 @@ template <
|
||||
decltype(auto) async_write(
|
||||
Stream& stream, const ConstBufferSequence& buff, CompletionToken&& token
|
||||
) {
|
||||
// TODO: find layer that has async write method
|
||||
if constexpr (has_async_write<Stream, ConstBufferSequence>)
|
||||
return stream.async_write(
|
||||
buff, std::forward<CompletionToken>(token)
|
||||
@ -148,7 +184,7 @@ decltype(auto) async_write(
|
||||
|
||||
template <typename TlsContext, typename Stream>
|
||||
void setup_tls_sni(const authority_path& ap, TlsContext& ctx, Stream& s) {
|
||||
if constexpr (has_tls_handshake<Stream>)
|
||||
if constexpr (has_tls_handshake<Stream>)
|
||||
assign_tls_sni(ap, ctx, s);
|
||||
else if constexpr (has_next_layer<Stream>)
|
||||
setup_tls_sni(ap, ctx, next_layer(s));
|
||||
|
@ -26,8 +26,10 @@ class cancellable_handler {
|
||||
cancellable_handler* _owner;
|
||||
|
||||
op_state(
|
||||
Handler&& handler, const Executor& ex, cancellable_handler* owner
|
||||
) : _handler(std::move(handler)),
|
||||
Handler&& handler, const Executor& ex,
|
||||
cancellable_handler* owner
|
||||
) :
|
||||
_handler(std::move(handler)),
|
||||
_handler_ex(tracking_executor(_handler, ex)),
|
||||
_owner(owner)
|
||||
{}
|
||||
@ -46,17 +48,18 @@ class cancellable_handler {
|
||||
{}
|
||||
|
||||
void operator()(asio::cancellation_type_t type) {
|
||||
if ((type & asio::cancellation_type_t::terminal) ==
|
||||
asio::cancellation_type_t::none)
|
||||
if (
|
||||
(type & asio::cancellation_type_t::terminal) ==
|
||||
asio::cancellation_type_t::none
|
||||
)
|
||||
return;
|
||||
|
||||
auto op = [wptr = _state_weak_ptr]() {
|
||||
if (auto state = wptr.lock())
|
||||
state->cancel_op();
|
||||
};
|
||||
asio::dispatch(
|
||||
_executor,
|
||||
std::move(op)
|
||||
);
|
||||
|
||||
asio::dispatch(_executor, std::move(op));
|
||||
}
|
||||
};
|
||||
|
||||
@ -69,9 +72,11 @@ public:
|
||||
_state = std::allocate_shared<op_state>(
|
||||
alloc, std::move(handler), ex, this
|
||||
);
|
||||
|
||||
auto slot = asio::get_associated_cancellation_slot(_state->_handler);
|
||||
if (slot.is_connected())
|
||||
slot.template emplace<cancel_proxy>(_state, ex);
|
||||
|
||||
_executor = ex;
|
||||
}
|
||||
|
||||
@ -106,11 +111,14 @@ public:
|
||||
auto handler_ex = std::move(_state->_handler_ex);
|
||||
_state.reset();
|
||||
|
||||
auto op = std::apply([&h](auto... args) {
|
||||
return asio::prepend(
|
||||
std::move(h), asio::error::operation_aborted, args...
|
||||
);
|
||||
}, CancelArgs {});
|
||||
auto op = std::apply(
|
||||
[&h](auto... args) {
|
||||
return asio::prepend(
|
||||
std::move(h), asio::error::operation_aborted, args...
|
||||
);
|
||||
},
|
||||
CancelArgs {}
|
||||
);
|
||||
|
||||
asio::dispatch(handler_ex, std::move(op));
|
||||
}
|
||||
|
@ -19,7 +19,9 @@ public:
|
||||
bounded_deque() = default;
|
||||
bounded_deque(size_t n) : _buffer(n) {}
|
||||
|
||||
size_t size() const { return _buffer.size(); }
|
||||
size_t size() const {
|
||||
return _buffer.size();
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void push_back(E&& e) {
|
||||
@ -28,13 +30,21 @@ public:
|
||||
_buffer.push_back(std::forward<E>(e));
|
||||
}
|
||||
|
||||
void pop_front() { _buffer.pop_front(); }
|
||||
void pop_front() {
|
||||
_buffer.pop_front();
|
||||
}
|
||||
|
||||
void clear() { _buffer.clear(); }
|
||||
void clear() {
|
||||
_buffer.clear();
|
||||
}
|
||||
|
||||
const auto& front() const noexcept { return _buffer.front(); }
|
||||
const auto& front() const noexcept {
|
||||
return _buffer.front();
|
||||
}
|
||||
|
||||
auto& front() noexcept { return _buffer.front(); }
|
||||
auto& front() noexcept {
|
||||
return _buffer.front();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Signatures>
|
||||
@ -48,6 +58,7 @@ struct channel_traits {
|
||||
template <typename R, typename... Args>
|
||||
struct channel_traits<R(error_code, Args...)> {
|
||||
static_assert(sizeof...(Args) > 0);
|
||||
|
||||
template <typename... NewSignatures>
|
||||
struct rebind {
|
||||
using other = channel_traits<NewSignatures...>;
|
||||
|
@ -109,7 +109,10 @@ public:
|
||||
class packet_id_allocator {
|
||||
struct interval {
|
||||
uint16_t start, end;
|
||||
interval(uint16_t start, uint16_t end) : start(start), end(end) {}
|
||||
|
||||
interval(uint16_t start, uint16_t end) :
|
||||
start(start), end(end)
|
||||
{}
|
||||
};
|
||||
|
||||
std::mutex _mtx;
|
||||
@ -152,7 +155,8 @@ public:
|
||||
*end_p = it->end;
|
||||
_free_ids.erase(it);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (!end_p)
|
||||
_free_ids.insert(it, interval(pid, pid - 1));
|
||||
else
|
||||
|
@ -49,17 +49,32 @@ public:
|
||||
|
||||
ring_buffer() = default;
|
||||
|
||||
template <typename Alloc> requires std::is_constructible_v<allocator_type, const Alloc&>
|
||||
explicit ring_buffer(size_type capacity, Alloc&& allocator) : _alloc((Alloc&&)allocator) {
|
||||
template <
|
||||
typename Alloc,
|
||||
std::enable_if_t<
|
||||
std::is_constructible_v<allocator_type, const Alloc&>, bool
|
||||
> = true
|
||||
>
|
||||
explicit ring_buffer(size_type capacity, Alloc&& allocator) :
|
||||
_alloc((Alloc&&)allocator)
|
||||
{
|
||||
reserve(capacity);
|
||||
}
|
||||
|
||||
template <typename Alloc> requires std::is_constructible_v<allocator_type, const Alloc&>
|
||||
explicit ring_buffer(Alloc&& allocator) : _alloc((Alloc&&)allocator) {
|
||||
}
|
||||
template <
|
||||
typename Alloc,
|
||||
std::enable_if_t<
|
||||
std::is_constructible_v<allocator_type, const Alloc&>,
|
||||
bool
|
||||
> = true
|
||||
>
|
||||
explicit ring_buffer(Alloc&& allocator) :
|
||||
_alloc((Alloc&&)allocator)
|
||||
{}
|
||||
|
||||
explicit ring_buffer(size_type capacity) : ring_buffer(capacity, allocator_type { }) {
|
||||
}
|
||||
explicit ring_buffer(size_type capacity) :
|
||||
ring_buffer(capacity, allocator_type { })
|
||||
{}
|
||||
|
||||
ring_buffer(ring_buffer&& other) noexcept :
|
||||
_buff { std::exchange(other._buff, nullptr) },
|
||||
@ -67,8 +82,7 @@ public:
|
||||
_back { std::exchange(other._back, 0) },
|
||||
_capacity { std::exchange(other._capacity, 0) },
|
||||
_alloc { other._alloc }
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
~ring_buffer() {
|
||||
if (_buff) {
|
||||
@ -78,7 +92,9 @@ public:
|
||||
}
|
||||
|
||||
ring_buffer& operator=(ring_buffer&& other) noexcept {
|
||||
if constexpr (allocator_traits::propagate_on_container_move_assignment::value) {
|
||||
if constexpr (
|
||||
allocator_traits::propagate_on_container_move_assignment::value
|
||||
) {
|
||||
clear_and_exchange_with(other);
|
||||
_alloc = other._alloc;
|
||||
}
|
||||
@ -220,7 +236,7 @@ public:
|
||||
_front = std::exchange(b._front, _front);
|
||||
_back = std::exchange(b._back, _back);
|
||||
_capacity = std::exchange(b._capacity, _capacity);
|
||||
_alloc = std::exchange(b._alloc, _alloc); //?? allocator_traits::propagate_on_container_swap::value
|
||||
_alloc = std::exchange(b._alloc, _alloc);
|
||||
}
|
||||
|
||||
void reserve(size_type new_capacity) noexcept {
|
||||
@ -244,7 +260,9 @@ public:
|
||||
if (_buff) {
|
||||
for (size_type i = 0; i < s; ++i) {
|
||||
auto& v = operator[](i);
|
||||
allocator_traits::construct(_alloc, &new_buff[i], (value_type&&)v);
|
||||
allocator_traits::construct(
|
||||
_alloc, &new_buff[i], (value_type&&)v
|
||||
);
|
||||
allocator_traits::destroy(_alloc, &v);
|
||||
}
|
||||
_alloc.deallocate(_buff, _capacity);
|
||||
@ -261,7 +279,7 @@ private:
|
||||
}
|
||||
|
||||
void grow_if_needed() {
|
||||
if (!_buff || full()) [[unlikely]]
|
||||
if (!_buff || full())
|
||||
reserve(_capacity == 0 ? min_capacity : _capacity * 2);
|
||||
}
|
||||
|
||||
@ -278,13 +296,14 @@ private:
|
||||
size_type _front = npos;
|
||||
size_type _back = 0;
|
||||
size_type _capacity = 0;
|
||||
[[no_unique_address]] allocator_type _alloc;
|
||||
allocator_type _alloc;
|
||||
|
||||
static constexpr size_type npos = static_cast<size_type>(-1);
|
||||
static constexpr size_type min_capacity = 4;
|
||||
};
|
||||
|
||||
template <typename T, typename Alloc> template <typename P, typename R, typename ring_pointer>
|
||||
template <typename T, typename Alloc>
|
||||
template <typename P, typename R, typename ring_pointer>
|
||||
class ring_buffer<T, Alloc>::_iter {
|
||||
public:
|
||||
using value_type = T;
|
||||
@ -341,8 +360,9 @@ public:
|
||||
private:
|
||||
friend class ring_buffer;
|
||||
|
||||
_iter(ring_pointer b, size_type i) : _b(b), _i(i), _p(&b->operator[](i)) {
|
||||
}
|
||||
_iter(ring_pointer b, size_type i) :
|
||||
_b(b), _i(i), _p(&b->operator[](i))
|
||||
{}
|
||||
|
||||
ring_pointer _b = nullptr;
|
||||
size_type _i = npos;
|
||||
@ -352,7 +372,7 @@ private:
|
||||
return a._i == b._i;
|
||||
}
|
||||
|
||||
friend auto operator<=>(const _iter& a, const _iter& b) noexcept {
|
||||
friend auto operator<(const _iter& a, const _iter& b) noexcept {
|
||||
return difference_type(a._i - b._i);
|
||||
}
|
||||
|
||||
@ -370,32 +390,38 @@ private:
|
||||
};
|
||||
|
||||
template <typename T, typename Alloc>
|
||||
typename ring_buffer<T, Alloc>::iterator ring_buffer<T, Alloc>::begin() noexcept {
|
||||
typename ring_buffer<T, Alloc>::iterator ring_buffer<T, Alloc>::begin()
|
||||
noexcept {
|
||||
return { this, 0 };
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc>
|
||||
typename ring_buffer<T, Alloc>::iterator ring_buffer<T, Alloc>::end() noexcept {
|
||||
typename ring_buffer<T, Alloc>::iterator ring_buffer<T, Alloc>::end()
|
||||
noexcept {
|
||||
return { this, size() };
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc>
|
||||
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::begin() const noexcept {
|
||||
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::begin()
|
||||
const noexcept {
|
||||
return { this, 0 };
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc>
|
||||
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::end() const noexcept {
|
||||
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::end()
|
||||
const noexcept {
|
||||
return { this, size() };
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc>
|
||||
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::cbegin() const noexcept {
|
||||
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::cbegin()
|
||||
const noexcept {
|
||||
return { this, 0 };
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc>
|
||||
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::cend() const noexcept {
|
||||
typename ring_buffer<T, Alloc>::const_iterator ring_buffer<T, Alloc>::cend()
|
||||
const noexcept {
|
||||
return { this, size() };
|
||||
}
|
||||
|
||||
@ -405,8 +431,8 @@ namespace std {
|
||||
|
||||
template <typename T, typename Alloc>
|
||||
void swap(
|
||||
async_mqtt5::detail::ring_buffer<T, Alloc>& a,
|
||||
async_mqtt5::detail::ring_buffer<T, Alloc>& b)
|
||||
async_mqtt5::detail::ring_buffer<T, Alloc>& a,
|
||||
async_mqtt5::detail::ring_buffer<T, Alloc>& b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
#ifndef ASYNC_MQTT5_UTF8_MQTT_HPP
|
||||
#define ASYNC_MQTT5_UTF8_MQTT_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace async_mqtt5::detail {
|
||||
|
@ -50,7 +50,7 @@ namespace client {
|
||||
/**
|
||||
* \brief MQTT client error codes.
|
||||
*
|
||||
* \details Represents error that occur on the client side.
|
||||
* \details Represents error that occur on the client side.
|
||||
*/
|
||||
enum class error : int {
|
||||
/// \cond INTERNAL
|
||||
@ -162,7 +162,7 @@ public:
|
||||
{}
|
||||
|
||||
constexpr explicit reason_code(uint8_t code) : _code(code) {}
|
||||
/// \endcond
|
||||
/// \endcond
|
||||
|
||||
/// Copy constructor.
|
||||
reason_code(const reason_code&) = default;
|
||||
@ -393,7 +393,7 @@ constexpr reason_code banned { 0x8a };
|
||||
/** The Server is shutting down. */
|
||||
constexpr reason_code server_shutting_down { 0x8b };
|
||||
|
||||
/** The authentication method is not supported or
|
||||
/** The authentication method is not supported or
|
||||
does not match the method currently in use. */
|
||||
constexpr reason_code bad_authentication_method { 0x8c };
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <async_mqtt5/detail/control_packet.hpp>
|
||||
#include <async_mqtt5/detail/internal_types.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
|
||||
|
||||
|
||||
namespace async_mqtt5::detail {
|
||||
@ -199,6 +199,7 @@ private:
|
||||
bool is_reply = code != control_code_e::publish &&
|
||||
code != control_code_e::auth &&
|
||||
code != control_code_e::disconnect;
|
||||
|
||||
if (is_reply) {
|
||||
auto packet_id = decoders::decode_packet_id(first).value();
|
||||
_svc._replies.dispatch(error_code {}, code, packet_id, first, last);
|
||||
|
@ -66,7 +66,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
bool prioritized() const { return _flags & send_flag::prioritized; }
|
||||
bool prioritized() const {
|
||||
return _flags & send_flag::prioritized;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -17,12 +17,19 @@ namespace async_mqtt5::detail {
|
||||
|
||||
namespace asio = boost::asio;
|
||||
|
||||
template <typename StreamType, typename TlsContext>
|
||||
template <
|
||||
typename StreamType, typename TlsContext,
|
||||
typename Enable = void
|
||||
>
|
||||
class stream_context;
|
||||
|
||||
template <typename StreamType, typename TlsContext>
|
||||
requires has_tls_layer<StreamType>
|
||||
class stream_context<StreamType, TlsContext> {
|
||||
template <
|
||||
typename StreamType, typename TlsContext
|
||||
>
|
||||
class stream_context<
|
||||
StreamType, TlsContext,
|
||||
std::enable_if_t<has_tls_layer<StreamType>>
|
||||
> {
|
||||
using tls_context_type = TlsContext;
|
||||
|
||||
mqtt_ctx _mqtt_context;
|
||||
@ -76,8 +83,10 @@ public:
|
||||
};
|
||||
|
||||
template <typename StreamType>
|
||||
requires (!has_tls_layer<StreamType>)
|
||||
class stream_context<StreamType, std::monostate> {
|
||||
class stream_context<
|
||||
StreamType, std::monostate,
|
||||
std::enable_if_t<!has_tls_layer<StreamType>>
|
||||
> {
|
||||
mqtt_ctx _mqtt_context;
|
||||
public:
|
||||
explicit stream_context(std::monostate) {}
|
||||
@ -189,8 +198,11 @@ public:
|
||||
return _stream.get_executor();
|
||||
}
|
||||
|
||||
decltype(auto) tls_context()
|
||||
requires (!std::is_same_v<TlsContext, std::monostate>) {
|
||||
template <
|
||||
typename Ctx = TlsContext,
|
||||
std::enable_if_t<!std::is_same_v<Ctx, std::monostate>, bool> = true
|
||||
>
|
||||
decltype(auto) tls_context() {
|
||||
return _stream_context.tls_context();
|
||||
}
|
||||
|
||||
@ -215,7 +227,10 @@ public:
|
||||
_stream.brokers(std::move(hosts), default_port);
|
||||
}
|
||||
|
||||
template <typename Authenticator>
|
||||
template <
|
||||
typename Authenticator,
|
||||
std::enable_if_t<is_authenticator<Authenticator>, bool> = true
|
||||
>
|
||||
void authenticator(Authenticator&& authenticator) {
|
||||
if (!is_open())
|
||||
_stream_context.authenticator(
|
||||
@ -339,7 +354,9 @@ public:
|
||||
}
|
||||
|
||||
bool channel_store_error(error_code ec) {
|
||||
return _rec_channel.try_send(ec, std::string {}, std::string {}, publish_props {});
|
||||
return _rec_channel.try_send(
|
||||
ec, std::string {}, std::string {}, publish_props {}
|
||||
);
|
||||
}
|
||||
|
||||
template <typename CompletionToken>
|
||||
|
@ -3,12 +3,10 @@
|
||||
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
#include <boost/spirit/home/x3/binary/binary.hpp>
|
||||
|
||||
#include <boost/fusion/adapted/std_tuple.hpp>
|
||||
|
||||
#include <async_mqtt5/property_types.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/internal/codecs/traits.hpp>
|
||||
#include <async_mqtt5/impl/codecs/traits.hpp>
|
||||
|
||||
namespace async_mqtt5::decoders {
|
||||
|
||||
@ -51,7 +49,7 @@ auto type_parse(It& first, const It last, const Parser& p) {
|
||||
rv = std::move(value);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename AttributeType, typename It, typename Parser>
|
||||
auto type_parse(It& first, const It last, const Parser& p) {
|
||||
@ -72,29 +70,39 @@ constexpr auto to(T& arg) {
|
||||
if constexpr (is_boost_iterator<attr_type>)
|
||||
arg = T { x3::_attr(ctx).begin(), x3::_attr(ctx).end() };
|
||||
else
|
||||
arg = x3::_attr(ctx);
|
||||
arg = x3::_attr(ctx);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename LenParser, typename Subject>
|
||||
template <typename LenParser, typename Subject, typename Enable = void>
|
||||
class scope_limit {};
|
||||
|
||||
template <typename LenParser, typename Subject>
|
||||
requires (x3::traits::is_parser<LenParser>::value)
|
||||
class scope_limit<LenParser, Subject> :
|
||||
public x3::unary_parser<Subject, scope_limit<LenParser, Subject>> {
|
||||
|
||||
class scope_limit<
|
||||
LenParser, Subject,
|
||||
std::enable_if_t<x3::traits::is_parser<LenParser>::value>
|
||||
> :
|
||||
public x3::unary_parser<Subject, scope_limit<LenParser, Subject>>
|
||||
{
|
||||
using base_type = x3::unary_parser<Subject, scope_limit<LenParser, Subject>>;
|
||||
LenParser _lp;
|
||||
public:
|
||||
using ctx_type = decltype(x3::make_context<struct _>(std::declval<Subject&>()));
|
||||
using attribute_type = typename x3::traits::attribute_of<Subject, ctx_type>::type;
|
||||
using ctx_type =
|
||||
decltype(x3::make_context<struct _>(std::declval<Subject&>()));
|
||||
using attribute_type =
|
||||
typename x3::traits::attribute_of<Subject, ctx_type>::type;
|
||||
|
||||
static bool const has_attribute = true;
|
||||
|
||||
scope_limit(const LenParser& lp, const Subject& subject) : base_type(subject), _lp(lp) {}
|
||||
scope_limit(const LenParser& lp, const Subject& subject) :
|
||||
base_type(subject), _lp(lp)
|
||||
{}
|
||||
|
||||
template <typename It, typename Ctx, typename RCtx, typename Attr>
|
||||
bool parse(It& first, const It last, const Ctx& ctx, RCtx& rctx, Attr& attr) const {
|
||||
bool parse(
|
||||
It& first, const It last,
|
||||
const Ctx& ctx, RCtx& rctx, Attr& attr
|
||||
) const {
|
||||
|
||||
It iter = first;
|
||||
typename x3::traits::attribute_of<LenParser, Ctx>::type len;
|
||||
@ -112,21 +120,31 @@ public:
|
||||
};
|
||||
|
||||
template <typename Size, typename Subject>
|
||||
requires (std::is_arithmetic_v<Size>)
|
||||
class scope_limit<Size, Subject> :
|
||||
public x3::unary_parser<Subject, scope_limit<Size, Subject>> {
|
||||
|
||||
class scope_limit<
|
||||
Size, Subject,
|
||||
std::enable_if_t<std::is_arithmetic_v<Size>>
|
||||
> :
|
||||
public x3::unary_parser<Subject, scope_limit<Size, Subject>>
|
||||
{
|
||||
using base_type = x3::unary_parser<Subject, scope_limit<Size, Subject>>;
|
||||
size_t _limit;
|
||||
public:
|
||||
using ctx_type = decltype(x3::make_context<struct _>(std::declval<Subject&>()));
|
||||
using attribute_type = typename x3::traits::attribute_of<Subject, ctx_type>::type;
|
||||
using ctx_type =
|
||||
decltype(x3::make_context<struct _>(std::declval<Subject&>()));
|
||||
using attribute_type =
|
||||
typename x3::traits::attribute_of<Subject, ctx_type>::type;
|
||||
|
||||
static bool const has_attribute = true;
|
||||
|
||||
scope_limit(Size limit, const Subject& subject) : base_type(subject), _limit(limit) {}
|
||||
scope_limit(Size limit, const Subject& subject) :
|
||||
base_type(subject), _limit(limit)
|
||||
{}
|
||||
|
||||
template <typename It, typename Ctx, typename RCtx, typename Attr>
|
||||
bool parse(It& first, const It last, const Ctx& ctx, RCtx& rctx, Attr& attr) const {
|
||||
bool parse(
|
||||
It& first, const It last,
|
||||
const Ctx& ctx, RCtx& rctx, Attr& attr
|
||||
) const {
|
||||
|
||||
It iter = first;
|
||||
if (iter + _limit > last)
|
||||
@ -140,7 +158,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <typename LenParser>
|
||||
template <typename LenParser, typename Enable = void>
|
||||
struct scope_limit_gen {
|
||||
template <typename Subject>
|
||||
auto operator[](const Subject& p) const {
|
||||
@ -150,8 +168,10 @@ struct scope_limit_gen {
|
||||
};
|
||||
|
||||
template <typename Size>
|
||||
requires (std::is_arithmetic_v<Size>)
|
||||
struct scope_limit_gen<Size> {
|
||||
struct scope_limit_gen<
|
||||
Size,
|
||||
std::enable_if_t<std::is_arithmetic_v<Size>>
|
||||
> {
|
||||
template <typename Subject>
|
||||
auto operator[](const Subject& p) const {
|
||||
return scope_limit<Size, Subject> { limit, x3::as_parser(p) };
|
||||
@ -159,19 +179,22 @@ struct scope_limit_gen<Size> {
|
||||
Size limit;
|
||||
};
|
||||
|
||||
template <typename Parser>
|
||||
requires (x3::traits::is_parser<Parser>::value)
|
||||
template <
|
||||
typename Parser,
|
||||
std::enable_if_t<x3::traits::is_parser<Parser>::value, bool> = true
|
||||
>
|
||||
scope_limit_gen<Parser> scope_limit_(const Parser& p) {
|
||||
return { p };
|
||||
}
|
||||
|
||||
template <typename Size>
|
||||
requires (std::is_arithmetic_v<Size>)
|
||||
template <
|
||||
typename Size,
|
||||
std::enable_if_t<std::is_arithmetic_v<Size>, bool> = true
|
||||
>
|
||||
scope_limit_gen<Size> scope_limit_(Size limit) {
|
||||
return { limit };
|
||||
}
|
||||
|
||||
|
||||
struct verbatim_parser : x3::parser<verbatim_parser> {
|
||||
using attribute_type = std::string;
|
||||
static bool const has_attribute = true;
|
||||
@ -191,7 +214,10 @@ struct varint_parser : x3::parser<varint_parser> {
|
||||
static bool const has_attribute = true;
|
||||
|
||||
template <typename It, typename Ctx, typename RCtx, typename Attr>
|
||||
bool parse(It& first, const It last, const Ctx& ctx, RCtx& rctx, Attr& attr) const {
|
||||
bool parse(
|
||||
It& first, const It last,
|
||||
const Ctx& ctx, RCtx& rctx, Attr& attr
|
||||
) const {
|
||||
|
||||
It iter = first;
|
||||
x3::skip_over(iter, last, ctx);
|
||||
@ -229,7 +255,10 @@ struct len_prefix_parser : x3::parser<len_prefix_parser> {
|
||||
static bool const has_attribute = true;
|
||||
|
||||
template <typename It, typename Ctx, typename RCtx, typename Attr>
|
||||
bool parse(It& first, const It last, const Ctx& ctx, RCtx& rctx, Attr& attr) const {
|
||||
bool parse(
|
||||
It& first, const It last,
|
||||
const Ctx& ctx, RCtx& rctx, Attr& attr
|
||||
) const {
|
||||
It iter = first;
|
||||
x3::skip_over(iter, last, ctx);
|
||||
|
||||
@ -238,7 +267,7 @@ struct len_prefix_parser : x3::parser<len_prefix_parser> {
|
||||
if (std::distance(iter, last) < len)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
else
|
||||
return false;
|
||||
|
||||
attr = std::string(iter, iter + len);
|
||||
@ -251,26 +280,34 @@ constexpr len_prefix_parser utf8_{};
|
||||
constexpr len_prefix_parser binary_{};
|
||||
|
||||
/*
|
||||
Boost Spirit incorrectly deduces atribute type for a parser of the form
|
||||
Boost Spirit incorrectly deduces atribute type for a parser of the form
|
||||
(eps(a) | parser1) >> (eps(b) | parser)
|
||||
and we had to create if_ parser to remedy the issue
|
||||
and we had to create if_ parser to remedy the issue
|
||||
*/
|
||||
|
||||
template <typename Subject>
|
||||
class conditional_parser : public x3::unary_parser<Subject, conditional_parser<Subject>> {
|
||||
class conditional_parser :
|
||||
public x3::unary_parser<Subject, conditional_parser<Subject>> {
|
||||
|
||||
using base_type = x3::unary_parser<Subject, conditional_parser<Subject>>;
|
||||
bool _condition;
|
||||
public:
|
||||
using ctx_type = decltype(x3::make_context<struct _>(std::declval<Subject&>()));
|
||||
using subject_attr_type = typename x3::traits::attribute_of<Subject, ctx_type>::type;
|
||||
using ctx_type =
|
||||
decltype(x3::make_context<struct _>(std::declval<Subject&>()));
|
||||
using subject_attr_type =
|
||||
typename x3::traits::attribute_of<Subject, ctx_type>::type;
|
||||
|
||||
using attribute_type = boost::optional<subject_attr_type>;
|
||||
static bool const has_attribute = true;
|
||||
|
||||
conditional_parser(const Subject& s, bool condition) : base_type(s), _condition(condition) {}
|
||||
conditional_parser(const Subject& s, bool condition) :
|
||||
base_type(s), _condition(condition) {}
|
||||
|
||||
template <typename It, typename Ctx, typename RCtx, typename Attr>
|
||||
bool parse(It& first, const It last, const Ctx& ctx, RCtx& rctx, Attr& attr) const {
|
||||
bool parse(
|
||||
It& first, const It last,
|
||||
const Ctx& ctx, RCtx& rctx, Attr& attr
|
||||
) const {
|
||||
if (!_condition)
|
||||
return true;
|
||||
|
||||
@ -308,12 +345,17 @@ namespace basic = async_mqtt5::decoders::basic;
|
||||
namespace detail {
|
||||
|
||||
template <typename It, typename Ctx, typename RCtx, typename Prop>
|
||||
bool parse_to_prop(It& iter, const It last, const Ctx& ctx, RCtx& rctx, Prop& prop) {
|
||||
bool parse_to_prop(
|
||||
It& iter, const It last,
|
||||
const Ctx& ctx, RCtx& rctx, Prop& prop
|
||||
) {
|
||||
using prop_type = decltype(prop);
|
||||
|
||||
bool rv = false;
|
||||
if constexpr (is_optional<prop_type>) {
|
||||
using value_type = typename std::remove_reference_t<prop_type>::value_type;
|
||||
using value_type =
|
||||
typename std::remove_reference_t<prop_type>::value_type;
|
||||
|
||||
if constexpr (std::is_same_v<value_type, uint8_t>) {
|
||||
uint8_t attr;
|
||||
rv = x3::byte_.parse(iter, last, ctx, rctx, attr);
|
||||
@ -363,7 +405,10 @@ public:
|
||||
static bool const has_attribute = true;
|
||||
|
||||
template <typename It, typename Ctx, typename RCtx, typename Attr>
|
||||
bool parse(It& first, const It last, const Ctx& ctx, RCtx& rctx, Attr& attr) const {
|
||||
bool parse(
|
||||
It& first, const It last,
|
||||
const Ctx& ctx, RCtx& rctx, Attr& attr
|
||||
) const {
|
||||
|
||||
It iter = first;
|
||||
x3::skip_over(iter, last, ctx);
|
||||
@ -379,13 +424,18 @@ public:
|
||||
// attr = Props{};
|
||||
|
||||
while (iter < scoped_last) {
|
||||
uint8_t prop_id = *iter++;
|
||||
uint8_t prop_id = *iter++;
|
||||
bool rv = true;
|
||||
It saved = iter;
|
||||
|
||||
attr.apply_on(prop_id, [&rv, &iter, scoped_last, &ctx, &rctx](auto& prop) {
|
||||
rv = detail::parse_to_prop(iter, scoped_last, ctx, rctx, prop);
|
||||
});
|
||||
attr.apply_on(
|
||||
prop_id,
|
||||
[&rv, &iter, scoped_last, &ctx, &rctx](auto& prop) {
|
||||
rv = detail::parse_to_prop(
|
||||
iter, scoped_last, ctx, rctx, prop
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// either rv = false or property with prop_id was not found
|
||||
if (!rv || iter == saved)
|
||||
@ -399,7 +449,7 @@ public:
|
||||
|
||||
|
||||
template <typename Props>
|
||||
constexpr auto props_ = prop_parser<Props>{};
|
||||
constexpr auto props_ = prop_parser<Props> {};
|
||||
|
||||
} // end namespace prop
|
||||
|
@ -4,10 +4,13 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include <boost/core/identity.hpp>
|
||||
#include <boost/endian/conversion.hpp>
|
||||
#include <boost/type_traits/is_detected_exact.hpp>
|
||||
#include <boost/type_traits/remove_cv_ref.hpp>
|
||||
|
||||
#include <async_mqtt5/property_types.hpp>
|
||||
#include <async_mqtt5/impl/internal/codecs/traits.hpp>
|
||||
#include <async_mqtt5/impl/codecs/traits.hpp>
|
||||
|
||||
namespace async_mqtt5::encoders {
|
||||
|
||||
@ -54,10 +57,13 @@ public:
|
||||
flag_def(repr val) : _val(val) {}
|
||||
flag_def() = default;
|
||||
|
||||
template <class T, typename projection = std::identity>
|
||||
requires (is_optional<T>)
|
||||
template <
|
||||
typename T,
|
||||
typename projection = boost::identity,
|
||||
std::enable_if_t<is_optional<T>, bool> = true
|
||||
>
|
||||
auto operator()(T&& value, projection proj = {}) const {
|
||||
if constexpr (std::is_same_v<projection, std::identity>) {
|
||||
if constexpr (std::is_same_v<projection, boost::identity>) {
|
||||
repr val = value.has_value();
|
||||
return flag_def<bits, repr> { val };
|
||||
}
|
||||
@ -68,8 +74,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, typename projection = std::identity>
|
||||
requires (!is_optional<T>)
|
||||
template <
|
||||
typename T,
|
||||
typename projection = boost::identity,
|
||||
std::enable_if_t<!is_optional<T>, bool> = true
|
||||
>
|
||||
auto operator()(T&& value, projection proj = {}) const {
|
||||
auto val = static_cast<repr>(std::invoke(proj, value));
|
||||
return flag_def<bits, repr> { val };
|
||||
@ -157,7 +166,7 @@ public:
|
||||
auto operator()(T&& val, projection proj) const {
|
||||
if constexpr (is_optional<T>) {
|
||||
using rv_type = std::invoke_result_t<
|
||||
projection, typename std::remove_cvref_t<T>::value_type
|
||||
projection, typename boost::remove_cv_ref_t<T>::value_type
|
||||
>;
|
||||
if (val.has_value())
|
||||
return (*this)(std::invoke(proj, *val));
|
||||
@ -181,7 +190,9 @@ class array_val : public encoder {
|
||||
T _val;
|
||||
bool _with_length;
|
||||
public:
|
||||
array_val(T val, bool with_length) : _val(val), _with_length(with_length) {
|
||||
array_val(T val, bool with_length) :
|
||||
_val(val), _with_length(with_length)
|
||||
{
|
||||
static_assert(
|
||||
std::is_reference_v<T> || std::is_same_v<T, std::string_view>
|
||||
);
|
||||
@ -204,11 +215,15 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename V>
|
||||
using has_size = decltype(std::declval<V&>().size());
|
||||
|
||||
template <typename U>
|
||||
static size_t val_length(U&& val) {
|
||||
if constexpr (std::same_as<std::remove_cvref_t<U>, const char*>)
|
||||
if constexpr (std::is_same_v<boost::remove_cv_ref_t<U>, const char*>)
|
||||
return std::strlen(val);
|
||||
if constexpr (requires { val.size(); })
|
||||
|
||||
if constexpr (boost::is_detected_exact_v<size_t, has_size, U>)
|
||||
return val.size();
|
||||
else // fallback to type const char (&)[N] (substract 1 for trailing 0)
|
||||
return sizeof(val) - 1;
|
||||
@ -243,7 +258,7 @@ public:
|
||||
auto operator()(T&& val, projection proj) const {
|
||||
if constexpr (is_optional<T>) {
|
||||
using rv_type = std::invoke_result_t<
|
||||
projection, typename std::remove_cvref_t<T>::value_type
|
||||
projection, typename boost::remove_cv_ref_t<T>::value_type
|
||||
>;
|
||||
if (val.has_value())
|
||||
return (*this)(std::invoke(proj, *val));
|
||||
@ -263,12 +278,13 @@ constexpr auto binary_ = array_def<true>{}; // for now
|
||||
constexpr auto verbatim_ = array_def<false>{};
|
||||
|
||||
|
||||
template <class T, class U>
|
||||
template <typename T, typename U>
|
||||
class composed_val : public encoder {
|
||||
T _lhs; U _rhs;
|
||||
public:
|
||||
composed_val(T lhs, U rhs) :
|
||||
_lhs(std::forward<T>(lhs)), _rhs(std::forward<U>(rhs)) {}
|
||||
_lhs(std::forward<T>(lhs)), _rhs(std::forward<U>(rhs))
|
||||
{}
|
||||
|
||||
size_t byte_size() const {
|
||||
return _lhs.byte_size() + _rhs.byte_size();
|
||||
@ -280,17 +296,22 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
requires (
|
||||
std::derived_from<std::decay_t<T>, encoder> &&
|
||||
std::derived_from<std::decay_t<U>, encoder>
|
||||
)
|
||||
template <
|
||||
typename T, typename U,
|
||||
std::enable_if_t<
|
||||
std::is_base_of_v<encoder, std::decay_t<T>> &&
|
||||
std::is_base_of_v<encoder, std::decay_t<U>>,
|
||||
bool
|
||||
> = true
|
||||
>
|
||||
inline auto operator&(T&& t, U&& u) {
|
||||
return composed_val(std::forward<T>(t), std::forward<U>(u));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
requires (std::derived_from<std::decay_t<T>, encoder>)
|
||||
template <
|
||||
typename T,
|
||||
std::enable_if_t<std::is_base_of_v<encoder, std::decay_t<T>>, bool> = true
|
||||
>
|
||||
std::string& operator<<(std::string& s, T&& t) {
|
||||
return t.encode(s);
|
||||
}
|
||||
@ -347,7 +368,7 @@ using encoder_types = std::tuple<
|
||||
prop_encoder_type<pp::content_type_t, basic::utf8_def>,
|
||||
prop_encoder_type<pp::response_topic_t, basic::utf8_def>,
|
||||
prop_encoder_type<pp::correlation_data_t, basic::utf8_def>,
|
||||
prop_encoder_type<pp::subscription_identifier_t, basic::int_def<intptr_t>>,
|
||||
prop_encoder_type<pp::subscription_identifier_t, basic::int_def<uint32_t>>,
|
||||
prop_encoder_type<pp::session_expiry_interval_t, basic::int_def<int32_t>>,
|
||||
prop_encoder_type<pp::assigned_client_identifier_t, basic::utf8_def>,
|
||||
prop_encoder_type<pp::server_keep_alive_t, basic::int_def<int16_t>>,
|
||||
@ -376,14 +397,18 @@ constexpr auto encoder_for_prop = typename std::tuple_element_t<
|
||||
>::value {};
|
||||
|
||||
|
||||
template <typename T, pp::property_type p>
|
||||
template <typename T, pp::property_type p, typename Enable = void>
|
||||
class prop_val;
|
||||
|
||||
template <typename T, pp::property_type p>
|
||||
requires (!is_vector<T> && is_optional<T>)
|
||||
class prop_val<T, p> : public basic::encoder {
|
||||
template <
|
||||
typename T, pp::property_type p
|
||||
>
|
||||
class prop_val<
|
||||
T, p,
|
||||
std::enable_if_t<!is_vector<T> && is_optional<T>>
|
||||
> : public basic::encoder {
|
||||
// T is always std::optional
|
||||
using opt_type = typename std::remove_cvref_t<T>::value_type;
|
||||
using opt_type = typename boost::remove_cv_ref_t<T>::value_type;
|
||||
// allows T to be reference type to std::optional
|
||||
static inline std::optional<opt_type> nulltype;
|
||||
T _val;
|
||||
@ -408,11 +433,15 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, pp::property_type p>
|
||||
requires (is_vector<T>)
|
||||
class prop_val<T, p> : public basic::encoder {
|
||||
template <
|
||||
typename T, pp::property_type p
|
||||
>
|
||||
class prop_val<
|
||||
T, p,
|
||||
std::enable_if_t<is_vector<T>>
|
||||
> : public basic::encoder {
|
||||
// allows T to be reference type to std::vector
|
||||
static inline std::remove_cvref_t<T> nulltype;
|
||||
static inline boost::remove_cv_ref_t<T> nulltype;
|
||||
T _val;
|
||||
public:
|
||||
prop_val(T val) : _val(val) {
|
||||
@ -464,7 +493,7 @@ class props_val : public basic::encoder {
|
||||
);
|
||||
}
|
||||
|
||||
template <class Func>
|
||||
template <typename Func>
|
||||
auto apply_each(Func&& func) const {
|
||||
return std::apply([&func](const auto&... props) {
|
||||
return (std::invoke(func, props), ...);
|
||||
@ -516,7 +545,7 @@ public:
|
||||
if (prop_container.has_value())
|
||||
return (*this)(*prop_container);
|
||||
return props_val<
|
||||
const typename std::remove_cvref_t<T>::value_type&
|
||||
const typename boost::remove_cv_ref_t<T>::value_type&
|
||||
>(true);
|
||||
}
|
||||
else {
|
@ -5,7 +5,7 @@
|
||||
#include <string>
|
||||
|
||||
#include <async_mqtt5/detail/internal_types.hpp>
|
||||
#include <async_mqtt5/impl/internal/codecs/base_decoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/base_decoders.hpp>
|
||||
|
||||
namespace async_mqtt5::decoders {
|
||||
|
||||
@ -131,9 +131,9 @@ inline std::optional<publish_message> decode_publish(
|
||||
uint8_t flags = control_byte & 0b1111;
|
||||
auto qos = qos_e((flags >> 1) & 0b11);
|
||||
|
||||
auto publish_ = basic::scope_limit_(remain_length)[
|
||||
basic::utf8_ >> basic::if_(qos != qos_e::at_most_once)[x3::big_word] >> x3::attr(flags) >>
|
||||
prop::props_<publish_props> >> basic::verbatim_
|
||||
auto publish_ = basic::scope_limit_(remain_length)[
|
||||
basic::utf8_ >> basic::if_(qos != qos_e::at_most_once)[x3::big_word] >>
|
||||
x3::attr(flags) >> prop::props_<publish_props> >> basic::verbatim_
|
||||
];
|
||||
return type_parse(it, it + remain_length, publish_);
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
#include <optional>
|
||||
|
||||
#include <async_mqtt5/types.hpp>
|
||||
#include <async_mqtt5/impl/internal/codecs/base_encoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/base_encoders.hpp>
|
||||
|
||||
namespace async_mqtt5::encoders {
|
||||
|
42
include/async_mqtt5/impl/codecs/traits.hpp
Normal file
42
include/async_mqtt5/impl/codecs/traits.hpp
Normal file
@ -0,0 +1,42 @@
|
||||
#ifndef ASYNC_MQTT5_TRAITS_HPP
|
||||
#define ASYNC_MQTT5_TRAITS_HPP
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/range/iterator_range_core.hpp>
|
||||
#include <boost/type_traits/remove_cv_ref.hpp>
|
||||
|
||||
|
||||
namespace async_mqtt5 {
|
||||
|
||||
template <typename>
|
||||
constexpr bool is_optional_impl = false;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_optional_impl<std::optional<T>> = true;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_optional = is_optional_impl<boost::remove_cv_ref_t<T>>;
|
||||
|
||||
template <typename, template <typename...> typename>
|
||||
constexpr bool is_specialization = false;
|
||||
|
||||
template <template <typename...> typename T, typename... Args>
|
||||
constexpr bool is_specialization<T<Args...>, T> = true;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_vector = is_specialization<
|
||||
boost::remove_cv_ref_t<T>, std::vector
|
||||
>;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_boost_iterator = is_specialization<
|
||||
boost::remove_cv_ref_t<T>, boost::iterator_range
|
||||
>;
|
||||
|
||||
|
||||
|
||||
} // end namespace async_mqtt5
|
||||
|
||||
#endif // !ASYNC_MQTT5_TRAITS_HPP
|
@ -18,8 +18,8 @@
|
||||
#include <async_mqtt5/detail/control_packet.hpp>
|
||||
#include <async_mqtt5/detail/internal_types.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
|
||||
|
||||
namespace async_mqtt5::detail {
|
||||
|
||||
@ -108,7 +108,9 @@ public:
|
||||
)
|
||||
);
|
||||
}
|
||||
else if constexpr (has_tls_handshake<typename next_layer_type<Stream>::type>) {
|
||||
else if constexpr (
|
||||
has_tls_handshake<typename next_layer_type<Stream>::type>
|
||||
) {
|
||||
_stream.next_layer().async_handshake(
|
||||
tls_handshake_type<typename next_layer_type<Stream>::type>::client,
|
||||
asio::append(
|
||||
@ -252,9 +254,8 @@ public:
|
||||
asio::async_read(
|
||||
_stream, buff,
|
||||
asio::prepend(
|
||||
asio::append(
|
||||
std::move(*this), code, first, last
|
||||
), on_read_packet {}
|
||||
asio::append(std::move(*this), code, first, last),
|
||||
on_read_packet {}
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -285,7 +286,6 @@ public:
|
||||
_ctx.ca_props = ca_props;
|
||||
_ctx.state.session_present(session_present);
|
||||
|
||||
// TODO: session_present logic
|
||||
// Unexpected result handling:
|
||||
// - If we don't have a Session State, and we get session_present = true,
|
||||
// we must close the network connection (and restart with a clean start)
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <async_mqtt5/detail/internal_types.hpp>
|
||||
#include <async_mqtt5/detail/cancellable_handler.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
|
||||
|
||||
namespace async_mqtt5::detail {
|
||||
|
||||
|
@ -84,7 +84,7 @@ public:
|
||||
}
|
||||
|
||||
void operator()(
|
||||
on_resolve, auto ord,
|
||||
on_resolve, std::array<std::size_t, 2> ord,
|
||||
error_code resolve_ec, epoints epts,
|
||||
error_code timer_ec, authority_path ap
|
||||
) {
|
||||
|
@ -1,39 +0,0 @@
|
||||
#ifndef ASYNC_MQTT5_MEMORY_H
|
||||
#define ASYNC_MQTT5_MEMORY_H
|
||||
|
||||
#include "memory_resource.h"
|
||||
|
||||
namespace pma {
|
||||
|
||||
template <typename T>
|
||||
class alloc : public polymorphic_allocator<T> {
|
||||
using base = polymorphic_allocator<T>;
|
||||
|
||||
public:
|
||||
alloc(pma::memory_resource* r) noexcept : base(r) {}
|
||||
|
||||
template <class T2>
|
||||
alloc(const alloc<T2>& other) noexcept : base(other.resource()) {}
|
||||
|
||||
using value_type = typename base::value_type;
|
||||
using pointer = typename base::value_type*;
|
||||
using const_pointer = const typename base::value_type*;
|
||||
using reference = typename base::value_type&;
|
||||
using const_reference = const typename base::value_type&;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
// https://stackoverflow.com/questions/27471053/example-usage-of-propagate-on-container-move-assignment
|
||||
|
||||
using propagate_on_container_copy_assignment = std::false_type;
|
||||
using propagate_on_container_move_assignment = std::false_type;
|
||||
using propagate_on_container_swap = std::false_type;
|
||||
|
||||
alloc select_on_container_copy_construction() const noexcept {
|
||||
return alloc(base::resource());
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace pma
|
||||
|
||||
#endif // !ASYNC_MQTT5_MEMORY_H
|
@ -1,515 +0,0 @@
|
||||
#ifndef ASYNC_MQTT5_MEMORY_RESOURCE_H
|
||||
#define ASYNC_MQTT5_MEMORY_RESOURCE_H
|
||||
|
||||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <tuple>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace pma {
|
||||
|
||||
struct erased_type { };
|
||||
|
||||
template <size_t...> struct __tuple_indices {};
|
||||
|
||||
template <class _IdxType, _IdxType... _Values>
|
||||
struct __integer_sequence {
|
||||
template <
|
||||
template <class _OIdxType, _OIdxType...> class _ToIndexSeq,
|
||||
class _ToIndexType
|
||||
>
|
||||
using __convert = _ToIndexSeq<_ToIndexType, _Values...>;
|
||||
|
||||
template <size_t _Sp>
|
||||
using __to_tuple_indices = __tuple_indices<(_Values + _Sp)...>;
|
||||
};
|
||||
|
||||
template <size_t _Ep, size_t _Sp>
|
||||
using __make_indices_imp =
|
||||
typename __make_integer_seq<__integer_sequence, size_t, _Ep - _Sp>::template
|
||||
__to_tuple_indices<_Sp>;
|
||||
|
||||
template <size_t _Ep, size_t _Sp = 0>
|
||||
struct __make_tuple_indices {
|
||||
static_assert(_Sp <= _Ep, "__make_tuple_indices input error");
|
||||
using type = __make_indices_imp<_Ep, _Sp>;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct allocator_arg_t {
|
||||
explicit allocator_arg_t() = default;
|
||||
};
|
||||
|
||||
template <class _Tp>
|
||||
struct __has_allocator_type
|
||||
{
|
||||
private:
|
||||
struct __two {char __lx; char __lxx;};
|
||||
template <class _Up> static __two __test(...);
|
||||
template <class _Up> static char __test(typename _Up::allocator_type* = 0);
|
||||
public:
|
||||
static const bool value = sizeof(__test<_Tp>(0)) == 1;
|
||||
};
|
||||
|
||||
template <class _Tp, class _Alloc, bool = __has_allocator_type<_Tp>::value>
|
||||
struct __uses_allocator :
|
||||
public std::integral_constant<
|
||||
bool,
|
||||
std::is_convertible<_Alloc, typename _Tp::allocator_type>::value
|
||||
>
|
||||
{};
|
||||
|
||||
template <class _Tp, class _Alloc>
|
||||
struct __uses_allocator<_Tp, _Alloc, false> : public std::false_type {};
|
||||
|
||||
template <class _Tp, class _Alloc>
|
||||
struct uses_allocator : public __uses_allocator<_Tp, _Alloc> {};
|
||||
|
||||
template <class _Tp, class _Alloc>
|
||||
inline constexpr size_t uses_allocator_v = uses_allocator<_Tp, _Alloc>::value;
|
||||
|
||||
template <
|
||||
class _Tp, class _Alloc,
|
||||
bool = std::uses_allocator<_Tp, _Alloc>::value,
|
||||
bool = __has_allocator_type<_Tp>::value
|
||||
>
|
||||
struct __lfts_uses_allocator : public std::false_type {};
|
||||
|
||||
template <class _Tp, class _Alloc>
|
||||
struct __lfts_uses_allocator<_Tp, _Alloc, false, false> :
|
||||
public std::false_type {};
|
||||
|
||||
template <class _Tp, class _Alloc, bool HasAlloc>
|
||||
struct __lfts_uses_allocator<_Tp, _Alloc, true, HasAlloc> :
|
||||
public std::true_type {};
|
||||
|
||||
template <class _Tp, class _Alloc>
|
||||
struct __lfts_uses_allocator<_Tp, _Alloc, false, true> :
|
||||
public std::integral_constant<
|
||||
bool,
|
||||
std::is_convertible<_Alloc, typename _Tp::allocator_type>::value
|
||||
|| std::is_same<erased_type, typename _Tp::allocator_type>::value
|
||||
>
|
||||
{};
|
||||
|
||||
template <bool _UsesAlloc, class _Tp, class _Alloc, class ..._Args>
|
||||
struct __lfts_uses_alloc_ctor_imp {
|
||||
static const int value = 0;
|
||||
};
|
||||
|
||||
template <class _Tp, class _Alloc, class ..._Args>
|
||||
struct __lfts_uses_alloc_ctor_imp<true, _Tp, _Alloc, _Args...>
|
||||
{
|
||||
static const bool __ic_first =
|
||||
std::is_constructible<_Tp, std::allocator_arg_t, _Alloc, _Args...>::value;
|
||||
|
||||
static const bool __ic_second =
|
||||
std::conditional<
|
||||
__ic_first,
|
||||
std::false_type,
|
||||
std::is_constructible<_Tp, _Args..., _Alloc>
|
||||
>::type::value;
|
||||
|
||||
static_assert(
|
||||
__ic_first || __ic_second,
|
||||
"Request for uses allocator construction is ill-formed"
|
||||
);
|
||||
|
||||
static const int value = __ic_first ? 1 : 2;
|
||||
};
|
||||
|
||||
template <class _Tp, class _Alloc, class ..._Args>
|
||||
struct __lfts_uses_alloc_ctor :
|
||||
std::integral_constant<int,
|
||||
__lfts_uses_alloc_ctor_imp<
|
||||
__lfts_uses_allocator<_Tp, _Alloc>::value
|
||||
, _Tp, _Alloc, _Args...
|
||||
>::value
|
||||
>
|
||||
{};
|
||||
|
||||
template <class _Tp, class _Allocator, class... _Args>
|
||||
inline void __user_alloc_construct_impl(
|
||||
std::integral_constant<int, 2>, _Tp *__storage,
|
||||
const _Allocator &__a, _Args &&... __args
|
||||
) {
|
||||
new (__storage) _Tp (std::forward<_Args>(__args)..., __a);
|
||||
}
|
||||
|
||||
template <class _Tp, class _Alloc, class ..._Args>
|
||||
inline void __lfts_user_alloc_construct(
|
||||
_Tp * __store, const _Alloc & __a, _Args &&... __args
|
||||
) {
|
||||
__user_alloc_construct_impl(
|
||||
typename __lfts_uses_alloc_ctor<_Tp, _Alloc, _Args...>::type(),
|
||||
__store, __a, std::forward<_Args>(__args)...
|
||||
);
|
||||
}
|
||||
|
||||
inline size_t __aligned_allocation_size(size_t __s, size_t __a) noexcept {
|
||||
return (__s + __a - 1) & ~(__a - 1);
|
||||
}
|
||||
|
||||
class memory_resource {
|
||||
static const size_t __max_align = alignof(max_align_t);
|
||||
|
||||
public:
|
||||
virtual ~memory_resource() = default;
|
||||
|
||||
void* allocate(size_t __bytes, size_t __align = __max_align) {
|
||||
return do_allocate(__bytes, __align);
|
||||
}
|
||||
|
||||
void deallocate(void * __p, size_t __bytes, size_t __align = __max_align) {
|
||||
do_deallocate(__p, __bytes, __align);
|
||||
}
|
||||
|
||||
bool is_equal(memory_resource const & __other) const noexcept {
|
||||
return do_is_equal(__other);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void* do_allocate(size_t, size_t) = 0;
|
||||
virtual void do_deallocate(void*, size_t, size_t) = 0;
|
||||
virtual bool do_is_equal(memory_resource const &) const noexcept = 0;
|
||||
};
|
||||
|
||||
inline bool operator==(
|
||||
memory_resource const & __lhs,
|
||||
memory_resource const & __rhs
|
||||
) noexcept {
|
||||
return &__lhs == &__rhs || __lhs.is_equal(__rhs);
|
||||
}
|
||||
|
||||
inline
|
||||
bool operator!=(
|
||||
memory_resource const & __lhs,
|
||||
memory_resource const & __rhs
|
||||
) noexcept {
|
||||
return !(__lhs == __rhs);
|
||||
}
|
||||
|
||||
memory_resource* new_delete_resource() noexcept;
|
||||
|
||||
memory_resource* null_memory_resource() noexcept;
|
||||
|
||||
memory_resource* get_default_resource() noexcept;
|
||||
|
||||
// memory_resource* set_default_resource(memory_resource * __new_res) noexcept;
|
||||
|
||||
template <class _ValueType>
|
||||
class polymorphic_allocator {
|
||||
public:
|
||||
using value_type = _ValueType;
|
||||
|
||||
polymorphic_allocator() noexcept :
|
||||
__res_(get_default_resource())
|
||||
{}
|
||||
|
||||
polymorphic_allocator(memory_resource* __r) noexcept :
|
||||
__res_(__r)
|
||||
{}
|
||||
|
||||
polymorphic_allocator(polymorphic_allocator const &) = default;
|
||||
|
||||
template <class _Tp>
|
||||
polymorphic_allocator(polymorphic_allocator<_Tp> const & __other) noexcept :
|
||||
__res_(__other.resource())
|
||||
{}
|
||||
|
||||
polymorphic_allocator &
|
||||
operator=(polymorphic_allocator const &) = delete;
|
||||
|
||||
_ValueType* allocate(size_t __n) {
|
||||
if (__n > __max_size())
|
||||
throw std::bad_array_new_length();
|
||||
return static_cast<_ValueType*>(
|
||||
__res_->allocate(__n * sizeof(_ValueType), alignof(_ValueType))
|
||||
);
|
||||
}
|
||||
|
||||
void deallocate(_ValueType * __p, size_t __n) noexcept {
|
||||
__res_->deallocate(__p, __n * sizeof(_ValueType), alignof(_ValueType));
|
||||
}
|
||||
|
||||
template <class _Tp, class ..._Ts>
|
||||
void construct(_Tp* __p, _Ts &&... __args) {
|
||||
__lfts_user_alloc_construct(
|
||||
__p, *this, std::forward<_Ts>(__args)...
|
||||
);
|
||||
}
|
||||
|
||||
template <class _T1, class _T2, class ..._Args1, class ..._Args2>
|
||||
void construct(
|
||||
std::pair<_T1, _T2>* __p, std::piecewise_construct_t,
|
||||
std::tuple<_Args1...> __x, std::tuple<_Args2...> __y
|
||||
) {
|
||||
::new ((void*)__p) std::pair<_T1, _T2>(
|
||||
std::piecewise_construct,
|
||||
__transform_tuple(
|
||||
typename __lfts_uses_alloc_ctor<
|
||||
_T1, polymorphic_allocator&, _Args1...
|
||||
>::type(),
|
||||
std::move(__x),
|
||||
typename __make_tuple_indices<sizeof...(_Args1)>::type{}
|
||||
),
|
||||
__transform_tuple(
|
||||
typename __lfts_uses_alloc_ctor<
|
||||
_T2, polymorphic_allocator&, _Args2...
|
||||
>::type(),
|
||||
std::move(__y),
|
||||
typename __make_tuple_indices<sizeof...(_Args2)>::type{}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
template <class _T1, class _T2>
|
||||
void construct(std::pair<_T1, _T2>* __p) {
|
||||
construct(__p, std::piecewise_construct, std::tuple<>(), std::tuple<>());
|
||||
}
|
||||
|
||||
template <class _T1, class _T2, class _Up, class _Vp>
|
||||
void construct(std::pair<_T1, _T2> * __p, _Up && __u, _Vp && __v) {
|
||||
construct(
|
||||
__p, std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<_Up>(__u)),
|
||||
std::forward_as_tuple(std::forward<_Vp>(__v))
|
||||
);
|
||||
}
|
||||
|
||||
template <class _T1, class _T2, class _U1, class _U2>
|
||||
void construct(std::pair<_T1, _T2> * __p, std::pair<_U1, _U2> const & __pr) {
|
||||
construct(
|
||||
__p, std::piecewise_construct,
|
||||
std::forward_as_tuple(__pr.first),
|
||||
std::forward_as_tuple(__pr.second)
|
||||
);
|
||||
}
|
||||
|
||||
template <class _T1, class _T2, class _U1, class _U2>
|
||||
void construct(std::pair<_T1, _T2> * __p, std::pair<_U1, _U2> && __pr) {
|
||||
construct(
|
||||
__p, std::piecewise_construct,
|
||||
std::forward_as_tuple(std::forward<_U1>(__pr.first)),
|
||||
std::forward_as_tuple(std::forward<_U2>(__pr.second))
|
||||
);
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
void destroy(_Tp * __p) noexcept {
|
||||
__p->~_Tp();
|
||||
}
|
||||
|
||||
polymorphic_allocator
|
||||
select_on_container_copy_construction() const noexcept {
|
||||
return polymorphic_allocator();
|
||||
}
|
||||
|
||||
memory_resource* resource() const noexcept {
|
||||
return __res_;
|
||||
}
|
||||
|
||||
private:
|
||||
template <class ..._Args, size_t ..._Idx>
|
||||
std::tuple<_Args&&...>
|
||||
__transform_tuple(
|
||||
std::integral_constant<int, 0>, std::tuple<_Args...>&& __t,
|
||||
__tuple_indices<_Idx...>
|
||||
) const {
|
||||
return std::forward_as_tuple(std::get<_Idx>(std::move(__t))...);
|
||||
}
|
||||
|
||||
template <class ..._Args, size_t ..._Idx>
|
||||
std::tuple<allocator_arg_t const&, polymorphic_allocator&, _Args&&...>
|
||||
__transform_tuple(
|
||||
std::integral_constant<int, 1>, std::tuple<_Args...> && __t,
|
||||
__tuple_indices<_Idx...>
|
||||
) {
|
||||
using _Tup = std::tuple<
|
||||
allocator_arg_t const&,
|
||||
polymorphic_allocator&, _Args&&...
|
||||
>;
|
||||
return _Tup(allocator_arg_t{}, *this, std::get<_Idx>(std::move(__t))...);
|
||||
}
|
||||
|
||||
template <class ..._Args, size_t ..._Idx>
|
||||
std::tuple<_Args&&..., polymorphic_allocator&>
|
||||
__transform_tuple(
|
||||
std::integral_constant<int, 2>, std::tuple<_Args...> && __t,
|
||||
__tuple_indices<_Idx...>
|
||||
) {
|
||||
using _Tup = std::tuple<_Args&&..., polymorphic_allocator&>;
|
||||
return _Tup(std::get<_Idx>(std::move(__t))..., *this);
|
||||
}
|
||||
|
||||
size_t __max_size() const noexcept {
|
||||
return std::numeric_limits<size_t>::max() / sizeof(value_type);
|
||||
}
|
||||
|
||||
memory_resource * __res_;
|
||||
};
|
||||
|
||||
template <class _Tp, class _Up>
|
||||
inline bool operator==(
|
||||
polymorphic_allocator<_Tp> const & __lhs,
|
||||
polymorphic_allocator<_Up> const & __rhs
|
||||
) noexcept {
|
||||
return *__lhs.resource() == *__rhs.resource();
|
||||
}
|
||||
|
||||
template <class _Tp, class _Up>
|
||||
inline bool operator!=(
|
||||
polymorphic_allocator<_Tp> const & __lhs,
|
||||
polymorphic_allocator<_Up> const & __rhs
|
||||
) noexcept {
|
||||
return !(__lhs == __rhs);
|
||||
}
|
||||
|
||||
template <class _CharAlloc>
|
||||
class __resource_adaptor_imp : public memory_resource {
|
||||
using _CTraits = std::allocator_traits<_CharAlloc>;
|
||||
static_assert(
|
||||
std::is_same<typename _CTraits::value_type, char>::value &&
|
||||
std::is_same<typename _CTraits::pointer, char*>::value &&
|
||||
std::is_same<typename _CTraits::void_pointer, void*>::value
|
||||
);
|
||||
|
||||
static const size_t _MaxAlign = alignof(max_align_t);
|
||||
|
||||
using _Alloc = typename _CTraits::template rebind_alloc<
|
||||
typename std::aligned_storage<_MaxAlign, _MaxAlign>::type
|
||||
>;
|
||||
|
||||
using _ValueType = typename _Alloc::value_type;
|
||||
|
||||
_Alloc __alloc_;
|
||||
|
||||
public:
|
||||
using allocator_type = _CharAlloc;
|
||||
|
||||
__resource_adaptor_imp() = default;
|
||||
__resource_adaptor_imp(__resource_adaptor_imp const &) = default;
|
||||
__resource_adaptor_imp(__resource_adaptor_imp &&) noexcept = default;
|
||||
|
||||
explicit __resource_adaptor_imp(allocator_type const & __a) :
|
||||
__alloc_(__a)
|
||||
{}
|
||||
|
||||
explicit __resource_adaptor_imp(allocator_type && __a) :
|
||||
__alloc_(std::move(__a))
|
||||
{}
|
||||
|
||||
__resource_adaptor_imp &
|
||||
operator=(__resource_adaptor_imp const &) = default;
|
||||
|
||||
allocator_type get_allocator() const {
|
||||
return __alloc_;
|
||||
}
|
||||
|
||||
private:
|
||||
void * do_allocate(size_t __bytes, size_t) override {
|
||||
if (__bytes > __max_size())
|
||||
throw std::bad_array_new_length();
|
||||
size_t __s = __aligned_allocation_size(__bytes, _MaxAlign) / _MaxAlign;
|
||||
return __alloc_.allocate(__s);
|
||||
}
|
||||
|
||||
void do_deallocate(void * __p, size_t __bytes, size_t) override {
|
||||
size_t __s = __aligned_allocation_size(__bytes, _MaxAlign) / _MaxAlign;
|
||||
__alloc_.deallocate((_ValueType*)__p, __s);
|
||||
}
|
||||
|
||||
bool do_is_equal(memory_resource const & __other) const noexcept override {
|
||||
auto __p = dynamic_cast<__resource_adaptor_imp const *>(&__other);
|
||||
return __p ? __alloc_ == __p->__alloc_ : false;
|
||||
}
|
||||
|
||||
size_t __max_size() const noexcept {
|
||||
return std::numeric_limits<size_t>::max() - _MaxAlign;
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Alloc>
|
||||
using resource_adaptor = __resource_adaptor_imp<
|
||||
typename std::allocator_traits<_Alloc>::template rebind_alloc<char>
|
||||
>;
|
||||
|
||||
class __new_delete_memory_resource_imp : public memory_resource {
|
||||
void* do_allocate(size_t size, size_t /*align*/) override {
|
||||
return new std::byte[size];
|
||||
}
|
||||
|
||||
void do_deallocate(void *p, size_t n, size_t align) override {
|
||||
delete [](std::byte*)(p);
|
||||
}
|
||||
|
||||
bool do_is_equal(memory_resource const & other) const noexcept override {
|
||||
return &other == this;
|
||||
}
|
||||
|
||||
public:
|
||||
~__new_delete_memory_resource_imp() override = default;
|
||||
};
|
||||
|
||||
class __null_memory_resource_imp : public memory_resource {
|
||||
public:
|
||||
~__null_memory_resource_imp() override = default;
|
||||
|
||||
protected:
|
||||
void* do_allocate(size_t, size_t) override {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
void do_deallocate(void*, size_t, size_t) override {}
|
||||
bool do_is_equal(memory_resource const & __other) const noexcept override {
|
||||
return &__other == this;
|
||||
}
|
||||
};
|
||||
|
||||
inline memory_resource* new_delete_resource() noexcept {
|
||||
static __new_delete_memory_resource_imp inst { };
|
||||
return &inst;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
// Commented out to prevent creation of polymorphic_allocator without
|
||||
// explicitly provided memory_resource
|
||||
|
||||
inline std::atomic<memory_resource*>& __default_memory_resource(
|
||||
bool set = false, memory_resource* = nullptr
|
||||
) {
|
||||
static std::atomic<memory_resource*> def {
|
||||
new_delete_resource()
|
||||
};
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
inline memory_resource * get_default_resource() noexcept {
|
||||
return __default_memory_resource();
|
||||
}
|
||||
|
||||
inline memory_resource * set_default_resource(memory_resource * __new_res) noexcept {
|
||||
return __default_memory_resource(true, __new_res);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
} // end namespace pma
|
||||
|
||||
#endif // !ASYNC_MQTT5_MEMORY_RESOURCE_H
|
@ -1,22 +0,0 @@
|
||||
#ifndef ASYNC_MQTT5_STRING_H
|
||||
#define ASYNC_MQTT5_STRING_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
namespace pma {
|
||||
|
||||
template <class _CharT, class _Traits = std::char_traits<_CharT>>
|
||||
using basic_string =
|
||||
std::basic_string<_CharT, _Traits, alloc<_CharT>>;
|
||||
|
||||
|
||||
using string = basic_string<char>;
|
||||
using u16string = basic_string<char16_t>;
|
||||
using u32string = basic_string<char32_t>;
|
||||
using wstring = basic_string<wchar_t>;
|
||||
|
||||
} // namespace pma
|
||||
|
||||
#endif // !ASYNC_MQTT5_STRING_H
|
@ -1,15 +0,0 @@
|
||||
#ifndef ASYNC_MQTT5_VECTOR_H
|
||||
#define ASYNC_MQTT5_VECTOR_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
namespace pma {
|
||||
|
||||
template <class _ValueT>
|
||||
using vector = std::vector<_ValueT, alloc<_ValueT>>;
|
||||
|
||||
} // namespace pma
|
||||
|
||||
#endif // !ASYNC_MQTT5_VECTOR_H
|
@ -1,33 +0,0 @@
|
||||
#ifndef ASYNC_MQTT5_TRAITS_HPP
|
||||
#define ASYNC_MQTT5_TRAITS_HPP
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/range/iterator_range_core.hpp>
|
||||
|
||||
namespace async_mqtt5 {
|
||||
|
||||
template <typename> constexpr bool is_optional_impl = false;
|
||||
template <typename T> constexpr bool is_optional_impl<std::optional<T>> = true;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_optional = is_optional_impl<std::remove_cvref_t<T>>;
|
||||
|
||||
template <class, template<class...> class>
|
||||
constexpr bool is_specialization = false;
|
||||
|
||||
template <template<class...> class T, class... Args>
|
||||
constexpr bool is_specialization<T<Args...>, T> = true;
|
||||
|
||||
template <typename T>
|
||||
concept is_vector = is_specialization<std::remove_cvref_t<T>, std::vector>;
|
||||
|
||||
template <typename T>
|
||||
concept is_boost_iterator = is_specialization<
|
||||
std::remove_cvref_t<T>, boost::iterator_range
|
||||
>;
|
||||
|
||||
} // end namespace async_mqtt5
|
||||
|
||||
#endif // !ASYNC_MQTT5_TRAITS_HPP
|
@ -12,7 +12,7 @@
|
||||
#include <async_mqtt5/detail/control_packet.hpp>
|
||||
#include <async_mqtt5/detail/internal_types.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
|
||||
|
||||
namespace async_mqtt5::detail {
|
||||
|
||||
@ -30,9 +30,7 @@ class ping_op {
|
||||
std::unique_ptr<asio::steady_timer> _ping_timer;
|
||||
|
||||
public:
|
||||
ping_op(
|
||||
const std::shared_ptr<client_service>& svc_ptr
|
||||
) :
|
||||
ping_op(const std::shared_ptr<client_service>& svc_ptr) :
|
||||
_svc_ptr(svc_ptr),
|
||||
_ping_timer(new asio::steady_timer(svc_ptr->get_executor()))
|
||||
{}
|
||||
|
@ -15,8 +15,8 @@
|
||||
#include <async_mqtt5/detail/internal_types.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/disconnect_op.hpp>
|
||||
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
|
||||
|
||||
namespace async_mqtt5::detail {
|
||||
|
||||
|
@ -13,8 +13,8 @@
|
||||
#include <async_mqtt5/detail/utf8_mqtt.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/disconnect_op.hpp>
|
||||
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
|
||||
|
||||
namespace async_mqtt5::detail {
|
||||
|
||||
@ -75,7 +75,8 @@ class publish_send_op {
|
||||
|
||||
public:
|
||||
publish_send_op(
|
||||
const std::shared_ptr<client_service>& svc_ptr, Handler&& handler
|
||||
const std::shared_ptr<client_service>& svc_ptr,
|
||||
Handler&& handler
|
||||
) :
|
||||
_svc_ptr(svc_ptr),
|
||||
_handler(std::move(handler), get_executor())
|
||||
@ -178,12 +179,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
qos_e q = qos_type,
|
||||
std::enable_if_t<q == qos_e::at_least_once, bool> = true
|
||||
>
|
||||
void operator()(
|
||||
on_puback, control_packet<allocator_type> publish,
|
||||
error_code ec, byte_citer first, byte_citer last
|
||||
)
|
||||
requires (qos_type == qos_e::at_least_once) {
|
||||
|
||||
) {
|
||||
if (ec == asio::error::try_again) // "resend unanswered"
|
||||
return send_publish(std::move(publish.set_dup()));
|
||||
|
||||
@ -210,12 +213,14 @@ public:
|
||||
complete(ec, *rc, packet_id, std::move(props));
|
||||
}
|
||||
|
||||
template <
|
||||
qos_e q = qos_type,
|
||||
std::enable_if_t<q == qos_e::exactly_once, bool> = true
|
||||
>
|
||||
void operator()(
|
||||
on_pubrec, control_packet<allocator_type> publish,
|
||||
error_code ec, byte_citer first, byte_citer last
|
||||
)
|
||||
requires (qos_type == qos_e::exactly_once) {
|
||||
|
||||
) {
|
||||
if (ec == asio::error::try_again) // "resend unanswered"
|
||||
return send_publish(std::move(publish.set_dup()));
|
||||
|
||||
@ -262,11 +267,13 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
template <
|
||||
qos_e q = qos_type,
|
||||
std::enable_if_t<q == qos_e::exactly_once, bool> = true
|
||||
>
|
||||
void operator()(
|
||||
on_pubrel, control_packet<allocator_type> pubrel, error_code ec
|
||||
)
|
||||
requires (qos_type == qos_e::exactly_once) {
|
||||
|
||||
) {
|
||||
if (ec == asio::error::try_again)
|
||||
return send_pubrel(std::move(pubrel), true);
|
||||
|
||||
@ -283,13 +290,15 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
template <
|
||||
qos_e q = qos_type,
|
||||
std::enable_if_t<q == qos_e::exactly_once, bool> = true
|
||||
>
|
||||
void operator()(
|
||||
on_pubcomp, control_packet<allocator_type> pubrel,
|
||||
error_code ec,
|
||||
byte_citer first, byte_citer last
|
||||
)
|
||||
requires (qos_type == qos_e::exactly_once) {
|
||||
|
||||
) {
|
||||
if (ec == asio::error::try_again) // "resend unanswered"
|
||||
return send_pubrel(std::move(pubrel), true);
|
||||
|
||||
@ -329,16 +338,21 @@ private:
|
||||
if (max_qos && uint8_t(qos_type) > *max_qos)
|
||||
return client::error::qos_not_supported;
|
||||
|
||||
auto retain_available = _svc_ptr->connack_prop(prop::retain_available);
|
||||
if (retain_available && *retain_available == 0 && retain == retain_e::yes)
|
||||
auto retain_avail = _svc_ptr->connack_prop(prop::retain_available);
|
||||
if (retain_avail && *retain_avail == 0 && retain == retain_e::yes)
|
||||
return client::error::retain_not_available;
|
||||
|
||||
auto topic_alias_max = _svc_ptr->connack_prop(prop::topic_alias_maximum);
|
||||
auto topic_alias = props[prop::topic_alias];
|
||||
if ((!topic_alias_max || topic_alias_max && *topic_alias_max == 0) && topic_alias)
|
||||
if (
|
||||
(!topic_alias_max || topic_alias_max && *topic_alias_max == 0) &&
|
||||
topic_alias
|
||||
)
|
||||
return client::error::topic_alias_maximum_reached;
|
||||
|
||||
if (topic_alias_max && topic_alias && *topic_alias > *topic_alias_max)
|
||||
return client::error::topic_alias_maximum_reached;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -351,23 +365,30 @@ private:
|
||||
);
|
||||
}
|
||||
|
||||
void complete(error_code ec)
|
||||
requires (qos_type == qos_e::at_most_once)
|
||||
{
|
||||
template <
|
||||
qos_e q = qos_type,
|
||||
std::enable_if_t<q == qos_e::at_most_once, bool> = true
|
||||
>
|
||||
void complete(error_code ec) {
|
||||
_handler.complete(ec);
|
||||
}
|
||||
|
||||
void complete_post(error_code ec)
|
||||
requires (qos_type == qos_e::at_most_once)
|
||||
{
|
||||
template <
|
||||
qos_e q = qos_type,
|
||||
std::enable_if_t<q == qos_e::at_most_once, bool> = true
|
||||
>
|
||||
void complete_post(error_code ec) {
|
||||
_handler.complete_post(ec);
|
||||
}
|
||||
|
||||
template <typename Props = on_publish_props_type<qos_type>>
|
||||
requires (
|
||||
std::is_same_v<Props, puback_props> ||
|
||||
std::is_same_v<Props, pubcomp_props>
|
||||
)
|
||||
template <
|
||||
typename Props = on_publish_props_type<qos_type>,
|
||||
std::enable_if_t<
|
||||
std::is_same_v<Props, puback_props> ||
|
||||
std::is_same_v<Props, pubcomp_props>,
|
||||
bool
|
||||
> = true
|
||||
>
|
||||
void complete(
|
||||
error_code ec, reason_code rc,
|
||||
uint16_t packet_id, Props&& props
|
||||
@ -376,11 +397,14 @@ private:
|
||||
_handler.complete(ec, rc, std::forward<Props>(props));
|
||||
}
|
||||
|
||||
template <typename Props = on_publish_props_type<qos_type>>
|
||||
requires (
|
||||
std::is_same_v<Props, puback_props> ||
|
||||
std::is_same_v<Props, pubcomp_props>
|
||||
)
|
||||
template <
|
||||
typename Props = on_publish_props_type<qos_type>,
|
||||
std::enable_if_t<
|
||||
std::is_same_v<Props, puback_props> ||
|
||||
std::is_same_v<Props, pubcomp_props>,
|
||||
bool
|
||||
> = true
|
||||
>
|
||||
void complete_post(error_code ec) {
|
||||
_handler.complete_post(ec, reason_codes::empty, Props {});
|
||||
}
|
||||
|
@ -8,8 +8,8 @@
|
||||
#include <async_mqtt5/detail/control_packet.hpp>
|
||||
#include <async_mqtt5/detail/any_authenticator.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/disconnect_op.hpp>
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include <async_mqtt5/impl/publish_rec_op.hpp>
|
||||
#include <async_mqtt5/impl/re_auth_op.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
|
||||
|
||||
namespace async_mqtt5::detail {
|
||||
|
||||
@ -84,6 +84,7 @@ private:
|
||||
byte_citer first, byte_citer last
|
||||
) {
|
||||
auto code = control_code_e(control_byte & 0b11110000);
|
||||
|
||||
switch (code) {
|
||||
case control_code_e::publish: {
|
||||
auto msg = decoders::decode_publish(
|
||||
@ -123,6 +124,7 @@ private:
|
||||
auto props = disconnect_props {};
|
||||
props[prop::reason_string] = reason;
|
||||
auto svc_ptr = _svc_ptr; // copy before this is moved
|
||||
|
||||
async_disconnect(
|
||||
disconnect_rc_e::malformed_packet, props, false, svc_ptr,
|
||||
asio::prepend(std::move(*this), on_disconnect {})
|
||||
|
@ -109,7 +109,7 @@ private:
|
||||
static bool should_reconnect(error_code ec) {
|
||||
using namespace asio::error;
|
||||
return ec == connection_aborted || ec == not_connected ||
|
||||
ec == timed_out || ec == connection_reset ||
|
||||
ec == timed_out || ec == connection_reset ||
|
||||
ec == broken_pipe || ec == asio::error::eof;
|
||||
}
|
||||
};
|
||||
|
@ -149,7 +149,8 @@ public:
|
||||
|
||||
void operator()(
|
||||
on_connect, typename Owner::stream_ptr sptr,
|
||||
auto ord, error_code connect_ec, error_code timer_ec
|
||||
std::array<std::size_t, 2> ord,
|
||||
error_code connect_ec, error_code timer_ec
|
||||
) {
|
||||
// connect_ec may be any of stream.async_connect() error codes
|
||||
// plus access_denied, connection_refused and
|
||||
|
@ -13,8 +13,8 @@
|
||||
#include <async_mqtt5/detail/internal_types.hpp>
|
||||
#include <async_mqtt5/detail/utf8_mqtt.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/disconnect_op.hpp>
|
||||
|
||||
@ -38,7 +38,8 @@ class subscribe_op {
|
||||
|
||||
public:
|
||||
subscribe_op(
|
||||
const std::shared_ptr<client_service>& svc_ptr, Handler&& handler
|
||||
const std::shared_ptr<client_service>& svc_ptr,
|
||||
Handler&& handler
|
||||
) :
|
||||
_svc_ptr(svc_ptr),
|
||||
_handler(std::move(handler), get_executor())
|
||||
@ -138,14 +139,18 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
static error_code validate_topics(const std::vector<subscribe_topic>& topics) {
|
||||
static error_code validate_topics(
|
||||
const std::vector<subscribe_topic>& topics
|
||||
) {
|
||||
for (const auto& topic: topics)
|
||||
if (!is_valid_utf8_topic(topic.topic_filter))
|
||||
return client::error::invalid_topic;
|
||||
return error_code {};
|
||||
}
|
||||
|
||||
static std::vector<reason_code> to_reason_codes(std::vector<uint8_t> codes) {
|
||||
static std::vector<reason_code> to_reason_codes(
|
||||
std::vector<uint8_t> codes
|
||||
) {
|
||||
std::vector<reason_code> ret;
|
||||
for (uint8_t code : codes) {
|
||||
auto rc = to_reason_code<reason_codes::category::suback>(code);
|
||||
@ -167,7 +172,8 @@ private:
|
||||
|
||||
void complete_post(error_code ec, size_t num_topics) {
|
||||
_handler.complete_post(
|
||||
ec, std::vector<reason_code> { num_topics, reason_codes::empty }, suback_props {}
|
||||
ec, std::vector<reason_code> { num_topics, reason_codes::empty },
|
||||
suback_props {}
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,8 @@
|
||||
#include <async_mqtt5/detail/utf8_mqtt.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/disconnect_op.hpp>
|
||||
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
|
||||
|
||||
namespace async_mqtt5::detail {
|
||||
|
||||
@ -35,7 +35,8 @@ class unsubscribe_op {
|
||||
|
||||
public:
|
||||
unsubscribe_op(
|
||||
const std::shared_ptr<client_service>& svc_ptr, Handler&& handler
|
||||
const std::shared_ptr<client_service>& svc_ptr,
|
||||
Handler&& handler
|
||||
) :
|
||||
_svc_ptr(svc_ptr),
|
||||
_handler(std::move(handler), get_executor())
|
||||
@ -128,7 +129,6 @@ public:
|
||||
}
|
||||
|
||||
auto& [props, reason_codes] = *unsuback;
|
||||
// TODO: perhaps do something with the topics we unsubscribed from (one day)
|
||||
|
||||
complete(
|
||||
ec, packet_id,
|
||||
@ -151,7 +151,6 @@ private:
|
||||
auto rc = to_reason_code<reason_codes::category::unsuback>(code);
|
||||
if (rc)
|
||||
ret.push_back(*rc);
|
||||
// TODO: on off chance that one of the rcs is invalid, should we push something to mark that?
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -169,7 +168,8 @@ private:
|
||||
|
||||
void complete_post(error_code ec, size_t num_topics) {
|
||||
_handler.complete_post(
|
||||
ec, std::vector<reason_code> { num_topics, reason_codes::empty }, unsuback_props {}
|
||||
ec, std::vector<reason_code> { num_topics, reason_codes::empty },
|
||||
unsuback_props {}
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -76,8 +76,13 @@ public:
|
||||
* std::is_convertible_v<ExecutionContext&, asio::execution_context&>
|
||||
* \endcode
|
||||
*/
|
||||
template <typename ExecutionContext>
|
||||
requires (std::is_convertible_v<ExecutionContext&, asio::execution_context&>)
|
||||
template <
|
||||
typename ExecutionContext,
|
||||
std::enable_if_t<
|
||||
std::is_convertible_v<ExecutionContext&, asio::execution_context&>,
|
||||
bool
|
||||
> = true
|
||||
>
|
||||
explicit mqtt_client(
|
||||
ExecutionContext& context,
|
||||
const std::string& cnf,
|
||||
@ -134,8 +139,12 @@ public:
|
||||
* !std::is_same_v<TlsContext, std::monostate>
|
||||
* \endcode
|
||||
*/
|
||||
template <
|
||||
typename Ctx = TlsContext,
|
||||
std::enable_if_t<!std::is_same_v<Ctx, std::monostate>, bool> = true
|
||||
>
|
||||
decltype(auto) tls_context()
|
||||
requires (!std::is_same_v<TlsContext, std::monostate>) {
|
||||
{
|
||||
return _svc_ptr->tls_context();
|
||||
}
|
||||
|
||||
@ -182,8 +191,10 @@ public:
|
||||
*
|
||||
* \see See \__CONNACK_PROPS\__ for all eligible properties.
|
||||
*/
|
||||
template <uint8_t p>
|
||||
decltype(auto) connection_property(std::integral_constant<uint8_t, p> prop) {
|
||||
template <prop::property_type p>
|
||||
decltype(auto) connection_property(
|
||||
std::integral_constant<prop::property_type, p> prop
|
||||
) {
|
||||
return _svc_ptr->connack_prop(prop);
|
||||
}
|
||||
|
||||
@ -272,7 +283,10 @@ public:
|
||||
* before the \ref run function is invoked again.
|
||||
*
|
||||
*/
|
||||
template <detail::is_authenticator Authenticator>
|
||||
template <
|
||||
typename Authenticator,
|
||||
std::enable_if_t<detail::is_authenticator<Authenticator>, bool> = true
|
||||
>
|
||||
mqtt_client& authenticator(Authenticator&& authenticator) {
|
||||
_svc_ptr->authenticator(std::forward<Authenticator>(authenticator));
|
||||
return *this;
|
||||
|
@ -123,7 +123,7 @@ public:
|
||||
|
||||
template <
|
||||
typename Func,
|
||||
typename = std::enable_if_t<is_apply_on<Func>::value>
|
||||
std::enable_if_t<is_apply_on<Func>::value, bool> = true
|
||||
>
|
||||
constexpr bool apply_on(uint8_t property_id, Func&& func)
|
||||
noexcept (is_nothrow_apply_on<Func>::value) {
|
||||
@ -154,7 +154,7 @@ public:
|
||||
|
||||
template <
|
||||
typename Func,
|
||||
typename = std::enable_if_t<is_visitor<Func>::value>
|
||||
std::enable_if_t<is_visitor<Func>::value, bool> = true
|
||||
>
|
||||
constexpr bool visit(Func&& func)
|
||||
const noexcept (is_nothrow_visitor<Func>::value) {
|
||||
@ -173,7 +173,7 @@ public:
|
||||
|
||||
template <
|
||||
typename Func,
|
||||
typename = std::enable_if_t<is_visitor<Func>::value>
|
||||
std::enable_if_t<is_visitor<Func>::value, bool> = true
|
||||
>
|
||||
constexpr bool visit(Func&& func)
|
||||
noexcept (is_nothrow_visitor<Func>::value) {
|
||||
|
@ -390,12 +390,12 @@ public:
|
||||
will& operator=(will&&) noexcept = default;
|
||||
|
||||
/// Get the Topic Name.
|
||||
constexpr std::string_view topic() const {
|
||||
std::string_view topic() const {
|
||||
return _topic;
|
||||
}
|
||||
|
||||
/// Get the Application Message.
|
||||
constexpr std::string_view message() const {
|
||||
std::string_view message() const {
|
||||
return _message;
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,8 @@
|
||||
|
||||
#include <async_mqtt5/detail/control_packet.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
|
||||
|
||||
namespace async_mqtt5::test {
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
|
||||
|
||||
#include "test_common/message_exchange.hpp"
|
||||
#include "test_common/packet_util.hpp"
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
|
||||
|
||||
#include <async_mqtt5.hpp>
|
||||
|
||||
|
105
test/unit/test/compilation_checks.cpp
Normal file
105
test/unit/test/compilation_checks.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/ssl/stream.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include <boost/beast/websocket/stream.hpp>
|
||||
|
||||
#include <async_mqtt5/detail/any_authenticator.hpp>
|
||||
#include <async_mqtt5/detail/async_traits.hpp>
|
||||
|
||||
#include <async_mqtt5/mqtt_client.hpp>
|
||||
|
||||
using namespace async_mqtt5;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(traits/*, *boost::unit_test::disabled()*/)
|
||||
|
||||
class good_authenticator {
|
||||
public:
|
||||
good_authenticator() = default;
|
||||
|
||||
template <typename CompletionToken>
|
||||
decltype(auto) async_auth(
|
||||
auth_step_e step, std::string data,
|
||||
CompletionToken&& token
|
||||
) {
|
||||
using error_code = boost::system::error_code;
|
||||
using Signature = void(error_code, std::string);
|
||||
|
||||
auto initiate = [](auto, auth_step_e, std::string) {};
|
||||
|
||||
return asio::async_initiate<CompletionToken, Signature>(
|
||||
initiate, token, step, std::move(data)
|
||||
);
|
||||
}
|
||||
|
||||
std::string_view method() const {
|
||||
return "method";
|
||||
}
|
||||
};
|
||||
|
||||
class bad_authenticator {
|
||||
public:
|
||||
bad_authenticator() = default;
|
||||
|
||||
void async_auth(std::string data) {}
|
||||
|
||||
std::string_view method() const {
|
||||
return "method";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(is_authenticator) {
|
||||
BOOST_STATIC_ASSERT(detail::is_authenticator<good_authenticator>);
|
||||
BOOST_STATIC_ASSERT(!detail::is_authenticator<bad_authenticator>);
|
||||
}
|
||||
|
||||
namespace asio = boost::asio;
|
||||
namespace beast = boost::beast;
|
||||
|
||||
using tcp_layer = asio::ip::tcp::socket;
|
||||
using tls_layer = asio::ssl::stream<asio::ip::tcp::socket>;
|
||||
using websocket_tcp_layer = beast::websocket::stream<tcp_layer>;
|
||||
using websocket_tls_layer = beast::websocket::stream<tls_layer>;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(async_traits) {
|
||||
BOOST_STATIC_ASSERT(!detail::has_next_layer<tcp_layer>);
|
||||
BOOST_STATIC_ASSERT(detail::has_next_layer<tls_layer>);
|
||||
BOOST_STATIC_ASSERT(detail::has_next_layer<websocket_tcp_layer>);
|
||||
BOOST_STATIC_ASSERT(detail::has_next_layer<websocket_tls_layer>);
|
||||
|
||||
BOOST_STATIC_ASSERT(!detail::has_tls_layer<tcp_layer>);
|
||||
BOOST_STATIC_ASSERT(detail::has_tls_layer<tls_layer>);
|
||||
BOOST_STATIC_ASSERT(!detail::has_tls_layer<websocket_tcp_layer>);
|
||||
BOOST_STATIC_ASSERT(detail::has_tls_layer<websocket_tls_layer>);
|
||||
|
||||
BOOST_STATIC_ASSERT(!detail::has_ws_handshake<tcp_layer>);
|
||||
BOOST_STATIC_ASSERT(!detail::has_ws_handshake<tls_layer>);
|
||||
BOOST_STATIC_ASSERT(detail::has_ws_handshake<websocket_tcp_layer>);
|
||||
BOOST_STATIC_ASSERT(detail::has_ws_handshake<websocket_tls_layer>);
|
||||
|
||||
BOOST_STATIC_ASSERT(!detail::has_tls_handshake<tcp_layer>);
|
||||
BOOST_STATIC_ASSERT(detail::has_tls_handshake<tls_layer>);
|
||||
BOOST_STATIC_ASSERT(!detail::has_tls_handshake<websocket_tcp_layer>);
|
||||
BOOST_STATIC_ASSERT(!detail::has_tls_handshake<websocket_tls_layer>);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(client_functions) {
|
||||
asio::io_context ioc;
|
||||
|
||||
mqtt_client<tcp_layer> tcp_client(ioc, "");
|
||||
tcp_client.authenticator(good_authenticator());
|
||||
auto data = tcp_client.connection_property(prop::authentication_data);
|
||||
|
||||
asio::ssl::context ctx(asio::ssl::context::tls_client);
|
||||
mqtt_client<
|
||||
tls_layer, asio::ssl::context
|
||||
> tls_client(ioc.get_executor(), "", std::move(ctx));
|
||||
tls_client.tls_context();
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
@ -2,8 +2,8 @@
|
||||
|
||||
#include <async_mqtt5/types.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/internal/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/internal/codecs/message_encoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_decoders.hpp>
|
||||
#include <async_mqtt5/impl/codecs/message_encoders.hpp>
|
||||
|
||||
using namespace async_mqtt5;
|
||||
using byte_citer = detail::byte_citer;
|
||||
|
Reference in New Issue
Block a user