diff --git a/parallel_hashmap/phmap.h b/parallel_hashmap/phmap.h index 34bbfaa..4a7e946 100644 --- a/parallel_hashmap/phmap.h +++ b/parallel_hashmap/phmap.h @@ -46,6 +46,7 @@ #include #include #include +#include #include "phmap_bits.h" #include "phmap_base.h" diff --git a/parallel_hashmap/phmap_base.h b/parallel_hashmap/phmap_base.h index aaddc1b..5c7a419 100644 --- a/parallel_hashmap/phmap_base.h +++ b/parallel_hashmap/phmap_base.h @@ -496,7 +496,8 @@ private: }; template -inline void AssertHashEnabled() +inline void AssertHashEnabled +() { using Helper = AssertHashEnabledHelper; Helper::Sink(Helper::DoIt()...); @@ -507,5 +508,1872 @@ inline void AssertHashEnabled() } // namespace phmap +// ----------------------------------------------------------------------------- +// hash_policy_traits +// ----------------------------------------------------------------------------- +namespace phmap { +namespace container_internal { + +// Defines how slots are initialized/destroyed/moved. +template +struct hash_policy_traits +{ +private: + struct ReturnKey + { + // We return `Key` here. + // When Key=T&, we forward the lvalue reference. + // When Key=T, we return by value to avoid a dangling reference. + // eg, for string_hash_map. + template + Key operator()(Key&& k, const Args&...) const { + return std::forward(k); + } + }; + + template + struct ConstantIteratorsImpl : std::false_type {}; + + template + struct ConstantIteratorsImpl> + : P::constant_iterators {}; + +public: + // The actual object stored in the hash table. + using slot_type = typename Policy::slot_type; + + // The type of the keys stored in the hashtable. + using key_type = typename Policy::key_type; + + // The argument type for insertions into the hashtable. This is different + // from value_type for increased performance. See initializer_list constructor + // and insert() member functions for more details. + using init_type = typename Policy::init_type; + + using reference = decltype(Policy::element(std::declval())); + using pointer = typename std::remove_reference::type*; + using value_type = typename std::remove_reference::type; + + // Policies can set this variable to tell raw_hash_set that all iterators + // should be constant, even `iterator`. This is useful for set-like + // containers. + // Defaults to false if not provided by the policy. + using constant_iterators = ConstantIteratorsImpl<>; + + // PRECONDITION: `slot` is UNINITIALIZED + // POSTCONDITION: `slot` is INITIALIZED + template + static void construct(Alloc* alloc, slot_type* slot, Args&&... args) { + Policy::construct(alloc, slot, std::forward(args)...); + } + + // PRECONDITION: `slot` is INITIALIZED + // POSTCONDITION: `slot` is UNINITIALIZED + template + static void destroy(Alloc* alloc, slot_type* slot) { + Policy::destroy(alloc, slot); + } + + // Transfers the `old_slot` to `new_slot`. Any memory allocated by the + // allocator inside `old_slot` to `new_slot` can be transferred. + // + // OPTIONAL: defaults to: + // + // clone(new_slot, std::move(*old_slot)); + // destroy(old_slot); + // + // PRECONDITION: `new_slot` is UNINITIALIZED and `old_slot` is INITIALIZED + // POSTCONDITION: `new_slot` is INITIALIZED and `old_slot` is + // UNINITIALIZED + template + static void transfer(Alloc* alloc, slot_type* new_slot, slot_type* old_slot) { + transfer_impl(alloc, new_slot, old_slot, 0); + } + + // PRECONDITION: `slot` is INITIALIZED + // POSTCONDITION: `slot` is INITIALIZED + template + static auto element(slot_type* slot) -> decltype(P::element(slot)) { + return P::element(slot); + } + + // Returns the amount of memory owned by `slot`, exclusive of `sizeof(*slot)`. + // + // If `slot` is nullptr, returns the constant amount of memory owned by any + // full slot or -1 if slots own variable amounts of memory. + // + // PRECONDITION: `slot` is INITIALIZED or nullptr + template + static size_t space_used(const slot_type* slot) { + return P::space_used(slot); + } + + // Provides generalized access to the key for elements, both for elements in + // the table and for elements that have not yet been inserted (or even + // constructed). We would like an API that allows us to say: `key(args...)` + // but we cannot do that for all cases, so we use this more general API that + // can be used for many things, including the following: + // + // - Given an element in a table, get its key. + // - Given an element initializer, get its key. + // - Given `emplace()` arguments, get the element key. + // + // Implementations of this must adhere to a very strict technical + // specification around aliasing and consuming arguments: + // + // Let `value_type` be the result type of `element()` without ref- and + // cv-qualifiers. The first argument is a functor, the rest are constructor + // arguments for `value_type`. Returns `std::forward(f)(k, xs...)`, where + // `k` is the element key, and `xs...` are the new constructor arguments for + // `value_type`. It's allowed for `k` to alias `xs...`, and for both to alias + // `ts...`. The key won't be touched once `xs...` are used to construct an + // element; `ts...` won't be touched at all, which allows `apply()` to consume + // any rvalues among them. + // + // If `value_type` is constructible from `Ts&&...`, `Policy::apply()` must not + // trigger a hard compile error unless it originates from `f`. In other words, + // `Policy::apply()` must be SFINAE-friendly. If `value_type` is not + // constructible from `Ts&&...`, either SFINAE or a hard compile error is OK. + // + // If `Ts...` is `[cv] value_type[&]` or `[cv] init_type[&]`, + // `Policy::apply()` must work. A compile error is not allowed, SFINAE or not. + template + static auto apply(F&& f, Ts&&... ts) + -> decltype(P::apply(std::forward(f), std::forward(ts)...)) { + return P::apply(std::forward(f), std::forward(ts)...); + } + + // Returns the "key" portion of the slot. + // Used for node handle manipulation. + template + static auto key(slot_type* slot) + -> decltype(P::apply(ReturnKey(), element(slot))) { + return P::apply(ReturnKey(), element(slot)); + } + + // Returns the "value" (as opposed to the "key") portion of the element. Used + // by maps to implement `operator[]`, `at()` and `insert_or_assign()`. + template + static auto value(T* elem) -> decltype(P::value(elem)) { + return P::value(elem); + } + +private: + + // Use auto -> decltype as an enabler. + template + static auto transfer_impl(Alloc* alloc, slot_type* new_slot, + slot_type* old_slot, int) + -> decltype((void)P::transfer(alloc, new_slot, old_slot)) { + P::transfer(alloc, new_slot, old_slot); + } + + template + static void transfer_impl(Alloc* alloc, slot_type* new_slot, + slot_type* old_slot, char) { + construct(alloc, new_slot, std::move(element(old_slot))); + destroy(alloc, old_slot); + } +}; + +} // namespace container_internal +} // namespace phmap + +// ----------------------------------------------------------------------------- +// optional.h +// ----------------------------------------------------------------------------- +#ifdef PHMAP_HAVE_STD_OPTIONAL + +#include // IWYU pragma: export + +namespace phmap { +using std::bad_optional_access; +using std::optional; +using std::make_optional; +using std::nullopt_t; +using std::nullopt; +} // namespace phmap + +#else + +#if defined(__clang__) + #if __has_feature(cxx_inheriting_constructors) + #define PHMAP_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1 + #endif +#elif (defined(__GNUC__) && \ + (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 8)) || \ + (__cpp_inheriting_constructors >= 200802) || \ + (defined(_MSC_VER) && _MSC_VER >= 1910) + + #define PHMAP_OPTIONAL_USE_INHERITING_CONSTRUCTORS 1 +#endif + +namespace phmap { + +class bad_optional_access : public std::exception +{ +public: + bad_optional_access() = default; + ~bad_optional_access() override; + const char* what() const noexcept override; +}; + +template +class optional; + +// -------------------------------- +struct nullopt_t +{ + struct init_t {}; + static init_t init; + + explicit constexpr nullopt_t(init_t& /*unused*/) {} +}; + +extern const nullopt_t nullopt; + +namespace optional_internal { + +// throw delegator +[[noreturn]] void throw_bad_optional_access(); + + +struct empty_struct {}; + +// This class stores the data in optional. +// It is specialized based on whether T is trivially destructible. +// This is the specialization for non trivially destructible type. +template ::value> +class optional_data_dtor_base +{ + struct dummy_type { + static_assert(sizeof(T) % sizeof(empty_struct) == 0, ""); + // Use an array to avoid GCC 6 placement-new warning. + empty_struct data[sizeof(T) / sizeof(empty_struct)]; + }; + +protected: + // Whether there is data or not. + bool engaged_; + // Data storage + union { + dummy_type dummy_; + T data_; + }; + + void destruct() noexcept { + if (engaged_) { + data_.~T(); + engaged_ = false; + } + } + + // dummy_ must be initialized for constexpr constructor. + constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {} + + template + constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) + : engaged_(true), data_(phmap::forward(args)...) {} + + ~optional_data_dtor_base() { destruct(); } +}; + +// Specialization for trivially destructible type. +template +class optional_data_dtor_base +{ + struct dummy_type { + static_assert(sizeof(T) % sizeof(empty_struct) == 0, ""); + // Use array to avoid GCC 6 placement-new warning. + empty_struct data[sizeof(T) / sizeof(empty_struct)]; + }; + +protected: + // Whether there is data or not. + bool engaged_; + // Data storage + union { + dummy_type dummy_; + T data_; + }; + void destruct() noexcept { engaged_ = false; } + + // dummy_ must be initialized for constexpr constructor. + constexpr optional_data_dtor_base() noexcept : engaged_(false), dummy_{{}} {} + + template + constexpr explicit optional_data_dtor_base(in_place_t, Args&&... args) + : engaged_(true), data_(phmap::forward(args)...) {} +}; + +template +class optional_data_base : public optional_data_dtor_base +{ +protected: + using base = optional_data_dtor_base; +#if PHMAP_OPTIONAL_USE_INHERITING_CONSTRUCTORS + using base::base; +#else + optional_data_base() = default; + + template + constexpr explicit optional_data_base(in_place_t t, Args&&... args) + : base(t, phmap::forward(args)...) {} +#endif + + template + void construct(Args&&... args) { + // Use dummy_'s address to work around casting cv-qualified T* to void*. + ::new (static_cast(&this->dummy_)) T(std::forward(args)...); + this->engaged_ = true; + } + + template + void assign(U&& u) { + if (this->engaged_) { + this->data_ = std::forward(u); + } else { + construct(std::forward(u)); + } + } +}; + +// TODO(phmap-team): Add another class using +// std::is_trivially_move_constructible trait when available to match +// http://cplusplus.github.io/LWG/lwg-defects.html#2900, for types that +// have trivial move but nontrivial copy. +// Also, we should be checking is_trivially_copyable here, which is not +// supported now, so we use is_trivially_* traits instead. +template ::value&& + phmap::is_trivially_copy_assignable::type>::value&& std::is_trivially_destructible::value> +class optional_data; + +// Trivially copyable types +template +class optional_data : public optional_data_base +{ +protected: +#if PHMAP_OPTIONAL_USE_INHERITING_CONSTRUCTORS + using optional_data_base::optional_data_base; +#else + optional_data() = default; + + template + constexpr explicit optional_data(in_place_t t, Args&&... args) + : optional_data_base(t, phmap::forward(args)...) {} +#endif +}; + +template +class optional_data : public optional_data_base +{ +protected: +#if PHMAP_OPTIONAL_USE_INHERITING_CONSTRUCTORS + using optional_data_base::optional_data_base; +#else + template + constexpr explicit optional_data(in_place_t t, Args&&... args) + : optional_data_base(t, phmap::forward(args)...) {} +#endif + + optional_data() = default; + + optional_data(const optional_data& rhs) : optional_data_base() { + if (rhs.engaged_) { + this->construct(rhs.data_); + } + } + + optional_data(optional_data&& rhs) noexcept( + phmap::default_allocator_is_nothrow::value || + std::is_nothrow_move_constructible::value) + : optional_data_base() { + if (rhs.engaged_) { + this->construct(std::move(rhs.data_)); + } + } + + optional_data& operator=(const optional_data& rhs) { + if (rhs.engaged_) { + this->assign(rhs.data_); + } else { + this->destruct(); + } + return *this; + } + + optional_data& operator=(optional_data&& rhs) noexcept( + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value) { + if (rhs.engaged_) { + this->assign(std::move(rhs.data_)); + } else { + this->destruct(); + } + return *this; + } +}; + +// Ordered by level of restriction, from low to high. +// Copyable implies movable. +enum class copy_traits { copyable = 0, movable = 1, non_movable = 2 }; + +// Base class for enabling/disabling copy/move constructor. +template +class optional_ctor_base; + +template <> +class optional_ctor_base +{ +public: + constexpr optional_ctor_base() = default; + optional_ctor_base(const optional_ctor_base&) = default; + optional_ctor_base(optional_ctor_base&&) = default; + optional_ctor_base& operator=(const optional_ctor_base&) = default; + optional_ctor_base& operator=(optional_ctor_base&&) = default; +}; + +template <> +class optional_ctor_base +{ +public: + constexpr optional_ctor_base() = default; + optional_ctor_base(const optional_ctor_base&) = delete; + optional_ctor_base(optional_ctor_base&&) = default; + optional_ctor_base& operator=(const optional_ctor_base&) = default; + optional_ctor_base& operator=(optional_ctor_base&&) = default; +}; + +template <> +class optional_ctor_base +{ +public: + constexpr optional_ctor_base() = default; + optional_ctor_base(const optional_ctor_base&) = delete; + optional_ctor_base(optional_ctor_base&&) = delete; + optional_ctor_base& operator=(const optional_ctor_base&) = default; + optional_ctor_base& operator=(optional_ctor_base&&) = default; +}; + +// Base class for enabling/disabling copy/move assignment. +template +class optional_assign_base; + +template <> +class optional_assign_base +{ +public: + constexpr optional_assign_base() = default; + optional_assign_base(const optional_assign_base&) = default; + optional_assign_base(optional_assign_base&&) = default; + optional_assign_base& operator=(const optional_assign_base&) = default; + optional_assign_base& operator=(optional_assign_base&&) = default; +}; + +template <> +class optional_assign_base +{ +public: + constexpr optional_assign_base() = default; + optional_assign_base(const optional_assign_base&) = default; + optional_assign_base(optional_assign_base&&) = default; + optional_assign_base& operator=(const optional_assign_base&) = delete; + optional_assign_base& operator=(optional_assign_base&&) = default; +}; + +template <> +class optional_assign_base +{ +public: + constexpr optional_assign_base() = default; + optional_assign_base(const optional_assign_base&) = default; + optional_assign_base(optional_assign_base&&) = default; + optional_assign_base& operator=(const optional_assign_base&) = delete; + optional_assign_base& operator=(optional_assign_base&&) = delete; +}; + +template +constexpr copy_traits get_ctor_copy_traits() +{ + return std::is_copy_constructible::value + ? copy_traits::copyable + : std::is_move_constructible::value ? copy_traits::movable + : copy_traits::non_movable; +} + +template +constexpr copy_traits get_assign_copy_traits() +{ + return phmap::is_copy_assignable::value && + std::is_copy_constructible::value + ? copy_traits::copyable + : phmap::is_move_assignable::value && + std::is_move_constructible::value + ? copy_traits::movable + : copy_traits::non_movable; +} + +// Whether T is constructible or convertible from optional. +template +struct is_constructible_convertible_from_optional + : std::integral_constant< + bool, std::is_constructible&>::value || + std::is_constructible&&>::value || + std::is_constructible&>::value || + std::is_constructible&&>::value || + std::is_convertible&, T>::value || + std::is_convertible&&, T>::value || + std::is_convertible&, T>::value || + std::is_convertible&&, T>::value> {}; + +// Whether T is constructible or convertible or assignable from optional. +template +struct is_constructible_convertible_assignable_from_optional + : std::integral_constant< + bool, is_constructible_convertible_from_optional::value || + std::is_assignable&>::value || + std::is_assignable&&>::value || + std::is_assignable&>::value || + std::is_assignable&&>::value> {}; + +// Helper function used by [optional.relops], [optional.comp_with_t], +// for checking whether an expression is convertible to bool. +bool convertible_to_bool(bool); + +// Base class for std::hash>: +// If std::hash> is enabled, it provides operator() to +// compute the hash; Otherwise, it is disabled. +// Reference N4659 23.14.15 [unord.hash]. +template +struct optional_hash_base +{ + optional_hash_base() = delete; + optional_hash_base(const optional_hash_base&) = delete; + optional_hash_base(optional_hash_base&&) = delete; + optional_hash_base& operator=(const optional_hash_base&) = delete; + optional_hash_base& operator=(optional_hash_base&&) = delete; +}; + +template +struct optional_hash_base >()( + std::declval >()))> +{ + using argument_type = phmap::optional; + using result_type = size_t; + size_t operator()(const phmap::optional& opt) const { + phmap::type_traits_internal::AssertHashEnabled>(); + if (opt) { + return std::hash >()(*opt); + } else { + return static_cast(0x297814aaad196e6dULL); + } + } +}; + +} // namespace optional_internal + +// ----------------------------------------------------------------------------- +// file utility.h +// ----------------------------------------------------------------------------- + +#include +#include + +// --------- identity.h +namespace phmap { +namespace internal { + +template +struct identity { + typedef T type; +}; + +template +using identity_t = typename identity::type; + +} // namespace internal +} // namespace phmap + + +// --------- inline_variable.h + +#ifdef __cpp_inline_variables + +#if defined(__clang__) + #define PHMAP_INTERNAL_EXTERN_DECL(type, name) \ + extern const ::phmap::internal::identity_t name; +#else // Otherwise, just define the macro to do nothing. + #define PHMAP_INTERNAL_EXTERN_DECL(type, name) +#endif // defined(__clang__) + +// See above comment at top of file for details. +#define PHMAP_INTERNAL_INLINE_CONSTEXPR(type, name, init) \ + PHMAP_INTERNAL_EXTERN_DECL(type, name) \ + inline constexpr ::phmap::internal::identity_t name = init + +#else + +// See above comment at top of file for details. +// +// Note: +// identity_t is used here so that the const and name are in the +// appropriate place for pointer types, reference types, function pointer +// types, etc.. +#define PHMAP_INTERNAL_INLINE_CONSTEXPR(var_type, name, init) \ + template \ + struct PhmapInternalInlineVariableHolder##name { \ + static constexpr ::phmap::internal::identity_t kInstance = init; \ + }; \ + \ + template \ + constexpr ::phmap::internal::identity_t \ + PhmapInternalInlineVariableHolder##name::kInstance; \ + \ + static constexpr const ::phmap::internal::identity_t& \ + name = /* NOLINT */ \ + PhmapInternalInlineVariableHolder##name<>::kInstance; \ + static_assert(sizeof(void (*)(decltype(name))) != 0, \ + "Silence unused variable warnings.") + +#endif // __cpp_inline_variables + + +// ----------- invoke.h + +namespace phmap { +namespace base_internal { + +template +struct StrippedAccept +{ + template + struct Accept : Derived::template AcceptImpl::type>::type...> {}; +}; + +// (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T +// and t1 is an object of type T or a reference to an object of type T or a +// reference to an object of a type derived from T. +struct MemFunAndRef : StrippedAccept +{ + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::is_base_of {}; + + template + struct AcceptImpl + : std::is_base_of {}; + + template + static decltype((std::declval().* + std::declval())(std::declval()...)) + Invoke(MemFun&& mem_fun, Obj&& obj, Args&&... args) { + return (std::forward(obj).* + std::forward(mem_fun))(std::forward(args)...); + } +}; + +// ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a +// class T and t1 is not one of the types described in the previous item. +struct MemFunAndPtr : StrippedAccept +{ + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant::value> {}; + + template + struct AcceptImpl + : std::integral_constant::value> {}; + + template + static decltype(((*std::declval()).* + std::declval())(std::declval()...)) + Invoke(MemFun&& mem_fun, Ptr&& ptr, Args&&... args) { + return ((*std::forward(ptr)).* + std::forward(mem_fun))(std::forward(args)...); + } +}; + +// t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is +// an object of type T or a reference to an object of type T or a reference +// to an object of a type derived from T. +struct DataMemAndRef : StrippedAccept +{ + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl : std::is_base_of {}; + + template + static decltype(std::declval().*std::declval()) Invoke( + DataMem&& data_mem, Ref&& ref) { + return std::forward(ref).*std::forward(data_mem); + } +}; + +// (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 +// is not one of the types described in the previous item. +struct DataMemAndPtr : StrippedAccept +{ + template + struct AcceptImpl : std::false_type {}; + + template + struct AcceptImpl + : std::integral_constant::value> {}; + + template + static decltype((*std::declval()).*std::declval()) Invoke( + DataMem&& data_mem, Ptr&& ptr) { + return (*std::forward(ptr)).*std::forward(data_mem); + } +}; + +// f(t1, t2, ..., tN) in all other cases. +struct Callable +{ + // Callable doesn't have Accept because it's the last clause that gets picked + // when none of the previous clauses are applicable. + template + static decltype(std::declval()(std::declval()...)) Invoke( + F&& f, Args&&... args) { + return std::forward(f)(std::forward(args)...); + } +}; + +// Resolves to the first matching clause. +template +struct Invoker +{ + typedef typename std::conditional< + MemFunAndRef::Accept::value, MemFunAndRef, + typename std::conditional< + MemFunAndPtr::Accept::value, MemFunAndPtr, + typename std::conditional< + DataMemAndRef::Accept::value, DataMemAndRef, + typename std::conditional::value, + DataMemAndPtr, Callable>::type>::type>:: + type>::type type; +}; + +// The result type of Invoke. +template +using InvokeT = decltype(Invoker::type::Invoke( + std::declval(), std::declval()...)); + +// Invoke(f, args...) is an implementation of INVOKE(f, args...) from section +// [func.require] of the C++ standard. +template +InvokeT Invoke(F&& f, Args&&... args) { + return Invoker::type::Invoke(std::forward(f), + std::forward(args)...); +} +} // namespace base_internal +} // namespace phmap + + +// ----------- utility.h + +namespace phmap { + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `phmap::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; + static constexpr size_t size() noexcept { return sizeof...(Ints); } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `phmap::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal { + +template +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template +struct Extend, SeqSize, 0> { + using type = integer_sequence; +}; + +template +struct Extend, SeqSize, 1> { + using type = integer_sequence; +}; + +// Recursion helper for 'make_integer_sequence'. +// 'Gen::type' is an alias for 'integer_sequence'. +template +struct Gen { + using type = + typename Extend::type, N / 2, N % 2>::type; +}; + +template +struct Gen { + using type = integer_sequence; +}; + +} // namespace utility_internal + +// Compile-time sequences of integers + +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template +using make_integer_sequence = typename utility_internal::Gen::type; + +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template +using make_index_sequence = make_integer_sequence; + +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template +using index_sequence_for = make_index_sequence; + +// Tag types + +#ifdef PHMAP_HAVE_STD_OPTIONAL + +using std::in_place_t; +using std::in_place; + +#else // PHMAP_HAVE_STD_OPTIONAL + +// in_place_t +// +// Tag type used to specify in-place construction, such as with +// `phmap::optional`, designed to be a drop-in replacement for C++17's +// `std::in_place_t`. +struct in_place_t {}; + +PHMAP_INTERNAL_INLINE_CONSTEXPR(in_place_t, in_place, {}); + +#endif // PHMAP_HAVE_STD_OPTIONAL + +#if defined(PHMAP_HAVE_STD_ANY) || defined(PHMAP_HAVE_STD_VARIANT) +using std::in_place_type_t; +#else + +// in_place_type_t +// +// Tag type used for in-place construction when the type to construct needs to +// be specified, such as with `phmap::any`, designed to be a drop-in replacement +// for C++17's `std::in_place_type_t`. +template +struct in_place_type_t {}; +#endif // PHMAP_HAVE_STD_ANY || PHMAP_HAVE_STD_VARIANT + +#ifdef PHMAP_HAVE_STD_VARIANT +using std::in_place_index_t; +#else + +// in_place_index_t +// +// Tag type used for in-place construction when the type to construct needs to +// be specified, such as with `phmap::any`, designed to be a drop-in replacement +// for C++17's `std::in_place_index_t`. +template +struct in_place_index_t {}; +#endif // PHMAP_HAVE_STD_VARIANT + +// Constexpr move and forward + +// move() +// +// A constexpr version of `std::move()`, designed to be a drop-in replacement +// for C++14's `std::move()`. +template +constexpr phmap::remove_reference_t&& move(T&& t) noexcept { + return static_cast&&>(t); +} + +// forward() +// +// A constexpr version of `std::forward()`, designed to be a drop-in replacement +// for C++14's `std::forward()`. +template +constexpr T&& forward( + phmap::remove_reference_t& t) noexcept { // NOLINT(runtime/references) + return static_cast(t); +} + +namespace utility_internal { +// Helper method for expanding tuple into a called method. +template +auto apply_helper(Functor&& functor, Tuple&& t, index_sequence) + -> decltype(phmap::base_internal::Invoke( + phmap::forward(functor), + std::get(phmap::forward(t))...)) { + return phmap::base_internal::Invoke( + phmap::forward(functor), + std::get(phmap::forward(t))...); +} + +} // namespace utility_internal + +// apply +// +// Invokes a Callable using elements of a tuple as its arguments. +// Each element of the tuple corresponds to an argument of the call (in order). +// Both the Callable argument and the tuple argument are perfect-forwarded. +// For member-function Callables, the first tuple element acts as the `this` +// pointer. `phmap::apply` is designed to be a drop-in replacement for C++17's +// `std::apply`. Unlike C++17's `std::apply`, this is not currently `constexpr`. +// +// Example: +// +// class Foo { +// public: +// void Bar(int); +// }; +// void user_function1(int, std::string); +// void user_function2(std::unique_ptr); +// auto user_lambda = [](int, int) {}; +// +// int main() +// { +// std::tuple tuple1(42, "bar"); +// // Invokes the first user function on int, std::string. +// phmap::apply(&user_function1, tuple1); +// +// std::tuple> tuple2(phmap::make_unique()); +// // Invokes the user function that takes ownership of the unique +// // pointer. +// phmap::apply(&user_function2, std::move(tuple2)); +// +// auto foo = phmap::make_unique(); +// std::tuple tuple3(foo.get(), 42); +// // Invokes the method Bar on foo with one argument, 42. +// phmap::apply(&Foo::Bar, tuple3); +// +// std::tuple tuple4(8, 9); +// // Invokes a lambda. +// phmap::apply(user_lambda, tuple4); +// } +template +auto apply(Functor&& functor, Tuple&& t) + -> decltype(utility_internal::apply_helper( + phmap::forward(functor), phmap::forward(t), + phmap::make_index_sequence::type>::value>{})) { + return utility_internal::apply_helper( + phmap::forward(functor), phmap::forward(t), + phmap::make_index_sequence::type>::value>{}); +} + +// exchange +// +// Replaces the value of `obj` with `new_value` and returns the old value of +// `obj`. `phmap::exchange` is designed to be a drop-in replacement for C++14's +// `std::exchange`. +// +// Example: +// +// Foo& operator=(Foo&& other) { +// ptr1_ = phmap::exchange(other.ptr1_, nullptr); +// int1_ = phmap::exchange(other.int1_, -1); +// return *this; +// } +template +T exchange(T& obj, U&& new_value) +{ + T old_value = phmap::move(obj); + obj = phmap::forward(new_value); + return old_value; +} + +} // namespace phmap + + +// ----------------------------------------------------------------------------- +// phmap::optional class definition +// ----------------------------------------------------------------------------- + +template +class optional : private optional_internal::optional_data, + private optional_internal::optional_ctor_base< + optional_internal::get_ctor_copy_traits()>, + private optional_internal::optional_assign_base< + optional_internal::get_assign_copy_traits()> +{ + using data_base = optional_internal::optional_data; + +public: + typedef T value_type; + + // Constructors + + // Constructs an `optional` holding an empty value, NOT a default constructed + // `T`. + constexpr optional() noexcept {} + + // Constructs an `optional` initialized with `nullopt` to hold an empty value. + constexpr optional(nullopt_t) noexcept {} // NOLINT(runtime/explicit) + + // Copy constructor, standard semantics + optional(const optional& src) = default; + + // Move constructor, standard semantics + optional(optional&& src) = default; + + // Constructs a non-empty `optional` direct-initialized value of type `T` from + // the arguments `std::forward(args)...` within the `optional`. + // (The `in_place_t` is a tag used to indicate that the contained object + // should be constructed in-place.) + template , + std::is_constructible >::value>* = nullptr> + constexpr explicit optional(InPlaceT, Args&&... args) + : data_base(in_place_t(), phmap::forward(args)...) {} + + // Constructs a non-empty `optional` direct-initialized value of type `T` from + // the arguments of an initializer_list and `std::forward(args)...`. + // (The `in_place_t` is a tag used to indicate that the contained object + // should be constructed in-place.) + template &, Args&&...>::value>::type> + constexpr explicit optional(in_place_t, std::initializer_list il, + Args&&... args) + : data_base(in_place_t(), il, phmap::forward(args)...) { + } + + // Value constructor (implicit) + template < + typename U = T, + typename std::enable_if< + phmap::conjunction::type> >, + phmap::negation, typename std::decay::type> >, + std::is_convertible, + std::is_constructible >::value, + bool>::type = false> + constexpr optional(U&& v) : data_base(in_place_t(), phmap::forward(v)) {} + + // Value constructor (explicit) + template < + typename U = T, + typename std::enable_if< + phmap::conjunction::type>>, + phmap::negation, typename std::decay::type>>, + phmap::negation>, + std::is_constructible>::value, + bool>::type = false> + explicit constexpr optional(U&& v) + : data_base(in_place_t(), phmap::forward(v)) {} + + // Converting copy constructor (implicit) + template >, + std::is_constructible, + phmap::negation< + optional_internal:: + is_constructible_convertible_from_optional >, + std::is_convertible >::value, + bool>::type = false> + optional(const optional& rhs) { + if (rhs) { + this->construct(*rhs); + } + } + + // Converting copy constructor (explicit) + template >, + std::is_constructible, + phmap::negation< + optional_internal:: + is_constructible_convertible_from_optional>, + phmap::negation>>::value, + bool>::type = false> + explicit optional(const optional& rhs) { + if (rhs) { + this->construct(*rhs); + } + } + + // Converting move constructor (implicit) + template >, + std::is_constructible, + phmap::negation< + optional_internal:: + is_constructible_convertible_from_optional >, + std::is_convertible >::value, + bool>::type = false> + optional(optional&& rhs) { + if (rhs) { + this->construct(std::move(*rhs)); + } + } + + // Converting move constructor (explicit) + template < + typename U, + typename std::enable_if< + phmap::conjunction< + phmap::negation>, std::is_constructible, + phmap::negation< + optional_internal::is_constructible_convertible_from_optional< + T, U>>, + phmap::negation>>::value, + bool>::type = false> + explicit optional(optional&& rhs) { + if (rhs) { + this->construct(std::move(*rhs)); + } + } + + // Destructor. Trivial if `T` is trivially destructible. + ~optional() = default; + + // Assignment Operators + + // Assignment from `nullopt` + // + // Example: + // + // struct S { int value; }; + // optional opt = phmap::nullopt; // Could also use opt = { }; + optional& operator=(nullopt_t) noexcept { + this->destruct(); + return *this; + } + + // Copy assignment operator, standard semantics + optional& operator=(const optional& src) = default; + + // Move assignment operator, standard semantics + optional& operator=(optional&& src) = default; + + // Value assignment operators + template < + typename U = T, + typename = typename std::enable_if, typename std::decay::type>>, + phmap::negation< + phmap::conjunction, + std::is_same::type>>>, + std::is_constructible, std::is_assignable>::value>::type> + optional& operator=(U&& v) { + this->assign(std::forward(v)); + return *this; + } + + template < + typename U, + typename = typename std::enable_if>, + std::is_constructible, std::is_assignable, + phmap::negation< + optional_internal:: + is_constructible_convertible_assignable_from_optional< + T, U>>>::value>::type> + optional& operator=(const optional& rhs) { + if (rhs) { + this->assign(*rhs); + } else { + this->destruct(); + } + return *this; + } + + template >, std::is_constructible, + std::is_assignable, + phmap::negation< + optional_internal:: + is_constructible_convertible_assignable_from_optional< + T, U>>>::value>::type> + optional& operator=(optional&& rhs) { + if (rhs) { + this->assign(std::move(*rhs)); + } else { + this->destruct(); + } + return *this; + } + + // Modifiers + + // optional::reset() + // + // Destroys the inner `T` value of an `phmap::optional` if one is present. + PHMAP_ATTRIBUTE_REINITIALIZES void reset() noexcept { this->destruct(); } + + // optional::emplace() + // + // (Re)constructs the underlying `T` in-place with the given forwarded + // arguments. + // + // Example: + // + // optional opt; + // opt.emplace(arg1,arg2,arg3); // Constructs Foo(arg1,arg2,arg3) + // + // If the optional is non-empty, and the `args` refer to subobjects of the + // current object, then behaviour is undefined, because the current object + // will be destructed before the new object is constructed with `args`. + template ::value>::type> + T& emplace(Args&&... args) { + this->destruct(); + this->construct(std::forward(args)...); + return reference(); + } + + // Emplace reconstruction overload for an initializer list and the given + // forwarded arguments. + // + // Example: + // + // struct Foo { + // Foo(std::initializer_list); + // }; + // + // optional opt; + // opt.emplace({1,2,3}); // Constructs Foo({1,2,3}) + template &, Args&&...>::value>::type> + T& emplace(std::initializer_list il, Args&&... args) { + this->destruct(); + this->construct(il, std::forward(args)...); + return reference(); + } + + // Swaps + + // Swap, standard semantics + void swap(optional& rhs) noexcept( + std::is_nothrow_move_constructible::value&& + std::is_trivial::value) { + if (*this) { + if (rhs) { + using std::swap; + swap(**this, *rhs); + } else { + rhs.construct(std::move(**this)); + this->destruct(); + } + } else { + if (rhs) { + this->construct(std::move(*rhs)); + rhs.destruct(); + } else { + // No effect (swap(disengaged, disengaged)). + } + } + } + + // Observers + + // optional::operator->() + // + // Accesses the underlying `T` value's member `m` of an `optional`. If the + // `optional` is empty, behavior is undefined. + // + // If you need myOpt->foo in constexpr, use (*myOpt).foo instead. + const T* operator->() const { + assert(this->engaged_); + return std::addressof(this->data_); + } + T* operator->() { + assert(this->engaged_); + return std::addressof(this->data_); + } + + // optional::operator*() + // + // Accesses the underlying `T` value of an `optional`. If the `optional` is + // empty, behavior is undefined. + constexpr const T& operator*() const & { return reference(); } + T& operator*() & { + assert(this->engaged_); + return reference(); + } + constexpr const T&& operator*() const && { + return phmap::move(reference()); + } + T&& operator*() && { + assert(this->engaged_); + return std::move(reference()); + } + + // optional::operator bool() + // + // Returns false if and only if the `optional` is empty. + // + // if (opt) { + // // do something with opt.value(); + // } else { + // // opt is empty. + // } + // + constexpr explicit operator bool() const noexcept { return this->engaged_; } + + // optional::has_value() + // + // Determines whether the `optional` contains a value. Returns `false` if and + // only if `*this` is empty. + constexpr bool has_value() const noexcept { return this->engaged_; } + +// Suppress bogus warning on MSVC: MSVC complains call to reference() after +// throw_bad_optional_access() is unreachable. +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable : 4702) +#endif // _MSC_VER + // optional::value() + // + // Returns a reference to an `optional`s underlying value. The constness + // and lvalue/rvalue-ness of the `optional` is preserved to the view of + // the `T` sub-object. Throws `phmap::bad_optional_access` when the `optional` + // is empty. + constexpr const T& value() const & { + return static_cast(*this) + ? reference() + : (optional_internal::throw_bad_optional_access(), reference()); + } + T& value() & { + return static_cast(*this) + ? reference() + : (optional_internal::throw_bad_optional_access(), reference()); + } + T&& value() && { // NOLINT(build/c++11) + return std::move( + static_cast(*this) + ? reference() + : (optional_internal::throw_bad_optional_access(), reference())); + } + constexpr const T&& value() const && { // NOLINT(build/c++11) + return phmap::move( + static_cast(*this) + ? reference() + : (optional_internal::throw_bad_optional_access(), reference())); + } +#ifdef _MSC_VER + #pragma warning(pop) +#endif // _MSC_VER + + // optional::value_or() + // + // Returns either the value of `T` or a passed default `v` if the `optional` + // is empty. + template + constexpr T value_or(U&& v) const& { + static_assert(std::is_copy_constructible::value, + "optional::value_or: T must by copy constructible"); + static_assert(std::is_convertible::value, + "optional::value_or: U must be convertible to T"); + return static_cast(*this) + ? **this + : static_cast(phmap::forward(v)); + } + template + T value_or(U&& v) && { // NOLINT(build/c++11) + static_assert(std::is_move_constructible::value, + "optional::value_or: T must by copy constructible"); + static_assert(std::is_convertible::value, + "optional::value_or: U must be convertible to T"); + return static_cast(*this) ? std::move(**this) + : static_cast(std::forward(v)); + } + +private: + // Private accessors for internal storage viewed as reference to T. + constexpr const T& reference() const { return this->data_; } + T& reference() { return this->data_; } + + // T constraint checks. You can't have an optional of nullopt_t, in_place_t + // or a reference. + static_assert( + !std::is_same::type>::value, + "optional is not allowed."); + static_assert( + !std::is_same::type>::value, + "optional is not allowed."); + static_assert(!std::is_reference::value, + "optional is not allowed."); +}; + +// Non-member functions + +// swap() +// +// Performs a swap between two `phmap::optional` objects, using standard +// semantics. +// +// NOTE: we assume `is_swappable()` is always `true`. A compile error will +// result if this is not the case. +template ::value, + bool>::type = false> +void swap(optional& a, optional& b) noexcept(noexcept(a.swap(b))) { + a.swap(b); +} + +// make_optional() +// +// Creates a non-empty `optional` where the type of `T` is deduced. An +// `phmap::optional` can also be explicitly instantiated with +// `make_optional(v)`. +// +// Note: `make_optional()` constructions may be declared `constexpr` for +// trivially copyable types `T`. Non-trivial types require copy elision +// support in C++17 for `make_optional` to support `constexpr` on such +// non-trivial types. +// +// Example: +// +// constexpr phmap::optional opt = phmap::make_optional(1); +// static_assert(opt.value() == 1, ""); +template +constexpr optional::type> make_optional(T&& v) { + return optional::type>(phmap::forward(v)); +} + +template +constexpr optional make_optional(Args&&... args) { + return optional(in_place_t(), phmap::forward(args)...); +} + +template +constexpr optional make_optional(std::initializer_list il, + Args&&... args) { + return optional(in_place_t(), il, + phmap::forward(args)...); +} + +// Relational operators [optional.relops] + +// Empty optionals are considered equal to each other and less than non-empty +// optionals. Supports relations between optional and optional, between +// optional and U, and between optional and nullopt. +// +// Note: We're careful to support T having non-bool relationals. + +// Requires: The expression, e.g. "*x == *y" shall be well-formed and its result +// shall be convertible to bool. +// The C++17 (N4606) "Returns:" statements are translated into +// code in an obvious way here, and the original text retained as function docs. +// Returns: If bool(x) != bool(y), false; otherwise if bool(x) == false, true; +// otherwise *x == *y. +template +constexpr auto operator==(const optional& x, const optional& y) + -> decltype(optional_internal::convertible_to_bool(*x == *y)) { + return static_cast(x) != static_cast(y) + ? false + : static_cast(x) == false ? true + : static_cast(*x == *y); +} + +// Returns: If bool(x) != bool(y), true; otherwise, if bool(x) == false, false; +// otherwise *x != *y. +template +constexpr auto operator!=(const optional& x, const optional& y) + -> decltype(optional_internal::convertible_to_bool(*x != *y)) { + return static_cast(x) != static_cast(y) + ? true + : static_cast(x) == false ? false + : static_cast(*x != *y); +} +// Returns: If !y, false; otherwise, if !x, true; otherwise *x < *y. +template +constexpr auto operator<(const optional& x, const optional& y) + -> decltype(optional_internal::convertible_to_bool(*x < *y)) { + return !y ? false : !x ? true : static_cast(*x < *y); +} +// Returns: If !x, false; otherwise, if !y, true; otherwise *x > *y. +template +constexpr auto operator>(const optional& x, const optional& y) + -> decltype(optional_internal::convertible_to_bool(*x > *y)) { + return !x ? false : !y ? true : static_cast(*x > *y); +} +// Returns: If !x, true; otherwise, if !y, false; otherwise *x <= *y. +template +constexpr auto operator<=(const optional& x, const optional& y) + -> decltype(optional_internal::convertible_to_bool(*x <= *y)) { + return !x ? true : !y ? false : static_cast(*x <= *y); +} +// Returns: If !y, true; otherwise, if !x, false; otherwise *x >= *y. +template +constexpr auto operator>=(const optional& x, const optional& y) + -> decltype(optional_internal::convertible_to_bool(*x >= *y)) { + return !y ? true : !x ? false : static_cast(*x >= *y); +} + +// Comparison with nullopt [optional.nullops] +// The C++17 (N4606) "Returns:" statements are used directly here. +template +constexpr bool operator==(const optional& x, nullopt_t) noexcept { + return !x; +} +template +constexpr bool operator==(nullopt_t, const optional& x) noexcept { + return !x; +} +template +constexpr bool operator!=(const optional& x, nullopt_t) noexcept { + return static_cast(x); +} +template +constexpr bool operator!=(nullopt_t, const optional& x) noexcept { + return static_cast(x); +} +template +constexpr bool operator<(const optional&, nullopt_t) noexcept { + return false; +} +template +constexpr bool operator<(nullopt_t, const optional& x) noexcept { + return static_cast(x); +} +template +constexpr bool operator<=(const optional& x, nullopt_t) noexcept { + return !x; +} +template +constexpr bool operator<=(nullopt_t, const optional&) noexcept { + return true; +} +template +constexpr bool operator>(const optional& x, nullopt_t) noexcept { + return static_cast(x); +} +template +constexpr bool operator>(nullopt_t, const optional&) noexcept { + return false; +} +template +constexpr bool operator>=(const optional&, nullopt_t) noexcept { + return true; +} +template +constexpr bool operator>=(nullopt_t, const optional& x) noexcept { + return !x; +} + +// Comparison with T [optional.comp_with_t] + +// Requires: The expression, e.g. "*x == v" shall be well-formed and its result +// shall be convertible to bool. +// The C++17 (N4606) "Equivalent to:" statements are used directly here. +template +constexpr auto operator==(const optional& x, const U& v) + -> decltype(optional_internal::convertible_to_bool(*x == v)) { + return static_cast(x) ? static_cast(*x == v) : false; +} +template +constexpr auto operator==(const U& v, const optional& x) + -> decltype(optional_internal::convertible_to_bool(v == *x)) { + return static_cast(x) ? static_cast(v == *x) : false; +} +template +constexpr auto operator!=(const optional& x, const U& v) + -> decltype(optional_internal::convertible_to_bool(*x != v)) { + return static_cast(x) ? static_cast(*x != v) : true; +} +template +constexpr auto operator!=(const U& v, const optional& x) + -> decltype(optional_internal::convertible_to_bool(v != *x)) { + return static_cast(x) ? static_cast(v != *x) : true; +} +template +constexpr auto operator<(const optional& x, const U& v) + -> decltype(optional_internal::convertible_to_bool(*x < v)) { + return static_cast(x) ? static_cast(*x < v) : true; +} +template +constexpr auto operator<(const U& v, const optional& x) + -> decltype(optional_internal::convertible_to_bool(v < *x)) { + return static_cast(x) ? static_cast(v < *x) : false; +} +template +constexpr auto operator<=(const optional& x, const U& v) + -> decltype(optional_internal::convertible_to_bool(*x <= v)) { + return static_cast(x) ? static_cast(*x <= v) : true; +} +template +constexpr auto operator<=(const U& v, const optional& x) + -> decltype(optional_internal::convertible_to_bool(v <= *x)) { + return static_cast(x) ? static_cast(v <= *x) : false; +} +template +constexpr auto operator>(const optional& x, const U& v) + -> decltype(optional_internal::convertible_to_bool(*x > v)) { + return static_cast(x) ? static_cast(*x > v) : false; +} +template +constexpr auto operator>(const U& v, const optional& x) + -> decltype(optional_internal::convertible_to_bool(v > *x)) { + return static_cast(x) ? static_cast(v > *x) : true; +} +template +constexpr auto operator>=(const optional& x, const U& v) + -> decltype(optional_internal::convertible_to_bool(*x >= v)) { + return static_cast(x) ? static_cast(*x >= v) : false; +} +template +constexpr auto operator>=(const U& v, const optional& x) + -> decltype(optional_internal::convertible_to_bool(v >= *x)) { + return static_cast(x) ? static_cast(v >= *x) : true; +} + +} // namespace phmap + +namespace std { + +// std::hash specialization for phmap::optional. +template +struct hash > + : phmap::optional_internal::optional_hash_base {}; + +} // namespace std + +#endif + +// ----------------------------------------------------------------------------- +// common.h +// ----------------------------------------------------------------------------- +namespace phmap { +namespace container_internal { + +template +struct IsTransparent : std::false_type {}; +template +struct IsTransparent> + : std::true_type {}; + +template +struct KeyArg +{ + // Transparent. Forward `K`. + template + using type = K; +}; + +template <> +struct KeyArg +{ + // Not transparent. Always use `key_type`. + template + using type = key_type; +}; + +// The node_handle concept from C++17. +// We specialize node_handle for sets and maps. node_handle_base holds the +// common API of both. +// ----------------------------------------------------------------------- +template +class node_handle_base +{ +protected: + using slot_type = typename PolicyTraits::slot_type; + +public: + using allocator_type = Alloc; + + constexpr node_handle_base() {} + + node_handle_base(node_handle_base&& other) noexcept { + *this = std::move(other); + } + + ~node_handle_base() { destroy(); } + + node_handle_base& operator=(node_handle_base&& other) noexcept { + destroy(); + if (!other.empty()) { + alloc_ = other.alloc_; + PolicyTraits::transfer(alloc(), slot(), other.slot()); + other.reset(); + } + return *this; + } + + bool empty() const noexcept { return !alloc_; } + explicit operator bool() const noexcept { return !empty(); } + allocator_type get_allocator() const { return *alloc_; } + +protected: + friend struct CommonAccess; + + node_handle_base(const allocator_type& a, slot_type* s) : alloc_(a) { + PolicyTraits::transfer(alloc(), slot(), s); + } + + void destroy() { + if (!empty()) { + PolicyTraits::destroy(alloc(), slot()); + reset(); + } + } + + void reset() { + assert(alloc_.has_value()); + alloc_ = phmap::nullopt; + } + + slot_type* slot() const { + assert(!empty()); + return reinterpret_cast(std::addressof(slot_space_)); + } + + allocator_type* alloc() { return std::addressof(*alloc_); } + +private: + phmap::optional alloc_; + mutable phmap::aligned_storage_t + slot_space_; +}; + +// For sets. +// --------- +template +class node_handle : public node_handle_base +{ + using Base = typename node_handle::node_handle_base; + +public: + using value_type = typename PolicyTraits::value_type; + + constexpr node_handle() {} + + value_type& value() const { return PolicyTraits::element(this->slot()); } + + value_type& key() const { return PolicyTraits::element(this->slot()); } + +private: + friend struct CommonAccess; + + node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {} +}; + +// For maps. +// --------- +template +class node_handle> + : public node_handle_base +{ + using Base = typename node_handle::node_handle_base; + +public: + using key_type = typename Policy::key_type; + using mapped_type = typename Policy::mapped_type; + + constexpr node_handle() {} + + auto key() const -> decltype(PolicyTraits::key(this->slot())) { + return PolicyTraits::key(this->slot()); + } + + mapped_type& mapped() const { + return PolicyTraits::value(&PolicyTraits::element(this->slot())); + } + +private: + friend struct CommonAccess; + + node_handle(const Alloc& a, typename Base::slot_type* s) : Base(a, s) {} +}; + +// Provide access to non-public node-handle functions. +struct CommonAccess +{ + template + static auto GetSlot(const Node& node) -> decltype(node.slot()) { + return node.slot(); + } + + template + static void Reset(Node* node) { + node->reset(); + } + + template + static T Make(Args&&... args) { + return T(std::forward(args)...); + } +}; + +// Implement the insert_return_type<> concept of C++17. +template +struct InsertReturnType +{ + Iterator position; + bool inserted; + NodeType node; +}; + +} // namespace container_internal +} // namespace phmap + #endif // phmap_base_h_guard_ diff --git a/parallel_hashmap/phmap_bits.h b/parallel_hashmap/phmap_bits.h index 6b3c9bf..d83fd16 100644 --- a/parallel_hashmap/phmap_bits.h +++ b/parallel_hashmap/phmap_bits.h @@ -354,5 +354,248 @@ inline void UnalignedStore64(void *p, uint64_t v) { memcpy(p, &v, sizeof v); } #define PHMAP_PREDICT_TRUE(x) (x) #endif +// ----------------------------------------------------------------------------- +// File: endian.h +// ----------------------------------------------------------------------------- + +namespace phmap { + +// Use compiler byte-swapping intrinsics if they are available. 32-bit +// and 64-bit versions are available in Clang and GCC as of GCC 4.3.0. +// The 16-bit version is available in Clang and GCC only as of GCC 4.8.0. +// For simplicity, we enable them all only for GCC 4.8.0 or later. +#if defined(__clang__) || \ + (defined(__GNUC__) && \ + ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ >= 5)) + + inline uint64_t gbswap_64(uint64_t host_int) { + return __builtin_bswap64(host_int); + } + inline uint32_t gbswap_32(uint32_t host_int) { + return __builtin_bswap32(host_int); + } + inline uint16_t gbswap_16(uint16_t host_int) { + return __builtin_bswap16(host_int); + } + +#elif defined(_MSC_VER) + + inline uint64_t gbswap_64(uint64_t host_int) { + return _byteswap_uint64(host_int); + } + inline uint32_t gbswap_32(uint32_t host_int) { + return _byteswap_ulong(host_int); + } + inline uint16_t gbswap_16(uint16_t host_int) { + return _byteswap_ushort(host_int); + } + +#elif defined(__APPLE__) + + inline uint64_t gbswap_64(uint64_t host_int) { return OSSwapInt16(host_int); } + inline uint32_t gbswap_32(uint32_t host_int) { return OSSwapInt32(host_int); } + inline uint16_t gbswap_16(uint16_t host_int) { return OSSwapInt64(host_int); } + +#else + + inline uint64_t gbswap_64(uint64_t host_int) { +#if defined(__GNUC__) && defined(__x86_64__) && !defined(__APPLE__) + // Adapted from /usr/include/byteswap.h. Not available on Mac. + if (__builtin_constant_p(host_int)) { + return __bswap_constant_64(host_int); + } else { + uint64_t result; + __asm__("bswap %0" : "=r"(result) : "0"(host_int)); + return result; + } +#elif defined(__GLIBC__) + return bswap_64(host_int); +#else + return (((host_int & uint64_t{0xFF}) << 56) | + ((host_int & uint64_t{0xFF00}) << 40) | + ((host_int & uint64_t{0xFF0000}) << 24) | + ((host_int & uint64_t{0xFF000000}) << 8) | + ((host_int & uint64_t{0xFF00000000}) >> 8) | + ((host_int & uint64_t{0xFF0000000000}) >> 24) | + ((host_int & uint64_t{0xFF000000000000}) >> 40) | + ((host_int & uint64_t{0xFF00000000000000}) >> 56)); +#endif // bswap_64 + } + + inline uint32_t gbswap_32(uint32_t host_int) { +#if defined(__GLIBC__) + return bswap_32(host_int); +#else + return (((host_int & uint32_t{0xFF}) << 24) | + ((host_int & uint32_t{0xFF00}) << 8) | + ((host_int & uint32_t{0xFF0000}) >> 8) | + ((host_int & uint32_t{0xFF000000}) >> 24)); +#endif + } + + inline uint16_t gbswap_16(uint16_t host_int) { +#if defined(__GLIBC__) + return bswap_16(host_int); +#else + return (((host_int & uint16_t{0xFF}) << 8) | + ((host_int & uint16_t{0xFF00}) >> 8)); +#endif + } + +#endif // intrinics available + +#ifdef PHMAP_IS_LITTLE_ENDIAN + + // Definitions for ntohl etc. that don't require us to include + // netinet/in.h. We wrap gbswap_32 and gbswap_16 in functions rather + // than just #defining them because in debug mode, gcc doesn't + // correctly handle the (rather involved) definitions of bswap_32. + // gcc guarantees that inline functions are as fast as macros, so + // this isn't a performance hit. + inline uint16_t ghtons(uint16_t x) { return gbswap_16(x); } + inline uint32_t ghtonl(uint32_t x) { return gbswap_32(x); } + inline uint64_t ghtonll(uint64_t x) { return gbswap_64(x); } + +#elif defined PHMAP_IS_BIG_ENDIAN + + // These definitions are simpler on big-endian machines + // These are functions instead of macros to avoid self-assignment warnings + // on calls such as "i = ghtnol(i);". This also provides type checking. + inline uint16_t ghtons(uint16_t x) { return x; } + inline uint32_t ghtonl(uint32_t x) { return x; } + inline uint64_t ghtonll(uint64_t x) { return x; } + +#else + #error \ + "Unsupported byte order: Either PHMAP_IS_BIG_ENDIAN or " \ + "PHMAP_IS_LITTLE_ENDIAN must be defined" +#endif // byte order + +inline uint16_t gntohs(uint16_t x) { return ghtons(x); } +inline uint32_t gntohl(uint32_t x) { return ghtonl(x); } +inline uint64_t gntohll(uint64_t x) { return ghtonll(x); } + +// Utilities to convert numbers between the current hosts's native byte +// order and little-endian byte order +// +// Load/Store methods are alignment safe +namespace little_endian { +// Conversion functions. +#ifdef PHMAP_IS_LITTLE_ENDIAN + + inline uint16_t FromHost16(uint16_t x) { return x; } + inline uint16_t ToHost16(uint16_t x) { return x; } + + inline uint32_t FromHost32(uint32_t x) { return x; } + inline uint32_t ToHost32(uint32_t x) { return x; } + + inline uint64_t FromHost64(uint64_t x) { return x; } + inline uint64_t ToHost64(uint64_t x) { return x; } + + inline constexpr bool IsLittleEndian() { return true; } + +#elif defined PHMAP_IS_BIG_ENDIAN + + inline uint16_t FromHost16(uint16_t x) { return gbswap_16(x); } + inline uint16_t ToHost16(uint16_t x) { return gbswap_16(x); } + + inline uint32_t FromHost32(uint32_t x) { return gbswap_32(x); } + inline uint32_t ToHost32(uint32_t x) { return gbswap_32(x); } + + inline uint64_t FromHost64(uint64_t x) { return gbswap_64(x); } + inline uint64_t ToHost64(uint64_t x) { return gbswap_64(x); } + + inline constexpr bool IsLittleEndian() { return false; } + +#endif /* ENDIAN */ + +// Functions to do unaligned loads and stores in little-endian order. +inline uint16_t Load16(const void *p) { + return ToHost16(PHMAP_INTERNAL_UNALIGNED_LOAD16(p)); +} + +inline void Store16(void *p, uint16_t v) { + PHMAP_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v)); +} + +inline uint32_t Load32(const void *p) { + return ToHost32(PHMAP_INTERNAL_UNALIGNED_LOAD32(p)); +} + +inline void Store32(void *p, uint32_t v) { + PHMAP_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v)); +} + +inline uint64_t Load64(const void *p) { + return ToHost64(PHMAP_INTERNAL_UNALIGNED_LOAD64(p)); +} + +inline void Store64(void *p, uint64_t v) { + PHMAP_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v)); +} + +} // namespace little_endian + +// Utilities to convert numbers between the current hosts's native byte +// order and big-endian byte order (same as network byte order) +// +// Load/Store methods are alignment safe +namespace big_endian { +#ifdef PHMAP_IS_LITTLE_ENDIAN + + inline uint16_t FromHost16(uint16_t x) { return gbswap_16(x); } + inline uint16_t ToHost16(uint16_t x) { return gbswap_16(x); } + + inline uint32_t FromHost32(uint32_t x) { return gbswap_32(x); } + inline uint32_t ToHost32(uint32_t x) { return gbswap_32(x); } + + inline uint64_t FromHost64(uint64_t x) { return gbswap_64(x); } + inline uint64_t ToHost64(uint64_t x) { return gbswap_64(x); } + + inline constexpr bool IsLittleEndian() { return true; } + +#elif defined PHMAP_IS_BIG_ENDIAN + + inline uint16_t FromHost16(uint16_t x) { return x; } + inline uint16_t ToHost16(uint16_t x) { return x; } + + inline uint32_t FromHost32(uint32_t x) { return x; } + inline uint32_t ToHost32(uint32_t x) { return x; } + + inline uint64_t FromHost64(uint64_t x) { return x; } + inline uint64_t ToHost64(uint64_t x) { return x; } + + inline constexpr bool IsLittleEndian() { return false; } + +#endif /* ENDIAN */ + +// Functions to do unaligned loads and stores in big-endian order. +inline uint16_t Load16(const void *p) { + return ToHost16(PHMAP_INTERNAL_UNALIGNED_LOAD16(p)); +} + +inline void Store16(void *p, uint16_t v) { + PHMAP_INTERNAL_UNALIGNED_STORE16(p, FromHost16(v)); +} + +inline uint32_t Load32(const void *p) { + return ToHost32(PHMAP_INTERNAL_UNALIGNED_LOAD32(p)); +} + +inline void Store32(void *p, uint32_t v) { + PHMAP_INTERNAL_UNALIGNED_STORE32(p, FromHost32(v)); +} + +inline uint64_t Load64(const void *p) { + return ToHost64(PHMAP_INTERNAL_UNALIGNED_LOAD64(p)); +} + +inline void Store64(void *p, uint64_t v) { + PHMAP_INTERNAL_UNALIGNED_STORE64(p, FromHost64(v)); +} + +} // namespace big_endian + +} // namespace phmap #endif // phmap_bits_h_guard_