Commit 660cc9bd authored by Dominik Charousset's avatar Dominik Charousset

Add overloads to detail::parse for all IP types

parent 5ebeb85c
...@@ -82,6 +82,18 @@ void parse(parse_state& ps, timespan& x); ...@@ -82,6 +82,18 @@ void parse(parse_state& ps, timespan& x);
void parse(parse_state& ps, atom_value& x); void parse(parse_state& ps, atom_value& x);
void parse(parse_state& ps, ipv4_address& x);
void parse(parse_state& ps, ipv4_subnet& x);
void parse(parse_state& ps, ipv4_endpoint& x);
void parse(parse_state& ps, ipv6_address& x);
void parse(parse_state& ps, ipv6_subnet& x);
void parse(parse_state& ps, ipv6_endpoint& x);
void parse(parse_state& ps, uri& x); void parse(parse_state& ps, uri& x);
// -- STL types ---------------------------------------------------------------- // -- STL types ----------------------------------------------------------------
......
...@@ -41,10 +41,10 @@ namespace parser { ...@@ -41,10 +41,10 @@ namespace parser {
struct read_ipv4_octet_consumer { struct read_ipv4_octet_consumer {
std::array<uint8_t, 4> bytes; std::array<uint8_t, 4> bytes;
int octets = 0; size_t octets = 0;
void value(uint8_t octet) { void value(uint8_t octet) {
bytes[static_cast<size_t>(octets++)] = octet; bytes[octets++] = octet;
} }
}; };
...@@ -73,14 +73,15 @@ void read_ipv4_octet(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -73,14 +73,15 @@ void read_ipv4_octet(state<Iterator, Sentinel>& ps, Consumer& consumer) {
/// Reads a number, i.e., on success produces either an `int64_t` or a /// Reads a number, i.e., on success produces either an `int64_t` or a
/// `double`. /// `double`.
template <class Iterator, class Sentinel, class Consumer> template <class Iterator, class Sentinel, class Consumer>
void read_ipv4_address(state<Iterator, Sentinel>& ps, Consumer& consumer) { void read_ipv4_address(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
read_ipv4_octet_consumer f; read_ipv4_octet_consumer f;
auto g = make_scope_guard([&] { auto g = make_scope_guard([&] {
if (ps.code <= pec::trailing_character) { if (ps.code <= pec::trailing_character) {
ipv4_address result{f.bytes}; ipv4_address result{f.bytes};
consumer.value(result); consumer.value(std::move(result));
} }
}); });
// clang-format off
start(); start();
state(init) { state(init) {
fsm_epsilon(read_ipv4_octet(ps, f), rd_dot, decimal_chars) fsm_epsilon(read_ipv4_octet(ps, f), rd_dot, decimal_chars)
...@@ -90,12 +91,13 @@ void read_ipv4_address(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -90,12 +91,13 @@ void read_ipv4_address(state<Iterator, Sentinel>& ps, Consumer& consumer) {
} }
state(rd_oct) { state(rd_oct) {
fsm_epsilon_if(f.octets < 3, read_ipv4_octet(ps, f), rd_dot, decimal_chars) fsm_epsilon_if(f.octets < 3, read_ipv4_octet(ps, f), rd_dot, decimal_chars)
fsm_epsilon_if(f.octets == 3, read_ipv4_octet(ps, f), done) fsm_epsilon_if(f.octets == 3, read_ipv4_octet(ps, f), done, decimal_chars)
} }
term_state(done) { term_state(done) {
// nop // nop
} }
fin(); fin();
// clang-format on
} }
} // namespace parser } // namespace parser
......
...@@ -174,7 +174,7 @@ read_ipv6_address_piece_consumer<F> make_read_ipv6_address_piece_consumer(F f) { ...@@ -174,7 +174,7 @@ read_ipv6_address_piece_consumer<F> make_read_ipv6_address_piece_consumer(F f) {
/// Reads a number, i.e., on success produces either an `int64_t` or a /// Reads a number, i.e., on success produces either an `int64_t` or a
/// `double`. /// `double`.
template <class Iterator, class Sentinel, class Consumer> template <class Iterator, class Sentinel, class Consumer>
void read_ipv6_address(state<Iterator, Sentinel>& ps, Consumer& consumer) { void read_ipv6_address(state<Iterator, Sentinel>& ps, Consumer&& consumer) {
// IPv6 allows omitting blocks of zeros, splitting the string into a part // IPv6 allows omitting blocks of zeros, splitting the string into a part
// before the zeros (prefix) and a part after the zeros (suffix). For example, // before the zeros (prefix) and a part after the zeros (suffix). For example,
// ff::1 is 00FF0000000000000000000000000001 // ff::1 is 00FF0000000000000000000000000001
...@@ -194,7 +194,7 @@ void read_ipv6_address(state<Iterator, Sentinel>& ps, Consumer& consumer) { ...@@ -194,7 +194,7 @@ void read_ipv6_address(state<Iterator, Sentinel>& ps, Consumer& consumer) {
for (size_t i = 0; i < ipv6_address::num_bytes; ++i) for (size_t i = 0; i < ipv6_address::num_bytes; ++i)
bytes[i] = prefix[i] | suffix[i]; bytes[i] = prefix[i] | suffix[i];
ipv6_address result{bytes}; ipv6_address result{bytes};
consumer.value(result); consumer.value(std::move(result));
} }
}); });
// We need to parse 2-byte hexadecimal numbers (x) and also keep track of // We need to parse 2-byte hexadecimal numbers (x) and also keep track of
......
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
#pragma once #pragma once
#include <cstdint>
#include <functional>
#include "caf/detail/comparable.hpp" #include "caf/detail/comparable.hpp"
#include "caf/ipv4_address.hpp" #include "caf/ipv4_address.hpp"
#include "caf/meta/type_name.hpp" #include "caf/meta/type_name.hpp"
......
...@@ -18,19 +18,26 @@ ...@@ -18,19 +18,26 @@
#pragma once #pragma once
#include <cstdint>
#include <functional>
#include "caf/detail/comparable.hpp" #include "caf/detail/comparable.hpp"
#include "caf/fwd.hpp"
#include "caf/ipv6_address.hpp" #include "caf/ipv6_address.hpp"
#include "caf/meta/type_name.hpp" #include "caf/meta/type_name.hpp"
namespace caf { namespace caf {
/// An IP endpoint that contains an ::ipv6_address and a port. /// An IP endpoint that contains an ::ipv6_address and a port.
class ipv6_endpoint : detail::comparable<ipv6_endpoint> { class ipv6_endpoint : detail::comparable<ipv6_endpoint>,
detail::comparable<ipv6_endpoint, ipv4_endpoint> {
public: public:
// -- constructors ----------------------------------------------------------- // -- constructors -----------------------------------------------------------
ipv6_endpoint(ipv6_address address, uint16_t port); ipv6_endpoint(ipv6_address address, uint16_t port);
ipv6_endpoint(ipv4_address address, uint16_t port);
ipv6_endpoint() = default; ipv6_endpoint() = default;
ipv6_endpoint(const ipv6_endpoint&) = default; ipv6_endpoint(const ipv6_endpoint&) = default;
...@@ -67,6 +74,11 @@ public: ...@@ -67,6 +74,11 @@ public:
/// value otherwise. /// value otherwise.
long compare(ipv6_endpoint x) const noexcept; long compare(ipv6_endpoint x) const noexcept;
/// Compares this endpoint to `x`.
/// @returns 0 if `*this == x`, a positive value if `*this > x` and a negative
/// value otherwise.
long compare(ipv4_endpoint x) const noexcept;
template <class Inspector> template <class Inspector>
friend typename Inspector::result_type inspect(Inspector& f, friend typename Inspector::result_type inspect(Inspector& f,
ipv6_endpoint& x) { ipv6_endpoint& x) {
......
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include "caf/ipv6_endpoint.hpp" #include "caf/ipv6_endpoint.hpp"
#include "caf/detail/fnv_hash.hpp" #include "caf/detail/fnv_hash.hpp"
#include "caf/ipv4_address.hpp"
#include "caf/ipv4_endpoint.hpp"
namespace caf { namespace caf {
...@@ -27,6 +29,11 @@ ipv6_endpoint::ipv6_endpoint(ipv6_address address, uint16_t port) ...@@ -27,6 +29,11 @@ ipv6_endpoint::ipv6_endpoint(ipv6_address address, uint16_t port)
// nop // nop
} }
ipv6_endpoint::ipv6_endpoint(ipv4_address address, uint16_t port)
: address_(address), port_(port) {
// nop
}
size_t ipv6_endpoint::hash_code() const noexcept { size_t ipv6_endpoint::hash_code() const noexcept {
auto result = detail::fnv_hash(address_.data()); auto result = detail::fnv_hash(address_.data());
return detail::fnv_hash_append(result, port_); return detail::fnv_hash_append(result, port_);
...@@ -37,6 +44,11 @@ long ipv6_endpoint::compare(ipv6_endpoint x) const noexcept { ...@@ -37,6 +44,11 @@ long ipv6_endpoint::compare(ipv6_endpoint x) const noexcept {
return res == 0 ? port_ - x.port() : res; return res == 0 ? port_ - x.port() : res;
} }
long ipv6_endpoint::compare(ipv4_endpoint x) const noexcept {
ipv6_endpoint y{x.address(), x.port()};
return compare(y);
}
std::string to_string(const ipv6_endpoint& x) { std::string to_string(const ipv6_endpoint& x) {
std::string result; std::string result;
auto addr = x.address(); auto addr = x.address();
......
...@@ -23,11 +23,19 @@ ...@@ -23,11 +23,19 @@
#include "caf/detail/parser/read_atom.hpp" #include "caf/detail/parser/read_atom.hpp"
#include "caf/detail/parser/read_bool.hpp" #include "caf/detail/parser/read_bool.hpp"
#include "caf/detail/parser/read_floating_point.hpp" #include "caf/detail/parser/read_floating_point.hpp"
#include "caf/detail/parser/read_ipv4_address.hpp"
#include "caf/detail/parser/read_ipv6_address.hpp"
#include "caf/detail/parser/read_signed_integer.hpp" #include "caf/detail/parser/read_signed_integer.hpp"
#include "caf/detail/parser/read_string.hpp" #include "caf/detail/parser/read_string.hpp"
#include "caf/detail/parser/read_timespan.hpp" #include "caf/detail/parser/read_timespan.hpp"
#include "caf/detail/parser/read_unsigned_integer.hpp" #include "caf/detail/parser/read_unsigned_integer.hpp"
#include "caf/detail/parser/read_uri.hpp" #include "caf/detail/parser/read_uri.hpp"
#include "caf/ipv4_address.hpp"
#include "caf/ipv4_endpoint.hpp"
#include "caf/ipv4_subnet.hpp"
#include "caf/ipv6_address.hpp"
#include "caf/ipv6_endpoint.hpp"
#include "caf/ipv6_subnet.hpp"
#include "caf/uri_builder.hpp" #include "caf/uri_builder.hpp"
#define PARSE_IMPL(type, parser_name) \ #define PARSE_IMPL(type, parser_name) \
...@@ -38,6 +46,42 @@ ...@@ -38,6 +46,42 @@
namespace caf { namespace caf {
namespace detail { namespace detail {
struct literal {
string_view str;
};
void parse(parse_state& ps, literal& x) {
CAF_ASSERT(x.str.size() > 0);
if (ps.current() != x.str[0]) {
ps.code = pec::unexpected_character;
return;
}
auto c = ps.next();
for (auto i = x.str.begin() + 1; i != x.str.end(); ++i) {
if (c != *i) {
ps.code = pec::unexpected_character;
return;
}
c = ps.next();
}
ps.code = ps.at_end() ? pec::success : pec::trailing_character;
}
void parse_sequence(parse_state&) {
// End of recursion.
}
template <class T, class... Ts>
void parse_sequence(parse_state& ps, T&& x, Ts&&... xs) {
parse(ps, x);
// TODO: use `if constexpr` when switching to C++17
if (sizeof...(Ts) > 0) {
CAF_ASSERT(ps.code != pec::success);
if (ps.code == pec::trailing_character)
parse_sequence(ps, std::forward<Ts>(xs)...);
}
}
PARSE_IMPL(bool, bool) PARSE_IMPL(bool, bool)
PARSE_IMPL(int8_t, signed_integer) PARSE_IMPL(int8_t, signed_integer)
...@@ -83,6 +127,74 @@ void parse(parse_state& ps, uri& x) { ...@@ -83,6 +127,74 @@ void parse(parse_state& ps, uri& x) {
x = builder.make(); x = builder.make();
} }
PARSE_IMPL(ipv4_address, ipv4_address)
void parse(parse_state& ps, ipv4_subnet& x) {
ipv4_address addr;
uint8_t prefix_length;
parse_sequence(ps, addr, literal{"/"}, prefix_length);
if (ps.code <= pec::trailing_character) {
if (prefix_length > 32) {
ps.code = pec::integer_overflow;
return;
}
x = ipv4_subnet{addr, prefix_length};
}
}
void parse(parse_state& ps, ipv4_endpoint& x) {
ipv4_address addr;
uint16_t port;
parse_sequence(ps, addr, literal{":"}, port);
if (ps.code <= pec::trailing_character)
x = ipv4_endpoint{addr, port};
}
PARSE_IMPL(ipv6_address, ipv6_address)
void parse(parse_state& ps, ipv6_subnet& x) {
// TODO: this algorithm is currently not one-pass. The reason we need to
// check whether the input is a valid ipv4_subnet first is that "1.2.3.0" is
// a valid IPv6 address, but "1.2.3.0/16" results in the wrong subnet when
// blindly reading the address as an IPv6 address.
auto nested = ps;
ipv4_subnet v4_subnet;
parse(nested, v4_subnet);
if (nested.code <= pec::trailing_character) {
ps.i = nested.i;
ps.code = nested.code;
ps.line = nested.line;
ps.column = nested.column;
x = ipv6_subnet{v4_subnet};
return;
}
ipv6_address addr;
uint8_t prefix_length;
parse_sequence(ps, addr, literal{"/"}, prefix_length);
if (ps.code <= pec::trailing_character) {
if (prefix_length > 128) {
ps.code = pec::integer_overflow;
return;
}
x = ipv6_subnet{addr, prefix_length};
}
}
void parse(parse_state& ps, ipv6_endpoint& x) {
ipv6_address addr;
uint16_t port;
if (ps.consume('[')) {
parse_sequence(ps, addr, literal{"]:"}, port);
} else {
ipv4_address v4_addr;
parse_sequence(ps, v4_addr, literal{":"}, port);
if (ps.code <= pec::trailing_character)
addr = ipv6_address{v4_addr};
}
if (ps.code <= pec::trailing_character)
x = ipv6_endpoint{addr, port};
}
void parse(parse_state& ps, std::string& x) { void parse(parse_state& ps, std::string& x) {
ps.skip_whitespaces(); ps.skip_whitespaces();
if (ps.current() == '"') { if (ps.current() == '"') {
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "caf/actor_system_config.hpp" #include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp" #include "caf/binary_deserializer.hpp"
#include "caf/byte.hpp" #include "caf/byte.hpp"
#include "caf/detail/parse.hpp"
#include "caf/ipv4_address.hpp" #include "caf/ipv4_address.hpp"
#include "caf/serializer_impl.hpp" #include "caf/serializer_impl.hpp"
#include "caf/span.hpp" #include "caf/span.hpp"
...@@ -38,18 +39,11 @@ using namespace caf; ...@@ -38,18 +39,11 @@ using namespace caf;
namespace { namespace {
ipv4_endpoint operator"" _ep(const char* str, size_t) { ipv4_endpoint operator"" _ep(const char* str, size_t size) {
std::array<uint8_t, 4> bytes; ipv4_endpoint result;
char* next = nullptr; if (auto err = detail::parse(string_view{str, size}, result))
bytes[0] = static_cast<uint8_t>(strtol(str, &next, 10)); CAF_FAIL("unable to parse input: " << err);
for (size_t n = 1; n < 4; ++n) { return result;
assert(*next == '.');
str = next + 1;
bytes[n] = static_cast<uint8_t>(strtol(str, &next, 10));
}
assert(*next == ':');
auto port = static_cast<uint16_t>(strtol(next + 1, nullptr, 10));
return ipv4_endpoint{ipv4_address{bytes}, port};
} }
struct fixture { struct fixture {
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#include "caf/actor_system_config.hpp" #include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp" #include "caf/binary_deserializer.hpp"
#include "caf/byte.hpp" #include "caf/byte.hpp"
#include "caf/ipv4_address.hpp"
#include "caf/ipv4_endpoint.hpp"
#include "caf/ipv6_address.hpp" #include "caf/ipv6_address.hpp"
#include "caf/serializer_impl.hpp" #include "caf/serializer_impl.hpp"
#include "caf/span.hpp" #include "caf/span.hpp"
...@@ -37,47 +39,11 @@ using namespace caf; ...@@ -37,47 +39,11 @@ using namespace caf;
namespace { namespace {
ipv6_endpoint operator"" _ep(const char* str, size_t) { ipv6_endpoint operator"" _ep(const char* str, size_t size) {
union { ipv6_endpoint result;
std::array<uint8_t, 16> xs_bytes; if (auto err = detail::parse(string_view{str, size}, result))
std::array<uint16_t, 8> xs; CAF_FAIL("unable to parse input: " << err);
}; return result;
union {
std::array<uint8_t, 16> ys_bytes;
std::array<uint16_t, 8> ys;
};
memset(xs_bytes.data(), 0, xs_bytes.size());
memset(ys_bytes.data(), 0, xs_bytes.size());
assert(*str == '[');
++str;
char* next = nullptr;
auto pull = [&] {
auto x = static_cast<uint16_t>(strtol(str, &next, 16));
return detail::to_network_order(x);
};
size_t n = 0;
do {
xs[n++] = pull();
assert(next != nullptr);
str = next + 1;
} while (*str != ':' && *str != ']');
if (*str == ':') {
++str;
do {
ys[0] = pull();
std::rotate(ys.begin(), ys.begin() + 1, ys.end());
assert(next != nullptr);
str = next + 1;
} while (*next != ']');
}
assert(*next == ']');
assert(*str == ':');
++str;
auto port = static_cast<uint16_t>(strtol(str, nullptr, 10));
std::array<uint8_t, 16> bytes;
for (size_t i = 0; i < 16; ++i)
bytes[i] = xs_bytes[i] | ys_bytes[i];
return ipv6_endpoint{ipv6_address{bytes}, port};
} }
struct fixture { struct fixture {
...@@ -138,6 +104,12 @@ CAF_TEST(constructing assigning and hash_code) { ...@@ -138,6 +104,12 @@ CAF_TEST(constructing assigning and hash_code) {
CAF_CHECK_EQUAL(ep1.hash_code(), ep2.hash_code()); CAF_CHECK_EQUAL(ep1.hash_code(), ep2.hash_code());
} }
CAF_TEST(comparison to IPv4) {
ipv4_endpoint v4{ipv4_address({127, 0, 0, 1}), 8080};
ipv6_endpoint v6{v4.address(), v4.port()};
CAF_CHECK_EQUAL(v4, v6);
}
CAF_TEST(to_string) { CAF_TEST(to_string) {
CHECK_TO_STRING("[::1]:8888"); CHECK_TO_STRING("[::1]:8888");
CHECK_TO_STRING("[4e::d00:0:ed00:0:1]:1234"); CHECK_TO_STRING("[4e::d00:0:ed00:0:1]:1234");
......
...@@ -23,6 +23,12 @@ ...@@ -23,6 +23,12 @@
#include "caf/test/dsl.hpp" #include "caf/test/dsl.hpp"
#include "caf/expected.hpp" #include "caf/expected.hpp"
#include "caf/ipv4_address.hpp"
#include "caf/ipv4_endpoint.hpp"
#include "caf/ipv4_subnet.hpp"
#include "caf/ipv6_address.hpp"
#include "caf/ipv6_endpoint.hpp"
#include "caf/ipv6_subnet.hpp"
#include "caf/string_view.hpp" #include "caf/string_view.hpp"
#include "caf/uri.hpp" #include "caf/uri.hpp"
...@@ -216,3 +222,51 @@ CAF_TEST(uris) { ...@@ -216,3 +222,51 @@ CAF_TEST(uris) {
CAF_CHECK_EQUAL(ls[1].authority().host, std::string{"actor-framework.org"}); CAF_CHECK_EQUAL(ls[1].authority().host, std::string{"actor-framework.org"});
CAF_CHECK_EQUAL(ls[1].path(), "doc"); CAF_CHECK_EQUAL(ls[1].path(), "doc");
} }
CAF_TEST(IPv4 address) {
CAF_CHECK_EQUAL(read<ipv4_address>("1.2.3.4"), ipv4_address({1, 2, 3, 4}));
CAF_CHECK_EQUAL(read<ipv4_address>("127.0.0.1"),
ipv4_address({127, 0, 0, 1}));
CAF_CHECK_EQUAL(read<ipv4_address>("256.0.0.1"), pec::integer_overflow);
}
CAF_TEST(IPv4 subnet) {
CAF_CHECK_EQUAL(read<ipv4_subnet>("1.2.3.0/24"),
ipv4_subnet(ipv4_address({1, 2, 3, 0}), 24));
CAF_CHECK_EQUAL(read<ipv4_subnet>("1.2.3.0/33"), pec::integer_overflow);
}
CAF_TEST(IPv4 endpoint) {
CAF_CHECK_EQUAL(read<ipv4_endpoint>("127.0.0.1:0"),
ipv4_endpoint(ipv4_address({127, 0, 0, 1}), 0));
CAF_CHECK_EQUAL(read<ipv4_endpoint>("127.0.0.1:65535"),
ipv4_endpoint(ipv4_address({127, 0, 0, 1}), 65535));
CAF_CHECK_EQUAL(read<ipv4_endpoint>("127.0.0.1:65536"),
pec::integer_overflow);
}
CAF_TEST(IPv6 address) {
CAF_CHECK_EQUAL(read<ipv6_address>("1.2.3.4"), ipv4_address({1, 2, 3, 4}));
CAF_CHECK_EQUAL(read<ipv6_address>("1::"), ipv6_address({{1}, {}}));
CAF_CHECK_EQUAL(read<ipv6_address>("::2"), ipv6_address({{}, {2}}));
CAF_CHECK_EQUAL(read<ipv6_address>("1::2"), ipv6_address({{1}, {2}}));
}
CAF_TEST(IPv6 subnet) {
CAF_CHECK_EQUAL(read<ipv6_subnet>("1.2.3.0/24"),
ipv6_subnet(ipv4_address({1, 2, 3, 0}), 24));
CAF_CHECK_EQUAL(read<ipv6_subnet>("1::/128"),
ipv6_subnet(ipv6_address({1}, {}), 128));
CAF_CHECK_EQUAL(read<ipv6_subnet>("1::/129"), pec::integer_overflow);
}
CAF_TEST(IPv6 endpoint) {
CAF_CHECK_EQUAL(read<ipv6_endpoint>("127.0.0.1:0"),
ipv6_endpoint(ipv4_address({127, 0, 0, 1}), 0));
CAF_CHECK_EQUAL(read<ipv6_endpoint>("127.0.0.1:65535"),
ipv6_endpoint(ipv4_address({127, 0, 0, 1}), 65535));
CAF_CHECK_EQUAL(read<ipv6_endpoint>("127.0.0.1:65536"),
pec::integer_overflow);
CAF_CHECK_EQUAL(read<ipv6_endpoint>("[1::2]:8080"),
ipv6_endpoint({{1}, {2}}, 8080));
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment