Unverified Commit f3203e75 authored by Joseph Noir's avatar Joseph Noir Committed by GitHub

Merge pull request #1082

Make FNV hash available as inspector
parents 4d53aa0d 75b33faf
...@@ -59,7 +59,6 @@ set(CAF_CORE_SOURCES ...@@ -59,7 +59,6 @@ set(CAF_CORE_SOURCES
src/detail/behavior_impl.cpp src/detail/behavior_impl.cpp
src/detail/behavior_stack.cpp src/detail/behavior_stack.cpp
src/detail/blocking_behavior.cpp src/detail/blocking_behavior.cpp
src/detail/fnv_hash.cpp
src/detail/get_mac_addresses.cpp src/detail/get_mac_addresses.cpp
src/detail/get_process_id.cpp src/detail/get_process_id.cpp
src/detail/get_root_uuid.cpp src/detail/get_root_uuid.cpp
...@@ -215,6 +214,7 @@ set(CAF_CORE_TEST_SOURCES ...@@ -215,6 +214,7 @@ set(CAF_CORE_TEST_SOURCES
test/function_view.cpp test/function_view.cpp
test/fused_downstream_manager.cpp test/fused_downstream_manager.cpp
test/handles.cpp test/handles.cpp
test/hash/fnv.cpp
test/inspector.cpp test/inspector.cpp
test/intrusive/drr_cached_queue.cpp test/intrusive/drr_cached_queue.cpp
test/intrusive/drr_queue.cpp test/intrusive/drr_queue.cpp
......
...@@ -69,6 +69,7 @@ ...@@ -69,6 +69,7 @@
#include "caf/function_view.hpp" #include "caf/function_view.hpp"
#include "caf/fused_downstream_manager.hpp" #include "caf/fused_downstream_manager.hpp"
#include "caf/group.hpp" #include "caf/group.hpp"
#include "caf/hash/fnv.hpp"
#include "caf/index_mapping.hpp" #include "caf/index_mapping.hpp"
#include "caf/init_global_meta_objects.hpp" #include "caf/init_global_meta_objects.hpp"
#include "caf/local_actor.hpp" #include "caf/local_actor.hpp"
......
...@@ -203,6 +203,15 @@ using type_id_t = uint16_t; ...@@ -203,6 +203,15 @@ using type_id_t = uint16_t;
/// @relates actor_system_config /// @relates actor_system_config
CAF_CORE_EXPORT const settings& content(const actor_system_config&); CAF_CORE_EXPORT const settings& content(const actor_system_config&);
// -- hash inspectors ----------------------------------------------------------
namespace hash {
template <class>
class fnv;
} // namespace hash
// -- intrusive containers ----------------------------------------------------- // -- intrusive containers -----------------------------------------------------
namespace intrusive { namespace intrusive {
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* | |___ / ___ \| _| Framework * * | |___ / ___ \| _| Framework *
* \____/_/ \_|_| * * \____/_/ \_|_| *
* * * *
* Copyright 2011-2019 Dominik Charousset * * Copyright 2011-2020 Dominik Charousset *
* * * *
* Distributed under the terms and conditions of the BSD 3-Clause License or * * 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 * * (at your option) under the terms and conditions of the Boost Software *
...@@ -21,52 +21,102 @@ ...@@ -21,52 +21,102 @@
#include <cstdint> #include <cstdint>
#include <type_traits> #include <type_traits>
#include "caf/detail/core_export.hpp" #include "caf/detail/ieee_754.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/read_inspector.hpp"
#include "caf/span.hpp"
#include "caf/string_view.hpp"
namespace caf::detail { namespace caf::hash {
/// Non-cryptographic hash function named after Glenn Fowler, Landon Curt Noll, /// Non-cryptographic hash algorithm (variant 1a) named after Glenn Fowler,
/// and Kiem-Phong Vo. /// Landon Curt Noll, and Kiem-Phong Vo.
/// ///
/// See: /// For more details regarding the public domain algorithm, see:
/// - https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function /// - https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
/// - http://www.isthe.com/chongo/tech/comp/fnv/index.html /// - http://www.isthe.com/chongo/tech/comp/fnv/index.html
CAF_CORE_EXPORT size_t fnv_hash(const unsigned char* first, ///
const unsigned char* last); /// @tparam T One of `uint32_t`, `uint64_t`, or `size_t`.
template <class T>
class fnv : public read_inspector<fnv<T>> {
public:
static_assert(sizeof(T) == 4 || sizeof(T) == 8);
CAF_CORE_EXPORT size_t fnv_hash_append(size_t intermediate, using result_type = void;
const unsigned char* first,
const unsigned char* last);
template <class T> constexpr fnv() noexcept : value(init()) {
enable_if_t<has_data_member<T>::value, size_t> fnv_hash(const T& x) { // nop
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> template <class Integral>
enable_if_t<has_data_member<T>::value, size_t> std::enable_if_t<std::is_integral<Integral>::value>
fnv_hash_append(size_t intermediate, const T& x) { apply(Integral x) noexcept {
auto ptr = x.data(); auto begin = reinterpret_cast<const uint8_t*>(&x);
auto first = reinterpret_cast<const uint8_t*>(ptr); append(begin, begin + sizeof(Integral));
auto last = first + (sizeof(decay_t<decltype(*ptr)>) * x.size()); }
return fnv_hash_append(intermediate, first, last);
}
template <class T> void apply(bool x) noexcept {
enable_if_t<std::is_integral<T>::value, size_t> fnv_hash(const T& x) { auto tmp = static_cast<uint8_t>(x);
auto first = reinterpret_cast<const uint8_t*>(&x); apply(tmp);
return fnv_hash(first, first + sizeof(T)); }
}
template <class T> void apply(float x) noexcept {
enable_if_t<std::is_integral<T>::value, size_t> apply(detail::pack754(x));
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)); void apply(double x) noexcept {
} apply(detail::pack754(x));
}
void apply(string_view x) noexcept {
auto begin = reinterpret_cast<const uint8_t*>(x.data());
append(begin, begin + x.size());
}
void apply(span<const byte> x) noexcept {
auto begin = reinterpret_cast<const uint8_t*>(x.data());
append(begin, begin + x.size());
}
template <class Enum>
std::enable_if_t<std::is_enum<Enum>::value> apply(Enum x) noexcept {
return apply(static_cast<std::underlying_type_t<Enum>>(x));
}
void begin_sequence(size_t) noexcept {
// nop
}
void end_sequence() noexcept {
// nop
}
/// Convenience function for computing an FNV1a hash value for given
/// arguments in one shot.
template <class... Ts>
static T compute(Ts&&... xs) noexcept {
fnv f;
f(std::forward<Ts>(xs)...);
return f.value;
}
T value;
private:
static constexpr T init() noexcept {
if constexpr (sizeof(T) == 4)
return 0x811C9DC5u;
else
return 0xCBF29CE484222325ull;
}
void append(const uint8_t* begin, const uint8_t* end) noexcept {
if constexpr (sizeof(T) == 4)
while (begin != end)
value = (*begin++ ^ value) * 0x01000193u;
else
while (begin != end)
value = (*begin++ ^ value) * 1099511628211ull;
}
};
} // namespace caf::detail } // namespace caf::hash
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#pragma once #pragma once
#include <type_traits> #include <type_traits>
#include <utility>
#include "caf/meta/annotation.hpp" #include "caf/meta/annotation.hpp"
......
...@@ -51,10 +51,16 @@ public: ...@@ -51,10 +51,16 @@ public:
template <class... Ts> template <class... Ts>
[[nodiscard]] auto operator()(Ts&&... xs) { [[nodiscard]] auto operator()(Ts&&... xs) {
using result_type = typename Subtype::result_type;
if constexpr (std::is_same<result_type, void>::value) {
auto dummy = unit;
static_cast<void>((try_apply(dummy, xs) && ...));
} else {
typename Subtype::result_type result; typename Subtype::result_type result;
static_cast<void>((try_apply(result, xs) && ...)); static_cast<void>((try_apply(result, xs) && ...));
return result; return result;
} }
}
private: private:
template <class Tuple, size_t... Is> template <class Tuple, size_t... Is>
......
...@@ -18,11 +18,12 @@ ...@@ -18,11 +18,12 @@
#include "caf/detail/type_id_list_builder.hpp" #include "caf/detail/type_id_list_builder.hpp"
#include <cstdint>
#include <mutex> #include <mutex>
#include <unordered_set> #include <unordered_set>
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/detail/fnv_hash.hpp" #include "caf/hash/fnv.hpp"
#include "caf/type_id_list.hpp" #include "caf/type_id_list.hpp"
namespace caf::detail { namespace caf::detail {
...@@ -30,9 +31,9 @@ namespace { ...@@ -30,9 +31,9 @@ namespace {
struct dyn_type_id_list { struct dyn_type_id_list {
dyn_type_id_list(type_id_t* storage) : storage(storage) { dyn_type_id_list(type_id_t* storage) : storage(storage) {
auto first = reinterpret_cast<unsigned char*>(storage); auto first = reinterpret_cast<const uint8_t*>(storage);
auto last = first + ((storage[0] + 1) * sizeof(type_id_t)); auto last = first + ((storage[0] + 1) * sizeof(type_id_t));
hash = fnv_hash(first, last); hash = caf::hash::fnv<size_t>::compute(make_span(first, last));
} }
dyn_type_id_list(dyn_type_id_list&& other) dyn_type_id_list(dyn_type_id_list&& other)
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#include "caf/ipv4_endpoint.hpp" #include "caf/ipv4_endpoint.hpp"
#include "caf/detail/fnv_hash.hpp" #include "caf/hash/fnv.hpp"
namespace caf { namespace caf {
...@@ -28,8 +28,7 @@ ipv4_endpoint::ipv4_endpoint(ipv4_address address, uint16_t port) ...@@ -28,8 +28,7 @@ ipv4_endpoint::ipv4_endpoint(ipv4_address address, uint16_t port)
} }
size_t ipv4_endpoint::hash_code() const noexcept { size_t ipv4_endpoint::hash_code() const noexcept {
auto result = detail::fnv_hash(address_.data()); return hash::fnv<size_t>::compute(address_, port_);
return detail::fnv_hash_append(result, port_);
} }
long ipv4_endpoint::compare(ipv4_endpoint x) const noexcept { long ipv4_endpoint::compare(ipv4_endpoint x) const noexcept {
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#include "caf/ipv6_endpoint.hpp" #include "caf/ipv6_endpoint.hpp"
#include "caf/detail/fnv_hash.hpp" #include "caf/hash/fnv.hpp"
#include "caf/ipv4_address.hpp" #include "caf/ipv4_address.hpp"
#include "caf/ipv4_endpoint.hpp" #include "caf/ipv4_endpoint.hpp"
...@@ -35,8 +35,7 @@ ipv6_endpoint::ipv6_endpoint(ipv4_address address, uint16_t port) ...@@ -35,8 +35,7 @@ ipv6_endpoint::ipv6_endpoint(ipv4_address address, uint16_t port)
} }
size_t ipv6_endpoint::hash_code() const noexcept { size_t ipv6_endpoint::hash_code() const noexcept {
auto result = detail::fnv_hash(address_.data()); return hash::fnv<size_t>::compute(address_, port_);
return detail::fnv_hash_append(result, port_);
} }
long ipv6_endpoint::compare(ipv6_endpoint x) const noexcept { long ipv6_endpoint::compare(ipv6_endpoint x) const noexcept {
......
...@@ -22,13 +22,13 @@ ...@@ -22,13 +22,13 @@
#include "caf/binary_serializer.hpp" #include "caf/binary_serializer.hpp"
#include "caf/deserializer.hpp" #include "caf/deserializer.hpp"
#include "caf/detail/append_percent_encoded.hpp" #include "caf/detail/append_percent_encoded.hpp"
#include "caf/detail/fnv_hash.hpp"
#include "caf/detail/overload.hpp" #include "caf/detail/overload.hpp"
#include "caf/detail/parse.hpp" #include "caf/detail/parse.hpp"
#include "caf/detail/parser/read_uri.hpp" #include "caf/detail/parser/read_uri.hpp"
#include "caf/detail/uri_impl.hpp" #include "caf/detail/uri_impl.hpp"
#include "caf/error.hpp" #include "caf/error.hpp"
#include "caf/expected.hpp" #include "caf/expected.hpp"
#include "caf/hash/fnv.hpp"
#include "caf/make_counted.hpp" #include "caf/make_counted.hpp"
#include "caf/optional.hpp" #include "caf/optional.hpp"
#include "caf/serializer.hpp" #include "caf/serializer.hpp"
...@@ -72,7 +72,7 @@ string_view uri::fragment() const noexcept { ...@@ -72,7 +72,7 @@ string_view uri::fragment() const noexcept {
} }
size_t uri::hash_code() const noexcept { size_t uri::hash_code() const noexcept {
return detail::fnv_hash(str()); return hash::fnv<size_t>::compute(str());
} }
optional<uri> uri::authority_only() const { optional<uri> uri::authority_only() const {
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* | |___ / ___ \| _| Framework * * | |___ / ___ \| _| Framework *
* \____/_/ \_|_| * * \____/_/ \_|_| *
* * * *
* Copyright 2011-2019 Dominik Charousset * * Copyright 2011-2020 Dominik Charousset *
* * * *
* Distributed under the terms and conditions of the BSD 3-Clause License or * * 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 * * (at your option) under the terms and conditions of the Boost Software *
...@@ -16,46 +16,49 @@ ...@@ -16,46 +16,49 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#include "caf/detail/fnv_hash.hpp" #define CAF_SUITE hash.fnv
#include <cstdint> #include "caf/hash/fnv.hpp"
namespace caf::detail { #include "caf/test/dsl.hpp"
namespace { #include <string>
#if SIZE_MAX == 0xFFFFFFFF using namespace caf;
constexpr size_t basis = 2166136261u; using namespace std::string_literals;
constexpr size_t prime = 16777619u; template <class... Ts>
auto fnv32_hash(Ts&&... xs) {
#elif SIZE_MAX == 0xFFFFFFFFFFFFFFFF return hash::fnv<uint32_t>::compute(std::forward<Ts>(xs)...);
}
constexpr size_t basis = 14695981039346656037u;
constexpr size_t prime = 1099511628211u;
#else
# error Platform and/or compiler not supported
#endif
} // namespace template <class... Ts>
auto fnv64_hash(Ts&&... xs) {
return hash::fnv<uint64_t>::compute(std::forward<Ts>(xs)...);
}
size_t fnv_hash(const unsigned char* first, const unsigned char* last) { CAF_TEST(FNV hashes build incrementally) {
return fnv_hash_append(basis, first, last); hash::fnv<uint32_t> f;
CAF_CHECK_EQUAL(f.value, 0x811C9DC5u);
f('a');
CAF_CHECK_EQUAL(f.value, 0xE40C292Cu);
f('b');
CAF_CHECK_EQUAL(f.value, 0x4D2505CAu);
f('c');
CAF_CHECK_EQUAL(f.value, 0x1A47E90Bu);
f('d');
CAF_CHECK_EQUAL(f.value, 0xCE3479BDu);
} }
size_t fnv_hash_append(size_t intermediate, const unsigned char* first, CAF_TEST(FNV supports uint32 hashing) {
const unsigned char* last) { CAF_CHECK_EQUAL(fnv32_hash(), 0x811C9DC5u);
auto result = intermediate; CAF_CHECK_EQUAL(fnv32_hash("abcd"s), 0xCE3479BDu);
for (; first != last; ++first) { CAF_CHECK_EQUAL(fnv32_hash("C++ Actor Framework"s), 0x2FF91FE5u);
result *= prime;
result ^= *first;
}
return result;
} }
} // namespace caf::detail CAF_TEST(FNV supports uint64 hashing) {
CAF_CHECK_EQUAL(fnv64_hash(), 0xCBF29CE484222325ull);
CAF_CHECK_EQUAL(fnv64_hash("abcd"s), 0xFC179F83EE0724DDull);
CAF_CHECK_EQUAL(fnv64_hash("C++ Actor Framework"s), 0xA229A760C3AF69C5ull);
}
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#include "caf/io/network/ip_endpoint.hpp" #include "caf/io/network/ip_endpoint.hpp"
#include "caf/detail/fnv_hash.hpp" #include "caf/hash/fnv.hpp"
#include "caf/io/network/native_socket.hpp" #include "caf/io/network/native_socket.hpp"
#include "caf/logger.hpp" #include "caf/logger.hpp"
#include "caf/sec.hpp" #include "caf/sec.hpp"
...@@ -44,9 +44,6 @@ ...@@ -44,9 +44,6 @@
using sa_family_t = short; using sa_family_t = short;
#endif #endif
using caf::detail::fnv_hash;
using caf::detail::fnv_hash_append;
namespace caf::io::network { namespace caf::io::network {
struct ip_endpoint::impl { struct ip_endpoint::impl {
...@@ -113,16 +110,12 @@ size_t ep_hash::operator()(const sockaddr& sa) const noexcept { ...@@ -113,16 +110,12 @@ size_t ep_hash::operator()(const sockaddr& sa) const noexcept {
} }
size_t ep_hash::hash(const sockaddr_in* sa) const noexcept { size_t ep_hash::hash(const sockaddr_in* sa) const noexcept {
auto result = fnv_hash(sa->sin_addr.s_addr); return hash::fnv<size_t>::compute(sa->sin_addr.s_addr, sa->sin_port);
result = fnv_hash_append(result, sa->sin_port);
return result;
} }
size_t ep_hash::hash(const sockaddr_in6* sa) const noexcept { size_t ep_hash::hash(const sockaddr_in6* sa) const noexcept {
auto& addr = sa->sin6_addr.s6_addr; return hash::fnv<size_t>::compute((make_span(sa->sin6_addr.s6_addr)),
auto result = fnv_hash(addr, addr + 16); sa->sin6_port);
result = fnv_hash_append(result, sa->sin6_port);
return result;
} }
bool operator==(const ip_endpoint& lhs, const ip_endpoint& rhs) { 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