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
src/detail/behavior_impl.cpp
src/detail/behavior_stack.cpp
src/detail/blocking_behavior.cpp
src/detail/fnv_hash.cpp
src/detail/get_mac_addresses.cpp
src/detail/get_process_id.cpp
src/detail/get_root_uuid.cpp
......@@ -215,6 +214,7 @@ set(CAF_CORE_TEST_SOURCES
test/function_view.cpp
test/fused_downstream_manager.cpp
test/handles.cpp
test/hash/fnv.cpp
test/inspector.cpp
test/intrusive/drr_cached_queue.cpp
test/intrusive/drr_queue.cpp
......
......@@ -69,6 +69,7 @@
#include "caf/function_view.hpp"
#include "caf/fused_downstream_manager.hpp"
#include "caf/group.hpp"
#include "caf/hash/fnv.hpp"
#include "caf/index_mapping.hpp"
#include "caf/init_global_meta_objects.hpp"
#include "caf/local_actor.hpp"
......
......@@ -203,6 +203,15 @@ using type_id_t = uint16_t;
/// @relates 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 -----------------------------------------------------
namespace intrusive {
......
......@@ -5,7 +5,7 @@
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* Copyright 2011-2020 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 *
......@@ -21,52 +21,102 @@
#include <cstdint>
#include <type_traits>
#include "caf/detail/core_export.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/ieee_754.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,
/// and Kiem-Phong Vo.
/// Non-cryptographic hash algorithm (variant 1a) named after Glenn Fowler,
/// 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
/// - 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,
const unsigned char* first,
const unsigned char* last);
using result_type = void;
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);
}
constexpr fnv() noexcept : value(init()) {
// nop
}
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 Integral>
std::enable_if_t<std::is_integral<Integral>::value>
apply(Integral x) noexcept {
auto begin = reinterpret_cast<const uint8_t*>(&x);
append(begin, begin + sizeof(Integral));
}
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));
}
void apply(bool x) noexcept {
auto tmp = static_cast<uint8_t>(x);
apply(tmp);
}
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));
}
void apply(float x) noexcept {
apply(detail::pack754(x));
}
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 @@
#pragma once
#include <type_traits>
#include <utility>
#include "caf/meta/annotation.hpp"
......
......@@ -51,10 +51,16 @@ public:
template <class... Ts>
[[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;
static_cast<void>((try_apply(result, xs) && ...));
return result;
}
}
private:
template <class Tuple, size_t... Is>
......
......@@ -18,11 +18,12 @@
#include "caf/detail/type_id_list_builder.hpp"
#include <cstdint>
#include <mutex>
#include <unordered_set>
#include "caf/config.hpp"
#include "caf/detail/fnv_hash.hpp"
#include "caf/hash/fnv.hpp"
#include "caf/type_id_list.hpp"
namespace caf::detail {
......@@ -30,9 +31,9 @@ namespace {
struct dyn_type_id_list {
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));
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)
......
......@@ -18,7 +18,7 @@
#include "caf/ipv4_endpoint.hpp"
#include "caf/detail/fnv_hash.hpp"
#include "caf/hash/fnv.hpp"
namespace caf {
......@@ -28,8 +28,7 @@ ipv4_endpoint::ipv4_endpoint(ipv4_address address, uint16_t port)
}
size_t ipv4_endpoint::hash_code() const noexcept {
auto result = detail::fnv_hash(address_.data());
return detail::fnv_hash_append(result, port_);
return hash::fnv<size_t>::compute(address_, port_);
}
long ipv4_endpoint::compare(ipv4_endpoint x) const noexcept {
......
......@@ -18,7 +18,7 @@
#include "caf/ipv6_endpoint.hpp"
#include "caf/detail/fnv_hash.hpp"
#include "caf/hash/fnv.hpp"
#include "caf/ipv4_address.hpp"
#include "caf/ipv4_endpoint.hpp"
......@@ -35,8 +35,7 @@ ipv6_endpoint::ipv6_endpoint(ipv4_address address, uint16_t port)
}
size_t ipv6_endpoint::hash_code() const noexcept {
auto result = detail::fnv_hash(address_.data());
return detail::fnv_hash_append(result, port_);
return hash::fnv<size_t>::compute(address_, port_);
}
long ipv6_endpoint::compare(ipv6_endpoint x) const noexcept {
......
......@@ -22,13 +22,13 @@
#include "caf/binary_serializer.hpp"
#include "caf/deserializer.hpp"
#include "caf/detail/append_percent_encoded.hpp"
#include "caf/detail/fnv_hash.hpp"
#include "caf/detail/overload.hpp"
#include "caf/detail/parse.hpp"
#include "caf/detail/parser/read_uri.hpp"
#include "caf/detail/uri_impl.hpp"
#include "caf/error.hpp"
#include "caf/expected.hpp"
#include "caf/hash/fnv.hpp"
#include "caf/make_counted.hpp"
#include "caf/optional.hpp"
#include "caf/serializer.hpp"
......@@ -72,7 +72,7 @@ string_view uri::fragment() 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 {
......
......@@ -5,7 +5,7 @@
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* Copyright 2011-2020 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 *
......@@ -16,46 +16,49 @@
* 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;
#elif SIZE_MAX == 0xFFFFFFFFFFFFFFFF
constexpr size_t basis = 14695981039346656037u;
constexpr size_t prime = 1099511628211u;
#else
# error Platform and/or compiler not supported
#endif
template <class... Ts>
auto fnv32_hash(Ts&&... xs) {
return hash::fnv<uint32_t>::compute(std::forward<Ts>(xs)...);
}
} // 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) {
return fnv_hash_append(basis, first, last);
CAF_TEST(FNV hashes build incrementally) {
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,
const unsigned char* last) {
auto result = intermediate;
for (; first != last; ++first) {
result *= prime;
result ^= *first;
}
return result;
CAF_TEST(FNV supports uint32 hashing) {
CAF_CHECK_EQUAL(fnv32_hash(), 0x811C9DC5u);
CAF_CHECK_EQUAL(fnv32_hash("abcd"s), 0xCE3479BDu);
CAF_CHECK_EQUAL(fnv32_hash("C++ Actor Framework"s), 0x2FF91FE5u);
}
} // 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 @@
#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/logger.hpp"
#include "caf/sec.hpp"
......@@ -44,9 +44,6 @@
using sa_family_t = short;
#endif
using caf::detail::fnv_hash;
using caf::detail::fnv_hash_append;
namespace caf::io::network {
struct ip_endpoint::impl {
......@@ -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 {
auto result = fnv_hash(sa->sin_addr.s_addr);
result = fnv_hash_append(result, sa->sin_port);
return result;
return hash::fnv<size_t>::compute(sa->sin_addr.s_addr, sa->sin_port);
}
size_t ep_hash::hash(const sockaddr_in6* sa) const noexcept {
auto& addr = sa->sin6_addr.s6_addr;
auto result = fnv_hash(addr, addr + 16);
result = fnv_hash_append(result, sa->sin6_port);
return result;
return hash::fnv<size_t>::compute((make_span(sa->sin6_addr.s6_addr)),
sa->sin6_port);
}
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