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::core
Boost::endian
Boost::fusion
Boost::optional
Boost::random
Boost::range
Boost::smart_ptr
Boost::spirit
Boost::system
Boost::type_traits
Threads::Threads

View File

@@ -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
;

View File

@@ -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

View File

@@ -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_);
}

View File

@@ -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,38 +190,72 @@ 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;
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()
? std::to_string(default_port)
: std::move(port),
std::move(path)
});
std::string port;
if (it < end && *it == ':') {
port = std::string(match_while("0-9", ++it, end));
if (port.empty()) break;
}
else b = hosts.end();
std::string path;
if (it < end && *it == '/')
path = std::string(match_while("/A-Za-z0-9._~-", it, end));
_servers.push_back({
std::move(host),
port.empty()
? std::to_string(default_port)
: std::move(port),
std::move(path)
});
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
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)

View File

@@ -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);

View File

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