/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include namespace folly { namespace detail { struct unique_hash_key_item { static inline constexpr size_t len_include_mask = ~(~size_t(0) >> 1); uint8_t const* buf{}; size_t len{}; // hi bit set means include len in hash std::pair unpack_len() const { constexpr auto mask = len_include_mask; return {len & ~mask, len & mask}; } }; } // namespace detail /// unique_hash_key_algo_strong_sha256_fn /// /// Hashes span using SHA256 with a key that /// may vary between processes. /// /// The size must be in {8, 16, 24, 32}. /// /// For use with unique_hash_key. template struct unique_hash_key_algo_strong_sha256_fn { static_assert(Size <= 32); static_assert(Size % 8 == 0); std::array operator()( span in) const noexcept; }; template inline constexpr unique_hash_key_algo_strong_sha256_fn unique_hash_key_algo_strong_sha256{}; #if __has_include() /// unique_hash_key_algo_strong_blake3_fn /// /// Hashes span using BLAKE3 with a key that /// may vary between processes. /// /// The size must be in {8, 16, 24, 32}. /// /// For use with unique_hash_key. template struct unique_hash_key_algo_strong_blake3_fn { static_assert(Size <= 32); static_assert(Size % 8 == 0); std::array operator()( span in) const noexcept; }; template inline constexpr unique_hash_key_algo_strong_blake3_fn unique_hash_key_algo_strong_blake3{}; #if __has_include() /// unique_hash_key_algo_fast_xxh3_fn /// /// Hashes span using XXH3 with a key that /// may vary between processes. /// /// The size must be in {8, 16}, corresponding to XXH3_64bits and XXH3_128bits. /// /// For use with unique_hash_key. template struct unique_hash_key_algo_fast_xxh3_fn { static_assert(Size <= 16); static_assert(Size % 8 == 0); std::array operator()( span in) const noexcept; }; template inline constexpr unique_hash_key_algo_fast_xxh3_fn unique_hash_key_algo_fast_xxh3{}; #endif // __has_include() #endif // __has_include() template constexpr size_t unique_hash_key_algo_size_v = decltype(Algo(span{})){}.size(); /// unique_hash_key /// /// A unique hashtable key cryptographically hashed from input data. /// /// The size must be in {8, 16, 24, 32}. /// /// Uniqueness is only guaranteed between two constructions when all of these /// conditions hold: /// * At sufficiently large sizes, likely at least size 16. /// * For cryptographically strong hash algorithms, such as BLAKE3. /// * With equivalent hash algorithm objects of the same type. /// * With packs of hashable arguments which have the same sequences of types. /// * With packs of hashable arguments that differ in at least one element. template class unique_hash_key { private: using self = unique_hash_key; static inline constexpr size_t data_size = Size; static inline constexpr size_t data_align = alignof(size_t); static_assert(data_size <= 32); static_assert(data_size % 8 == 0); using item_type = detail::unique_hash_key_item; using data_type = std::array; template static inline constexpr bool is_algo_v = std::is_invocable_v>; alignas(data_align) data_type const data_; template < typename Int, std::enable_if_t, int> = 0> static item_type init_item( Int const& arg, [[maybe_unused]] size_t idx, [[maybe_unused]] size_t size) { auto const buf = reinterpret_cast(&arg); return {buf, sizeof(arg)}; } static item_type init_item(std::string_view arg, size_t idx, size_t size) { constexpr auto mask = item_type::len_include_mask; auto const buf = reinterpret_cast(arg.data()); auto const hi = mask * size_t(idx + 1 < size); return {buf, arg.size() | hi}; } template static data_type init(Algo const algo, Arg const&... arg) noexcept { constexpr auto len = sizeof...(Arg); size_t idxr = 0; size_t idxl = 0; std::array items{}; ((items[idxl++] = init_item(arg, idxr++, len)), ...); return algo(items); } template > static constexpr bool is_span_compatible_v = // !std::is_volatile_v && // std::is_integral_v && // std::is_unsigned_v && // !std::is_same_v && // !std::is_same_v && // alignof(V) <= data_align && // (E == data_size / sizeof(T) || E == dynamic_extent); public: template < typename Algo, typename... Arg, std::enable_if_t, int> = 0> explicit unique_hash_key(Algo const algo, Arg const&... arg) noexcept : data_{init(algo, arg...)} { static_assert(sizeof(self) == Size); static_assert(alignof(self) == alignof(size_t)); } template < typename T, std::size_t E, std::enable_if_t, int> = 0> explicit operator span() const noexcept { constexpr auto count = E == dynamic_extent ? data_size / sizeof(T) : E; return span{reinterpret_cast(data_.data()), count}; } friend auto operator==(self const& a, self const& b) noexcept { return a.data_ == b.data_; } friend auto operator!=(self const& a, self const& b) noexcept { return a.data_ != b.data_; } }; /// unique_hash_key_with /// /// A unique-hash-key of the size returned by the given hash algorithm, using /// the given algorithm. /// /// Hash values are unique to the process. But see the caveats regarding /// uniqueness above, with tuples replacing packs. /// /// It is explicitly permitted to copy-construct unique_hash_key (base class) /// instances from instances of unique_hash_key_with (derived class). /// There is no concern about object slicing. template class unique_hash_key_with : public unique_hash_key> { private: static constexpr size_t size = unique_hash_key_algo_size_v; using self = unique_hash_key_with; using base = unique_hash_key; template explicit unique_hash_key_with( std::tuple const& arg, std::index_sequence) : base(Algo, std::get(arg)...) {} public: template explicit unique_hash_key_with(std::tuple const& arg) : self(arg, std::index_sequence_for{}) {} }; /// unique_hash_key_strong_sha256 /// /// A unique-hash-key of the given size using the cryptographically strong /// SHA256 hash algorithm from OpenSSL. /// /// Hash values are unique to the process. But see the caveats regarding /// uniqueness above, with tuples replacing packs. template using unique_hash_key_strong_sha256 = unique_hash_key_with>; #if __has_include() /// unique_hash_key_strong_blake3 /// /// A unique-hash-key of the given size using the cryptographically strong /// BLAKE3 hash algorithm. /// /// Hash values are unique to the process. But see the caveats regarding /// uniqueness above, with tuples replacing packs. template using unique_hash_key_strong_blake3 = unique_hash_key_with>; #if __has_include() /// unique_hash_key_strong_blake3 /// /// A unique-hash-key of the given size using the fast but not cryptographically /// strong XXH3 algorithm. /// /// Hash values are unique to the process. But see the caveats regarding /// uniqueness above, with tuples replacing packs. template using unique_hash_key_fast_xxh3 = unique_hash_key_with>; #endif // __has_include() #endif // __has_include() } // namespace folly namespace std { template struct hash<::folly::unique_hash_key> { using folly_is_avalanching = std::true_type; size_t operator()(::folly::unique_hash_key const& key) const noexcept { return ::folly::span{key}[0]; } }; template struct hash<::folly::unique_hash_key_with> : hash<::folly::unique_hash_key< ::folly::unique_hash_key_algo_size_v>> {}; } // namespace std