Commit 007a64e7 authored by Dominik Charousset's avatar Dominik Charousset

Implement an URI-based node ID

parent 52409b7e
......@@ -54,6 +54,7 @@ set(LIBCAF_CORE_SRCS
src/event_based_actor.cpp
src/execution_unit.cpp
src/exit_reason.cpp
src/fnv_hash.cpp
src/forwarding_actor_proxy.cpp
src/get_mac_addresses.cpp
src/get_process_id.cpp
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstdint>
#include <type_traits>
#include "caf/detail/type_traits.hpp"
namespace caf {
namespace detail {
/// Non-cryptographic hash function named after Glenn Fowler, Landon Curt Noll,
/// and Kiem-Phong Vo.
///
/// See:
/// - https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
/// - http://www.isthe.com/chongo/tech/comp/fnv/index.html
size_t fnv_hash(const unsigned char* first, const unsigned char* last);
size_t fnv_hash_append(size_t intermediate, const unsigned char* first,
const unsigned char* last);
template <class T>
enable_if_t<has_data_member<T>::value, size_t> fnv_hash(const T& x) {
auto ptr = x.data();
auto first = reinterpret_cast<const uint8_t*>(ptr);
auto last = first + (sizeof(decay_t<decltype(*ptr)>) * x.size());
return fnv_hash(first, last);
}
template <class T>
enable_if_t<has_data_member<T>::value, size_t>
fnv_hash_append(size_t intermediate, const T& x) {
auto ptr = x.data();
auto first = reinterpret_cast<const uint8_t*>(ptr);
auto last = first + (sizeof(decay_t<decltype(*ptr)>) * x.size());
return fnv_hash_append(intermediate, first, last);
}
template <class T>
enable_if_t<std::is_integral<T>::value, size_t> fnv_hash(const T& x) {
auto first = reinterpret_cast<const uint8_t*>(&x);
return fnv_hash(first, first + sizeof(T));
}
template <class T>
enable_if_t<std::is_integral<T>::value, size_t> fnv_hash_append(size_t interim,
const T& x) {
auto first = reinterpret_cast<const uint8_t*>(&x);
return fnv_hash_append(interim, first, first + sizeof(T));
}
} // namespace detail
} // namespace caf
......@@ -29,6 +29,7 @@
#include "caf/intrusive_ptr.hpp"
#include "caf/none.hpp"
#include "caf/ref_counted.hpp"
#include "caf/uri.hpp"
namespace caf {
......@@ -125,6 +126,47 @@ public:
host_id_type host_;
};
// A technology-agnostic node identifier using an URI.
class uri_data final : public data {
public:
// -- constants ------------------------------------------------------------
/// Identifies this data implementation type.
static constexpr atom_value class_id = atom("uri");
// -- constructors, destructors, and assignment operators ------------------
explicit uri_data(uri value);
// -- properties -----------------------------------------------------------
const uri& value() const noexcept {
return value_;
}
// -- interface implementation ---------------------------------------------
bool valid() const noexcept override;
size_t hash_code() const noexcept override;
atom_value implementation_id() const noexcept override;
int compare(const data& other) const noexcept override;
void print(std::string& dst) const override;
error serialize(serializer& sink) const override;
error deserialize(deserializer& source) override;
private:
// -- member variables -----------------------------------------------------
uri value_;
};
// -- constructors, destructors, and assignment operators --------------------
constexpr node_id() noexcept {
......@@ -207,6 +249,10 @@ void append_to_string(std::string& str, const node_id& x);
/// @relates node_id
std::string to_string(const node_id& x);
/// Creates a node ID from the URI `from`.
/// @relates node_id
node_id make_node_id(uri from);
/// Creates a node ID from `process_id` and `host_id`.
/// @param process_id System-wide unique process identifier.
/// @param host_id Unique hash value representing a single CAF node.
......
......@@ -105,6 +105,9 @@ public:
/// Returns the fragment component.
string_view fragment() const noexcept;
/// Returns a hash code over all components.
size_t hash_code() const noexcept;
// -- comparison -------------------------------------------------------------
int compare(const uri& other) const noexcept;
......@@ -135,3 +138,14 @@ std::string to_string(const uri& x);
error parse(string_view str, uri& dest);
} // namespace caf
namespace std {
template <>
struct hash<caf::uri> {
size_t operator()(const caf::uri& x) const noexcept {
return x.hash_code();
}
};
} // namespace std
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/detail/fnv_hash.hpp"
namespace caf {
namespace detail {
namespace {
template <size_t IntegerSize>
struct hash_conf_helper;
template <>
struct hash_conf_helper<4> {
static constexpr size_t basis = 2166136261u;
static constexpr size_t prime = 16777619u;
};
template <>
struct hash_conf_helper<8> {
static constexpr size_t basis = 14695981039346656037u;
static constexpr size_t prime = 1099511628211u;
};
struct hash_conf : hash_conf_helper<sizeof(size_t)> {};
} // namespace
size_t fnv_hash(const unsigned char* first, const unsigned char* last) {
return fnv_hash_append(hash_conf::basis, first, last);
}
size_t fnv_hash_append(size_t intermediate, const unsigned char* first,
const unsigned char* last) {
auto result = intermediate;
for (; first != last; ++first) {
result *= hash_conf::prime;
result ^= *first;
}
return result;
}
} // namespace detail
} // namespace caf
......@@ -123,6 +123,48 @@ error node_id::default_data::deserialize(deserializer& source) {
return source(pid_, host_);
}
node_id::uri_data::uri_data(uri value) : value_(std::move(value)) {
// nop
}
bool node_id::uri_data::valid() const noexcept {
return !value_.empty();
}
size_t node_id::uri_data::hash_code() const noexcept {
std::hash<uri> f;
return f(value_);
}
atom_value node_id::uri_data::implementation_id() const noexcept {
return class_id;
}
int node_id::uri_data::compare(const data& other) const noexcept {
if (this == &other)
return 0;
auto other_id = other.implementation_id();
if (class_id != other_id)
return caf::compare(class_id, other_id);
return value_.compare(static_cast<const uri_data&>(other).value_);
}
void node_id::uri_data::print(std::string& dst) const {
if (!valid()) {
dst += "invalid-node";
return;
}
dst += to_string(value_);
}
error node_id::uri_data::serialize(serializer& sink) const {
return sink(value_);
}
error node_id::uri_data::deserialize(deserializer& source) {
return source(value_);
}
node_id& node_id::operator=(const none_t&) {
data_.reset();
return *this;
......@@ -203,6 +245,11 @@ std::string to_string(const node_id& x) {
return result;
}
node_id make_node_id(uri from) {
auto ptr = make_counted<node_id::uri_data>(std::move(from));
return node_id{std::move(ptr)};
}
node_id make_node_id(uint32_t process_id,
const node_id::default_data::host_id_type& host_id) {
auto ptr = make_counted<node_id::default_data>(process_id, host_id);
......
......@@ -19,6 +19,7 @@
#include "caf/uri.hpp"
#include "caf/deserializer.hpp"
#include "caf/detail/fnv_hash.hpp"
#include "caf/detail/parser/read_uri.hpp"
#include "caf/detail/uri_impl.hpp"
#include "caf/error.hpp"
......@@ -64,6 +65,10 @@ string_view uri::fragment() const noexcept {
return impl_->fragment;
}
size_t uri::hash_code() const noexcept {
return detail::fnv_hash(str());
}
// -- comparison ---------------------------------------------------------------
int uri::compare(const uri& other) const noexcept {
......
......@@ -18,10 +18,10 @@
#include "caf/io/network/ip_endpoint.hpp"
#include "caf/sec.hpp"
#include "caf/logger.hpp"
#include "caf/detail/fnv_hash.hpp"
#include "caf/io/network/native_socket.hpp"
#include "caf/logger.hpp"
#include "caf/sec.hpp"
#ifdef CAF_WINDOWS
# include <winsock2.h>
......@@ -41,41 +41,8 @@
using sa_family_t = short;
#endif
namespace {
template <class SizeType = size_t>
struct hash_conf {
template <class T = SizeType>
static constexpr caf::detail::enable_if_t<(sizeof(T) == 4), size_t> basis() {
return 2166136261u;
}
template <class T = SizeType>
static constexpr caf::detail::enable_if_t<(sizeof(T) == 4), size_t> prime() {
return 16777619u;
}
template <class T = SizeType>
static constexpr caf::detail::enable_if_t<(sizeof(T) == 8), size_t> basis() {
return 14695981039346656037u;
}
template <class T = SizeType>
static constexpr caf::detail::enable_if_t<(sizeof(T) == 8), size_t> prime() {
return 1099511628211u;
}
};
constexpr uint8_t static_bytes[] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF
};
constexpr size_t prehash(int i = 11) {
return (i > 0)
? (prehash(i - 1) * hash_conf<>::prime()) ^ static_bytes[i]
: (hash_conf<>::basis() * hash_conf<>::prime()) ^ static_bytes[i];
}
} // namespace <anonymous>
using caf::detail::fnv_hash;
using caf::detail::fnv_hash_append;
namespace caf {
namespace io {
......@@ -145,32 +112,16 @@ size_t ep_hash::operator()(const sockaddr& sa) const noexcept {
}
size_t ep_hash::hash(const sockaddr_in* sa) const noexcept {
auto& addr = sa->sin_addr;
size_t res = prehash();
// the first loop was replaces with `constexpr size_t prehash()`
for (int i = 0; i < 4; ++i) {
res = res * hash_conf<>::prime();
res = res ^ ((addr.s_addr >> i) & 0xFF);
}
res = res * hash_conf<>::prime();
res = res ^ (sa->sin_port >> 1);
res = res * hash_conf<>::prime();
res = res ^ (sa->sin_port & 0xFF);
return res;
auto result = fnv_hash(sa->sin_addr.s_addr);
result = fnv_hash_append(result, sa->sin_port);
return result;
}
size_t ep_hash::hash(const sockaddr_in6* sa) const noexcept {
auto& addr = sa->sin6_addr;
size_t res = hash_conf<>::basis();
for (int i = 0; i < 16; ++i) {
res = res * hash_conf<>::prime();
res = res ^ addr.s6_addr[i];
}
res = res * hash_conf<>::prime();
res = res ^ (sa->sin6_port >> 1);
res = res * hash_conf<>::prime();
res = res ^ (sa->sin6_port & 0xFF);
return res;
auto& addr = sa->sin6_addr.s6_addr;
auto result = fnv_hash(addr, addr + 16);
result = fnv_hash_append(result, sa->sin6_port);
return result;
}
bool operator==(const ip_endpoint& lhs, const ip_endpoint& rhs) {
......
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