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:
Bruno Iljazovic
2025-09-18 20:25:30 +02:00
parent 620df1f993
commit 150ba94d14
8 changed files with 250 additions and 376 deletions

View File

@@ -49,12 +49,9 @@ else()
Boost::container Boost::container
Boost::core Boost::core
Boost::endian Boost::endian
Boost::fusion
Boost::optional
Boost::random Boost::random
Boost::range Boost::range
Boost::smart_ptr Boost::smart_ptr
Boost::spirit
Boost::system Boost::system
Boost::type_traits Boost::type_traits
Threads::Threads Threads::Threads

View File

@@ -13,12 +13,9 @@ constant boost_dependencies :
/boost/container//boost_container /boost/container//boost_container
/boost/core//boost_core /boost/core//boost_core
/boost/endian//boost_endian /boost/endian//boost_endian
/boost/fusion//boost_fusion
/boost/optional//boost_optional
/boost/random//boost_random /boost/random//boost_random
/boost/range//boost_range /boost/range//boost_range
/boost/smart_ptr//boost_smart_ptr /boost/smart_ptr//boost_smart_ptr
/boost/spirit//boost_spirit
/boost/system//boost_system /boost/system//boost_system
/boost/type_traits//boost_type_traits /boost/type_traits//boost_type_traits
; ;

View File

