/* * Copyright (C) 2015-present ScyllaDB */ /* * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0 */ #pragma once #include #include #include #include #include #include #include "seastarx.hh" // // This hashing differs from std::hash<> in that it decouples knowledge about // type structure from the way the hash value is calculated: // * appending_hash instantiation knows about what data should be included in the hash for type T. // * Hasher object knows how to combine the data into the final hash. // // The appending_hash should always feed some data into the hasher, regardless of the state the object is in, // in order for the hash to be highly sensitive for value changes. For example, vector> should // ideally feed different values for empty vector and a vector with a single empty optional. // // appending_hash is machine-independent. // template concept Hasher = requires(H& h, const char* ptr, size_t size) { { h.update(ptr, size) } noexcept -> std::same_as; }; template concept HasherReturning = Hasher && requires (H& h) { { h.finalize() } -> std::convertible_to; }; class hasher { public: virtual ~hasher() = default; virtual void update(const char* ptr, size_t size) noexcept = 0; }; template struct appending_hash; template requires Hasher inline void feed_hash(H& h, const T& value, Args&&... args) noexcept(noexcept(std::declval>()(h, value, args...))) { appending_hash()(h, value, std::forward(args)...); }; template requires std::is_arithmetic_v struct appending_hash { template requires Hasher void operator()(H& h, T value) const noexcept { auto value_le = cpu_to_le(value); h.update(reinterpret_cast(&value_le), sizeof(T)); } }; template<> struct appending_hash { template requires Hasher void operator()(H& h, bool value) const noexcept { feed_hash(h, static_cast(value)); } }; template<> struct appending_hash { template requires Hasher void operator()(H& h, double d) const noexcept { // Mimics serializer for CQL double type, for inter-machine stability. if (std::isnan(d)) { d = std::numeric_limits::quiet_NaN(); } static_assert(sizeof(double) == sizeof(uint64_t)); uint64_t i; memcpy(&i, &d, sizeof(i)); feed_hash(h, cpu_to_le(i)); } }; template requires std::is_enum_v struct appending_hash { template requires Hasher void operator()(H& h, const T& value) const noexcept { feed_hash(h, static_cast>(value)); } }; template struct appending_hash> { template requires Hasher void operator()(H& h, const std::optional& value) const noexcept { if (value) { feed_hash(h, true); feed_hash(h, *value); } else { feed_hash(h, false); } } }; template struct appending_hash> { template requires Hasher void operator()(H& h, const std::unique_ptr& value) const noexcept { if (value) { feed_hash(h, true); feed_hash(h, *value); } else { feed_hash(h, false); } } }; template struct appending_hash { template requires Hasher void operator()(H& h, const char (&value) [N]) const noexcept { feed_hash(h, N); h.update(value, N); } }; template struct appending_hash> { template requires Hasher void operator()(H& h, const std::vector& value) const noexcept { feed_hash(h, value.size()); for (auto&& v : value) { appending_hash()(h, v); } } }; template struct appending_hash> { template requires Hasher void operator()(H& h, const std::map& value) const noexcept { feed_hash(h, value.size()); for (auto&& e : value) { appending_hash()(h, e.first); appending_hash()(h, e.second); } } }; template struct appending_hash> { template requires Hasher void operator()(H& h, const std::unordered_map& value) const noexcept { std::map sorted(value.begin(), value.end()); feed_hash(h, sorted); } }; template<> struct appending_hash { template requires Hasher void operator()(H& h, const sstring& v) const noexcept { feed_hash(h, v.size()); h.update(reinterpret_cast(v.cbegin()), v.size() * sizeof(sstring::value_type)); } }; template<> struct appending_hash { template requires Hasher void operator()(H& h, const std::string& v) const noexcept { feed_hash(h, v.size()); h.update(reinterpret_cast(v.data()), v.size() * sizeof(std::string::value_type)); } }; template struct appending_hash> { template requires Hasher void operator()(H& h, std::chrono::duration v) const noexcept { feed_hash(h, v.count()); } }; template struct appending_hash> { template requires Hasher void operator()(H& h, std::chrono::time_point v) const noexcept { feed_hash(h, v.time_since_epoch().count()); } };