mirror of
https://github.com/boostorg/mqtt5.git
synced 2025-09-25 08:20:56 +02:00
Drop Spirit dependency
Summary: Ref #36 Reviewers: ivica Reviewed By: ivica Subscribers: miljen Differential Revision: https://repo.mireo.local/D37107
This commit is contained in:
@@ -49,12 +49,9 @@ else()
|
||||
Boost::container
|
||||
Boost::core
|
||||
Boost::endian
|
||||
Boost::fusion
|
||||
Boost::optional
|
||||
Boost::random
|
||||
Boost::range
|
||||
Boost::smart_ptr
|
||||
Boost::spirit
|
||||
Boost::system
|
||||
Boost::type_traits
|
||||
Threads::Threads
|
||||
|
@@ -13,12 +13,9 @@ constant boost_dependencies :
|
||||
/boost/container//boost_container
|
||||
/boost/core//boost_core
|
||||
/boost/endian//boost_endian
|
||||
/boost/fusion//boost_fusion
|
||||
/boost/optional//boost_optional
|
||||
/boost/random//boost_random
|
||||
/boost/range//boost_range
|
||||
/boost/smart_ptr//boost_smart_ptr
|
||||
/boost/spirit//boost_spirit
|
||||
/boost/system//boost_system
|
||||
/boost/type_traits//boost_type_traits
|
||||
;
|
||||
|
@@ -12,11 +12,7 @@
|
||||
|
||||
#include <boost/mqtt5/detail/traits.hpp>
|
||||
|
||||
#include <boost/fusion/adapted/std_tuple.hpp>
|
||||
#include <boost/fusion/container/deque.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
#include <boost/spirit/home/x3/binary/binary.hpp>
|
||||
#include <boost/endian/conversion.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -24,217 +20,49 @@
|
||||
|
||||
namespace boost::mqtt5::decoders {
|
||||
|
||||
namespace x3 = boost::spirit::x3;
|
||||
|
||||
template <typename T>
|
||||
struct convert { using type = T; };
|
||||
|
||||
template <typename ...Args>
|
||||
struct convert<boost::fusion::deque<Args...>> {
|
||||
using type = std::tuple<typename convert<Args>::type...>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct convert<boost::optional<T>> {
|
||||
using type = std::optional<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct convert<std::vector<T>> {
|
||||
using type = std::vector<typename convert<T>::type>;
|
||||
};
|
||||
|
||||
template <typename T, typename Parser>
|
||||
constexpr auto as(Parser&& p) {
|
||||
return x3::rule<struct _, T>{} = std::forward<Parser>(p);
|
||||
}
|
||||
|
||||
|
||||
template <typename It, typename Parser>
|
||||
auto type_parse(It& first, const It last, const Parser& p) {
|
||||
using ctx_type = decltype(x3::make_context<struct _>(std::declval<Parser&>()));
|
||||
using attr_type = typename x3::traits::attribute_of<Parser, ctx_type>::type;
|
||||
|
||||
using rv_type = typename convert<attr_type>::type;
|
||||
using rv_type = typename Parser::attribute_type;
|
||||
|
||||
std::optional<rv_type> rv;
|
||||
rv_type value {};
|
||||
if (x3::phrase_parse(first, last, as<rv_type>(p), x3::eps(false), value))
|
||||
rv = std::move(value);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
template <typename AttributeType, typename It, typename Parser>
|
||||
auto type_parse(It& first, const It last, const Parser& p) {
|
||||
std::optional<AttributeType> rv;
|
||||
AttributeType value {};
|
||||
if (x3::phrase_parse(first, last, as<AttributeType>(p), x3::eps(false), value))
|
||||
if (p.parse(first, last, value))
|
||||
rv = std::move(value);
|
||||
return rv;
|
||||
}
|
||||
|
||||
namespace basic {
|
||||
|
||||
template <typename T>
|
||||
constexpr auto to(T& arg) {
|
||||
return [&](auto& ctx) {
|
||||
using ctx_type = decltype(ctx);
|
||||
using attr_type = decltype(x3::_attr(std::declval<const ctx_type&>()));
|
||||
if constexpr (detail::is_boost_iterator<attr_type>)
|
||||
arg = T { x3::_attr(ctx).begin(), x3::_attr(ctx).end() };
|
||||
else
|
||||
arg = x3::_attr(ctx);
|
||||
};
|
||||
}
|
||||
template <typename Attr>
|
||||
struct int_parser {
|
||||
using attribute_type = Attr;
|
||||
|
||||
template <typename LenParser, typename Subject, typename Enable = void>
|
||||
class scope_limit {};
|
||||
|
||||
template <typename LenParser, typename 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;
|
||||
|
||||
static bool const has_attribute = true;
|
||||
|
||||
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 {
|
||||
|
||||
It iter = first;
|
||||
typename x3::traits::attribute_of<LenParser, Ctx>::type len;
|
||||
if (!_lp.parse(iter, last, ctx, rctx, len))
|
||||
return false;
|
||||
if (iter + len > last)
|
||||
template <typename It>
|
||||
bool parse(It& first, const It last, Attr& attr) const {
|
||||
constexpr size_t byte_size = sizeof(Attr);
|
||||
if (std::distance(first, last) < static_cast<ptrdiff_t>(byte_size))
|
||||
return false;
|
||||
|
||||
if (!base_type::subject.parse(iter, iter + len, ctx, rctx, attr))
|
||||
return false;
|
||||
using namespace boost::endian;
|
||||
attr = endian_load<Attr, sizeof(Attr), order::big>(
|
||||
reinterpret_cast<const uint8_t*>(static_cast<const char*>(&*first))
|
||||
);
|
||||
first += sizeof(Attr);
|
||||
|
||||
first = iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Size, typename 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;
|
||||
inline constexpr int_parser<uint8_t> byte_;
|
||||
inline constexpr int_parser<uint16_t> word_;
|
||||
inline constexpr int_parser<uint32_t> dword_;
|
||||
|
||||
static bool const has_attribute = true;
|
||||
|
||||
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 {
|
||||
|
||||
It iter = first;
|
||||
if (iter + _limit > last)
|
||||
return false;
|
||||
|
||||
if (!base_type::subject.parse(iter, iter + _limit, ctx, rctx, attr))
|
||||
return false;
|
||||
|
||||
first = iter;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename LenParser, typename Enable = void>
|
||||
struct scope_limit_gen {
|
||||
template <typename Subject>
|
||||
auto operator[](const Subject& p) const {
|
||||
return scope_limit<LenParser, Subject> { _lp, x3::as_parser(p) };
|
||||
}
|
||||
LenParser _lp;
|
||||
};
|
||||
|
||||
template <typename 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) };
|
||||
}
|
||||
Size limit;
|
||||
};
|
||||
|
||||
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,
|
||||
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;
|
||||
|
||||
template <typename It, typename Ctx, typename RCtx, typename Attr>
|
||||
bool parse(It& first, const It last, const Ctx&, RCtx&, Attr& attr) const {
|
||||
attr = std::string { first, last };
|
||||
first = last;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr auto verbatim_ = verbatim_parser{};
|
||||
|
||||
struct varint_parser : x3::parser<varint_parser> {
|
||||
struct varint_parser {
|
||||
using attribute_type = int32_t;
|
||||
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&, Attr& attr
|
||||
) const {
|
||||
|
||||
It iter = first;
|
||||
x3::skip_over(iter, last, ctx);
|
||||
template <typename It>
|
||||
bool parse(It& first, const It last, int32_t& attr) const {
|
||||
auto iter = first;
|
||||
|
||||
if (iter == last)
|
||||
return false;
|
||||
@@ -262,22 +90,17 @@ struct varint_parser : x3::parser<varint_parser> {
|
||||
}
|
||||
};
|
||||
|
||||
constexpr varint_parser varint_{};
|
||||
inline constexpr varint_parser varint_;
|
||||
|
||||
struct len_prefix_parser : x3::parser<len_prefix_parser> {
|
||||
struct len_prefix_parser {
|
||||
using attribute_type = std::string;
|
||||
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 {
|
||||
It iter = first;
|
||||
x3::skip_over(iter, last, ctx);
|
||||
template <typename It>
|
||||
bool parse(It& first, const It last, std::string& attr) const {
|
||||
auto iter = first;
|
||||
|
||||
typename x3::traits::attribute_of<decltype(x3::big_word), Ctx>::type len;
|
||||
if (x3::big_word.parse(iter, last, ctx, rctx, len)) {
|
||||
typename decltype(word_)::attribute_type len;
|
||||
if (word_.parse(iter, last, len)) {
|
||||
if (std::distance(iter, last) < len)
|
||||
return false;
|
||||
}
|
||||
@@ -290,44 +113,25 @@ struct len_prefix_parser : x3::parser<len_prefix_parser> {
|
||||
}
|
||||
};
|
||||
|
||||
constexpr len_prefix_parser utf8_ {};
|
||||
constexpr len_prefix_parser binary_ {};
|
||||
|
||||
/*
|
||||
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
|
||||
*/
|
||||
inline constexpr len_prefix_parser utf8_ {};
|
||||
inline constexpr len_prefix_parser binary_ {};
|
||||
|
||||
template <typename Subject>
|
||||
class conditional_parser :
|
||||
public x3::unary_parser<Subject, conditional_parser<Subject>> {
|
||||
struct conditional_parser {
|
||||
using subject_attr_type = typename Subject::attribute_type;
|
||||
using attribute_type = std::optional<subject_attr_type>;
|
||||
|
||||
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;
|
||||
Subject p;
|
||||
bool condition;
|
||||
|
||||
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) {}
|
||||
|
||||
template <typename It, typename Ctx, typename RCtx, typename Attr>
|
||||
bool parse(
|
||||
It& first, const It last,
|
||||
const Ctx& ctx, RCtx& rctx, Attr& attr
|
||||
) const {
|
||||
if (!_condition)
|
||||
template <typename It>
|
||||
bool parse(It& first, const It last, attribute_type& attr) const {
|
||||
if (!condition)
|
||||
return true;
|
||||
|
||||
It iter = first;
|
||||
auto iter = first;
|
||||
subject_attr_type sattr {};
|
||||
if (!base_type::subject.parse(iter, last, ctx, rctx, sattr))
|
||||
if (!p.parse(iter, last, sattr))
|
||||
return false;
|
||||
|
||||
attr.emplace(std::move(sattr));
|
||||
@@ -340,59 +144,155 @@ struct conditional_gen {
|
||||
bool _condition;
|
||||
|
||||
template <typename Subject>
|
||||
auto operator[](const Subject& p) const {
|
||||
return conditional_parser<Subject> { p, _condition };
|
||||
conditional_parser<Subject> operator[](const Subject& p) const {
|
||||
return { p, _condition };
|
||||
}
|
||||
};
|
||||
|
||||
inline conditional_gen if_(bool condition) {
|
||||
constexpr conditional_gen if_(bool condition) {
|
||||
return { condition };
|
||||
}
|
||||
|
||||
struct verbatim_parser {
|
||||
using attribute_type = std::string;
|
||||
|
||||
template <typename It>
|
||||
bool parse(It& first, const It last, std::string& attr) const {
|
||||
attr = std::string { first, last };
|
||||
first = last;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
inline constexpr auto verbatim_ = verbatim_parser{};
|
||||
|
||||
template <typename... Ps>
|
||||
struct seq_parser {
|
||||
using attribute_type = std::tuple<typename Ps::attribute_type...>;
|
||||
|
||||
std::tuple<Ps...> parsers;
|
||||
|
||||
template <typename It>
|
||||
bool parse(It& first, const It last, attribute_type& attr) const {
|
||||
return parse_impl(
|
||||
first, last, attr, std::make_index_sequence<sizeof...(Ps)>{}
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename It, size_t... Is>
|
||||
bool parse_impl(
|
||||
It& first, const It last, attribute_type& attr,
|
||||
std::index_sequence<Is...>
|
||||
) const {
|
||||
auto iter = first;
|
||||
bool rv = (
|
||||
std::get<Is>(parsers).parse(iter, last, std::get<Is>(attr))
|
||||
&& ...
|
||||
);
|
||||
if (rv)
|
||||
first = iter;
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Ps, typename P2>
|
||||
constexpr seq_parser<Ps..., P2> operator>>(
|
||||
const seq_parser<Ps...>& p1, const P2& p2
|
||||
) {
|
||||
return { std::tuple_cat(p1.parsers, std::make_tuple(p2)) };
|
||||
}
|
||||
|
||||
template <typename P1, typename P2>
|
||||
constexpr seq_parser<P1, P2> operator>>(const P1& p1, const P2& p2) {
|
||||
return { std::make_tuple(p1, p2) };
|
||||
}
|
||||
|
||||
template <typename Attr>
|
||||
struct attr_parser {
|
||||
using attribute_type = Attr;
|
||||
|
||||
Attr attr;
|
||||
|
||||
template <typename It>
|
||||
bool parse(It&, const It, Attr& attr) const {
|
||||
attr = this->attr;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Attr>
|
||||
constexpr attr_parser<Attr> attr(const Attr& val) {
|
||||
return { val };
|
||||
}
|
||||
|
||||
template <typename Subject>
|
||||
struct plus_parser {
|
||||
using subject_attr_type = typename Subject::attribute_type;
|
||||
using attribute_type = std::vector<subject_attr_type>;
|
||||
|
||||
Subject p;
|
||||
|
||||
template <typename It, typename Attr>
|
||||
bool parse(It& first, const It last, Attr& attr) const {
|
||||
bool success = false;
|
||||
while (first < last) {
|
||||
subject_attr_type sattr {};
|
||||
auto iter = first;
|
||||
if (!p.parse(iter, last, sattr)) break;
|
||||
|
||||
success = true;
|
||||
first = iter;
|
||||
attr.push_back(std::move(sattr));
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename P>
|
||||
constexpr plus_parser<P> operator+(const P& p) {
|
||||
return { p };
|
||||
}
|
||||
|
||||
} // end namespace basic
|
||||
|
||||
|
||||
namespace prop {
|
||||
|
||||
namespace basic = boost::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
|
||||
) {
|
||||
template <typename It, typename Prop>
|
||||
bool parse_to_prop(It& iter, const It last, Prop& prop) {
|
||||
using namespace boost::mqtt5::detail;
|
||||
using prop_type = std::remove_reference_t<decltype(prop)>;
|
||||
|
||||
bool rv = false;
|
||||
|
||||
if constexpr (std::is_same_v<prop_type, uint8_t>)
|
||||
rv = x3::byte_.parse(iter, last, ctx, rctx, prop);
|
||||
rv = basic::byte_.parse(iter, last, prop);
|
||||
else if constexpr (std::is_same_v<prop_type, uint16_t>)
|
||||
rv = x3::big_word.parse(iter, last, ctx, rctx, prop);
|
||||
rv = basic::word_.parse(iter, last, prop);
|
||||
else if constexpr (std::is_same_v<prop_type, int32_t>)
|
||||
rv = basic::varint_.parse(iter, last, ctx, rctx, prop);
|
||||
rv = basic::varint_.parse(iter, last, prop);
|
||||
else if constexpr (std::is_same_v<prop_type, uint32_t>)
|
||||
rv = x3::big_dword.parse(iter, last, ctx, rctx, prop);
|
||||
rv = basic::dword_.parse(iter, last, prop);
|
||||
else if constexpr (std::is_same_v<prop_type, std::string>)
|
||||
rv = basic::utf8_.parse(iter, last, ctx, rctx, prop);
|
||||
rv = basic::utf8_.parse(iter, last, prop);
|
||||
|
||||
else if constexpr (is_optional<prop_type>) {
|
||||
typename prop_type::value_type val;
|
||||
rv = parse_to_prop(iter, last, ctx, rctx, val);
|
||||
rv = parse_to_prop(iter, last, val);
|
||||
if (rv) prop.emplace(std::move(val));
|
||||
}
|
||||
|
||||
else if constexpr (is_pair<prop_type>) {
|
||||
rv = parse_to_prop(iter, last, ctx, rctx, prop.first);
|
||||
rv = parse_to_prop(iter, last, ctx, rctx, prop.second);
|
||||
rv = parse_to_prop(iter, last, prop.first);
|
||||
rv = parse_to_prop(iter, last, prop.second);
|
||||
}
|
||||
|
||||
else if constexpr (is_vector<prop_type> || is_small_vector<prop_type>) {
|
||||
typename std::remove_reference_t<prop_type>::value_type value;
|
||||
rv = parse_to_prop(iter, last, ctx, rctx, value);
|
||||
rv = parse_to_prop(iter, last, value);
|
||||
if (rv) prop.push_back(std::move(value));
|
||||
}
|
||||
|
||||
@@ -402,28 +302,21 @@ bool parse_to_prop(
|
||||
} // end namespace detail
|
||||
|
||||
template <typename Props>
|
||||
class prop_parser : public x3::parser<prop_parser<Props>> {
|
||||
public:
|
||||
struct prop_parser {
|
||||
using attribute_type = Props;
|
||||
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 {
|
||||
|
||||
It iter = first;
|
||||
x3::skip_over(iter, last, ctx);
|
||||
template <typename It>
|
||||
bool parse(It& first, const It last, Props& attr) const {
|
||||
auto iter = first;
|
||||
|
||||
if (iter == last)
|
||||
return true;
|
||||
|
||||
int32_t props_length;
|
||||
if (!basic::varint_.parse(iter, last, ctx, rctx, props_length))
|
||||
if (!basic::varint_.parse(iter, last, props_length))
|
||||
return false;
|
||||
|
||||
const It scoped_last = iter + props_length;
|
||||
const auto scoped_last = iter + props_length;
|
||||
// attr = Props{};
|
||||
|
||||
while (iter < scoped_last) {
|
||||
@@ -433,10 +326,8 @@ public:
|
||||
|
||||
attr.apply_on(
|
||||
prop_id,
|
||||
[&rv, &iter, scoped_last, &ctx, &rctx](auto& prop) {
|
||||
rv = detail::parse_to_prop(
|
||||
iter, scoped_last, ctx, rctx, prop
|
||||
);
|
||||
[&rv, &iter, scoped_last](auto& prop) {
|
||||
rv = detail::parse_to_prop(iter, scoped_last, prop);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -450,13 +341,11 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename Props>
|
||||
constexpr auto props_ = prop_parser<Props> {};
|
||||
|
||||
} // end namespace prop
|
||||
|
||||
|
||||
} // end namespace boost::mqtt5::decoders
|
||||
|
||||
#endif // !BOOST_MQTT5_BASE_DECODERS_HPP
|
||||
|
@@ -32,7 +32,7 @@ using fixed_header = std::tuple<
|
||||
inline std::optional<fixed_header> decode_fixed_header(
|
||||
byte_citer& it, const byte_citer last
|
||||
) {
|
||||
auto fixed_header_ = x3::byte_ >> basic::varint_;
|
||||
constexpr auto fixed_header_ = basic::byte_ >> basic::varint_;
|
||||
return type_parse(it, last, fixed_header_);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ using packet_id = uint16_t;
|
||||
inline std::optional<packet_id> decode_packet_id(
|
||||
byte_citer& it
|
||||
) {
|
||||
auto packet_id_ = x3::big_word;
|
||||
constexpr auto packet_id_ = basic::word_;
|
||||
return type_parse(it, it + sizeof(uint16_t), packet_id_);
|
||||
}
|
||||
|
||||
@@ -58,11 +58,11 @@ using connect_message = std::tuple<
|
||||
inline std::optional<connect_message> decode_connect(
|
||||
uint32_t remain_length, byte_citer& it
|
||||
) {
|
||||
auto var_header_ =
|
||||
constexpr auto var_header_ =
|
||||
basic::utf8_ >> // MQTT
|
||||
x3::byte_ >> // (num 5)
|
||||
x3::byte_ >> // conn_flags_
|
||||
x3::big_word >> // keep_alive
|
||||
basic::byte_ >> // (num 5)
|
||||
basic::byte_ >> // conn_flags_
|
||||
basic::word_ >> // keep_alive
|
||||
prop::props_<connect_props>;
|
||||
|
||||
const byte_citer end = it + remain_length;
|
||||
@@ -124,9 +124,7 @@ using connack_message = std::tuple<
|
||||
inline std::optional<connack_message> decode_connack(
|
||||
uint32_t remain_length, byte_citer& it
|
||||
) {
|
||||
auto connack_ = basic::scope_limit_(remain_length)[
|
||||
x3::byte_ >> x3::byte_ >> prop::props_<connack_props>
|
||||
];
|
||||
constexpr auto connack_ = basic::byte_ >> basic::byte_ >> prop::props_<connack_props>;
|
||||
return type_parse(it, it + remain_length, connack_);
|
||||
}
|
||||
|
||||
@@ -144,10 +142,10 @@ 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::utf8_ >> basic::if_(qos != qos_e::at_most_once)[basic::word_] >>
|
||||
basic::attr(flags) >> prop::props_<publish_props> >>
|
||||
basic::verbatim_;
|
||||
return type_parse(it, it + remain_length, publish_);
|
||||
}
|
||||
|
||||
@@ -161,9 +159,7 @@ inline std::optional<puback_message> decode_puback(
|
||||
) {
|
||||
if (remain_length == 0)
|
||||
return puback_message {};
|
||||
auto puback_ = basic::scope_limit_(remain_length)[
|
||||
x3::byte_ >> prop::props_<puback_props>
|
||||
];
|
||||
constexpr auto puback_ = basic::byte_ >> prop::props_<puback_props>;
|
||||
return type_parse(it, it + remain_length, puback_);
|
||||
}
|
||||
|
||||
@@ -177,9 +173,7 @@ inline std::optional<pubrec_message> decode_pubrec(
|
||||
) {
|
||||
if (remain_length == 0)
|
||||
return pubrec_message {};
|
||||
auto pubrec_ = basic::scope_limit_(remain_length)[
|
||||
x3::byte_ >> prop::props_<pubrec_props>
|
||||
];
|
||||
constexpr auto pubrec_ = basic::byte_ >> prop::props_<pubrec_props>;
|
||||
return type_parse(it, it + remain_length, pubrec_);
|
||||
}
|
||||
|
||||
@@ -193,9 +187,7 @@ inline std::optional<pubrel_message> decode_pubrel(
|
||||
) {
|
||||
if (remain_length == 0)
|
||||
return pubrel_message {};
|
||||
auto pubrel_ = basic::scope_limit_(remain_length)[
|
||||
x3::byte_ >> prop::props_<pubrel_props>
|
||||
];
|
||||
constexpr auto pubrel_ = basic::byte_ >> prop::props_<pubrel_props>;
|
||||
return type_parse(it, it + remain_length, pubrel_);
|
||||
}
|
||||
|
||||
@@ -209,9 +201,7 @@ inline std::optional<pubcomp_message> decode_pubcomp(
|
||||
) {
|
||||
if (remain_length == 0)
|
||||
return pubcomp_message {};
|
||||
auto pubcomp_ = basic::scope_limit_(remain_length)[
|
||||
x3::byte_ >> prop::props_<pubcomp_props>
|
||||
];
|
||||
constexpr auto pubcomp_ = basic::byte_ >> prop::props_<pubcomp_props>;
|
||||
return type_parse(it, it + remain_length, pubcomp_);
|
||||
}
|
||||
|
||||
@@ -223,9 +213,7 @@ using subscribe_message = std::tuple<
|
||||
inline std::optional<subscribe_message> decode_subscribe(
|
||||
uint32_t remain_length, byte_citer& it
|
||||
) {
|
||||
auto subscribe_ = basic::scope_limit_(remain_length)[
|
||||
prop::props_<subscribe_props> >> +(basic::utf8_ >> x3::byte_)
|
||||
];
|
||||
constexpr auto subscribe_ = prop::props_<subscribe_props> >> +(basic::utf8_ >> basic::byte_);
|
||||
return type_parse(it, it + remain_length, subscribe_);
|
||||
}
|
||||
|
||||
@@ -237,9 +225,7 @@ using suback_message = std::tuple<
|
||||
inline std::optional<suback_message> decode_suback(
|
||||
uint32_t remain_length, byte_citer& it
|
||||
) {
|
||||
auto suback_ = basic::scope_limit_(remain_length)[
|
||||
prop::props_<suback_props> >> +x3::byte_
|
||||
];
|
||||
constexpr auto suback_ = prop::props_<suback_props> >> +basic::byte_;
|
||||
return type_parse(it, it + remain_length, suback_);
|
||||
}
|
||||
|
||||
@@ -251,9 +237,7 @@ using unsubscribe_message = std::tuple<
|
||||
inline std::optional<unsubscribe_message> decode_unsubscribe(
|
||||
uint32_t remain_length, byte_citer& it
|
||||
) {
|
||||
auto unsubscribe_ = basic::scope_limit_(remain_length)[
|
||||
prop::props_<unsubscribe_props> >> +basic::utf8_
|
||||
];
|
||||
constexpr auto unsubscribe_ = prop::props_<unsubscribe_props> >> +basic::utf8_;
|
||||
return type_parse(it, it + remain_length, unsubscribe_);
|
||||
}
|
||||
|
||||
@@ -265,9 +249,7 @@ using unsuback_message = std::tuple<
|
||||
inline std::optional<unsuback_message> decode_unsuback(
|
||||
uint32_t remain_length, byte_citer& it
|
||||
) {
|
||||
auto unsuback_ = basic::scope_limit_(remain_length)[
|
||||
prop::props_<unsuback_props> >> +x3::byte_
|
||||
];
|
||||
constexpr auto unsuback_ = prop::props_<unsuback_props> >> +basic::byte_;
|
||||
return type_parse(it, it + remain_length, unsuback_);
|
||||
}
|
||||
|
||||
@@ -281,9 +263,7 @@ inline std::optional<disconnect_message> decode_disconnect(
|
||||
) {
|
||||
if (remain_length == 0)
|
||||
return disconnect_message {};
|
||||
auto disconnect_ = basic::scope_limit_(remain_length)[
|
||||
x3::byte_ >> prop::props_<disconnect_props>
|
||||
];
|
||||
constexpr auto disconnect_ = basic::byte_ >> prop::props_<disconnect_props>;
|
||||
return type_parse(it, it + remain_length, disconnect_);
|
||||
}
|
||||
|
||||
@@ -297,9 +277,7 @@ inline std::optional<auth_message> decode_auth(
|
||||
) {
|
||||
if (remain_length == 0)
|
||||
return auth_message {};
|
||||
auto auth_ = basic::scope_limit_(remain_length)[
|
||||
x3::byte_ >> prop::props_<auth_props>
|
||||
];
|
||||
constexpr auto auth_ = basic::byte_ >> prop::props_<auth_props>;
|
||||
return type_parse(it, it + remain_length, auth_);
|
||||
}
|
||||
|
||||
|
@@ -8,9 +8,8 @@
|
||||
#ifndef BOOST_MQTT5_ENDPOINTS_HPP
|
||||
#define BOOST_MQTT5_ENDPOINTS_HPP
|
||||
|
||||
#include <boost/mqtt5/types.hpp>
|
||||
|
||||
#include <boost/mqtt5/detail/log_invoke.hpp>
|
||||
#include <boost/mqtt5/detail/internal_types.hpp>
|
||||
|
||||
#include <boost/asio/append.hpp>
|
||||
#include <boost/asio/associated_allocator.hpp>
|
||||
@@ -23,7 +22,6 @@
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/prepend.hpp>
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
@@ -155,16 +153,6 @@ class endpoints {
|
||||
template <typename Owner, typename Handler>
|
||||
friend class resolve_op;
|
||||
|
||||
template <typename T>
|
||||
static constexpr auto to_(T& arg) {
|
||||
return [&](auto& ctx) { arg = boost::spirit::x3::_attr(ctx); };
|
||||
}
|
||||
|
||||
template <typename T, typename Parser>
|
||||
static constexpr auto as_(Parser&& p){
|
||||
return boost::spirit::x3::rule<struct _, T>{} = std::forward<Parser>(p);
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename Executor>
|
||||
endpoints(
|
||||
@@ -202,26 +190,25 @@ public:
|
||||
}
|
||||
|
||||
void brokers(std::string hosts, uint16_t default_port) {
|
||||
namespace x3 = boost::spirit::x3;
|
||||
|
||||
_servers.clear();
|
||||
|
||||
std::string host, port, path;
|
||||
|
||||
// loosely based on RFC 3986
|
||||
auto unreserved_ = x3::char_("-a-zA-Z_0-9._~");
|
||||
auto digit_ = x3::char_("0-9");
|
||||
auto separator_ = x3::char_(',');
|
||||
for (auto it = hosts.cbegin(), end = hosts.cend(); it != end;) {
|
||||
skip_spaces(it, end);
|
||||
|
||||
auto host_ = as_<std::string>(+unreserved_)[to_(host)];
|
||||
auto port_ = as_<std::string>(':' >> +digit_)[to_(port)];
|
||||
auto path_ = as_<std::string>(+(x3::char_('/') >> *unreserved_))[to_(path)];
|
||||
auto uri_ = *x3::omit[x3::space] >> (host_ >> -port_ >> -path_) >>
|
||||
(*x3::omit[x3::space] >> x3::omit[separator_ | x3::eoi]);
|
||||
auto host = std::string(match_while("A-Za-z0-9._~-", it, end));
|
||||
if (host.empty()) break;
|
||||
|
||||
std::string port;
|
||||
if (it < end && *it == ':') {
|
||||
port = std::string(match_while("0-9", ++it, end));
|
||||
if (port.empty()) break;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (it < end && *it == '/')
|
||||
path = std::string(match_while("/A-Za-z0-9._~-", it, end));
|
||||
|
||||
for (auto b = hosts.begin(); b != hosts.end(); ) {
|
||||
host.clear(); port.clear(); path.clear();
|
||||
if (phrase_parse(b, hosts.end(), uri_, x3::eps(false))) {
|
||||
_servers.push_back({
|
||||
std::move(host),
|
||||
port.empty()
|
||||
@@ -229,11 +216,46 @@ public:
|
||||
: std::move(port),
|
||||
std::move(path)
|
||||
});
|
||||
}
|
||||
else b = hosts.end();
|
||||
|
||||
skip_spaces(it, end);
|
||||
if (it == end || *it++ != ',') break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr void skip_spaces(
|
||||
byte_citer& it, const byte_citer& end
|
||||
) {
|
||||
match_while(" \t\r\n\f\v", it, end);
|
||||
};
|
||||
|
||||
|
||||
static constexpr std::string_view match_while(
|
||||
const char* chs, byte_citer& it, const byte_citer& end
|
||||
) {
|
||||
if (it == end)
|
||||
return {};
|
||||
|
||||
auto beg = it;
|
||||
while (it < end && is_any_of(chs, *it)) ++it;
|
||||
|
||||
return { &*beg, static_cast<size_t>(it - beg) };
|
||||
}
|
||||
|
||||
static constexpr bool is_any_of(const char* chs, char c) {
|
||||
while (*chs)
|
||||
if (*(chs + 1) == '-' && *(chs + 2)) {
|
||||
if (c >= *chs && c <= *(chs + 2))
|
||||
return true;
|
||||
chs += 3;
|
||||
}
|
||||
else {
|
||||
if (c == *chs)
|
||||
return true;
|
||||
++chs;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@@ -21,12 +21,9 @@ set(deps
|
||||
container
|
||||
core
|
||||
endian
|
||||
fusion
|
||||
optional
|
||||
random
|
||||
range
|
||||
smart_ptr
|
||||
spirit
|
||||
system
|
||||
type_traits
|
||||
|
||||
@@ -42,10 +39,11 @@ set(deps
|
||||
intrusive
|
||||
# logic # Beast dependency
|
||||
mp11
|
||||
optional
|
||||
preprocessor
|
||||
static_assert
|
||||
# static_string # Beast dependency
|
||||
type_index
|
||||
# type_index # Beast dependency
|
||||
winapi
|
||||
move
|
||||
function_types
|
||||
@@ -64,12 +62,8 @@ set(deps
|
||||
iterator
|
||||
regex
|
||||
"function"
|
||||
phoenix
|
||||
pool
|
||||
proto
|
||||
thread
|
||||
unordered
|
||||
variant
|
||||
variant2
|
||||
describe
|
||||
predef
|
||||
@@ -77,10 +71,8 @@ set(deps
|
||||
lexical_cast
|
||||
numeric/conversion
|
||||
tokenizer
|
||||
atomic
|
||||
chrono
|
||||
fusion
|
||||
exception
|
||||
ratio
|
||||
)
|
||||
|
||||
foreach(dep IN LISTS deps)
|
||||
|
@@ -74,7 +74,7 @@ BOOST_FIXTURE_TEST_CASE(single_host, shared_test_data) {
|
||||
BOOST_FIXTURE_TEST_CASE(multiple_hosts, shared_test_data) {
|
||||
bool finished = false;
|
||||
// "example.invalid" will not be resolved
|
||||
ep.brokers("127.0.0.1:1001,127.0.0.1/path1, example.invalid, localhost:1002/path1/path-2/path.3_", 1000);
|
||||
ep.brokers("127.0.0.1:1001,127.0.0.1/~path1, example.invalid, localhost:1002/path1/path-2/path.3_", 1000);
|
||||
|
||||
asio::co_spawn(ioc, [&]() -> asio::awaitable<void> {
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
@@ -90,7 +90,7 @@ BOOST_FIXTURE_TEST_CASE(multiple_hosts, shared_test_data) {
|
||||
BOOST_TEST(!eps.empty());
|
||||
BOOST_TEST(ap.host == "127.0.0.1");
|
||||
BOOST_TEST(ap.port == "1000");
|
||||
BOOST_TEST(ap.path == "/path1");
|
||||
BOOST_TEST(ap.path == "/~path1");
|
||||
|
||||
std::tie(ec, eps, ap) = co_await ep.async_next_endpoint(use_nothrow_awaitable);
|
||||
BOOST_TEST(ec == success);
|
||||
|
@@ -3,8 +3,7 @@
|
||||
"version-semver": "0.0.1",
|
||||
"dependencies": [
|
||||
"boost-asio",
|
||||
"boost-beast",
|
||||
"boost-spirit"
|
||||
"boost-beast"
|
||||
],
|
||||
"default-features": [],
|
||||
"features": {
|
||||
|
Reference in New Issue
Block a user