@@ -12,11 +12,7 @@
#include <boost/mqtt5/detail/traits.hpp> #include <boost/mqtt5/detail/traits.hpp>
#include <boost/fusion/adapted/std_tuple.hpp> #include <boost/endian/conversion.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 <cstdint> #include <cstdint>
#include <string> #include <string>
@@ -24,217 +20,49 @@
namespace boost::mqtt5::decoders { 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> template <typename It, typename Parser>
auto type_parse(It& first, const It last, const Parser& p) { auto type_parse(It& first, const It last, const Parser& p) {
using ctx_type = decltype(x3::make_context<struct _>(std::declval<Parser&>())); using rv_type = typename Parser::attribute_type;
using attr_type = typename x3::traits::attribute_of<Parser, ctx_type>::type;
using rv_type = typename convert<attr_type>::type;
std::optional<rv_type> rv; std::optional<rv_type> rv;
rv_type value {}; rv_type value {};
if (x3::phrase_parse(first, last, as<rv_type>(p), x3::eps(false), value)) if (p.parse(first, last, 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))
rv = std::move(value); rv = std::move(value);
return rv; return rv;
} }
namespace basic { namespace basic {
template <typename T> template <typename Attr>
constexpr auto to(T& arg) { struct int_parser {
return [&](auto& ctx) { using attribute_type = Attr;
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 LenParser, typename Subject, typename Enable = void> template <typename It>
class scope_limit {}; bool parse(It& first, const It last, Attr& attr) const {
constexpr size_t byte_size = sizeof(Attr);
template <typename LenParser, typename Subject> if (std::distance(first, last) < static_cast<ptrdiff_t>(byte_size))
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)
return false; return false;
if (!base_type::subject.parse(iter, iter + len, ctx, rctx, attr)) using namespace boost::endian;
return false; 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; return true;
} }
}; };
template <typename Size, typename Subject> inline constexpr int_parser<uint8_t> byte_;
class scope_limit< inline constexpr int_parser<uint16_t> word_;
Size, Subject, inline constexpr int_parser<uint32_t> dword_;
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;
static bool const has_attribute = true; struct varint_parser {
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> {
using attribute_type = int32_t; using attribute_type = int32_t;
static bool const has_attribute = true;
template <typename It, typename Ctx, typename RCtx, typename Attr> template <typename It>
bool parse( bool parse(It& first, const It last, int32_t& attr) const {
It& first, const It last, auto iter = first;
const Ctx& ctx, RCtx&, Attr& attr
) const {
It iter = first;
x3::skip_over(iter, last, ctx);
if (iter == last) if (iter == last)
return false; 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; using attribute_type = std::string;
static bool const has_attribute = true;
template <typename It, typename Ctx, typename RCtx, typename Attr> template <typename It>
bool parse( bool parse(It& first, const It last, std::string& attr) const {
It& first, const It last, auto iter = first;
const Ctx& ctx, RCtx& rctx, Attr& attr
) const {
It iter = first;
x3::skip_over(iter, last, ctx);
typename x3::traits::attribute_of<decltype(x3::big_word), Ctx>::type len; typename decltype(word_)::attribute_type len;
if (x3::big_word.parse(iter, last, ctx, rctx, len)) { if (word_.parse(iter, last, len)) {
if (std::distance(iter, last) < len) if (std::distance(iter, last) < len)
return false; return false;
} }
@@ -290,44 +113,25 @@ struct len_prefix_parser : x3::parser<len_prefix_parser> {
} }
}; };
constexpr len_prefix_parser utf8_ {}; inline constexpr len_prefix_parser utf8_ {};
constexpr len_prefix_parser binary_ {}; inline 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
*/
template <typename Subject> template <typename Subject>
class conditional_parser : struct conditional_parser {
public x3::unary_parser<Subject, conditional_parser<Subject>> { 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>>; Subject p;
bool _condition; 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 attribute_type = boost::optional<subject_attr_type>; template <typename It>
static bool const has_attribute = true; bool parse(It& first, const It last, attribute_type& attr) const {
if (!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 {
if (!_condition)
return true; return true;
It iter = first; auto iter = first;
subject_attr_type sattr {}; subject_attr_type sattr {};
if (!base_type::subject.parse(iter, last, ctx, rctx, sattr)) if (!p.parse(iter, last, sattr))
return false; return false;
attr.emplace(std::move(sattr)); attr.emplace(std::move(sattr));
@@ -340,59 +144,155 @@ struct conditional_gen {
bool _condition; bool _condition;
template <typename Subject> template <typename Subject>
auto operator[](const Subject& p) const { conditional_parser<Subject> operator[](const Subject& p) const {
return conditional_parser<Subject> { p, _condition }; return { p, _condition };
} }
}; };
inline conditional_gen if_(bool condition) { constexpr conditional_gen if_(bool condition) {
return { 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 } // end namespace basic
namespace prop { namespace prop {
namespace basic = boost::mqtt5::decoders::basic;
namespace detail { namespace detail {
template <typename It, typename Ctx, typename RCtx, typename Prop> template <typename It, typename Prop>
bool parse_to_prop( bool parse_to_prop(It& iter, const It last, Prop& prop) {
It& iter, const It last,
const Ctx& ctx, RCtx& rctx, Prop& prop
) {
using namespace boost::mqtt5::detail; using namespace boost::mqtt5::detail;
using prop_type = std::remove_reference_t<decltype(prop)>; using prop_type = std::remove_reference_t<decltype(prop)>;
bool rv = false; bool rv = false;
if constexpr (std::is_same_v<prop_type, uint8_t>) 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>) 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>) 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>) 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>) 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>) { else if constexpr (is_optional<prop_type>) {
typename prop_type::value_type val; 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)); if (rv) prop.emplace(std::move(val));
} }
else if constexpr (is_pair<prop_type>) { else if constexpr (is_pair<prop_type>) {
rv = parse_to_prop(iter, last, ctx, rctx, prop.first); rv = parse_to_prop(iter, last, prop.first);
rv = parse_to_prop(iter, last, ctx, rctx, prop.second); rv = parse_to_prop(iter, last, prop.second);
} }
else if constexpr (is_vector<prop_type> || is_small_vector<prop_type>) { else if constexpr (is_vector<prop_type> || is_small_vector<prop_type>) {
typename std::remove_reference_t<prop_type>::value_type value; 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)); if (rv) prop.push_back(std::move(value));
} }
@@ -402,28 +302,21 @@ bool parse_to_prop(
} // end namespace detail } // end namespace detail
template <typename Props> template <typename Props>
class prop_parser : public x3::parser<prop_parser<Props>> { struct prop_parser {
public:
using attribute_type = Props; using attribute_type = Props;
static bool const has_attribute = true;
template <typename It, typename Ctx, typename RCtx, typename Attr> template <typename It>
bool parse( bool parse(It& first, const It last, Props& attr) const {
It& first, const It last, auto iter = first;
const Ctx& ctx, RCtx& rctx, Attr& attr
) const {
It iter = first;
x3::skip_over(iter, last, ctx);
if (iter == last) if (iter == last)
return true; return true;
int32_t props_length; int32_t props_length;
if (!basic::varint_.parse(iter, last, ctx, rctx, props_length)) if (!basic::varint_.parse(iter, last, props_length))
return false; return false;
const It scoped_last = iter + props_length; const auto scoped_last = iter + props_length;
// attr = Props{}; // attr = Props{};
while (iter < scoped_last) { while (iter < scoped_last) {
@@ -433,10 +326,8 @@ public:
attr.apply_on( attr.apply_on(
prop_id, prop_id,
[&rv, &iter, scoped_last, &ctx, &rctx](auto& prop) { [&rv, &iter, scoped_last](auto& prop) {
rv = detail::parse_to_prop( rv = detail::parse_to_prop(iter, scoped_last, prop);
iter, scoped_last, ctx, rctx, prop
);
} }
); );
@@ -450,13 +341,11 @@ public:
} }
}; };
template <typename Props> template <typename Props>
constexpr auto props_ = prop_parser<Props> {}; constexpr auto props_ = prop_parser<Props> {};
} // end namespace prop } // end namespace prop
} // end namespace boost::mqtt5::decoders } // end namespace boost::mqtt5::decoders
#endif // !BOOST_MQTT5_BASE_DECODERS_HPP #endif // !BOOST_MQTT5_BASE_DECODERS_HPP

View File

@@ -32,7 +32,7 @@ using fixed_header = std::tuple<
inline std::optional<fixed_header> decode_fixed_header( inline std::optional<fixed_header> decode_fixed_header(
byte_citer& it, const byte_citer last 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_); return type_parse(it, last, fixed_header_);
} }
@@ -41,7 +41,7 @@ using packet_id = uint16_t;
inline std::optional<packet_id> decode_packet_id( inline std::optional<packet_id> decode_packet_id(
byte_citer& it 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_); 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( inline std::optional<connect_message> decode_connect(
uint32_t remain_length, byte_citer& it uint32_t remain_length, byte_citer& it
) { ) {
auto var_header_ = constexpr auto var_header_ =
basic::utf8_ >> // MQTT basic::utf8_ >> // MQTT
x3::byte_ >> // (num 5) basic::byte_ >> // (num 5)
x3::byte_ >> // conn_flags_ basic::byte_ >> // conn_flags_
x3::big_word >> // keep_alive basic::word_ >> // keep_alive
prop::props_<connect_props>; prop::props_<connect_props>;
const byte_citer end = it + remain_length; const byte_citer end = it + remain_length;
@@ -124,9 +124,7 @@ using connack_message = std::tuple<
inline std::optional<connack_message> decode_connack( inline std::optional<connack_message> decode_connack(
uint32_t remain_length, byte_citer& it uint32_t remain_length, byte_citer& it
) { ) {
auto connack_ = basic::scope_limit_(remain_length)[ constexpr auto connack_ = basic::byte_ >> basic::byte_ >> prop::props_<connack_props>;
x3::byte_ >> x3::byte_ >> prop::props_<connack_props>
];
return type_parse(it, it + remain_length, connack_); 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; uint8_t flags = control_byte & 0b1111;
auto qos = qos_e((flags >> 1) & 0b11); auto qos = qos_e((flags >> 1) & 0b11);
auto publish_ = basic::scope_limit_(remain_length)[ auto publish_ =
basic::utf8_ >> basic::if_(qos != qos_e::at_most_once)[x3::big_word] >> basic::utf8_ >> basic::if_(qos != qos_e::at_most_once)[basic::word_] >>
x3::attr(flags) >> prop::props_<publish_props> >> basic::verbatim_ basic::attr(flags) >> prop::props_<publish_props> >>
]; basic::verbatim_;
return type_parse(it, it + remain_length, publish_); return type_parse(it, it + remain_length, publish_);
} }
@@ -161,9 +159,7 @@ inline std::optional<puback_message> decode_puback(
) { ) {
if (remain_length == 0) if (remain_length == 0)
return puback_message {}; return puback_message {};
auto puback_ = basic::scope_limit_(remain_length)[ constexpr auto puback_ = basic::byte_ >> prop::props_<puback_props>;
x3::byte_ >> prop::props_<puback_props>
];
return type_parse(it, it + remain_length, puback_); return type_parse(it, it + remain_length, puback_);
} }
@@ -177,9 +173,7 @@ inline std::optional<pubrec_message> decode_pubrec(
) { ) {
if (remain_length == 0) if (remain_length == 0)
return pubrec_message {}; return pubrec_message {};
auto pubrec_ = basic::scope_limit_(remain_length)[ constexpr auto pubrec_ = basic::byte_ >> prop::props_<pubrec_props>;
x3::byte_ >> prop::props_<pubrec_props>
];
return type_parse(it, it + remain_length, pubrec_); return type_parse(it, it + remain_length, pubrec_);
} }
@@ -193,9 +187,7 @@ inline std::optional<pubrel_message> decode_pubrel(
) { ) {
if (remain_length == 0) if (remain_length == 0)
return pubrel_message {}; return pubrel_message {};
auto pubrel_ = basic::scope_limit_(remain_length)[ constexpr auto pubrel_ = basic::byte_ >> prop::props_<pubrel_props>;
x3::byte_ >> prop::props_<pubrel_props>
];
return type_parse(it, it + remain_length, pubrel_); return type_parse(it, it + remain_length, pubrel_);
} }
@@ -209,9 +201,7 @@ inline std::optional<pubcomp_message> decode_pubcomp(
) { ) {
if (remain_length == 0) if (remain_length == 0)
return pubcomp_message {}; return pubcomp_message {};
auto pubcomp_ = basic::scope_limit_(remain_length)[ constexpr auto pubcomp_ = basic::byte_ >> prop::props_<pubcomp_props>;
x3::byte_ >> prop::props_<pubcomp_props>
];
return type_parse(it, it + remain_length, pubcomp_); return type_parse(it, it + remain_length, pubcomp_);
} }
@@ -223,9 +213,7 @@ using subscribe_message = std::tuple<
inline std::optional<subscribe_message> decode_subscribe( inline std::optional<subscribe_message> decode_subscribe(
uint32_t remain_length, byte_citer& it uint32_t remain_length, byte_citer& it
) { ) {
auto subscribe_ = basic::scope_limit_(remain_length)[ constexpr auto subscribe_ = prop::props_<subscribe_props> >> +(basic::utf8_ >> basic::byte_);
prop::props_<subscribe_props> >> +(basic::utf8_ >> x3::byte_)
];
return type_parse(it, it + remain_length, subscribe_); return type_parse(it, it + remain_length, subscribe_);
} }
@@ -237,9 +225,7 @@ using suback_message = std::tuple<
inline std::optional<suback_message> decode_suback( inline std::optional<suback_message> decode_suback(
uint32_t remain_length, byte_citer& it uint32_t remain_length, byte_citer& it
) { ) {
auto suback_ = basic::scope_limit_(remain_length)[ constexpr auto suback_ = prop::props_<suback_props> >> +basic::byte_;
prop::props_<suback_props> >> +x3::byte_
];
return type_parse(it, it + remain_length, suback_); return type_parse(it, it + remain_length, suback_);
} }
@@ -251,9 +237,7 @@ using unsubscribe_message = std::tuple<
inline std::optional<unsubscribe_message> decode_unsubscribe( inline std::optional<unsubscribe_message> decode_unsubscribe(
uint32_t remain_length, byte_citer& it uint32_t remain_length, byte_citer& it
) { ) {
auto unsubscribe_ = basic::scope_limit_(remain_length)[ constexpr auto unsubscribe_ = prop::props_<unsubscribe_props> >> +basic::utf8_;
prop::props_<unsubscribe_props> >> +basic::utf8_
];
return type_parse(it, it + remain_length, unsubscribe_); return type_parse(it, it + remain_length, unsubscribe_);
} }
@@ -265,9 +249,7 @@ using unsuback_message = std::tuple<
inline std::optional<unsuback_message> decode_unsuback( inline std::optional<unsuback_message> decode_unsuback(
uint32_t remain_length, byte_citer& it uint32_t remain_length, byte_citer& it
) { ) {
auto unsuback_ = basic::scope_limit_(remain_length)[ constexpr auto unsuback_ = prop::props_<unsuback_props> >> +basic::byte_;
prop::props_<unsuback_props> >> +x3::byte_
];
return type_parse(it, it + remain_length, unsuback_); return type_parse(it, it + remain_length, unsuback_);
} }
@@ -281,9 +263,7 @@ inline std::optional<disconnect_message> decode_disconnect(
) { ) {
if (remain_length == 0) if (remain_length == 0)
return disconnect_message {}; return disconnect_message {};
auto disconnect_ = basic::scope_limit_(remain_length)[ constexpr auto disconnect_ = basic::byte_ >> prop::props_<disconnect_props>;
x3::byte_ >> prop::props_<disconnect_props>
];
return type_parse(it, it + remain_length, disconnect_); return type_parse(it, it + remain_length, disconnect_);
} }
@@ -297,9 +277,7 @@ inline std::optional<auth_message> decode_auth(
) { ) {
if (remain_length == 0) if (remain_length == 0)
return auth_message {}; return auth_message {};
auto auth_ = basic::scope_limit_(remain_length)[ constexpr auto auth_ = basic::byte_ >> prop::props_<auth_props>;
x3::byte_ >> prop::props_<auth_props>
];
return type_parse(it, it + remain_length, auth_); return type_parse(it, it + remain_length, auth_);
} }

View File

@@ -8,9 +8,8 @@
#ifndef BOOST_MQTT5_ENDPOINTS_HPP #ifndef BOOST_MQTT5_ENDPOINTS_HPP
#define 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/log_invoke.hpp>
#include <boost/mqtt5/detail/internal_types.hpp>
#include <boost/asio/append.hpp> #include <boost/asio/append.hpp>
#include <boost/asio/associated_allocator.hpp> #include <boost/asio/associated_allocator.hpp>
@@ -23,7 +22,6 @@
#include <boost/asio/ip/tcp.hpp> #include <boost/asio/ip/tcp.hpp>
#include <boost/asio/post.hpp> #include <boost/asio/post.hpp>
#include <boost/asio/prepend.hpp> #include <boost/asio/prepend.hpp>
#include <boost/spirit/home/x3.hpp>
#include <array> #include <array>
#include <chrono> #include <chrono>
@@ -155,16 +153,6 @@ class endpoints {
template <typename Owner, typename Handler> template <typename Owner, typename Handler>
friend class resolve_op; 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: public:
template <typename Executor> template <typename Executor>
endpoints( endpoints(
@@ -202,26 +190,25 @@ public:
} }
void brokers(std::string hosts, uint16_t default_port) { void brokers(std::string hosts, uint16_t default_port) {
namespace x3 = boost::spirit::x3;
_servers.clear(); _servers.clear();
std::string host, port, path;
// loosely based on RFC 3986 // loosely based on RFC 3986
auto unreserved_ = x3::char_("-a-zA-Z_0-9._~"); for (auto it = hosts.cbegin(), end = hosts.cend(); it != end;) {
auto digit_ = x3::char_("0-9"); skip_spaces(it, end);
auto separator_ = x3::char_(',');
auto host_ = as_<std::string>(+unreserved_)[to_(host)]; auto host = std::string(match_while("A-Za-z0-9._~-", it, end));
auto port_ = as_<std::string>(':' >> +digit_)[to_(port)]; if (host.empty()) break;
auto path_ = as_<std::string>(+(x3::char_('/') >> *unreserved_))[to_(path)];
auto uri_ = *x3::omit[x3::space] >> (host_ >> -port_ >> -path_) >> std::string port;
(*x3::omit[x3::space] >> x3::omit[separator_ | x3::eoi]); 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({ _servers.push_back({
std::move(host), std::move(host),
port.empty() port.empty()
@@ -229,11 +216,46 @@ public:
: std::move(port), : std::move(port),
std::move(path) 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;
}
}; };

View File

@@ -21,12 +21,9 @@ set(deps
container container
core core
endian endian
fusion
optional
random random
range range
smart_ptr smart_ptr
spirit
system system
type_traits type_traits
@@ -42,10 +39,11 @@ set(deps
intrusive intrusive
# logic # Beast dependency # logic # Beast dependency
mp11 mp11
optional
preprocessor preprocessor
static_assert static_assert
# static_string # Beast dependency # static_string # Beast dependency
type_index # type_index # Beast dependency
winapi winapi
move move
function_types function_types
@@ -64,12 +62,8 @@ set(deps
iterator iterator
regex regex
"function" "function"
phoenix
pool pool
proto
thread
unordered unordered
variant
variant2 variant2
describe describe
predef predef
@@ -77,10 +71,8 @@ set(deps
lexical_cast lexical_cast
numeric/conversion numeric/conversion
tokenizer tokenizer
atomic fusion
chrono
exception exception
ratio
) )
foreach(dep IN LISTS deps) foreach(dep IN LISTS deps)

View File

@@ -74,7 +74,7 @@ BOOST_FIXTURE_TEST_CASE(single_host, shared_test_data) {
BOOST_FIXTURE_TEST_CASE(multiple_hosts, shared_test_data) { BOOST_FIXTURE_TEST_CASE(multiple_hosts, shared_test_data) {
bool finished = false; bool finished = false;
// "example.invalid" will not be resolved // "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> { asio::co_spawn(ioc, [&]() -> asio::awaitable<void> {
for (size_t i = 0; i < 3; ++i) { 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(!eps.empty());
BOOST_TEST(ap.host == "127.0.0.1"); BOOST_TEST(ap.host == "127.0.0.1");
BOOST_TEST(ap.port == "1000"); 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); std::tie(ec, eps, ap) = co_await ep.async_next_endpoint(use_nothrow_awaitable);
BOOST_TEST(ec == success); BOOST_TEST(ec == success);

View File

@@ -3,8 +3,7 @@
"version-semver": "0.0.1", "version-semver": "0.0.1",
"dependencies": [ "dependencies": [
"boost-asio", "boost-asio",
"boost-beast", "boost-beast"
"boost-spirit"
], ],
"default-features": [], "default-features": [],
"features": { "features": {