diff --git a/boost_unordered.hpp b/boost_unordered.hpp index a99f8ea..c45ce47 100644 --- a/boost_unordered.hpp +++ b/boost_unordered.hpp @@ -29,6 +29,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #pragma once +#include #include #include #include @@ -221,6 +222,7 @@ template std::size_t hash_unordered_range( It, It ); * * Copyright 2023 Christian Mazakas. * Copyright 2023 Joaquin M Lopez Munoz. + * Copyright 2024 Braden Ganetsky. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -231,6 +233,10 @@ template std::size_t hash_unordered_range( It, It ); #ifndef BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP #define BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + namespace boost { namespace unordered { @@ -258,12 +264,188 @@ namespace boost { typename concurrent_flat_set::size_type erase_if( concurrent_flat_set& c, Predicate pred); +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class Pred = std::equal_to > + using concurrent_flat_set = boost::unordered::concurrent_flat_set >; + } // namespace pmr +#endif + } // namespace unordered using boost::unordered::concurrent_flat_set; } // namespace boost #endif // BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP +// Copyright (C) 2024 Braden Ganetsky +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP +#define BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP + +namespace boost { + namespace unordered { + namespace detail { + namespace foa { + template struct check_key_type_t + { + static_assert(std::is_constructible::value, + "key_type must be constructible from Args"); + }; + template struct check_key_type_t + { + static_assert(std::is_constructible::value, + "key_type must be default constructible"); + }; + template struct check_key_type_t + { + static_assert(std::is_constructible::value, + "key_type must be copy constructible"); + }; + template struct check_key_type_t + { + static_assert(std::is_constructible::value, + "key_type must be move constructible"); + }; + + template struct check_mapped_type_t + { + static_assert(std::is_constructible::value, + "mapped_type must be constructible from Args"); + }; + template struct check_mapped_type_t + { + static_assert(std::is_constructible::value, + "mapped_type must be default constructible"); + }; + template + struct check_mapped_type_t + { + static_assert(std::is_constructible::value, + "mapped_type must be copy constructible"); + }; + template struct check_mapped_type_t + { + static_assert(std::is_constructible::value, + "mapped_type must be move constructible"); + }; + + template struct map_types_constructibility + { + using key_type = typename TypePolicy::key_type; + using mapped_type = typename TypePolicy::mapped_type; + using init_type = typename TypePolicy::init_type; + using value_type = typename TypePolicy::value_type; + + template + static void check(A&, X*, Args&&...) + { + // Pass through, as we cannot say anything about a general allocator + } + + template static void check_key_type() + { + (void)check_key_type_t{}; + } + template static void check_mapped_type() + { + (void)check_mapped_type_t{}; + } + + template + static void check(std::allocator&, key_type*, Arg&&) + { + check_key_type(); + } + + template + static void check( + std::allocator&, value_type*, Arg1&&, Arg2&&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check(std::allocator&, value_type*, + const std::pair&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check( + std::allocator&, value_type*, std::pair&&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check(std::allocator&, value_type*, + std::piecewise_construct_t, std::tuple&&, + std::tuple&&) + { + check_key_type(); + check_mapped_type(); + } + + template + static void check( + std::allocator&, init_type*, Arg1&&, Arg2&&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check(std::allocator&, init_type*, + const std::pair&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check( + std::allocator&, init_type*, std::pair&&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check(std::allocator&, init_type*, + std::piecewise_construct_t, std::tuple&&, + std::tuple&&) + { + check_key_type(); + check_mapped_type(); + } + }; + + template struct set_types_constructibility + { + using key_type = typename TypePolicy::key_type; + using value_type = typename TypePolicy::value_type; + static_assert(std::is_same::value, ""); + + template + static void check(A&, X*, Args&&...) + { + // Pass through, as we cannot say anything about a general allocator + } + + template + static void check(std::allocator&, key_type*, Args&&...) + { + (void)check_key_type_t{}; + } + }; + } // namespace foa + } // namespace detail + } // namespace unordered +} // namespace boost + +#endif // BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP // Copyright (C) 2023 Christian Mazakas // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -285,6 +467,9 @@ namespace boost { using element_type = value_type; + using types = flat_set_types; + using constructibility_checker = set_types_constructibility; + static Key& value_from(element_type& x) { return x; } static element_type&& move(element_type& x) { return std::move(x); } @@ -292,6 +477,7 @@ namespace boost { template static void construct(A& al, value_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } @@ -301,8 +487,8 @@ namespace boost { } }; } // namespace foa - } // namespace detail - } // namespace unordered + } // namespace detail + } // namespace unordered } // namespace boost #endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP @@ -534,20 +720,48 @@ class empty_value { T value_; }; +#ifdef BOOST_MSVC +/* +This is a workaround to an MSVC bug when T is a nested class: +https://developercommunity.visualstudio.com/t/Compiler-bug:-Incorrect-C2247-and-C2248/10690025 +*/ +namespace detail { + +template +class empty_value_base + : public T { +public: + empty_value_base() = default; + + template + constexpr empty_value_base(U&& value, Args&&... args) + : T(std::forward(value), std::forward(args)...) { } +}; + +} +#endif + template class empty_value +#ifdef BOOST_MSVC + : detail::empty_value_base { + typedef detail::empty_value_base empty_base_; +#else : T { + typedef T empty_base_; +#endif + public: typedef T type; empty_value() = default; constexpr empty_value(boost::empty_init_t) - : T() { } + : empty_base_() { } template constexpr empty_value(boost::empty_init_t, U&& value, Args&&... args) - : T(std::forward(value), std::forward(args)...) { } + : empty_base_(std::forward(value), std::forward(args)...) { } constexpr const T& get() const noexcept { return *this; @@ -1028,41 +1242,1042 @@ namespace boost { constexpr static bool const value = false; }; - template - struct is_allocator().allocate(std::size_t{}))> > - { - constexpr static bool const value = true; - }; + template + struct is_allocator().allocate(std::size_t{}))> > + { + constexpr static bool const value = true; + }; + + template + constexpr bool const is_allocator_v = is_allocator::value; + + template + constexpr bool const is_hash_v = + !std::is_integral::value && !is_allocator_v; + + template constexpr bool const is_pred_v = !is_allocator_v

; + + template + using iter_key_t = + typename std::iterator_traits::value_type::first_type; + template + using iter_val_t = + typename std::iterator_traits::value_type::second_type; + template + using iter_to_alloc_t = + typename std::pair const, iter_val_t >; +#endif + +#if BOOST_CXX_VERSION < 201703L + template + constexpr typename std::add_const::type& as_const(T& t) noexcept + { + return t; + } + template void as_const(const T&&) = delete; +#else + using std::as_const; +#endif + } // namespace detail + } // namespace unordered +} // namespace boost + +#endif // BOOST_UNORDERED_DETAIL_TYPE_TRAITS_HPP +/* Hash function characterization. + * + * Copyright 2022-2024 Joaquin M Lopez Munoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_HASH_TRAITS_HPP +#define BOOST_UNORDERED_HASH_TRAITS_HPP + +namespace boost{ +namespace unordered{ + +namespace detail{ + +template +struct hash_is_avalanching_impl:std::false_type{}; + +template +struct avalanching_value +{ + static constexpr bool value=IsAvalanching::value; +}; + +/* may be explicitly marked as BOOST_DEPRECATED in the future */ +template<> struct avalanching_value +{ + static constexpr bool value=true; +}; + +template +struct hash_is_avalanching_impl< + Hash, + boost::unordered::detail::void_t +>:std::integral_constant< + bool, + avalanching_value::value +>{}; + +template +struct hash_is_avalanching_impl< + Hash, + typename std::enable_if<((void)Hash::is_avalanching,true)>::type +>{}; + +} + +/* Each trait can be partially specialized by users for concrete hash functions + * when actual characterization differs from default. + */ + +/* hash_is_avalanching::value is: + * - false if Hash::is_avalanching is not present. + * - Hash::is_avalanching::value if this is present and constexpr-convertible + * to a bool. + * - true if Hash::is_avalanching is void (deprecated). + * - ill-formed otherwise. + */ +template +struct hash_is_avalanching: detail::hash_is_avalanching_impl::type{}; + +} +} + +#endif +// Copyright 2024 Braden Ganetsky +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +// Generated on 2024-08-25T17:48:54 + +#ifndef BOOST_UNORDERED_UNORDERED_PRINTERS_HPP +#define BOOST_UNORDERED_UNORDERED_PRINTERS_HPP + +#ifndef BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS +#ifdef __ELF__ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Woverlength-strings" +#endif +__asm__(".pushsection \".debug_gdb_scripts\", \"MS\",@progbits,1\n" + ".ascii \"\\4gdb.inlined-script.BOOST_UNORDERED_UNORDERED_PRINTERS_HPP\\n\"\n" + ".ascii \"import gdb.printing\\n\"\n" + ".ascii \"import gdb.xmethod\\n\"\n" + ".ascii \"import re\\n\"\n" + ".ascii \"import math\\n\"\n" + + ".ascii \"class BoostUnorderedHelpers:\\n\"\n" + ".ascii \" def maybe_unwrap_atomic(n):\\n\"\n" + ".ascii \" if f\\\"{n.type.strip_typedefs()}\\\".startswith(\\\"std::atomic<\\\"):\\n\"\n" + ".ascii \" underlying_type = n.type.template_argument(0)\\n\"\n" + ".ascii \" return n.cast(underlying_type)\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" return n\\n\"\n" + + ".ascii \" def maybe_unwrap_foa_element(e):\\n\"\n" + ".ascii \" if f\\\"{e.type.strip_typedefs()}\\\".startswith(\\\"boost::unordered::detail::foa::element_type<\\\"):\\n\"\n" + ".ascii \" return e[\\\"p\\\"]\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" return e\\n\"\n" + + ".ascii \" def maybe_unwrap_reference(value):\\n\"\n" + ".ascii \" if value.type.code == gdb.TYPE_CODE_REF:\\n\"\n" + ".ascii \" return value.referenced_value()\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" return value\\n\"\n" + + ".ascii \" def countr_zero(n):\\n\"\n" + ".ascii \" for i in range(32):\\n\"\n" + ".ascii \" if (n & (1 << i)) != 0:\\n\"\n" + ".ascii \" return i\\n\"\n" + ".ascii \" return 32\\n\"\n" + + ".ascii \"class BoostUnorderedPointerCustomizationPoint:\\n\"\n" + ".ascii \" def __init__(self, any_ptr):\\n\"\n" + ".ascii \" vis = gdb.default_visualizer(any_ptr)\\n\"\n" + ".ascii \" if vis is None:\\n\"\n" + ".ascii \" self.to_address = lambda ptr: ptr\\n\"\n" + ".ascii \" self.next = lambda ptr, offset: ptr + offset\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" self.to_address = lambda ptr: ptr if (ptr.type.code == gdb.TYPE_CODE_PTR) else type(vis).boost_to_address(ptr)\\n\"\n" + ".ascii \" self.next = lambda ptr, offset: type(vis).boost_next(ptr, offset)\\n\"\n" + + ".ascii \"class BoostUnorderedFcaPrinter:\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)\\n\"\n" + ".ascii \" self.name = f\\\"{self.val.type.strip_typedefs()}\\\".split(\\\"<\\\")[0]\\n\"\n" + ".ascii \" self.name = self.name.replace(\\\"boost::unordered::\\\", \\\"boost::\\\")\\n\"\n" + ".ascii \" self.is_map = self.name.endswith(\\\"map\\\")\\n\"\n" + ".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"table_\\\"][\\\"buckets_\\\"][\\\"buckets\\\"])\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" size = self.val[\\\"table_\\\"][\\\"size_\\\"]\\n\"\n" + ".ascii \" return f\\\"{self.name} with {size} elements\\\"\\n\"\n" + + ".ascii \" def display_hint(self):\\n\"\n" + ".ascii \" return \\\"map\\\"\\n\"\n" + + ".ascii \" def children(self):\\n\"\n" + ".ascii \" def generator():\\n\"\n" + ".ascii \" grouped_buckets = self.val[\\\"table_\\\"][\\\"buckets_\\\"]\\n\"\n" + + ".ascii \" size = grouped_buckets[\\\"size_\\\"]\\n\"\n" + ".ascii \" buckets = grouped_buckets[\\\"buckets\\\"]\\n\"\n" + ".ascii \" bucket_index = 0\\n\"\n" + + ".ascii \" count = 0\\n\"\n" + ".ascii \" while bucket_index != size:\\n\"\n" + ".ascii \" current_bucket = self.cpo.next(self.cpo.to_address(buckets), bucket_index)\\n\"\n" + ".ascii \" node = self.cpo.to_address(current_bucket.dereference()[\\\"next\\\"])\\n\"\n" + ".ascii \" while node != 0:\\n\"\n" + ".ascii \" value = node.dereference()[\\\"buf\\\"][\\\"t_\\\"]\\n\"\n" + ".ascii \" if self.is_map:\\n\"\n" + ".ascii \" first = value[\\\"first\\\"]\\n\"\n" + ".ascii \" second = value[\\\"second\\\"]\\n\"\n" + ".ascii \" yield \\\"\\\", first\\n\"\n" + ".ascii \" yield \\\"\\\", second\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" yield \\\"\\\", count\\n\"\n" + ".ascii \" yield \\\"\\\", value\\n\"\n" + ".ascii \" count += 1\\n\"\n" + ".ascii \" node = self.cpo.to_address(node.dereference()[\\\"next\\\"])\\n\"\n" + ".ascii \" bucket_index += 1\\n\"\n" + + ".ascii \" return generator()\\n\"\n" + + ".ascii \"class BoostUnorderedFcaIteratorPrinter:\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + ".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"p\\\"])\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" if self.valid():\\n\"\n" + ".ascii \" value = self.cpo.to_address(self.val[\\\"p\\\"]).dereference()[\\\"buf\\\"][\\\"t_\\\"]\\n\"\n" + ".ascii \" return f\\\"iterator = {{ {value} }}\\\"\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" return \\\"iterator = { end iterator }\\\"\\n\"\n" + + ".ascii \" def valid(self):\\n\"\n" + ".ascii \" return (self.cpo.to_address(self.val[\\\"p\\\"]) != 0) and (self.cpo.to_address(self.val[\\\"itb\\\"][\\\"p\\\"]) != 0)\\n\"\n" + + ".ascii \"class BoostUnorderedFoaTableCoreCumulativeStatsPrinter:\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" return \\\"[stats]\\\"\\n\"\n" + + ".ascii \" def display_hint(self):\\n\"\n" + ".ascii \" return \\\"map\\\"\\n\"\n" + + ".ascii \" def children(self):\\n\"\n" + ".ascii \" def generator():\\n\"\n" + ".ascii \" members = [\\\"insertion\\\", \\\"successful_lookup\\\", \\\"unsuccessful_lookup\\\"]\\n\"\n" + ".ascii \" for member in members:\\n\"\n" + ".ascii \" yield \\\"\\\", member\\n\"\n" + ".ascii \" yield \\\"\\\", self.val[member]\\n\"\n" + ".ascii \" return generator()\\n\"\n" + + ".ascii \"class BoostUnorderedFoaCumulativeStatsPrinter:\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + ".ascii \" self.n = self.val[\\\"n\\\"]\\n\"\n" + ".ascii \" self.N = self.val.type.template_argument(0)\\n\"\n" + + ".ascii \" def display_hint(self):\\n\"\n" + ".ascii \" return \\\"map\\\"\\n\"\n" + + ".ascii \" def children(self):\\n\"\n" + ".ascii \" def generator():\\n\"\n" + ".ascii \" yield \\\"\\\", \\\"count\\\"\\n\"\n" + ".ascii \" yield \\\"\\\", self.n\\n\"\n" + + ".ascii \" sequence_stats_data = gdb.lookup_type(\\\"boost::unordered::detail::foa::sequence_stats_data\\\")\\n\"\n" + ".ascii \" data = self.val[\\\"data\\\"]\\n\"\n" + ".ascii \" arr = data.address.reinterpret_cast(sequence_stats_data.pointer())\\n\"\n" + ".ascii \" def build_string(idx):\\n\"\n" + ".ascii \" entry = arr[idx]\\n\"\n" + ".ascii \" avg = float(entry[\\\"m\\\"])\\n\"\n" + ".ascii \" var = float(entry[\\\"s\\\"] / self.n) if (self.n != 0) else 0.0\\n\"\n" + ".ascii \" dev = math.sqrt(var)\\n\"\n" + ".ascii \" return f\\\"{{avg = {avg}, var = {var}, dev = {dev}}}\\\"\\n\"\n" + + ".ascii \" if self.N > 0:\\n\"\n" + ".ascii \" yield \\\"\\\", \\\"probe_length\\\"\\n\"\n" + ".ascii \" yield \\\"\\\", build_string(0)\\n\"\n" + ".ascii \" if self.N > 1:\\n\"\n" + ".ascii \" yield \\\"\\\", \\\"num_comparisons\\\"\\n\"\n" + ".ascii \" yield \\\"\\\", build_string(1)\\n\"\n" + + ".ascii \" return generator()\\n\"\n" + + ".ascii \"class BoostUnorderedFoaPrinter:\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)\\n\"\n" + ".ascii \" self.name = f\\\"{self.val.type.strip_typedefs()}\\\".split(\\\"<\\\")[0]\\n\"\n" + ".ascii \" self.name = self.name.replace(\\\"boost::unordered::\\\", \\\"boost::\\\")\\n\"\n" + ".ascii \" self.is_map = self.name.endswith(\\\"map\\\")\\n\"\n" + ".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"table_\\\"][\\\"arrays\\\"][\\\"groups_\\\"])\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" size = BoostUnorderedHelpers.maybe_unwrap_atomic(self.val[\\\"table_\\\"][\\\"size_ctrl\\\"][\\\"size\\\"])\\n\"\n" + ".ascii \" return f\\\"{self.name} with {size} elements\\\"\\n\"\n" + + ".ascii \" def display_hint(self):\\n\"\n" + ".ascii \" return \\\"map\\\"\\n\"\n" + + ".ascii \" def is_regular_layout(self, group):\\n\"\n" + ".ascii \" typename = group[\\\"m\\\"].type.strip_typedefs()\\n\"\n" + ".ascii \" array_size = typename.sizeof // typename.target().sizeof\\n\"\n" + ".ascii \" if array_size == 16:\\n\"\n" + ".ascii \" return True\\n\"\n" + ".ascii \" elif array_size == 2:\\n\"\n" + ".ascii \" return False\\n\"\n" + + ".ascii \" def match_occupied(self, group):\\n\"\n" + ".ascii \" m = group[\\\"m\\\"]\\n\"\n" + ".ascii \" at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b][\\\"n\\\"])\\n\"\n" + + ".ascii \" if self.is_regular_layout(group):\\n\"\n" + ".ascii \" bits = [1 << b for b in range(16) if at(b) == 0]\\n\"\n" + ".ascii \" return 0x7FFF & ~sum(bits)\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" xx = at(0) | at(1)\\n\"\n" + ".ascii \" yy = xx | (xx >> 32)\\n\"\n" + ".ascii \" return 0x7FFF & (yy | (yy >> 16))\\n\"\n" + + ".ascii \" def is_sentinel(self, group, pos):\\n\"\n" + ".ascii \" m = group[\\\"m\\\"]\\n\"\n" + ".ascii \" at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b][\\\"n\\\"])\\n\"\n" + + ".ascii \" N = group[\\\"N\\\"]\\n\"\n" + ".ascii \" sentinel_ = group[\\\"sentinel_\\\"]\\n\"\n" + ".ascii \" if self.is_regular_layout(group):\\n\"\n" + ".ascii \" return pos == N-1 and at(N-1) == sentinel_\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" return pos == N-1 and (at(0) & 0x4000400040004000) == 0x4000 and (at(1) & 0x4000400040004000) == 0\\n\"\n" + + ".ascii \" def children(self):\\n\"\n" + ".ascii \" def generator():\\n\"\n" + ".ascii \" table = self.val[\\\"table_\\\"]\\n\"\n" + ".ascii \" groups = self.cpo.to_address(table[\\\"arrays\\\"][\\\"groups_\\\"])\\n\"\n" + ".ascii \" elements = self.cpo.to_address(table[\\\"arrays\\\"][\\\"elements_\\\"])\\n\"\n" + + ".ascii \" pc_ = groups.cast(gdb.lookup_type(\\\"unsigned char\\\").pointer())\\n\"\n" + ".ascii \" p_ = elements\\n\"\n" + ".ascii \" first_time = True\\n\"\n" + ".ascii \" mask = 0\\n\"\n" + ".ascii \" n0 = 0\\n\"\n" + ".ascii \" n = 0\\n\"\n" + + ".ascii \" count = 0\\n\"\n" + ".ascii \" while p_ != 0:\\n\"\n" + ".ascii \" # This if block mirrors the condition in the begin() call\\n\"\n" + ".ascii \" if (not first_time) or (self.match_occupied(groups.dereference()) & 1):\\n\"\n" + ".ascii \" pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(p_)\\n\"\n" + ".ascii \" value = self.cpo.to_address(pointer).dereference()\\n\"\n" + ".ascii \" if self.is_map:\\n\"\n" + ".ascii \" first = value[\\\"first\\\"]\\n\"\n" + ".ascii \" second = value[\\\"second\\\"]\\n\"\n" + ".ascii \" yield \\\"\\\", first\\n\"\n" + ".ascii \" yield \\\"\\\", second\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" yield \\\"\\\", count\\n\"\n" + ".ascii \" yield \\\"\\\", value\\n\"\n" + ".ascii \" count += 1\\n\"\n" + ".ascii \" first_time = False\\n\"\n" + + ".ascii \" n0 = pc_.cast(gdb.lookup_type(\\\"uintptr_t\\\")) % groups.dereference().type.sizeof\\n\"\n" + ".ascii \" pc_ = self.cpo.next(pc_, -n0)\\n\"\n" + + ".ascii \" mask = (self.match_occupied(pc_.cast(groups.type).dereference()) >> (n0+1)) << (n0+1)\\n\"\n" + ".ascii \" while mask == 0:\\n\"\n" + ".ascii \" pc_ = self.cpo.next(pc_, groups.dereference().type.sizeof)\\n\"\n" + ".ascii \" p_ = self.cpo.next(p_, groups.dereference()[\\\"N\\\"])\\n\"\n" + ".ascii \" mask = self.match_occupied(pc_.cast(groups.type).dereference())\\n\"\n" + + ".ascii \" n = BoostUnorderedHelpers.countr_zero(mask)\\n\"\n" + ".ascii \" if self.is_sentinel(pc_.cast(groups.type).dereference(), n):\\n\"\n" + ".ascii \" p_ = 0\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" pc_ = self.cpo.next(pc_, n)\\n\"\n" + ".ascii \" p_ = self.cpo.next(p_, n - n0)\\n\"\n" + + ".ascii \" return generator()\\n\"\n" + + ".ascii \"class BoostUnorderedFoaIteratorPrinter:\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + ".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"p_\\\"])\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" if self.valid():\\n\"\n" + ".ascii \" element = self.cpo.to_address(self.val[\\\"p_\\\"])\\n\"\n" + ".ascii \" pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(element)\\n\"\n" + ".ascii \" value = self.cpo.to_address(pointer).dereference()\\n\"\n" + ".ascii \" return f\\\"iterator = {{ {value} }}\\\"\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" return \\\"iterator = { end iterator }\\\"\\n\"\n" + + ".ascii \" def valid(self):\\n\"\n" + ".ascii \" return (self.cpo.to_address(self.val[\\\"p_\\\"]) != 0) and (self.cpo.to_address(self.val[\\\"pc_\\\"]) != 0)\\n\"\n" + + ".ascii \"def boost_unordered_build_pretty_printer():\\n\"\n" + ".ascii \" pp = gdb.printing.RegexpCollectionPrettyPrinter(\\\"boost_unordered\\\")\\n\"\n" + ".ascii \" add_template_printer = lambda name, printer: pp.add_printer(name, f\\\"^{name}<.*>$\\\", printer)\\n\"\n" + ".ascii \" add_concrete_printer = lambda name, printer: pp.add_printer(name, f\\\"^{name}$\\\", printer)\\n\"\n" + + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_map\\\", BoostUnorderedFcaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_multimap\\\", BoostUnorderedFcaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_set\\\", BoostUnorderedFcaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_multiset\\\", BoostUnorderedFcaPrinter)\\n\"\n" + + ".ascii \" add_template_printer(\\\"boost::unordered::detail::iterator_detail::iterator\\\", BoostUnorderedFcaIteratorPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::detail::iterator_detail::c_iterator\\\", BoostUnorderedFcaIteratorPrinter)\\n\"\n" + + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_flat_map\\\", BoostUnorderedFoaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_flat_set\\\", BoostUnorderedFoaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_node_map\\\", BoostUnorderedFoaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_node_set\\\", BoostUnorderedFoaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::concurrent_flat_map\\\", BoostUnorderedFoaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::concurrent_flat_set\\\", BoostUnorderedFoaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::concurrent_node_map\\\", BoostUnorderedFoaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::concurrent_node_set\\\", BoostUnorderedFoaPrinter)\\n\"\n" + + ".ascii \" add_template_printer(\\\"boost::unordered::detail::foa::table_iterator\\\", BoostUnorderedFoaIteratorPrinter)\\n\"\n" + + ".ascii \" add_concrete_printer(\\\"boost::unordered::detail::foa::table_core_cumulative_stats\\\", BoostUnorderedFoaTableCoreCumulativeStatsPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::detail::foa::cumulative_stats\\\", BoostUnorderedFoaCumulativeStatsPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::detail::foa::concurrent_cumulative_stats\\\", BoostUnorderedFoaCumulativeStatsPrinter)\\n\"\n" + + ".ascii \" return pp\\n\"\n" + + ".ascii \"gdb.printing.register_pretty_printer(gdb.current_objfile(), boost_unordered_build_pretty_printer())\\n\"\n" + + ".ascii \"# https://sourceware.org/gdb/current/onlinedocs/gdb.html/Writing-an-Xmethod.html\\n\"\n" + ".ascii \"class BoostUnorderedFoaGetStatsMethod(gdb.xmethod.XMethod):\\n\"\n" + ".ascii \" def __init__(self):\\n\"\n" + ".ascii \" gdb.xmethod.XMethod.__init__(self, \\\"get_stats\\\")\\n\"\n" + + ".ascii \" def get_worker(self, method_name):\\n\"\n" + ".ascii \" if method_name == \\\"get_stats\\\":\\n\"\n" + ".ascii \" return BoostUnorderedFoaGetStatsWorker()\\n\"\n" + + ".ascii \"class BoostUnorderedFoaGetStatsWorker(gdb.xmethod.XMethodWorker):\\n\"\n" + ".ascii \" def get_arg_types(self):\\n\"\n" + ".ascii \" return None\\n\"\n" + + ".ascii \" def get_result_type(self, obj):\\n\"\n" + ".ascii \" return gdb.lookup_type(\\\"boost::unordered::detail::foa::table_core_cumulative_stats\\\")\\n\"\n" + + ".ascii \" def __call__(self, obj):\\n\"\n" + ".ascii \" try:\\n\"\n" + ".ascii \" return obj[\\\"table_\\\"][\\\"cstats\\\"]\\n\"\n" + ".ascii \" except gdb.error:\\n\"\n" + ".ascii \" print(\\\"Error: Binary was compiled without stats. Recompile with `BOOST_UNORDERED_ENABLE_STATS` defined.\\\")\\n\"\n" + ".ascii \" return\\n\"\n" + + ".ascii \"class BoostUnorderedFoaMatcher(gdb.xmethod.XMethodMatcher):\\n\"\n" + ".ascii \" def __init__(self):\\n\"\n" + ".ascii \" gdb.xmethod.XMethodMatcher.__init__(self, 'BoostUnorderedFoaMatcher')\\n\"\n" + ".ascii \" self.methods = [BoostUnorderedFoaGetStatsMethod()]\\n\"\n" + + ".ascii \" def match(self, class_type, method_name):\\n\"\n" + ".ascii \" template_name = f\\\"{class_type.strip_typedefs()}\\\".split(\\\"<\\\")[0]\\n\"\n" + ".ascii \" regex = \\\"^boost::unordered::(unordered|concurrent)_(flat|node)_(map|set)$\\\"\\n\"\n" + ".ascii \" if not re.match(regex, template_name):\\n\"\n" + ".ascii \" return None\\n\"\n" + + ".ascii \" workers = []\\n\"\n" + ".ascii \" for method in self.methods:\\n\"\n" + ".ascii \" if method.enabled:\\n\"\n" + ".ascii \" worker = method.get_worker(method_name)\\n\"\n" + ".ascii \" if worker:\\n\"\n" + ".ascii \" workers.append(worker)\\n\"\n" + ".ascii \" return workers\\n\"\n" + + ".ascii \"gdb.xmethod.register_xmethod_matcher(None, BoostUnorderedFoaMatcher())\\n\"\n" + + ".ascii \"\\\"\\\"\\\" Fancy pointer support \\\"\\\"\\\"\\n\"\n" + + ".ascii \"\\\"\\\"\\\"\\n\"\n" + ".ascii \"To allow your own fancy pointer type to interact with Boost.Unordered GDB pretty-printers,\\n\"\n" + ".ascii \"create a pretty-printer for your own type with the following additional methods.\\n\"\n" + + ".ascii \"(Note, this is assuming the presence of a type alias `pointer` for the underlying\\n\"\n" + ".ascii \"raw pointer type, Substitute whichever name is applicable in your case.)\\n\"\n" + + ".ascii \"`boost_to_address(fancy_ptr)`\\n\"\n" + ".ascii \" * A static method, but `@staticmethod` is not required\\n\"\n" + ".ascii \" * Parameter `fancy_ptr` of type `gdb.Value`\\n\"\n" + ".ascii \" * Its `.type` will be your fancy pointer type\\n\"\n" + ".ascii \" * Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer\\n\"\n" + ".ascii \" * This method should be equivalent to calling `operator->()` on your fancy pointer in C++\\n\"\n" + + ".ascii \"`boost_next(raw_ptr, offset)`\\n\"\n" + ".ascii \" * Parameter `raw_ptr` of type `gdb.Value`\\n\"\n" + ".ascii \" * Its `.type` will be `pointer`\\n\"\n" + ".ascii \" * Parameter `offset`\\n\"\n" + ".ascii \" * Either has integer type, or is of type `gdb.Value` with an underlying integer\\n\"\n" + ".ascii \" * Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer, as if you did the following operations\\n\"\n" + ".ascii \" 1. Convert the incoming raw pointer to your fancy pointer\\n\"\n" + ".ascii \" 2. Use operator+= to add the offset to the fancy pointer\\n\"\n" + ".ascii \" 3. Convert back to the raw pointer\\n\"\n" + ".ascii \" * Note, you will not actually do these operations as stated. You will do equivalent lower-level operations that emulate having done the above\\n\"\n" + ".ascii \" * Ultimately, it will be as if you called `operator+()` on your fancy pointer in C++, but using only raw pointers\\n\"\n" + + ".ascii \"Example\\n\"\n" + ".ascii \"```\\n\"\n" + ".ascii \"class MyFancyPtrPrinter:\\n\"\n" + ".ascii \" ...\\n\"\n" + + ".ascii \" # Equivalent to `operator->()`\\n\"\n" + ".ascii \" def boost_to_address(fancy_ptr):\\n\"\n" + ".ascii \" ...\\n\"\n" + ".ascii \" return ...\\n\"\n" + + ".ascii \" # Equivalent to `operator+()`\\n\"\n" + ".ascii \" def boost_next(raw_ptr, offset):\\n\"\n" + ".ascii \" ...\\n\"\n" + ".ascii \" return ...\\n\"\n" + + ".ascii \" ...\\n\"\n" + ".ascii \"```\\n\"\n" + ".ascii \"\\\"\\\"\\\"\\n\"\n" + + ".byte 0\n" + ".popsection\n"); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#endif // defined(__ELF__) +#endif // !defined(BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS) + +#endif // !defined(BOOST_UNORDERED_UNORDERED_PRINTERS_HPP) +#ifndef BOOST_CORE_DETAIL_SP_THREAD_PAUSE_HPP_INCLUDED +#define BOOST_CORE_DETAIL_SP_THREAD_PAUSE_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// boost/core/detail/sp_thread_pause.hpp +// +// inline void bost::core::sp_thread_pause(); +// +// Emits a "pause" instruction. +// +// Copyright 2008, 2020, 2023 Peter Dimov +// Distributed under the Boost Software License, Version 1.0 +// https://www.boost.org/LICENSE_1_0.txt + +#ifdef __has_builtin +# if __has_builtin(__builtin_ia32_pause) && !defined(__INTEL_COMPILER) +# define BOOST_CORE_HAS_BUILTIN_IA32_PAUSE +# endif +#endif + +#ifdef BOOST_CORE_HAS_BUILTIN_IA32_PAUSE + +# define BOOST_CORE_SP_PAUSE() __builtin_ia32_pause() + +#elif defined(_MSC_VER) && ( defined(_M_IX86) || defined(_M_X64) ) + +# include +# define BOOST_CORE_SP_PAUSE() _mm_pause() + +#elif defined(_MSC_VER) && ( defined(_M_ARM) || defined(_M_ARM64) ) + +# include +# define BOOST_CORE_SP_PAUSE() __yield() + +#elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) + +# define BOOST_CORE_SP_PAUSE() __asm__ __volatile__( "rep; nop" : : : "memory" ) + +#elif defined(__GNUC__) && ( (defined(__ARM_ARCH) && __ARM_ARCH >= 8) || defined(__ARM_ARCH_8A__) || defined(__aarch64__) ) + +# define BOOST_CORE_SP_PAUSE() __asm__ __volatile__( "yield" : : : "memory" ) + +#else + +# define BOOST_CORE_SP_PAUSE() ((void)0) + +#endif + +namespace boost +{ +namespace core +{ + +BOOST_FORCEINLINE void sp_thread_pause() noexcept +{ + BOOST_CORE_SP_PAUSE(); +} + +} // namespace core +} // namespace boost + +#undef BOOST_CORE_SP_PAUSE + +#endif // #ifndef BOOST_CORE_DETAIL_SP_THREAD_PAUSE_HPP_INCLUDED +#ifndef BOOST_CORE_DETAIL_SP_WIN32_SLEEP_HPP_INCLUDED +#define BOOST_CORE_DETAIL_SP_WIN32_SLEEP_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// boost/core/detail/sp_win32_sleep.hpp +// +// Declares the Win32 Sleep() function. +// +// Copyright 2008, 2020 Peter Dimov +// Distributed under the Boost Software License, Version 1.0 +// https://www.boost.org/LICENSE_1_0.txt + +#ifdef BOOST_USE_WINDOWS_H +# include +#endif + +namespace boost +{ +namespace core +{ +namespace detail +{ + +#ifndef BOOST_USE_WINDOWS_H + +#if defined(__clang__) && defined(__x86_64__) +// clang x64 warns that __stdcall is ignored +# define BOOST_CORE_SP_STDCALL +#else +# define BOOST_CORE_SP_STDCALL __stdcall +#endif + +#ifdef __LP64__ // Cygwin 64 + extern "C" __declspec(dllimport) void BOOST_CORE_SP_STDCALL Sleep( unsigned int ms ); +#else + extern "C" __declspec(dllimport) void BOOST_CORE_SP_STDCALL Sleep( unsigned long ms ); +#endif + +extern "C" __declspec(dllimport) int BOOST_CORE_SP_STDCALL SwitchToThread(); + +#undef BOOST_CORE_SP_STDCALL + +#endif // !defined( BOOST_USE_WINDOWS_H ) + +} // namespace detail +} // namespace core +} // namespace boost + +#endif // #ifndef BOOST_CORE_DETAIL_SP_WIN32_SLEEP_HPP_INCLUDED +#ifndef BOOST_CORE_DETAIL_SP_THREAD_YIELD_HPP_INCLUDED +#define BOOST_CORE_DETAIL_SP_THREAD_YIELD_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// boost/core/detail/sp_thread_yield.hpp +// +// inline void bost::core::sp_thread_yield(); +// +// Gives up the remainder of the time slice, +// as if by calling sched_yield(). +// +// Copyright 2008, 2020 Peter Dimov +// Distributed under the Boost Software License, Version 1.0 +// https://www.boost.org/LICENSE_1_0.txt + +#if defined( _WIN32 ) || defined( __WIN32__ ) || defined( __CYGWIN__ ) + +#ifdef BOOST_SP_REPORT_IMPLEMENTATION + BOOST_PRAGMA_MESSAGE("Using SwitchToThread() in sp_thread_yield") +#endif + +namespace boost +{ +namespace core +{ +namespace detail +{ + +inline void sp_thread_yield() noexcept +{ + SwitchToThread(); +} + +} // namespace detail + +using boost::core::detail::sp_thread_yield; + +} // namespace core +} // namespace boost + +#elif defined(BOOST_HAS_SCHED_YIELD) + +#ifdef BOOST_SP_REPORT_IMPLEMENTATION + BOOST_PRAGMA_MESSAGE("Using sched_yield() in sp_thread_yield") +#endif + +#ifndef _AIX +# include +#else + // AIX's sched.h defines ::var which sometimes conflicts with Lambda's var + extern "C" int sched_yield(void); +#endif + +namespace boost +{ +namespace core +{ + +inline void sp_thread_yield() noexcept +{ + sched_yield(); +} + +} // namespace core +} // namespace boost + +#else + +#ifdef BOOST_SP_REPORT_IMPLEMENTATION + BOOST_PRAGMA_MESSAGE("Using sp_thread_pause() in sp_thread_yield") +#endif + +namespace boost +{ +namespace core +{ + +inline void sp_thread_yield() noexcept +{ + sp_thread_pause(); +} + +} // namespace core +} // namespace boost + +#endif + +#endif // #ifndef BOOST_CORE_DETAIL_SP_THREAD_YIELD_HPP_INCLUDED +#ifndef BOOST_CORE_DETAIL_SP_THREAD_SLEEP_HPP_INCLUDED +#define BOOST_CORE_DETAIL_SP_THREAD_SLEEP_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// boost/core/detail/sp_thread_sleep.hpp +// +// inline void bost::core::sp_thread_sleep(); +// +// Cease execution for a while to yield to other threads, +// as if by calling nanosleep() with an appropriate interval. +// +// Copyright 2008, 2020, 2023 Peter Dimov +// Distributed under the Boost Software License, Version 1.0 +// https://www.boost.org/LICENSE_1_0.txt + +#if defined( _WIN32 ) || defined( __WIN32__ ) || defined( __CYGWIN__ ) + +#ifdef BOOST_SP_REPORT_IMPLEMENTATION + BOOST_PRAGMA_MESSAGE("Using Sleep(1) in sp_thread_sleep") +#endif + +namespace boost +{ +namespace core +{ +namespace detail +{ + +inline void sp_thread_sleep() noexcept +{ + Sleep( 1 ); +} + +} // namespace detail + +using boost::core::detail::sp_thread_sleep; + +} // namespace core +} // namespace boost + +#elif defined(BOOST_HAS_NANOSLEEP) + +#ifdef BOOST_SP_REPORT_IMPLEMENTATION + BOOST_PRAGMA_MESSAGE("Using nanosleep() in sp_thread_sleep") +#endif + +#include + +#if defined(BOOST_HAS_PTHREADS) && !defined(__ANDROID__) +# include +#endif + +namespace boost +{ +namespace core +{ + +inline void sp_thread_sleep() noexcept +{ +#if defined(BOOST_HAS_PTHREADS) && !defined(__ANDROID__) + + int oldst; + pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldst ); + +#endif + + // g++ -Wextra warns on {} or {0} + struct timespec rqtp = { 0, 0 }; + + // POSIX says that timespec has tv_sec and tv_nsec + // But it doesn't guarantee order or placement + + rqtp.tv_sec = 0; + rqtp.tv_nsec = 1000; + + nanosleep( &rqtp, 0 ); + +#if defined(BOOST_HAS_PTHREADS) && !defined(__ANDROID__) + + pthread_setcancelstate( oldst, &oldst ); + +#endif + +} + +} // namespace core +} // namespace boost + +#else + +#ifdef BOOST_SP_REPORT_IMPLEMENTATION + BOOST_PRAGMA_MESSAGE("Using sp_thread_yield() in sp_thread_sleep") +#endif + +namespace boost +{ +namespace core +{ + +inline void sp_thread_sleep() noexcept +{ + sp_thread_yield(); +} + +} // namespace core +} // namespace boost + +#endif + +#endif // #ifndef BOOST_CORE_DETAIL_SP_THREAD_SLEEP_HPP_INCLUDED +#ifndef BOOST_CORE_YIELD_PRIMITIVES_HPP_INCLUDED +#define BOOST_CORE_YIELD_PRIMITIVES_HPP_INCLUDED + +// Copyright 2023 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#endif // #ifndef BOOST_CORE_YIELD_PRIMITIVES_HPP_INCLUDED +#ifndef BOOST_UNORDERED_DETAIL_FOA_RW_SPINLOCK_HPP_INCLUDED +#define BOOST_UNORDERED_DETAIL_FOA_RW_SPINLOCK_HPP_INCLUDED + +// Copyright 2023 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +namespace boost{ +namespace unordered{ +namespace detail{ +namespace foa{ + +class rw_spinlock +{ +private: + + // bit 31: locked exclusive + // bit 30: writer pending + // bit 29..0: reader lock count + + static constexpr std::uint32_t locked_exclusive_mask = 1u << 31; // 0x8000'0000 + static constexpr std::uint32_t writer_pending_mask = 1u << 30; // 0x4000'0000 + static constexpr std::uint32_t reader_lock_count_mask = writer_pending_mask - 1; // 0x3FFF'FFFF + + std::atomic state_ = {}; + +private: + + // Effects: Provides a hint to the implementation that the current thread + // has been unable to make progress for k+1 iterations. + + static void yield( unsigned k ) noexcept + { + unsigned const sleep_every = 1024; // see below + + k %= sleep_every; + + if( k < 5 ) + { + // Intel recommendation from the Optimization Reference Manual + // Exponentially increase number of PAUSE instructions each + // iteration until reaching a maximum which is approximately + // one timeslice long (2^4 == 16 in our case) + + unsigned const pause_count = 1u << k; + + for( unsigned i = 0; i < pause_count; ++i ) + { + boost::core::sp_thread_pause(); + } + } + else if( k < sleep_every - 1 ) + { + // Once the maximum number of PAUSE instructions is reached, + // we switch to yielding the timeslice immediately + + boost::core::sp_thread_yield(); + } + else + { + // After `sleep_every` iterations of no progress, we sleep, + // to avoid a deadlock if a lower priority thread has the lock + + boost::core::sp_thread_sleep(); + } + } + +public: + + bool try_lock_shared() noexcept + { + std::uint32_t st = state_.load( std::memory_order_relaxed ); + + if( st >= reader_lock_count_mask ) + { + // either bit 31 set, bit 30 set, or reader count is max + return false; + } + + std::uint32_t newst = st + 1; + return state_.compare_exchange_strong( st, newst, std::memory_order_acquire, std::memory_order_relaxed ); + } + + void lock_shared() noexcept + { + for( unsigned k = 0; ; ++k ) + { + std::uint32_t st = state_.load( std::memory_order_relaxed ); + + if( st < reader_lock_count_mask ) + { + std::uint32_t newst = st + 1; + if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return; + } + + yield( k ); + } + } + + void unlock_shared() noexcept + { + // pre: locked shared, not locked exclusive + + state_.fetch_sub( 1, std::memory_order_release ); + + // if the writer pending bit is set, there's a writer waiting + // let it acquire the lock; it will clear the bit on unlock + } + + bool try_lock() noexcept + { + std::uint32_t st = state_.load( std::memory_order_relaxed ); + + if( st & locked_exclusive_mask ) + { + // locked exclusive + return false; + } + + if( st & reader_lock_count_mask ) + { + // locked shared + return false; + } + + std::uint32_t newst = locked_exclusive_mask; + return state_.compare_exchange_strong( st, newst, std::memory_order_acquire, std::memory_order_relaxed ); + } + + void lock() noexcept + { + for( unsigned k = 0; ; ++k ) + { + std::uint32_t st = state_.load( std::memory_order_relaxed ); + + if( st & locked_exclusive_mask ) + { + // locked exclusive, spin + } + else if( ( st & reader_lock_count_mask ) == 0 ) + { + // not locked exclusive, not locked shared, try to lock + + std::uint32_t newst = locked_exclusive_mask; + if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return; + } + else if( st & writer_pending_mask ) + { + // writer pending bit already set, nothing to do + } + else + { + // locked shared, set writer pending bit - template - constexpr bool const is_allocator_v = is_allocator::value; + std::uint32_t newst = st | writer_pending_mask; + state_.compare_exchange_weak( st, newst, std::memory_order_relaxed, std::memory_order_relaxed ); + } - template - constexpr bool const is_hash_v = - !std::is_integral::value && !is_allocator_v; + yield( k ); + } + } - template constexpr bool const is_pred_v = !is_allocator_v

; + void unlock() noexcept + { + // pre: locked exclusive, not locked shared + state_.store( 0, std::memory_order_release ); + } +}; - template - using iter_key_t = - typename std::iterator_traits::value_type::first_type; - template - using iter_val_t = - typename std::iterator_traits::value_type::second_type; - template - using iter_to_alloc_t = - typename std::pair const, iter_val_t >; -#endif - } // namespace detail - } // namespace unordered -} // namespace boost +} +} +} +} -#endif // BOOST_UNORDERED_DETAIL_TYPE_TRAITS_HPP -/* Hash function characterization. - * - * Copyright 2022 Joaquin M Lopez Munoz. +#endif // BOOST_UNORDERED_DETAIL_FOA_RW_SPINLOCK_HPP_INCLUDED +/* Copyright 2024 Joaquin M Lopez Munoz. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -1070,34 +2285,166 @@ namespace boost { * See https://www.boost.org/libs/unordered for library home page. */ -#ifndef BOOST_UNORDERED_HASH_TRAITS_HPP -#define BOOST_UNORDERED_HASH_TRAITS_HPP +#ifndef BOOST_UNORDERED_DETAIL_FOA_CUMULATIVE_STATS_HPP +#define BOOST_UNORDERED_DETAIL_FOA_CUMULATIVE_STATS_HPP + +#include + +#ifdef BOOST_HAS_THREADS +#include +#endif namespace boost{ namespace unordered{ - namespace detail{ +namespace foa{ -template -struct hash_is_avalanching_impl: std::false_type{}; +/* Cumulative one-pass calculation of the average, variance and deviation of + * running sequences. + */ -template -struct hash_is_avalanching_impl >: - std::true_type{}; +struct sequence_stats_data +{ + double m=0.0; + double m_prior=0.0; + double s=0.0; +}; -} +struct welfords_algorithm +{ + template + int operator()(T&& x,sequence_stats_data& d)const noexcept + { + static_assert( + noexcept(static_cast(x)), + "Argument conversion to double must not throw."); -/* Each trait can be partially specialized by users for concrete hash functions - * when actual characterization differs from default. - */ + d.m_prior=d.m; + d.m+=(static_cast(x)-d.m)/static_cast(n); + d.s+=(n!=1)* + (static_cast(x)-d.m_prior)*(static_cast(x)-d.m); + + return 0; + } + + std::size_t n; +}; + +struct sequence_stats_summary +{ + double average; + double variance; + double deviation; +}; -/* hash_is_avalanching::value is true when the type Hash::is_avalanching - * is present, false otherwise. +/* Stats calculated jointly for N same-sized sequences to save the space + * for count. */ -template -struct hash_is_avalanching: detail::hash_is_avalanching_impl::type{}; +template +class cumulative_stats +{ +public: + struct summary + { + std::size_t count; + std::array sequence_summary; + }; + + void reset()noexcept{*this=cumulative_stats();} + + template + void add(Ts&&... xs)noexcept + { + static_assert( + sizeof...(Ts)==N,"A sample must be provided for each sequence."); + + if(BOOST_UNLIKELY(++n==0)){ + reset(); + n=1; + } + mp11::tuple_transform( + welfords_algorithm{n}, + std::forward_as_tuple(std::forward(xs)...), + data); + } + + summary get_summary()const noexcept + { + summary res; + res.count=n; + for(std::size_t i=0;i(n):0.0, + deviation=std::sqrt(variance); + res.sequence_summary[i]={average,variance,deviation}; + } + return res; + } + +private: + std::size_t n=0; + std::array data; +}; + +#ifdef BOOST_HAS_THREADS + +template +class concurrent_cumulative_stats:cumulative_stats +{ + using super=cumulative_stats; + using lock_guard=std::lock_guard; + +public: + using summary=typename super::summary; + + concurrent_cumulative_stats()noexcept:super{}{} + concurrent_cumulative_stats(const concurrent_cumulative_stats& x)noexcept: + concurrent_cumulative_stats{x,lock_guard{x.mut}}{} + + concurrent_cumulative_stats& + operator=(const concurrent_cumulative_stats& x)noexcept + { + auto x1=x; + lock_guard lck{mut}; + static_cast(*this)=x1; + return *this; + } + + void reset()noexcept + { + lock_guard lck{mut}; + super::reset(); + } + + template + void add(Ts&&... xs)noexcept + { + lock_guard lck{mut}; + super::add(std::forward(xs)...); + } + + summary get_summary()const noexcept + { + lock_guard lck{mut}; + return super::get_summary(); + } + +private: + concurrent_cumulative_stats(const super& x,lock_guard&&):super{x}{} + + mutable rw_spinlock mut; +}; + +#else + +template +using concurrent_cumulative_stats=cumulative_stats; + +#endif + +} +} } } @@ -1986,6 +3333,7 @@ struct pow2_quadratic_prober pow2_quadratic_prober(std::size_t pos_):pos{pos_}{} inline std::size_t get()const{return pos;} + inline std::size_t length()const{return step+1;} /* next returns false when the whole array has been traversed, which ends * probing (in practice, full-table probing will only happen with very small @@ -2134,6 +3482,11 @@ struct table_arrays rebind; using group_type_pointer_traits=std::pointer_traits; + // For natvis purposes + using char_pointer= + typename std::pointer_traits::template + rebind; + table_arrays( std::size_t gsi,std::size_t gsm, group_type_pointer pg,value_type_pointer pe): @@ -2247,6 +3600,54 @@ struct table_arrays value_type_pointer elements_; }; +#ifdef BOOST_UNORDERED_ENABLE_STATS +/* stats support */ + +struct table_core_cumulative_stats +{ + concurrent_cumulative_stats<1> insertion; + concurrent_cumulative_stats<2> successful_lookup, + unsuccessful_lookup; +}; + +struct table_core_insertion_stats +{ + std::size_t count; + sequence_stats_summary probe_length; +}; + +struct table_core_lookup_stats +{ + std::size_t count; + sequence_stats_summary probe_length; + sequence_stats_summary num_comparisons; +}; + +struct table_core_stats +{ + table_core_insertion_stats insertion; + table_core_lookup_stats successful_lookup, + unsuccessful_lookup; +}; + +#define BOOST_UNORDERED_ADD_STATS(stats,args) stats.add args +#define BOOST_UNORDERED_SWAP_STATS(stats1,stats2) std::swap(stats1,stats2) +#define BOOST_UNORDERED_COPY_STATS(stats1,stats2) stats1=stats2 +#define BOOST_UNORDERED_RESET_STATS_OF(x) x.reset_stats() +#define BOOST_UNORDERED_STATS_COUNTER(name) std::size_t name=0 +#define BOOST_UNORDERED_INCREMENT_STATS_COUNTER(name) ++name + +#else + +#define BOOST_UNORDERED_ADD_STATS(stats,args) ((void)0) +#define BOOST_UNORDERED_SWAP_STATS(stats1,stats2) ((void)0) +#define BOOST_UNORDERED_COPY_STATS(stats1,stats2) ((void)0) +#define BOOST_UNORDERED_RESET_STATS_OF(x) ((void)0) +#define BOOST_UNORDERED_STATS_COUNTER(name) ((void)0) +#define BOOST_UNORDERED_INCREMENT_STATS_COUNTER(name) ((void)0) + +#endif + struct if_constexpr_void_else{void operator()()const{}}; template @@ -2507,6 +3908,16 @@ table_core:empty_value,empty_value,empty_value using locator=table_locator; using arrays_holder_type=arrays_holder; +#ifdef BOOST_UNORDERED_ENABLE_STATS + using cumulative_stats=table_core_cumulative_stats; + using stats=table_core_stats; +#endif + +#ifdef BOOST_GCC +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + table_core( std::size_t n=default_bucket_count,const Hash& h_=Hash(), const Pred& pred_=Pred(),const Allocator& al_=Allocator()): @@ -2515,6 +3926,10 @@ table_core:empty_value,empty_value,empty_value size_ctrl{initial_max_load(),0} {} +#ifdef BOOST_GCC +#pragma GCC diagnostic pop +#endif + /* genericize on an ArraysFn so that we can do things like delay an * allocation for the group_access data required by cfoa after the move * constructors of Hash, Pred have been invoked @@ -2541,6 +3956,7 @@ table_core:empty_value,empty_value,empty_value x.arrays=ah.release(); x.size_ctrl.ml=x.initial_max_load(); x.size_ctrl.size=0; + BOOST_UNORDERED_SWAP_STATS(cstats,x.cstats); } table_core(table_core&& x) @@ -2566,11 +3982,13 @@ table_core:empty_value,empty_value,empty_value using std::swap; swap(arrays,x.arrays); swap(size_ctrl,x.size_ctrl); + BOOST_UNORDERED_SWAP_STATS(cstats,x.cstats); } else{ reserve(x.size()); clear_on_exit c{x}; (void)c; + BOOST_UNORDERED_RESET_STATS_OF(x); /* This works because subsequent x.clear() does not depend on the * elements' values. @@ -2686,9 +4104,11 @@ table_core:empty_value,empty_value,empty_value arrays=x.arrays; size_ctrl.ml=std::size_t(x.size_ctrl.ml); size_ctrl.size=std::size_t(x.size_ctrl.size); + BOOST_UNORDERED_COPY_STATS(cstats,x.cstats); x.arrays=ah.release(); x.size_ctrl.ml=x.initial_max_load(); x.size_ctrl.size=0; + BOOST_UNORDERED_RESET_STATS_OF(x); } else{ swap(h(),x.h()); @@ -2698,6 +4118,7 @@ table_core:empty_value,empty_value,empty_value noshrink_reserve(x.size()); clear_on_exit c{x}; (void)c; + BOOST_UNORDERED_RESET_STATS_OF(x); /* This works because subsequent x.clear() does not depend on the * elements' values. @@ -2751,6 +4172,7 @@ table_core:empty_value,empty_value,empty_value BOOST_FORCEINLINE locator find( const Key& x,std::size_t pos0,std::size_t hash)const { + BOOST_UNORDERED_STATS_COUNTER(num_cmps); prober pb(pos0); do{ auto pos=pb.get(); @@ -2762,18 +4184,25 @@ table_core:empty_value,empty_value,empty_value auto p=elements+pos*N; BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N); do{ + BOOST_UNORDERED_INCREMENT_STATS_COUNTER(num_cmps); auto n=unchecked_countr_zero(mask); if(BOOST_LIKELY(bool(pred()(x,key_from(p[n]))))){ + BOOST_UNORDERED_ADD_STATS( + cstats.successful_lookup,(pb.length(),num_cmps)); return {pg,n,p+n}; } mask&=mask-1; }while(mask); } if(BOOST_LIKELY(pg->is_not_overflowed(hash))){ + BOOST_UNORDERED_ADD_STATS( + cstats.unsuccessful_lookup,(pb.length(),num_cmps)); return {}; } } while(BOOST_LIKELY(pb.next(arrays.groups_size_mask))); + BOOST_UNORDERED_ADD_STATS( + cstats.unsuccessful_lookup,(pb.length(),num_cmps)); return {}; } @@ -2858,6 +4287,38 @@ table_core:empty_value,empty_value,empty_value rehash(std::size_t(std::ceil(float(n)/mlf))); } +#ifdef BOOST_UNORDERED_ENABLE_STATS + stats get_stats()const + { + auto insertion=cstats.insertion.get_summary(); + auto successful_lookup=cstats.successful_lookup.get_summary(); + auto unsuccessful_lookup=cstats.unsuccessful_lookup.get_summary(); + return{ + { + insertion.count, + insertion.sequence_summary[0] + }, + { + successful_lookup.count, + successful_lookup.sequence_summary[0], + successful_lookup.sequence_summary[1] + }, + { + unsuccessful_lookup.count, + unsuccessful_lookup.sequence_summary[0], + unsuccessful_lookup.sequence_summary[1] + }, + }; + } + + void reset_stats()noexcept + { + cstats.insertion.reset(); + cstats.successful_lookup.reset(); + cstats.unsuccessful_lookup.reset(); + } +#endif + friend bool operator==(const table_core& x,const table_core& y) { return @@ -3065,8 +4526,12 @@ table_core:empty_value,empty_value,empty_value return true; } - arrays_type arrays; - size_ctrl_type size_ctrl; + arrays_type arrays; + size_ctrl_type size_ctrl; + +#ifdef BOOST_UNORDERED_ENABLE_STATS + mutable cumulative_stats cstats; +#endif private: template< @@ -3079,6 +4544,11 @@ table_core:empty_value,empty_value,empty_value using pred_base=empty_value; using allocator_base=empty_value; +#ifdef BOOST_GCC +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + /* used by allocator-extended move ctor */ table_core(Hash&& h_,Pred&& pred_,const Allocator& al_): @@ -3089,6 +4559,10 @@ table_core:empty_value,empty_value,empty_value { } +#ifdef BOOST_GCC +#pragma GCC diagnostic pop +#endif + arrays_type new_arrays(std::size_t n)const { return arrays_type::new_(typename arrays_type::allocator_type(al()),n); @@ -3355,6 +4829,7 @@ table_core:empty_value,empty_value,empty_value auto p=arrays_.elements()+pos*N+n; construct_element(p,std::forward(args)...); pg->set(n,hash); + BOOST_UNORDERED_ADD_STATS(cstats.insertion,(pb.length())); return {pg,n,p}; } else pg->mark_overflow(hash); @@ -3377,7 +4852,7 @@ table_core:empty_value,empty_value,empty_value #endif /* Fast open-addressing hash table. * - * Copyright 2022-2023 Joaquin M Lopez Munoz. + * Copyright 2022-2024 Joaquin M Lopez Munoz. * Copyright 2023 Christian Mazakas. * Copyright 2024 Braden Ganetsky. * Distributed under the Boost Software License, Version 1.0. @@ -3716,6 +5191,10 @@ class table:table_core_impl const_iterator>::type; using erase_return_type=table_erase_return_type; +#ifdef BOOST_UNORDERED_ENABLE_STATS + using stats=typename super::stats; +#endif + table( std::size_t n=default_bucket_count,const Hash& h_=Hash(), const Pred& pred_=Pred(),const Allocator& al_=Allocator()): @@ -3897,6 +5376,11 @@ class table:table_core_impl using super::rehash; using super::reserve; +#ifdef BOOST_UNORDERED_ENABLE_STATS + using super::get_stats; + using super::reset_stats; +#endif + template friend std::size_t erase_if(table& x,Predicate& pr) { @@ -3939,6 +5423,7 @@ class table:table_core_impl x.arrays=ah.release(); x.size_ctrl.ml=x.initial_max_load(); x.size_ctrl.size=0; + BOOST_UNORDERED_SWAP_STATS(this->cstats,x.cstats); } template @@ -4003,6 +5488,7 @@ class table:table_core_impl #endif // Copyright (C) 2022 Christian Mazakas +// Copyright (C) 2024 Braden Ganetsky // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -4032,6 +5518,15 @@ namespace boost { void swap(unordered_flat_set& lhs, unordered_flat_set& rhs) noexcept(noexcept(lhs.swap(rhs))); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class KeyEqual = std::equal_to > + using unordered_flat_set = boost::unordered::unordered_flat_set >; + } // namespace pmr +#endif } // namespace unordered using boost::unordered::unordered_flat_set; @@ -5530,6 +7025,7 @@ namespace boost #endif // #ifndef BOOST_FUNCTIONAL_HASH_HASH_HPP // Copyright (C) 2022-2023 Christian Mazakas +// Copyright (C) 2024 Joaquin M Lopez Munoz // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -5585,6 +7081,10 @@ namespace boost { using iterator = typename table_type::iterator; using const_iterator = typename table_type::const_iterator; +#ifdef BOOST_UNORDERED_ENABLE_STATS + using stats = typename table_type::stats; +#endif + unordered_flat_set() : unordered_flat_set(0) {} explicit unordered_flat_set(size_type n, hasher const& h = hasher(), @@ -5686,6 +7186,7 @@ namespace boost { { } + template unordered_flat_set( concurrent_flat_set&& other) : table_(std::move(other.table_)) @@ -5707,6 +7208,13 @@ namespace boost { return *this; } + unordered_flat_set& operator=(std::initializer_list il) + { + this->clear(); + this->insert(il.begin(), il.end()); + return *this; + } + allocator_type get_allocator() const noexcept { return table_.get_allocator(); @@ -5987,6 +7495,14 @@ namespace boost { void reserve(size_type n) { table_.reserve(n); } +#ifdef BOOST_UNORDERED_ENABLE_STATS + /// Stats + /// + stats get_stats() const { return table_.get_stats(); } + + void reset_stats() noexcept { table_.reset_stats(); } +#endif + /// Observers /// @@ -6123,6 +7639,7 @@ namespace boost { /* Fast open-addressing concurrent hashmap. * * Copyright 2023 Christian Mazakas. + * Copyright 2024 Braden Ganetsky. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -6160,6 +7677,15 @@ namespace boost { typename concurrent_flat_map::size_type erase_if( concurrent_flat_map& c, Predicate pred); +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class Pred = std::equal_to > + using concurrent_flat_map = boost::unordered::concurrent_flat_map > >; + } // namespace pmr +#endif + } // namespace unordered using boost::unordered::concurrent_flat_map; @@ -6191,6 +7717,9 @@ namespace boost { using element_type = value_type; + using types = flat_map_types; + using constructibility_checker = map_types_constructibility; + static value_type& value_from(element_type& x) { return x; } template @@ -6214,18 +7743,21 @@ namespace boost { template static void construct(A& al, init_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } template static void construct(A& al, value_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } template static void construct(A& al, key_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } @@ -6245,8 +7777,8 @@ namespace boost { } }; } // namespace foa - } // namespace detail - } // namespace unordered + } // namespace detail + } // namespace unordered } // namespace boost #endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP @@ -6419,9 +7951,12 @@ template std::basic_ostream & operator<<( std::basic_ost # define BOOST_CURRENT_LOCATION ::boost::source_location(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN()) -#elif defined(BOOST_GCC) && BOOST_GCC >= 70000 +#elif defined(BOOST_GCC) && BOOST_GCC >= 80000 // The built-ins are available in 4.8+, but are not constant expressions until 7 +// In addition, reproducible builds require -ffile-prefix-map, which is GCC 8 +// https://github.com/boostorg/assert/issues/38 + # define BOOST_CURRENT_LOCATION ::boost::source_location(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION()) #elif defined(BOOST_GCC) && BOOST_GCC >= 50000 @@ -7283,6 +8818,7 @@ namespace boost { #endif // BOOST_UNORDERED_DETAIL_THROW_EXCEPTION_HPP // Copyright (C) 2022 Christian Mazakas +// Copyright (C) 2024 Braden Ganetsky // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7312,6 +8848,16 @@ namespace boost { void swap(unordered_flat_map& lhs, unordered_flat_map& rhs) noexcept(noexcept(lhs.swap(rhs))); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class KeyEqual = std::equal_to > + using unordered_flat_map = + boost::unordered::unordered_flat_map > >; + } // namespace pmr +#endif } // namespace unordered using boost::unordered::unordered_flat_map; @@ -7319,6 +8865,7 @@ namespace boost { #endif // Copyright (C) 2022-2023 Christian Mazakas +// Copyright (C) 2024 Joaquin M Lopez Munoz // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -7376,6 +8923,10 @@ namespace boost { using iterator = typename table_type::iterator; using const_iterator = typename table_type::const_iterator; +#ifdef BOOST_UNORDERED_ENABLE_STATS + using stats = typename table_type::stats; +#endif + unordered_flat_map() : unordered_flat_map(0) {} explicit unordered_flat_map(size_type n, hasher const& h = hasher(), @@ -7477,6 +9028,7 @@ namespace boost { { } + template unordered_flat_map( concurrent_flat_map&& other) : table_(std::move(other.table_)) @@ -7498,6 +9050,13 @@ namespace boost { return *this; } + unordered_flat_map& operator=(std::initializer_list il) + { + this->clear(); + this->insert(il.begin(), il.end()); + return *this; + } + allocator_type get_allocator() const noexcept { return table_.get_allocator(); @@ -7954,6 +9513,14 @@ namespace boost { void reserve(size_type n) { table_.reserve(n); } +#ifdef BOOST_UNORDERED_ENABLE_STATS + /// Stats + /// + stats get_stats() const { return table_.get_stats(); } + + void reset_stats() noexcept { table_.reset_stats(); } +#endif + /// Observers /// @@ -8078,25 +9645,82 @@ namespace boost { boost::hash >, std::equal_to >, Allocator>; - template > > - unordered_flat_map(std::initializer_list >, Allocator) - -> unordered_flat_map, T, - boost::hash >, - std::equal_to >, Allocator>; + template > > + unordered_flat_map(std::initializer_list >, Allocator) + -> unordered_flat_map, T, + boost::hash >, + std::equal_to >, Allocator>; + + template >, + class = std::enable_if_t > > + unordered_flat_map(std::initializer_list >, std::size_t, + Hash, Allocator) -> unordered_flat_map, T, + Hash, std::equal_to >, Allocator>; +#endif + + } // namespace unordered +} // namespace boost + +#endif +/* Fast open-addressing, node-based concurrent hashset. + * + * Copyright 2023 Christian Mazakas. + * Copyright 2023-2024 Joaquin M Lopez Munoz. + * Copyright 2024 Braden Ganetsky. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP +#define BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP + +namespace boost { + namespace unordered { + + template , + class Pred = std::equal_to, + class Allocator = std::allocator > + class concurrent_node_set; + + template + bool operator==( + concurrent_node_set const& lhs, + concurrent_node_set const& rhs); + + template + bool operator!=( + concurrent_node_set const& lhs, + concurrent_node_set const& rhs); + + template + void swap(concurrent_node_set& x, + concurrent_node_set& y) + noexcept(noexcept(x.swap(y))); - template >, - class = std::enable_if_t > > - unordered_flat_map(std::initializer_list >, std::size_t, - Hash, Allocator) -> unordered_flat_map, T, - Hash, std::equal_to >, Allocator>; + template + typename concurrent_node_set::size_type erase_if( + concurrent_node_set& c, Predicate pred); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class Pred = std::equal_to > + using concurrent_node_set = boost::unordered::concurrent_node_set >; + } // namespace pmr #endif } // namespace unordered + + using boost::unordered::concurrent_node_set; } // namespace boost -#endif +#endif // BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP /* Copyright 2023 Christian Mazakas. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at @@ -8160,6 +9784,7 @@ struct element_type #endif // BOOST_UNORDERED_DETAIL_FOA_ELEMENT_TYPE_HPP /* Copyright 2023 Christian Mazakas. + * Copyright 2024 Joaquin M Lopez Munoz. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -8183,6 +9808,13 @@ struct insert_return_type NodeType node; }; +template +struct iteratorless_insert_return_type +{ + bool inserted; + NodeType node; +}; + template struct node_handle_base { @@ -8198,7 +9830,27 @@ struct node_handle_base element_type p_; [[no_unique_address]] opt_storage a_; - protected: + friend struct node_handle_access; + + template + void move_assign_allocator_if(node_handle_base&& nh)noexcept + { + move_assign_allocator_if( + std::integral_constant{}, std::move(nh)); + } + + void move_assign_allocator_if( + std::true_type, node_handle_base&& nh)noexcept + { + al()=std::move(nh.al()); + } + + void move_assign_allocator_if( + std::false_type, node_handle_base&&)noexcept + { + } + +protected: node_value_type& data()noexcept { return *(p_.p); @@ -8282,9 +9934,7 @@ struct node_handle_base BOOST_ASSERT(pocma||al()==nh.al()); type_policy::destroy(al(),&p_); - if(pocma){ - al()=std::move(nh.al()); - } + move_assign_allocator_if(std::move(nh)); p_=std::move(nh.p_); nh.reset(); @@ -8309,7 +9959,17 @@ struct node_handle_base } } - allocator_type get_allocator()const noexcept{return al();} + allocator_type get_allocator()const + { +#ifdef BOOST_GCC + /* GCC lifetime analysis incorrectly warns about uninitialized + * allocator object under some circumstances. + */ + if(empty())__builtin_unreachable(); +#endif + return al(); + } + explicit operator bool()const noexcept{ return !empty();} [[nodiscard]] bool empty()const noexcept{return p_.p==nullptr;} @@ -8352,12 +10012,134 @@ struct node_handle_base } }; +// Internal usage of node_handle_base protected API + +struct node_handle_access +{ + template + using node_type = node_handle_base; + +#if BOOST_CLANG_VERSION < 190000 + // https://github.com/llvm/llvm-project/issues/25708 + + template + struct element_type_impl + { + using type = typename node_type::element_type; + }; + template + using element_type = typename element_type_impl::type; +#else + template + using element_type = typename node_type::element_type; +#endif + + template + static element_type& + element(node_type& nh)noexcept + { + return nh.element(); + } + + template + static element_type + const& element(node_type const& nh)noexcept + { + return nh.element(); + } + + template + static void emplace( + node_type& nh, + element_type&& x, Allocator a) + { + nh.emplace(std::move(x), a); + } + + template + static void reset(node_type& nh) + { + nh.reset(); + } +}; + +template +class node_handle_emplacer_class +{ + using access = node_handle_access; + using node_type = access::node_type; + using element_type = access::element_type; + + node_type & nh; + +public: + node_handle_emplacer_class(node_type& nh_): nh(nh_) {} + + void operator()(element_type&& x,Allocator a) + { + access::emplace(nh, std::move(x), a); + } +}; + +template +node_handle_emplacer_class +node_handle_emplacer(node_handle_base& nh) +{ + return {nh}; +} + } } } } #endif // BOOST_UNORDERED_DETAIL_FOA_NODE_HANDLE_HPP +/* Copyright 2023 Christian Mazakas. + * Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_SET_HANDLE_HPP +#define BOOST_UNORDERED_DETAIL_FOA_NODE_SET_HANDLE_HPP + +namespace boost{ +namespace unordered{ +namespace detail{ +namespace foa{ + +template +struct node_set_handle + : public detail::foa::node_handle_base +{ +private: + using base_type = detail::foa::node_handle_base; + + using typename base_type::type_policy; + +public: + using value_type = typename TypePolicy::value_type; + + constexpr node_set_handle() noexcept = default; + node_set_handle(node_set_handle&& nh) noexcept = default; + node_set_handle& operator=(node_set_handle&&) noexcept = default; + + value_type& value() const + { + BOOST_ASSERT(!this->empty()); + return const_cast(this->data()); + } +}; + +} +} +} +} + +#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_SET_HANDLE_HPP // Copyright (C) 2023 Christian Mazakas // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8380,6 +10162,9 @@ namespace boost { using element_type = foa::element_type; + using types = node_set_types; + using constructibility_checker = set_types_constructibility; + static value_type& value_from(element_type const& x) { return *x.p; } static Key const& extract(element_type const& k) { return *k.p; } static element_type&& move(element_type& x) { return std::move(x); } @@ -8389,7 +10174,7 @@ namespace boost { static void construct( A& al, element_type* p, element_type const& copy) { - construct(al, p, *copy.p); + construct(al, p, detail::as_const(*copy.p)); } template @@ -8403,6 +10188,7 @@ namespace boost { template static void construct(A& al, value_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } @@ -8412,9 +10198,12 @@ namespace boost { p->p = std::allocator_traits>::allocate(al, 1); BOOST_TRY { + auto address = std::to_address(p->p); + constructibility_checker::check( + al, address, std::forward(args)...); std::allocator_traits>::construct( - al, std::to_address(p->p), std::forward(args)...); + al, address, std::forward(args)...); } BOOST_CATCH(...) { @@ -8440,12 +10229,13 @@ namespace boost { }; } // namespace foa - } // namespace detail - } // namespace unordered + } // namespace detail + } // namespace unordered } // namespace boost #endif // BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2024 Braden Ganetsky // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8475,6 +10265,15 @@ namespace boost { void swap(unordered_node_set& lhs, unordered_node_set& rhs) noexcept(noexcept(lhs.swap(rhs))); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class KeyEqual = std::equal_to > + using unordered_node_set = boost::unordered::unordered_node_set >; + } // namespace pmr +#endif } // namespace unordered using boost::unordered::unordered_node_set; @@ -8482,6 +10281,7 @@ namespace boost { #endif // Copyright (C) 2022-2023 Christian Mazakas +// Copyright (C) 2024 Joaquin M Lopez Munoz // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8498,37 +10298,12 @@ namespace boost { #pragma warning(disable : 4714) #endif - namespace detail { - template - struct node_set_handle - : public detail::foa::node_handle_base - { - private: - using base_type = detail::foa::node_handle_base; - - using typename base_type::type_policy; - - template - friend class boost::unordered::unordered_node_set; - - public: - using value_type = typename TypePolicy::value_type; - - constexpr node_set_handle() noexcept = default; - node_set_handle(node_set_handle&& nh) noexcept = default; - node_set_handle& operator=(node_set_handle&&) noexcept = default; - - value_type& value() const - { - BOOST_ASSERT(!this->empty()); - return const_cast(this->data()); - } - }; - } // namespace detail - template class unordered_node_set { + template + friend class concurrent_node_set; + using set_types = detail::foa::node_set_types::void_pointer>; @@ -8562,12 +10337,16 @@ namespace boost { typename std::allocator_traits::const_pointer; using iterator = typename table_type::iterator; using const_iterator = typename table_type::const_iterator; - using node_type = detail::node_set_handle::template rebind_alloc< typename set_types::value_type>>; using insert_return_type = detail::foa::insert_return_type; +#ifdef BOOST_UNORDERED_ENABLE_STATS + using stats = typename table_type::stats; +#endif + unordered_node_set() : unordered_node_set(0) {} explicit unordered_node_set(size_type n, hasher const& h = hasher(), @@ -8669,6 +10448,13 @@ namespace boost { { } + template + unordered_node_set( + concurrent_node_set&& other) + : table_(std::move(other.table_)) + { + } + ~unordered_node_set() = default; unordered_node_set& operator=(unordered_node_set const& other) @@ -8684,6 +10470,13 @@ namespace boost { return *this; } + unordered_node_set& operator=(std::initializer_list il) + { + this->clear(); + this->insert(il.begin(), il.end()); + return *this; + } + allocator_type get_allocator() const noexcept { return table_.get_allocator(); @@ -8771,15 +10564,17 @@ namespace boost { insert_return_type insert(node_type&& nh) { + using access = detail::foa::node_handle_access; + if (nh.empty()) { return {end(), false, node_type{}}; } BOOST_ASSERT(get_allocator() == nh.get_allocator()); - auto itp = table_.insert(std::move(nh.element())); + auto itp = table_.insert(std::move(access::element(nh))); if (itp.second) { - nh.reset(); + access::reset(nh); return {itp.first, true, node_type{}}; } else { return {itp.first, false, std::move(nh)}; @@ -8788,15 +10583,17 @@ namespace boost { iterator insert(const_iterator, node_type&& nh) { + using access = detail::foa::node_handle_access; + if (nh.empty()) { return end(); } BOOST_ASSERT(get_allocator() == nh.get_allocator()); - auto itp = table_.insert(std::move(nh.element())); + auto itp = table_.insert(std::move(access::element(nh))); if (itp.second) { - nh.reset(); + access::reset(nh); return itp.first; } else { return itp.first; @@ -8854,7 +10651,8 @@ namespace boost { BOOST_ASSERT(pos != end()); node_type nh; auto elem = table_.extract(pos); - nh.emplace(std::move(elem), get_allocator()); + detail::foa::node_handle_emplacer(nh)( + std::move(elem), get_allocator()); return nh; } @@ -9026,6 +10824,14 @@ namespace boost { void reserve(size_type n) { table_.reserve(n); } +#ifdef BOOST_UNORDERED_ENABLE_STATS + /// Stats + /// + stats get_stats() const { return table_.get_stats(); } + + void reset_stats() noexcept { table_.reset_stats(); } +#endif + /// Observers /// @@ -9159,6 +10965,117 @@ namespace boost { } // namespace boost #endif +/* Fast open-addressing, node-based concurrent hashmap. + * + * Copyright 2023 Christian Mazakas. + * Copyright 2024 Braden Ganetsky. + * Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_CONCURRENT_NODE_MAP_FWD_HPP +#define BOOST_UNORDERED_CONCURRENT_NODE_MAP_FWD_HPP + +namespace boost { + namespace unordered { + + template , + class Pred = std::equal_to, + class Allocator = std::allocator > > + class concurrent_node_map; + + template + bool operator==( + concurrent_node_map const& lhs, + concurrent_node_map const& rhs); + + template + bool operator!=( + concurrent_node_map const& lhs, + concurrent_node_map const& rhs); + + template + void swap(concurrent_node_map& x, + concurrent_node_map& y) + noexcept(noexcept(x.swap(y))); + + template + typename concurrent_node_map::size_type erase_if( + concurrent_node_map& c, Predicate pred); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class Pred = std::equal_to > + using concurrent_node_map = boost::unordered::concurrent_node_map > >; + } // namespace pmr +#endif + + } // namespace unordered + + using boost::unordered::concurrent_node_map; +} // namespace boost + +#endif // BOOST_UNORDERED_CONCURRENT_NODE_MAP_FWD_HPP +/* Copyright 2023 Christian Mazakas. + * Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_HANDLE_HPP +#define BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_HANDLE_HPP + +namespace boost{ +namespace unordered{ +namespace detail{ +namespace foa{ + +template +struct node_map_handle + : public node_handle_base +{ +private: + using base_type = node_handle_base; + + using typename base_type::type_policy; + +public: + using key_type = typename TypePolicy::key_type; + using mapped_type = typename TypePolicy::mapped_type; + + constexpr node_map_handle() noexcept = default; + node_map_handle(node_map_handle&& nh) noexcept = default; + + node_map_handle& operator=(node_map_handle&&) noexcept = default; + + key_type& key() const + { + BOOST_ASSERT(!this->empty()); + return const_cast(this->data().first); + } + + mapped_type& mapped() const + { + BOOST_ASSERT(!this->empty()); + return const_cast(this->data().second); + } +}; + +} +} +} +} + +#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_HANDLE_HPP // Copyright (C) 2023 Christian Mazakas // Copyright (C) 2024 Braden Ganetsky // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -9184,6 +11101,9 @@ namespace boost { using element_type = foa::element_type; + using types = node_map_types; + using constructibility_checker = map_types_constructibility; + static value_type& value_from(element_type const& x) { return *(x.p); @@ -9223,24 +11143,27 @@ namespace boost { static void construct( A& al, element_type* p, element_type const& copy) { - construct(al, p, *copy.p); + construct(al, p, detail::as_const(*copy.p)); } template static void construct(A& al, init_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } template static void construct(A& al, value_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } template static void construct(A& al, key_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } @@ -9250,9 +11173,12 @@ namespace boost { p->p = std::allocator_traits>::allocate(al, 1); BOOST_TRY { + auto address = std::to_address(p->p); + constructibility_checker::check( + al, address, std::forward(args)...); std::allocator_traits>::construct( - al, std::to_address(p->p), std::forward(args)...); + al, address, std::forward(args)...); } BOOST_CATCH(...) { @@ -9288,12 +11214,13 @@ namespace boost { }; } // namespace foa - } // namespace detail - } // namespace unordered + } // namespace detail + } // namespace unordered } // namespace boost #endif // BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP // Copyright (C) 2022 Christian Mazakas +// Copyright (C) 2024 Braden Ganetsky // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -9323,6 +11250,16 @@ namespace boost { void swap(unordered_node_map& lhs, unordered_node_map& rhs) noexcept(noexcept(lhs.swap(rhs))); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class KeyEqual = std::equal_to > + using unordered_node_map = + boost::unordered::unordered_node_map > >; + } // namespace pmr +#endif } // namespace unordered using boost::unordered::unordered_node_map; @@ -9330,6 +11267,7 @@ namespace boost { #endif // Copyright (C) 2022-2023 Christian Mazakas +// Copyright (C) 2024 Joaquin M Lopez Munoz // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -9346,45 +11284,13 @@ namespace boost { #pragma warning(disable : 4714) #endif - namespace detail { - template - struct node_map_handle - : public detail::foa::node_handle_base - { - private: - using base_type = detail::foa::node_handle_base; - - using typename base_type::type_policy; - - template - friend class boost::unordered::unordered_node_map; - - public: - using key_type = typename TypePolicy::key_type; - using mapped_type = typename TypePolicy::mapped_type; - - constexpr node_map_handle() noexcept = default; - node_map_handle(node_map_handle&& nh) noexcept = default; - - node_map_handle& operator=(node_map_handle&&) noexcept = default; - - key_type& key() const - { - BOOST_ASSERT(!this->empty()); - return const_cast(this->data().first); - } - - mapped_type& mapped() const - { - BOOST_ASSERT(!this->empty()); - return const_cast(this->data().second); - } - }; - } // namespace detail - template class unordered_node_map { + template + friend class concurrent_node_map; + using map_types = detail::foa::node_map_types::void_pointer>; @@ -9419,12 +11325,16 @@ namespace boost { typename std::allocator_traits::const_pointer; using iterator = typename table_type::iterator; using const_iterator = typename table_type::const_iterator; - using node_type = detail::node_map_handle::template rebind_alloc< typename map_types::value_type>>; using insert_return_type = detail::foa::insert_return_type; +#ifdef BOOST_UNORDERED_ENABLE_STATS + using stats = typename table_type::stats; +#endif + unordered_node_map() : unordered_node_map(0) {} explicit unordered_node_map(size_type n, hasher const& h = hasher(), @@ -9526,6 +11436,13 @@ namespace boost { { } + template + unordered_node_map( + concurrent_node_map&& other) + : table_(std::move(other.table_)) + { + } + ~unordered_node_map() = default; unordered_node_map& operator=(unordered_node_map const& other) @@ -9541,6 +11458,13 @@ namespace boost { return *this; } + unordered_node_map& operator=(std::initializer_list il) + { + this->clear(); + this->insert(il.begin(), il.end()); + return *this; + } + allocator_type get_allocator() const noexcept { return table_.get_allocator(); @@ -9613,15 +11537,17 @@ namespace boost { insert_return_type insert(node_type&& nh) { + using access = detail::foa::node_handle_access; + if (nh.empty()) { return {end(), false, node_type{}}; } BOOST_ASSERT(get_allocator() == nh.get_allocator()); - auto itp = table_.insert(std::move(nh.element())); + auto itp = table_.insert(std::move(access::element(nh))); if (itp.second) { - nh.reset(); + access::reset(nh); return {itp.first, true, node_type{}}; } else { return {itp.first, false, std::move(nh)}; @@ -9630,15 +11556,17 @@ namespace boost { iterator insert(const_iterator, node_type&& nh) { + using access = detail::foa::node_handle_access; + if (nh.empty()) { return end(); } BOOST_ASSERT(get_allocator() == nh.get_allocator()); - auto itp = table_.insert(std::move(nh.element())); + auto itp = table_.insert(std::move(access::element(nh))); if (itp.second) { - nh.reset(); + access::reset(nh); return itp.first; } else { return itp.first; @@ -9813,7 +11741,8 @@ namespace boost { BOOST_ASSERT(pos != end()); node_type nh; auto elem = table_.extract(pos); - nh.emplace(std::move(elem), get_allocator()); + detail::foa::node_handle_emplacer(nh)( + std::move(elem), get_allocator()); return nh; } @@ -10059,6 +11988,14 @@ namespace boost { void reserve(size_type n) { table_.reserve(n); } +#ifdef BOOST_UNORDERED_ENABLE_STATS + /// Stats + /// + stats get_stats() const { return table_.get_stats(); } + + void reset_stats() noexcept { table_.reset_stats(); } +#endif + /// Observers /// diff --git a/include/boost/assert/source_location.hpp b/include/boost/assert/source_location.hpp index 765fafd..e0fba7c 100644 --- a/include/boost/assert/source_location.hpp +++ b/include/boost/assert/source_location.hpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include @@ -174,9 +173,12 @@ template std::basic_ostream & operator<<( std::basic_ost # define BOOST_CURRENT_LOCATION ::boost::source_location(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION(), __builtin_COLUMN()) -#elif defined(BOOST_GCC) && BOOST_GCC >= 70000 +#elif defined(BOOST_GCC) && BOOST_GCC >= 80000 // The built-ins are available in 4.8+, but are not constant expressions until 7 +// In addition, reproducible builds require -ffile-prefix-map, which is GCC 8 +// https://github.com/boostorg/assert/issues/38 + # define BOOST_CURRENT_LOCATION ::boost::source_location(__builtin_FILE(), __builtin_LINE(), __builtin_FUNCTION()) #elif defined(BOOST_GCC) && BOOST_GCC >= 50000 diff --git a/include/boost/core/detail/sp_thread_pause.hpp b/include/boost/core/detail/sp_thread_pause.hpp new file mode 100644 index 0000000..67738e2 --- /dev/null +++ b/include/boost/core/detail/sp_thread_pause.hpp @@ -0,0 +1,71 @@ +#ifndef BOOST_CORE_DETAIL_SP_THREAD_PAUSE_HPP_INCLUDED +#define BOOST_CORE_DETAIL_SP_THREAD_PAUSE_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// boost/core/detail/sp_thread_pause.hpp +// +// inline void bost::core::sp_thread_pause(); +// +// Emits a "pause" instruction. +// +// Copyright 2008, 2020, 2023 Peter Dimov +// Distributed under the Boost Software License, Version 1.0 +// https://www.boost.org/LICENSE_1_0.txt + +#include + +#if defined(__has_builtin) +# if __has_builtin(__builtin_ia32_pause) && !defined(__INTEL_COMPILER) +# define BOOST_CORE_HAS_BUILTIN_IA32_PAUSE +# endif +#endif + +#if defined(BOOST_CORE_HAS_BUILTIN_IA32_PAUSE) + +# define BOOST_CORE_SP_PAUSE() __builtin_ia32_pause() + +#elif defined(_MSC_VER) && ( defined(_M_IX86) || defined(_M_X64) ) + +# include +# define BOOST_CORE_SP_PAUSE() _mm_pause() + +#elif defined(_MSC_VER) && ( defined(_M_ARM) || defined(_M_ARM64) ) + +# include +# define BOOST_CORE_SP_PAUSE() __yield() + +#elif defined(__GNUC__) && ( defined(__i386__) || defined(__x86_64__) ) + +# define BOOST_CORE_SP_PAUSE() __asm__ __volatile__( "rep; nop" : : : "memory" ) + +#elif defined(__GNUC__) && ( (defined(__ARM_ARCH) && __ARM_ARCH >= 8) || defined(__ARM_ARCH_8A__) || defined(__aarch64__) ) + +# define BOOST_CORE_SP_PAUSE() __asm__ __volatile__( "yield" : : : "memory" ) + +#else + +# define BOOST_CORE_SP_PAUSE() ((void)0) + +#endif + +namespace boost +{ +namespace core +{ + +BOOST_FORCEINLINE void sp_thread_pause() noexcept +{ + BOOST_CORE_SP_PAUSE(); +} + +} // namespace core +} // namespace boost + +#undef BOOST_CORE_SP_PAUSE + +#endif // #ifndef BOOST_CORE_DETAIL_SP_THREAD_PAUSE_HPP_INCLUDED diff --git a/include/boost/core/detail/sp_thread_sleep.hpp b/include/boost/core/detail/sp_thread_sleep.hpp new file mode 100644 index 0000000..66f9eb2 --- /dev/null +++ b/include/boost/core/detail/sp_thread_sleep.hpp @@ -0,0 +1,121 @@ +#ifndef BOOST_CORE_DETAIL_SP_THREAD_SLEEP_HPP_INCLUDED +#define BOOST_CORE_DETAIL_SP_THREAD_SLEEP_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// boost/core/detail/sp_thread_sleep.hpp +// +// inline void bost::core::sp_thread_sleep(); +// +// Cease execution for a while to yield to other threads, +// as if by calling nanosleep() with an appropriate interval. +// +// Copyright 2008, 2020, 2023 Peter Dimov +// Distributed under the Boost Software License, Version 1.0 +// https://www.boost.org/LICENSE_1_0.txt + +#include + +#if defined( _WIN32 ) || defined( __WIN32__ ) || defined( __CYGWIN__ ) + +#if defined(BOOST_SP_REPORT_IMPLEMENTATION) + BOOST_PRAGMA_MESSAGE("Using Sleep(1) in sp_thread_sleep") +#endif + +#include + +namespace boost +{ +namespace core +{ +namespace detail +{ + +inline void sp_thread_sleep() noexcept +{ + Sleep( 1 ); +} + +} // namespace detail + +using boost::core::detail::sp_thread_sleep; + +} // namespace core +} // namespace boost + +#elif defined(BOOST_HAS_NANOSLEEP) + +#if defined(BOOST_SP_REPORT_IMPLEMENTATION) + BOOST_PRAGMA_MESSAGE("Using nanosleep() in sp_thread_sleep") +#endif + +#include + +#if defined(BOOST_HAS_PTHREADS) && !defined(__ANDROID__) +# include +#endif + +namespace boost +{ +namespace core +{ + +inline void sp_thread_sleep() noexcept +{ +#if defined(BOOST_HAS_PTHREADS) && !defined(__ANDROID__) + + int oldst; + pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldst ); + +#endif + + // g++ -Wextra warns on {} or {0} + struct timespec rqtp = { 0, 0 }; + + // POSIX says that timespec has tv_sec and tv_nsec + // But it doesn't guarantee order or placement + + rqtp.tv_sec = 0; + rqtp.tv_nsec = 1000; + + nanosleep( &rqtp, 0 ); + +#if defined(BOOST_HAS_PTHREADS) && !defined(__ANDROID__) + + pthread_setcancelstate( oldst, &oldst ); + +#endif + +} + +} // namespace core +} // namespace boost + +#else + +#if defined(BOOST_SP_REPORT_IMPLEMENTATION) + BOOST_PRAGMA_MESSAGE("Using sp_thread_yield() in sp_thread_sleep") +#endif + +#include + +namespace boost +{ +namespace core +{ + +inline void sp_thread_sleep() noexcept +{ + sp_thread_yield(); +} + +} // namespace core +} // namespace boost + +#endif + +#endif // #ifndef BOOST_CORE_DETAIL_SP_THREAD_SLEEP_HPP_INCLUDED diff --git a/include/boost/core/detail/sp_thread_yield.hpp b/include/boost/core/detail/sp_thread_yield.hpp new file mode 100644 index 0000000..72c3066 --- /dev/null +++ b/include/boost/core/detail/sp_thread_yield.hpp @@ -0,0 +1,99 @@ +#ifndef BOOST_CORE_DETAIL_SP_THREAD_YIELD_HPP_INCLUDED +#define BOOST_CORE_DETAIL_SP_THREAD_YIELD_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// boost/core/detail/sp_thread_yield.hpp +// +// inline void bost::core::sp_thread_yield(); +// +// Gives up the remainder of the time slice, +// as if by calling sched_yield(). +// +// Copyright 2008, 2020 Peter Dimov +// Distributed under the Boost Software License, Version 1.0 +// https://www.boost.org/LICENSE_1_0.txt + +#include + +#if defined( _WIN32 ) || defined( __WIN32__ ) || defined( __CYGWIN__ ) + +#if defined(BOOST_SP_REPORT_IMPLEMENTATION) + BOOST_PRAGMA_MESSAGE("Using SwitchToThread() in sp_thread_yield") +#endif + +#include + +namespace boost +{ +namespace core +{ +namespace detail +{ + +inline void sp_thread_yield() noexcept +{ + SwitchToThread(); +} + +} // namespace detail + +using boost::core::detail::sp_thread_yield; + +} // namespace core +} // namespace boost + +#elif defined(BOOST_HAS_SCHED_YIELD) + +#if defined(BOOST_SP_REPORT_IMPLEMENTATION) + BOOST_PRAGMA_MESSAGE("Using sched_yield() in sp_thread_yield") +#endif + +#ifndef _AIX +# include +#else + // AIX's sched.h defines ::var which sometimes conflicts with Lambda's var + extern "C" int sched_yield(void); +#endif + +namespace boost +{ +namespace core +{ + +inline void sp_thread_yield() noexcept +{ + sched_yield(); +} + +} // namespace core +} // namespace boost + +#else + +#if defined(BOOST_SP_REPORT_IMPLEMENTATION) + BOOST_PRAGMA_MESSAGE("Using sp_thread_pause() in sp_thread_yield") +#endif + +#include + +namespace boost +{ +namespace core +{ + +inline void sp_thread_yield() noexcept +{ + sp_thread_pause(); +} + +} // namespace core +} // namespace boost + +#endif + +#endif // #ifndef BOOST_CORE_DETAIL_SP_THREAD_YIELD_HPP_INCLUDED diff --git a/include/boost/core/detail/sp_win32_sleep.hpp b/include/boost/core/detail/sp_win32_sleep.hpp new file mode 100644 index 0000000..adec53e --- /dev/null +++ b/include/boost/core/detail/sp_win32_sleep.hpp @@ -0,0 +1,54 @@ +#ifndef BOOST_CORE_DETAIL_SP_WIN32_SLEEP_HPP_INCLUDED +#define BOOST_CORE_DETAIL_SP_WIN32_SLEEP_HPP_INCLUDED + +// MS compatible compilers support #pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +// boost/core/detail/sp_win32_sleep.hpp +// +// Declares the Win32 Sleep() function. +// +// Copyright 2008, 2020 Peter Dimov +// Distributed under the Boost Software License, Version 1.0 +// https://www.boost.org/LICENSE_1_0.txt + +#if defined( BOOST_USE_WINDOWS_H ) +# include +#endif + +namespace boost +{ +namespace core +{ +namespace detail +{ + +#if !defined( BOOST_USE_WINDOWS_H ) + +#if defined(__clang__) && defined(__x86_64__) +// clang x64 warns that __stdcall is ignored +# define BOOST_CORE_SP_STDCALL +#else +# define BOOST_CORE_SP_STDCALL __stdcall +#endif + +#if defined(__LP64__) // Cygwin 64 + extern "C" __declspec(dllimport) void BOOST_CORE_SP_STDCALL Sleep( unsigned int ms ); +#else + extern "C" __declspec(dllimport) void BOOST_CORE_SP_STDCALL Sleep( unsigned long ms ); +#endif + +extern "C" __declspec(dllimport) int BOOST_CORE_SP_STDCALL SwitchToThread(); + +#undef BOOST_CORE_SP_STDCALL + +#endif // !defined( BOOST_USE_WINDOWS_H ) + +} // namespace detail +} // namespace core +} // namespace boost + +#endif // #ifndef BOOST_CORE_DETAIL_SP_WIN32_SLEEP_HPP_INCLUDED diff --git a/include/boost/core/empty_value.hpp b/include/boost/core/empty_value.hpp index 1419020..b08569f 100644 --- a/include/boost/core/empty_value.hpp +++ b/include/boost/core/empty_value.hpp @@ -72,20 +72,48 @@ class empty_value { T value_; }; +#if defined(BOOST_MSVC) +/* +This is a workaround to an MSVC bug when T is a nested class: +https://developercommunity.visualstudio.com/t/Compiler-bug:-Incorrect-C2247-and-C2248/10690025 +*/ +namespace detail { + +template +class empty_value_base + : public T { +public: + empty_value_base() = default; + + template + constexpr empty_value_base(U&& value, Args&&... args) + : T(std::forward(value), std::forward(args)...) { } +}; + +} /* detail */ +#endif + template class empty_value +#if defined(BOOST_MSVC) + : detail::empty_value_base { + typedef detail::empty_value_base empty_base_; +#else : T { + typedef T empty_base_; +#endif + public: typedef T type; empty_value() = default; constexpr empty_value(boost::empty_init_t) - : T() { } + : empty_base_() { } template constexpr empty_value(boost::empty_init_t, U&& value, Args&&... args) - : T(std::forward(value), std::forward(args)...) { } + : empty_base_(std::forward(value), std::forward(args)...) { } constexpr const T& get() const noexcept { return *this; diff --git a/include/boost/core/yield_primitives.hpp b/include/boost/core/yield_primitives.hpp new file mode 100644 index 0000000..899453e --- /dev/null +++ b/include/boost/core/yield_primitives.hpp @@ -0,0 +1,12 @@ +#ifndef BOOST_CORE_YIELD_PRIMITIVES_HPP_INCLUDED +#define BOOST_CORE_YIELD_PRIMITIVES_HPP_INCLUDED + +// Copyright 2023 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include + +#endif // #ifndef BOOST_CORE_YIELD_PRIMITIVES_HPP_INCLUDED diff --git a/include/boost/unordered/concurrent_flat_map_fwd.hpp b/include/boost/unordered/concurrent_flat_map_fwd.hpp index 99fa130..bb584f3 100644 --- a/include/boost/unordered/concurrent_flat_map_fwd.hpp +++ b/include/boost/unordered/concurrent_flat_map_fwd.hpp @@ -1,6 +1,7 @@ /* Fast open-addressing concurrent hashmap. * * Copyright 2023 Christian Mazakas. + * Copyright 2024 Braden Ganetsky. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -11,11 +12,16 @@ #ifndef BOOST_UNORDERED_CONCURRENT_FLAT_MAP_FWD_HPP #define BOOST_UNORDERED_CONCURRENT_FLAT_MAP_FWD_HPP +#include #include #include #include +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + namespace boost { namespace unordered { @@ -43,6 +49,15 @@ namespace boost { typename concurrent_flat_map::size_type erase_if( concurrent_flat_map& c, Predicate pred); +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class Pred = std::equal_to > + using concurrent_flat_map = boost::unordered::concurrent_flat_map > >; + } // namespace pmr +#endif + } // namespace unordered using boost::unordered::concurrent_flat_map; diff --git a/include/boost/unordered/concurrent_flat_set_fwd.hpp b/include/boost/unordered/concurrent_flat_set_fwd.hpp index 03bcc5b..396aa4e 100644 --- a/include/boost/unordered/concurrent_flat_set_fwd.hpp +++ b/include/boost/unordered/concurrent_flat_set_fwd.hpp @@ -2,6 +2,7 @@ * * Copyright 2023 Christian Mazakas. * Copyright 2023 Joaquin M Lopez Munoz. + * Copyright 2024 Braden Ganetsky. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -12,11 +13,16 @@ #ifndef BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP #define BOOST_UNORDERED_CONCURRENT_FLAT_SET_FWD_HPP +#include #include #include #include +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + namespace boost { namespace unordered { @@ -44,6 +50,15 @@ namespace boost { typename concurrent_flat_set::size_type erase_if( concurrent_flat_set& c, Predicate pred); +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class Pred = std::equal_to > + using concurrent_flat_set = boost::unordered::concurrent_flat_set >; + } // namespace pmr +#endif + } // namespace unordered using boost::unordered::concurrent_flat_set; diff --git a/include/boost/unordered/concurrent_node_map_fwd.hpp b/include/boost/unordered/concurrent_node_map_fwd.hpp new file mode 100644 index 0000000..7f499b0 --- /dev/null +++ b/include/boost/unordered/concurrent_node_map_fwd.hpp @@ -0,0 +1,67 @@ +/* Fast open-addressing, node-based concurrent hashmap. + * + * Copyright 2023 Christian Mazakas. + * Copyright 2024 Braden Ganetsky. + * Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_CONCURRENT_NODE_MAP_FWD_HPP +#define BOOST_UNORDERED_CONCURRENT_NODE_MAP_FWD_HPP + +#include +#include + +#include +#include + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + +namespace boost { + namespace unordered { + + template , + class Pred = std::equal_to, + class Allocator = std::allocator > > + class concurrent_node_map; + + template + bool operator==( + concurrent_node_map const& lhs, + concurrent_node_map const& rhs); + + template + bool operator!=( + concurrent_node_map const& lhs, + concurrent_node_map const& rhs); + + template + void swap(concurrent_node_map& x, + concurrent_node_map& y) + noexcept(noexcept(x.swap(y))); + + template + typename concurrent_node_map::size_type erase_if( + concurrent_node_map& c, Predicate pred); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class Pred = std::equal_to > + using concurrent_node_map = boost::unordered::concurrent_node_map > >; + } // namespace pmr +#endif + + } // namespace unordered + + using boost::unordered::concurrent_node_map; +} // namespace boost + +#endif // BOOST_UNORDERED_CONCURRENT_NODE_MAP_FWD_HPP diff --git a/include/boost/unordered/concurrent_node_set_fwd.hpp b/include/boost/unordered/concurrent_node_set_fwd.hpp new file mode 100644 index 0000000..08a1f79 --- /dev/null +++ b/include/boost/unordered/concurrent_node_set_fwd.hpp @@ -0,0 +1,67 @@ +/* Fast open-addressing, node-based concurrent hashset. + * + * Copyright 2023 Christian Mazakas. + * Copyright 2023-2024 Joaquin M Lopez Munoz. + * Copyright 2024 Braden Ganetsky. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP +#define BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP + +#include +#include + +#include +#include + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + +namespace boost { + namespace unordered { + + template , + class Pred = std::equal_to, + class Allocator = std::allocator > + class concurrent_node_set; + + template + bool operator==( + concurrent_node_set const& lhs, + concurrent_node_set const& rhs); + + template + bool operator!=( + concurrent_node_set const& lhs, + concurrent_node_set const& rhs); + + template + void swap(concurrent_node_set& x, + concurrent_node_set& y) + noexcept(noexcept(x.swap(y))); + + template + typename concurrent_node_set::size_type erase_if( + concurrent_node_set& c, Predicate pred); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class Pred = std::equal_to > + using concurrent_node_set = boost::unordered::concurrent_node_set >; + } // namespace pmr +#endif + + } // namespace unordered + + using boost::unordered::concurrent_node_set; +} // namespace boost + +#endif // BOOST_UNORDERED_CONCURRENT_NODE_SET_FWD_HPP diff --git a/include/boost/unordered/detail/foa/core.hpp b/include/boost/unordered/detail/foa/core.hpp index 9f3124a..ca6779b 100644 --- a/include/boost/unordered/detail/foa/core.hpp +++ b/include/boost/unordered/detail/foa/core.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,10 @@ #include #include +#if defined(BOOST_UNORDERED_ENABLE_STATS) +#include +#endif + #if !defined(BOOST_UNORDERED_DISABLE_SSE2) #if defined(BOOST_UNORDERED_ENABLE_SSE2)|| \ defined(__SSE2__)|| \ @@ -858,6 +863,7 @@ struct pow2_quadratic_prober pow2_quadratic_prober(std::size_t pos_):pos{pos_}{} inline std::size_t get()const{return pos;} + inline std::size_t length()const{return step+1;} /* next returns false when the whole array has been traversed, which ends * probing (in practice, full-table probing will only happen with very small @@ -1006,6 +1012,11 @@ struct table_arrays rebind; using group_type_pointer_traits=std::pointer_traits; + // For natvis purposes + using char_pointer= + typename std::pointer_traits::template + rebind; + table_arrays( std::size_t gsi,std::size_t gsm, group_type_pointer pg,value_type_pointer pe): @@ -1119,6 +1130,54 @@ struct table_arrays value_type_pointer elements_; }; +#if defined(BOOST_UNORDERED_ENABLE_STATS) +/* stats support */ + +struct table_core_cumulative_stats +{ + concurrent_cumulative_stats<1> insertion; + concurrent_cumulative_stats<2> successful_lookup, + unsuccessful_lookup; +}; + +struct table_core_insertion_stats +{ + std::size_t count; + sequence_stats_summary probe_length; +}; + +struct table_core_lookup_stats +{ + std::size_t count; + sequence_stats_summary probe_length; + sequence_stats_summary num_comparisons; +}; + +struct table_core_stats +{ + table_core_insertion_stats insertion; + table_core_lookup_stats successful_lookup, + unsuccessful_lookup; +}; + +#define BOOST_UNORDERED_ADD_STATS(stats,args) stats.add args +#define BOOST_UNORDERED_SWAP_STATS(stats1,stats2) std::swap(stats1,stats2) +#define BOOST_UNORDERED_COPY_STATS(stats1,stats2) stats1=stats2 +#define BOOST_UNORDERED_RESET_STATS_OF(x) x.reset_stats() +#define BOOST_UNORDERED_STATS_COUNTER(name) std::size_t name=0 +#define BOOST_UNORDERED_INCREMENT_STATS_COUNTER(name) ++name + +#else + +#define BOOST_UNORDERED_ADD_STATS(stats,args) ((void)0) +#define BOOST_UNORDERED_SWAP_STATS(stats1,stats2) ((void)0) +#define BOOST_UNORDERED_COPY_STATS(stats1,stats2) ((void)0) +#define BOOST_UNORDERED_RESET_STATS_OF(x) ((void)0) +#define BOOST_UNORDERED_STATS_COUNTER(name) ((void)0) +#define BOOST_UNORDERED_INCREMENT_STATS_COUNTER(name) ((void)0) + +#endif + struct if_constexpr_void_else{void operator()()const{}}; template @@ -1382,6 +1441,16 @@ table_core:empty_value,empty_value,empty_value using locator=table_locator; using arrays_holder_type=arrays_holder; +#if defined(BOOST_UNORDERED_ENABLE_STATS) + using cumulative_stats=table_core_cumulative_stats; + using stats=table_core_stats; +#endif + +#if defined(BOOST_GCC) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + table_core( std::size_t n=default_bucket_count,const Hash& h_=Hash(), const Pred& pred_=Pred(),const Allocator& al_=Allocator()): @@ -1390,6 +1459,10 @@ table_core:empty_value,empty_value,empty_value size_ctrl{initial_max_load(),0} {} +#if defined(BOOST_GCC) +#pragma GCC diagnostic pop +#endif + /* genericize on an ArraysFn so that we can do things like delay an * allocation for the group_access data required by cfoa after the move * constructors of Hash, Pred have been invoked @@ -1416,6 +1489,7 @@ table_core:empty_value,empty_value,empty_value x.arrays=ah.release(); x.size_ctrl.ml=x.initial_max_load(); x.size_ctrl.size=0; + BOOST_UNORDERED_SWAP_STATS(cstats,x.cstats); } table_core(table_core&& x) @@ -1441,11 +1515,13 @@ table_core:empty_value,empty_value,empty_value using std::swap; swap(arrays,x.arrays); swap(size_ctrl,x.size_ctrl); + BOOST_UNORDERED_SWAP_STATS(cstats,x.cstats); } else{ reserve(x.size()); clear_on_exit c{x}; (void)c; /* unused var warning */ + BOOST_UNORDERED_RESET_STATS_OF(x); /* This works because subsequent x.clear() does not depend on the * elements' values. @@ -1561,9 +1637,11 @@ table_core:empty_value,empty_value,empty_value arrays=x.arrays; size_ctrl.ml=std::size_t(x.size_ctrl.ml); size_ctrl.size=std::size_t(x.size_ctrl.size); + BOOST_UNORDERED_COPY_STATS(cstats,x.cstats); x.arrays=ah.release(); x.size_ctrl.ml=x.initial_max_load(); x.size_ctrl.size=0; + BOOST_UNORDERED_RESET_STATS_OF(x); } else{ swap(h(),x.h()); @@ -1573,6 +1651,7 @@ table_core:empty_value,empty_value,empty_value noshrink_reserve(x.size()); clear_on_exit c{x}; (void)c; /* unused var warning */ + BOOST_UNORDERED_RESET_STATS_OF(x); /* This works because subsequent x.clear() does not depend on the * elements' values. @@ -1626,6 +1705,7 @@ table_core:empty_value,empty_value,empty_value BOOST_FORCEINLINE locator find( const Key& x,std::size_t pos0,std::size_t hash)const { + BOOST_UNORDERED_STATS_COUNTER(num_cmps); prober pb(pos0); do{ auto pos=pb.get(); @@ -1637,18 +1717,25 @@ table_core:empty_value,empty_value,empty_value auto p=elements+pos*N; BOOST_UNORDERED_PREFETCH_ELEMENTS(p,N); do{ + BOOST_UNORDERED_INCREMENT_STATS_COUNTER(num_cmps); auto n=unchecked_countr_zero(mask); if(BOOST_LIKELY(bool(pred()(x,key_from(p[n]))))){ + BOOST_UNORDERED_ADD_STATS( + cstats.successful_lookup,(pb.length(),num_cmps)); return {pg,n,p+n}; } mask&=mask-1; }while(mask); } if(BOOST_LIKELY(pg->is_not_overflowed(hash))){ + BOOST_UNORDERED_ADD_STATS( + cstats.unsuccessful_lookup,(pb.length(),num_cmps)); return {}; } } while(BOOST_LIKELY(pb.next(arrays.groups_size_mask))); + BOOST_UNORDERED_ADD_STATS( + cstats.unsuccessful_lookup,(pb.length(),num_cmps)); return {}; } @@ -1733,6 +1820,38 @@ table_core:empty_value,empty_value,empty_value rehash(std::size_t(std::ceil(float(n)/mlf))); } +#if defined(BOOST_UNORDERED_ENABLE_STATS) + stats get_stats()const + { + auto insertion=cstats.insertion.get_summary(); + auto successful_lookup=cstats.successful_lookup.get_summary(); + auto unsuccessful_lookup=cstats.unsuccessful_lookup.get_summary(); + return{ + { + insertion.count, + insertion.sequence_summary[0] + }, + { + successful_lookup.count, + successful_lookup.sequence_summary[0], + successful_lookup.sequence_summary[1] + }, + { + unsuccessful_lookup.count, + unsuccessful_lookup.sequence_summary[0], + unsuccessful_lookup.sequence_summary[1] + }, + }; + } + + void reset_stats()noexcept + { + cstats.insertion.reset(); + cstats.successful_lookup.reset(); + cstats.unsuccessful_lookup.reset(); + } +#endif + friend bool operator==(const table_core& x,const table_core& y) { return @@ -1940,8 +2059,12 @@ table_core:empty_value,empty_value,empty_value return true; } - arrays_type arrays; - size_ctrl_type size_ctrl; + arrays_type arrays; + size_ctrl_type size_ctrl; + +#if defined(BOOST_UNORDERED_ENABLE_STATS) + mutable cumulative_stats cstats; +#endif private: template< @@ -1954,6 +2077,11 @@ table_core:empty_value,empty_value,empty_value using pred_base=empty_value; using allocator_base=empty_value; +#if defined(BOOST_GCC) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + /* used by allocator-extended move ctor */ table_core(Hash&& h_,Pred&& pred_,const Allocator& al_): @@ -1964,6 +2092,10 @@ table_core:empty_value,empty_value,empty_value { } +#if defined(BOOST_GCC) +#pragma GCC diagnostic pop +#endif + arrays_type new_arrays(std::size_t n)const { return arrays_type::new_(typename arrays_type::allocator_type(al()),n); @@ -2230,6 +2362,7 @@ table_core:empty_value,empty_value,empty_value auto p=arrays_.elements()+pos*N+n; construct_element(p,std::forward(args)...); pg->set(n,hash); + BOOST_UNORDERED_ADD_STATS(cstats.insertion,(pb.length())); return {pg,n,p}; } else pg->mark_overflow(hash); diff --git a/include/boost/unordered/detail/foa/cumulative_stats.hpp b/include/boost/unordered/detail/foa/cumulative_stats.hpp new file mode 100644 index 0000000..78678be --- /dev/null +++ b/include/boost/unordered/detail/foa/cumulative_stats.hpp @@ -0,0 +1,176 @@ +/* Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_DETAIL_FOA_CUMULATIVE_STATS_HPP +#define BOOST_UNORDERED_DETAIL_FOA_CUMULATIVE_STATS_HPP + +#include +#include +#include +#include + +#if defined(BOOST_HAS_THREADS) +#include +#include +#endif + +namespace boost{ +namespace unordered{ +namespace detail{ +namespace foa{ + +/* Cumulative one-pass calculation of the average, variance and deviation of + * running sequences. + */ + +struct sequence_stats_data +{ + double m=0.0; + double m_prior=0.0; + double s=0.0; +}; + +struct welfords_algorithm /* 0-based */ +{ + template + int operator()(T&& x,sequence_stats_data& d)const noexcept + { + static_assert( + noexcept(static_cast(x)), + "Argument conversion to double must not throw."); + + d.m_prior=d.m; + d.m+=(static_cast(x)-d.m)/static_cast(n); + d.s+=(n!=1)* + (static_cast(x)-d.m_prior)*(static_cast(x)-d.m); + + return 0; /* mp11::tuple_transform requires that return type not be void */ + } + + std::size_t n; +}; + +struct sequence_stats_summary +{ + double average; + double variance; + double deviation; +}; + +/* Stats calculated jointly for N same-sized sequences to save the space + * for count. + */ + +template +class cumulative_stats +{ +public: + struct summary + { + std::size_t count; + std::array sequence_summary; + }; + + void reset()noexcept{*this=cumulative_stats();} + + template + void add(Ts&&... xs)noexcept + { + static_assert( + sizeof...(Ts)==N,"A sample must be provided for each sequence."); + + if(BOOST_UNLIKELY(++n==0)){ /* wraparound */ + reset(); + n=1; + } + mp11::tuple_transform( + welfords_algorithm{n}, + std::forward_as_tuple(std::forward(xs)...), + data); + } + + summary get_summary()const noexcept + { + summary res; + res.count=n; + for(std::size_t i=0;i(n):0.0, /* biased */ + deviation=std::sqrt(variance); + res.sequence_summary[i]={average,variance,deviation}; + } + return res; + } + +private: + std::size_t n=0; + std::array data; +}; + +#if defined(BOOST_HAS_THREADS) + +template +class concurrent_cumulative_stats:cumulative_stats +{ + using super=cumulative_stats; + using lock_guard=std::lock_guard; + +public: + using summary=typename super::summary; + + concurrent_cumulative_stats()noexcept:super{}{} + concurrent_cumulative_stats(const concurrent_cumulative_stats& x)noexcept: + concurrent_cumulative_stats{x,lock_guard{x.mut}}{} + + concurrent_cumulative_stats& + operator=(const concurrent_cumulative_stats& x)noexcept + { + auto x1=x; + lock_guard lck{mut}; + static_cast(*this)=x1; + return *this; + } + + void reset()noexcept + { + lock_guard lck{mut}; + super::reset(); + } + + template + void add(Ts&&... xs)noexcept + { + lock_guard lck{mut}; + super::add(std::forward(xs)...); + } + + summary get_summary()const noexcept + { + lock_guard lck{mut}; + return super::get_summary(); + } + +private: + concurrent_cumulative_stats(const super& x,lock_guard&&):super{x}{} + + mutable rw_spinlock mut; +}; + +#else + +template +using concurrent_cumulative_stats=cumulative_stats; + +#endif + +} /* namespace foa */ +} /* namespace detail */ +} /* namespace unordered */ +} /* namespace boost */ + +#endif diff --git a/include/boost/unordered/detail/foa/flat_map_types.hpp b/include/boost/unordered/detail/foa/flat_map_types.hpp index bbabd8f..7575589 100644 --- a/include/boost/unordered/detail/foa/flat_map_types.hpp +++ b/include/boost/unordered/detail/foa/flat_map_types.hpp @@ -6,6 +6,8 @@ #ifndef BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP #define BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP +#include + namespace boost { namespace unordered { @@ -24,6 +26,9 @@ namespace boost { using element_type = value_type; + using types = flat_map_types; + using constructibility_checker = map_types_constructibility; + static value_type& value_from(element_type& x) { return x; } template @@ -47,18 +52,21 @@ namespace boost { template static void construct(A& al, init_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } template static void construct(A& al, value_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } template static void construct(A& al, key_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } @@ -78,8 +86,8 @@ namespace boost { } }; } // namespace foa - } // namespace detail - } // namespace unordered + } // namespace detail + } // namespace unordered } // namespace boost #endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP diff --git a/include/boost/unordered/detail/foa/flat_set_types.hpp b/include/boost/unordered/detail/foa/flat_set_types.hpp index 78d5aa7..ecda31e 100644 --- a/include/boost/unordered/detail/foa/flat_set_types.hpp +++ b/include/boost/unordered/detail/foa/flat_set_types.hpp @@ -5,6 +5,8 @@ #ifndef BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP #define BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP +#include + namespace boost { namespace unordered { @@ -20,6 +22,9 @@ namespace boost { using element_type = value_type; + using types = flat_set_types; + using constructibility_checker = set_types_constructibility; + static Key& value_from(element_type& x) { return x; } static element_type&& move(element_type& x) { return std::move(x); } @@ -27,6 +32,7 @@ namespace boost { template static void construct(A& al, value_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } @@ -36,8 +42,8 @@ namespace boost { } }; } // namespace foa - } // namespace detail - } // namespace unordered + } // namespace detail + } // namespace unordered } // namespace boost #endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP diff --git a/include/boost/unordered/detail/foa/node_handle.hpp b/include/boost/unordered/detail/foa/node_handle.hpp index abf9cb2..c525df0 100644 --- a/include/boost/unordered/detail/foa/node_handle.hpp +++ b/include/boost/unordered/detail/foa/node_handle.hpp @@ -1,4 +1,5 @@ /* Copyright 2023 Christian Mazakas. + * Copyright 2024 Joaquin M Lopez Munoz. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -11,7 +12,9 @@ #include +#include #include +#include namespace boost{ namespace unordered{ @@ -26,6 +29,13 @@ struct insert_return_type NodeType node; }; +template +struct iteratorless_insert_return_type +{ + bool inserted; + NodeType node; +}; + template struct node_handle_base { @@ -41,7 +51,27 @@ struct node_handle_base element_type p_; [[no_unique_address]] opt_storage a_; - protected: + friend struct node_handle_access; + + template + void move_assign_allocator_if(node_handle_base&& nh)noexcept + { + move_assign_allocator_if( + std::integral_constant{}, std::move(nh)); + } + + void move_assign_allocator_if( + std::true_type, node_handle_base&& nh)noexcept + { + al()=std::move(nh.al()); + } + + void move_assign_allocator_if( + std::false_type, node_handle_base&&)noexcept + { + } + +protected: node_value_type& data()noexcept { return *(p_.p); @@ -125,9 +155,7 @@ struct node_handle_base BOOST_ASSERT(pocma||al()==nh.al()); type_policy::destroy(al(),&p_); - if(pocma){ - al()=std::move(nh.al()); - } + move_assign_allocator_if(std::move(nh)); p_=std::move(nh.p_); nh.reset(); @@ -152,7 +180,17 @@ struct node_handle_base } } - allocator_type get_allocator()const noexcept{return al();} + allocator_type get_allocator()const + { +#if defined(BOOST_GCC) + /* GCC lifetime analysis incorrectly warns about uninitialized + * allocator object under some circumstances. + */ + if(empty())__builtin_unreachable(); +#endif + return al(); + } + explicit operator bool()const noexcept{ return !empty();} [[nodiscard]] bool empty()const noexcept{return p_.p==nullptr;} @@ -195,6 +233,82 @@ struct node_handle_base } }; +// Internal usage of node_handle_base protected API + +struct node_handle_access +{ + template + using node_type = node_handle_base; + +#if BOOST_CLANG_VERSION < 190000 + // https://github.com/llvm/llvm-project/issues/25708 + + template + struct element_type_impl + { + using type = typename node_type::element_type; + }; + template + using element_type = typename element_type_impl::type; +#else + template + using element_type = typename node_type::element_type; +#endif + + template + static element_type& + element(node_type& nh)noexcept + { + return nh.element(); + } + + template + static element_type + const& element(node_type const& nh)noexcept + { + return nh.element(); + } + + template + static void emplace( + node_type& nh, + element_type&& x, Allocator a) + { + nh.emplace(std::move(x), a); + } + + template + static void reset(node_type& nh) + { + nh.reset(); + } +}; + +template +class node_handle_emplacer_class +{ + using access = node_handle_access; + using node_type = access::node_type; + using element_type = access::element_type; + + node_type & nh; + +public: + node_handle_emplacer_class(node_type& nh_): nh(nh_) {} + + void operator()(element_type&& x,Allocator a) + { + access::emplace(nh, std::move(x), a); + } +}; + +template +node_handle_emplacer_class +node_handle_emplacer(node_handle_base& nh) +{ + return {nh}; +} + } } } diff --git a/include/boost/unordered/detail/foa/node_map_handle.hpp b/include/boost/unordered/detail/foa/node_map_handle.hpp new file mode 100644 index 0000000..8df9227 --- /dev/null +++ b/include/boost/unordered/detail/foa/node_map_handle.hpp @@ -0,0 +1,56 @@ +/* Copyright 2023 Christian Mazakas. + * Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_HANDLE_HPP +#define BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_HANDLE_HPP + +#include + +namespace boost{ +namespace unordered{ +namespace detail{ +namespace foa{ + +template +struct node_map_handle + : public node_handle_base +{ +private: + using base_type = node_handle_base; + + using typename base_type::type_policy; + +public: + using key_type = typename TypePolicy::key_type; + using mapped_type = typename TypePolicy::mapped_type; + + constexpr node_map_handle() noexcept = default; + node_map_handle(node_map_handle&& nh) noexcept = default; + + node_map_handle& operator=(node_map_handle&&) noexcept = default; + + key_type& key() const + { + BOOST_ASSERT(!this->empty()); + return const_cast(this->data().first); + } + + mapped_type& mapped() const + { + BOOST_ASSERT(!this->empty()); + return const_cast(this->data().second); + } +}; + +} +} +} +} + +#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_HANDLE_HPP diff --git a/include/boost/unordered/detail/foa/node_map_types.hpp b/include/boost/unordered/detail/foa/node_map_types.hpp index 100ded6..9261cc0 100644 --- a/include/boost/unordered/detail/foa/node_map_types.hpp +++ b/include/boost/unordered/detail/foa/node_map_types.hpp @@ -7,6 +7,8 @@ #define BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP #include +#include +#include #include @@ -27,6 +29,9 @@ namespace boost { using element_type = foa::element_type; + using types = node_map_types; + using constructibility_checker = map_types_constructibility; + static value_type& value_from(element_type const& x) { return *(x.p); @@ -66,24 +71,27 @@ namespace boost { static void construct( A& al, element_type* p, element_type const& copy) { - construct(al, p, *copy.p); + construct(al, p, detail::as_const(*copy.p)); } template static void construct(A& al, init_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } template static void construct(A& al, value_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } template static void construct(A& al, key_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } @@ -93,9 +101,12 @@ namespace boost { p->p = std::allocator_traits>::allocate(al, 1); BOOST_TRY { + auto address = std::to_address(p->p); + constructibility_checker::check( + al, address, std::forward(args)...); std::allocator_traits>::construct( - al, std::to_address(p->p), std::forward(args)...); + al, address, std::forward(args)...); } BOOST_CATCH(...) { @@ -131,8 +142,8 @@ namespace boost { }; } // namespace foa - } // namespace detail - } // namespace unordered + } // namespace detail + } // namespace unordered } // namespace boost #endif // BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP diff --git a/include/boost/unordered/detail/foa/node_set_handle.hpp b/include/boost/unordered/detail/foa/node_set_handle.hpp new file mode 100644 index 0000000..c3bc065 --- /dev/null +++ b/include/boost/unordered/detail/foa/node_set_handle.hpp @@ -0,0 +1,48 @@ +/* Copyright 2023 Christian Mazakas. + * Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See https://www.boost.org/libs/unordered for library home page. + */ + +#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_SET_HANDLE_HPP +#define BOOST_UNORDERED_DETAIL_FOA_NODE_SET_HANDLE_HPP + +#include + +namespace boost{ +namespace unordered{ +namespace detail{ +namespace foa{ + +template +struct node_set_handle + : public detail::foa::node_handle_base +{ +private: + using base_type = detail::foa::node_handle_base; + + using typename base_type::type_policy; + +public: + using value_type = typename TypePolicy::value_type; + + constexpr node_set_handle() noexcept = default; + node_set_handle(node_set_handle&& nh) noexcept = default; + node_set_handle& operator=(node_set_handle&&) noexcept = default; + + value_type& value() const + { + BOOST_ASSERT(!this->empty()); + return const_cast(this->data()); + } +}; + +} +} +} +} + +#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_SET_HANDLE_HPP diff --git a/include/boost/unordered/detail/foa/node_set_types.hpp b/include/boost/unordered/detail/foa/node_set_types.hpp index c42f262..e653840 100644 --- a/include/boost/unordered/detail/foa/node_set_types.hpp +++ b/include/boost/unordered/detail/foa/node_set_types.hpp @@ -6,6 +6,8 @@ #define BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP #include +#include +#include #include @@ -24,6 +26,9 @@ namespace boost { using element_type = foa::element_type; + using types = node_set_types; + using constructibility_checker = set_types_constructibility; + static value_type& value_from(element_type const& x) { return *x.p; } static Key const& extract(element_type const& k) { return *k.p; } static element_type&& move(element_type& x) { return std::move(x); } @@ -33,7 +38,7 @@ namespace boost { static void construct( A& al, element_type* p, element_type const& copy) { - construct(al, p, *copy.p); + construct(al, p, detail::as_const(*copy.p)); } template @@ -47,6 +52,7 @@ namespace boost { template static void construct(A& al, value_type* p, Args&&... args) { + constructibility_checker::check(al, p, std::forward(args)...); std::allocator_traits>::construct(al, p, std::forward(args)...); } @@ -56,9 +62,12 @@ namespace boost { p->p = std::allocator_traits>::allocate(al, 1); BOOST_TRY { + auto address = std::to_address(p->p); + constructibility_checker::check( + al, address, std::forward(args)...); std::allocator_traits>::construct( - al, std::to_address(p->p), std::forward(args)...); + al, address, std::forward(args)...); } BOOST_CATCH(...) { @@ -84,8 +93,8 @@ namespace boost { }; } // namespace foa - } // namespace detail - } // namespace unordered + } // namespace detail + } // namespace unordered } // namespace boost #endif // BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP diff --git a/include/boost/unordered/detail/foa/rw_spinlock.hpp b/include/boost/unordered/detail/foa/rw_spinlock.hpp new file mode 100644 index 0000000..8437fd3 --- /dev/null +++ b/include/boost/unordered/detail/foa/rw_spinlock.hpp @@ -0,0 +1,179 @@ +#ifndef BOOST_UNORDERED_DETAIL_FOA_RW_SPINLOCK_HPP_INCLUDED +#define BOOST_UNORDERED_DETAIL_FOA_RW_SPINLOCK_HPP_INCLUDED + +// Copyright 2023 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include + +namespace boost{ +namespace unordered{ +namespace detail{ +namespace foa{ + +class rw_spinlock +{ +private: + + // bit 31: locked exclusive + // bit 30: writer pending + // bit 29..0: reader lock count + + static constexpr std::uint32_t locked_exclusive_mask = 1u << 31; // 0x8000'0000 + static constexpr std::uint32_t writer_pending_mask = 1u << 30; // 0x4000'0000 + static constexpr std::uint32_t reader_lock_count_mask = writer_pending_mask - 1; // 0x3FFF'FFFF + + std::atomic state_ = {}; + +private: + + // Effects: Provides a hint to the implementation that the current thread + // has been unable to make progress for k+1 iterations. + + static void yield( unsigned k ) noexcept + { + unsigned const sleep_every = 1024; // see below + + k %= sleep_every; + + if( k < 5 ) + { + // Intel recommendation from the Optimization Reference Manual + // Exponentially increase number of PAUSE instructions each + // iteration until reaching a maximum which is approximately + // one timeslice long (2^4 == 16 in our case) + + unsigned const pause_count = 1u << k; + + for( unsigned i = 0; i < pause_count; ++i ) + { + boost::core::sp_thread_pause(); + } + } + else if( k < sleep_every - 1 ) + { + // Once the maximum number of PAUSE instructions is reached, + // we switch to yielding the timeslice immediately + + boost::core::sp_thread_yield(); + } + else + { + // After `sleep_every` iterations of no progress, we sleep, + // to avoid a deadlock if a lower priority thread has the lock + + boost::core::sp_thread_sleep(); + } + } + +public: + + bool try_lock_shared() noexcept + { + std::uint32_t st = state_.load( std::memory_order_relaxed ); + + if( st >= reader_lock_count_mask ) + { + // either bit 31 set, bit 30 set, or reader count is max + return false; + } + + std::uint32_t newst = st + 1; + return state_.compare_exchange_strong( st, newst, std::memory_order_acquire, std::memory_order_relaxed ); + } + + void lock_shared() noexcept + { + for( unsigned k = 0; ; ++k ) + { + std::uint32_t st = state_.load( std::memory_order_relaxed ); + + if( st < reader_lock_count_mask ) + { + std::uint32_t newst = st + 1; + if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return; + } + + yield( k ); + } + } + + void unlock_shared() noexcept + { + // pre: locked shared, not locked exclusive + + state_.fetch_sub( 1, std::memory_order_release ); + + // if the writer pending bit is set, there's a writer waiting + // let it acquire the lock; it will clear the bit on unlock + } + + bool try_lock() noexcept + { + std::uint32_t st = state_.load( std::memory_order_relaxed ); + + if( st & locked_exclusive_mask ) + { + // locked exclusive + return false; + } + + if( st & reader_lock_count_mask ) + { + // locked shared + return false; + } + + std::uint32_t newst = locked_exclusive_mask; + return state_.compare_exchange_strong( st, newst, std::memory_order_acquire, std::memory_order_relaxed ); + } + + void lock() noexcept + { + for( unsigned k = 0; ; ++k ) + { + std::uint32_t st = state_.load( std::memory_order_relaxed ); + + if( st & locked_exclusive_mask ) + { + // locked exclusive, spin + } + else if( ( st & reader_lock_count_mask ) == 0 ) + { + // not locked exclusive, not locked shared, try to lock + + std::uint32_t newst = locked_exclusive_mask; + if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return; + } + else if( st & writer_pending_mask ) + { + // writer pending bit already set, nothing to do + } + else + { + // locked shared, set writer pending bit + + std::uint32_t newst = st | writer_pending_mask; + state_.compare_exchange_weak( st, newst, std::memory_order_relaxed, std::memory_order_relaxed ); + } + + yield( k ); + } + } + + void unlock() noexcept + { + // pre: locked exclusive, not locked shared + state_.store( 0, std::memory_order_release ); + } +}; + +} /* namespace foa */ +} /* namespace detail */ +} /* namespace unordered */ +} /* namespace boost */ + +#endif // BOOST_UNORDERED_DETAIL_FOA_RW_SPINLOCK_HPP_INCLUDED diff --git a/include/boost/unordered/detail/foa/table.hpp b/include/boost/unordered/detail/foa/table.hpp index 3886483..1ccaf91 100644 --- a/include/boost/unordered/detail/foa/table.hpp +++ b/include/boost/unordered/detail/foa/table.hpp @@ -1,6 +1,6 @@ /* Fast open-addressing hash table. * - * Copyright 2022-2023 Joaquin M Lopez Munoz. + * Copyright 2022-2024 Joaquin M Lopez Munoz. * Copyright 2023 Christian Mazakas. * Copyright 2024 Braden Ganetsky. * Distributed under the Boost Software License, Version 1.0. @@ -352,6 +352,10 @@ class table:table_core_impl const_iterator>::type; using erase_return_type=table_erase_return_type; +#if defined(BOOST_UNORDERED_ENABLE_STATS) + using stats=typename super::stats; +#endif + table( std::size_t n=default_bucket_count,const Hash& h_=Hash(), const Pred& pred_=Pred(),const Allocator& al_=Allocator()): @@ -533,6 +537,11 @@ class table:table_core_impl using super::rehash; using super::reserve; +#if defined(BOOST_UNORDERED_ENABLE_STATS) + using super::get_stats; + using super::reset_stats; +#endif + template friend std::size_t erase_if(table& x,Predicate& pr) { @@ -575,6 +584,7 @@ class table:table_core_impl x.arrays=ah.release(); x.size_ctrl.ml=x.initial_max_load(); x.size_ctrl.size=0; + BOOST_UNORDERED_SWAP_STATS(this->cstats,x.cstats); } template diff --git a/include/boost/unordered/detail/foa/types_constructibility.hpp b/include/boost/unordered/detail/foa/types_constructibility.hpp new file mode 100644 index 0000000..10b9208 --- /dev/null +++ b/include/boost/unordered/detail/foa/types_constructibility.hpp @@ -0,0 +1,172 @@ +// Copyright (C) 2024 Braden Ganetsky +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP +#define BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP + +#include +#include +#include +#include + +namespace boost { + namespace unordered { + namespace detail { + namespace foa { + template struct check_key_type_t + { + static_assert(std::is_constructible::value, + "key_type must be constructible from Args"); + }; + template struct check_key_type_t + { + static_assert(std::is_constructible::value, + "key_type must be default constructible"); + }; + template struct check_key_type_t + { + static_assert(std::is_constructible::value, + "key_type must be copy constructible"); + }; + template struct check_key_type_t + { + static_assert(std::is_constructible::value, + "key_type must be move constructible"); + }; + + template struct check_mapped_type_t + { + static_assert(std::is_constructible::value, + "mapped_type must be constructible from Args"); + }; + template struct check_mapped_type_t + { + static_assert(std::is_constructible::value, + "mapped_type must be default constructible"); + }; + template + struct check_mapped_type_t + { + static_assert(std::is_constructible::value, + "mapped_type must be copy constructible"); + }; + template struct check_mapped_type_t + { + static_assert(std::is_constructible::value, + "mapped_type must be move constructible"); + }; + + template struct map_types_constructibility + { + using key_type = typename TypePolicy::key_type; + using mapped_type = typename TypePolicy::mapped_type; + using init_type = typename TypePolicy::init_type; + using value_type = typename TypePolicy::value_type; + + template + static void check(A&, X*, Args&&...) + { + // Pass through, as we cannot say anything about a general allocator + } + + template static void check_key_type() + { + (void)check_key_type_t{}; + } + template static void check_mapped_type() + { + (void)check_mapped_type_t{}; + } + + template + static void check(std::allocator&, key_type*, Arg&&) + { + check_key_type(); + } + + template + static void check( + std::allocator&, value_type*, Arg1&&, Arg2&&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check(std::allocator&, value_type*, + const std::pair&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check( + std::allocator&, value_type*, std::pair&&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check(std::allocator&, value_type*, + std::piecewise_construct_t, std::tuple&&, + std::tuple&&) + { + check_key_type(); + check_mapped_type(); + } + + template + static void check( + std::allocator&, init_type*, Arg1&&, Arg2&&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check(std::allocator&, init_type*, + const std::pair&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check( + std::allocator&, init_type*, std::pair&&) + { + check_key_type(); + check_mapped_type(); + } + template + static void check(std::allocator&, init_type*, + std::piecewise_construct_t, std::tuple&&, + std::tuple&&) + { + check_key_type(); + check_mapped_type(); + } + }; + + template struct set_types_constructibility + { + using key_type = typename TypePolicy::key_type; + using value_type = typename TypePolicy::value_type; + static_assert(std::is_same::value, ""); + + template + static void check(A&, X*, Args&&...) + { + // Pass through, as we cannot say anything about a general allocator + } + + template + static void check(std::allocator&, key_type*, Args&&...) + { + (void)check_key_type_t{}; + } + }; + } // namespace foa + } // namespace detail + } // namespace unordered +} // namespace boost + +#endif // BOOST_UNORDERED_DETAIL_FOA_TYPES_CONSTRUCTIBILITY_HPP diff --git a/include/boost/unordered/detail/mulx.hpp b/include/boost/unordered/detail/mulx.hpp index 78e12ee..365bbaf 100644 --- a/include/boost/unordered/detail/mulx.hpp +++ b/include/boost/unordered/detail/mulx.hpp @@ -8,7 +8,6 @@ #include #include -#include #if defined(_MSC_VER) && !defined(__clang__) # include diff --git a/include/boost/unordered/detail/type_traits.hpp b/include/boost/unordered/detail/type_traits.hpp index 24f2445..59197bd 100644 --- a/include/boost/unordered/detail/type_traits.hpp +++ b/include/boost/unordered/detail/type_traits.hpp @@ -183,6 +183,17 @@ namespace boost { using iter_to_alloc_t = typename std::pair const, iter_val_t >; #endif + +#if BOOST_CXX_VERSION < 201703L + template + constexpr typename std::add_const::type& as_const(T& t) noexcept + { + return t; + } + template void as_const(const T&&) = delete; +#else + using std::as_const; +#endif } // namespace detail } // namespace unordered } // namespace boost diff --git a/include/boost/unordered/hash_traits.hpp b/include/boost/unordered/hash_traits.hpp index d5f94a7..5e2b1af 100644 --- a/include/boost/unordered/hash_traits.hpp +++ b/include/boost/unordered/hash_traits.hpp @@ -1,6 +1,6 @@ /* Hash function characterization. * - * Copyright 2022 Joaquin M Lopez Munoz. + * Copyright 2022-2024 Joaquin M Lopez Munoz. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) @@ -19,12 +19,34 @@ namespace unordered{ namespace detail{ template -struct hash_is_avalanching_impl: std::false_type{}; +struct hash_is_avalanching_impl:std::false_type{}; + +template +struct avalanching_value +{ + static constexpr bool value=IsAvalanching::value; +}; + +/* may be explicitly marked as BOOST_DEPRECATED in the future */ +template<> struct avalanching_value +{ + static constexpr bool value=true; +}; + +template +struct hash_is_avalanching_impl< + Hash, + boost::unordered::detail::void_t +>:std::integral_constant< + bool, + avalanching_value::value +>{}; template -struct hash_is_avalanching_impl >: - std::true_type{}; +struct hash_is_avalanching_impl< + Hash, + typename std::enable_if<((void)Hash::is_avalanching,true)>::type +>{}; /* Hash::is_avalanching is not a type: compile error downstream */ } /* namespace detail */ @@ -32,8 +54,12 @@ struct hash_is_avalanching_impl::value is true when the type Hash::is_avalanching - * is present, false otherwise. +/* hash_is_avalanching::value is: + * - false if Hash::is_avalanching is not present. + * - Hash::is_avalanching::value if this is present and constexpr-convertible + * to a bool. + * - true if Hash::is_avalanching is void (deprecated). + * - ill-formed otherwise. */ template struct hash_is_avalanching: detail::hash_is_avalanching_impl::type{}; diff --git a/include/boost/unordered/unordered_flat_map.hpp b/include/boost/unordered/unordered_flat_map.hpp index f515e0c..71ec734 100644 --- a/include/boost/unordered/unordered_flat_map.hpp +++ b/include/boost/unordered/unordered_flat_map.hpp @@ -1,4 +1,5 @@ // Copyright (C) 2022-2023 Christian Mazakas +// Copyright (C) 2024 Joaquin M Lopez Munoz // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -72,6 +73,10 @@ namespace boost { using iterator = typename table_type::iterator; using const_iterator = typename table_type::const_iterator; +#if defined(BOOST_UNORDERED_ENABLE_STATS) + using stats = typename table_type::stats; +#endif + unordered_flat_map() : unordered_flat_map(0) {} explicit unordered_flat_map(size_type n, hasher const& h = hasher(), @@ -173,6 +178,7 @@ namespace boost { { } + template unordered_flat_map( concurrent_flat_map&& other) : table_(std::move(other.table_)) @@ -194,6 +200,13 @@ namespace boost { return *this; } + unordered_flat_map& operator=(std::initializer_list il) + { + this->clear(); + this->insert(il.begin(), il.end()); + return *this; + } + allocator_type get_allocator() const noexcept { return table_.get_allocator(); @@ -650,6 +663,14 @@ namespace boost { void reserve(size_type n) { table_.reserve(n); } +#if defined(BOOST_UNORDERED_ENABLE_STATS) + /// Stats + /// + stats get_stats() const { return table_.get_stats(); } + + void reset_stats() noexcept { table_.reset_stats(); } +#endif + /// Observers /// diff --git a/include/boost/unordered/unordered_flat_map_fwd.hpp b/include/boost/unordered/unordered_flat_map_fwd.hpp index 7f1b1bc..75a606d 100644 --- a/include/boost/unordered/unordered_flat_map_fwd.hpp +++ b/include/boost/unordered/unordered_flat_map_fwd.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2022 Christian Mazakas +// Copyright (C) 2024 Braden Ganetsky // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,6 +14,10 @@ #include #include +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + namespace boost { namespace unordered { template , @@ -34,6 +39,16 @@ namespace boost { void swap(unordered_flat_map& lhs, unordered_flat_map& rhs) noexcept(noexcept(lhs.swap(rhs))); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class KeyEqual = std::equal_to > + using unordered_flat_map = + boost::unordered::unordered_flat_map > >; + } // namespace pmr +#endif } // namespace unordered using boost::unordered::unordered_flat_map; diff --git a/include/boost/unordered/unordered_flat_set.hpp b/include/boost/unordered/unordered_flat_set.hpp index 62a0268..8cabd28 100644 --- a/include/boost/unordered/unordered_flat_set.hpp +++ b/include/boost/unordered/unordered_flat_set.hpp @@ -1,4 +1,5 @@ // Copyright (C) 2022-2023 Christian Mazakas +// Copyright (C) 2024 Joaquin M Lopez Munoz // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -68,6 +69,10 @@ namespace boost { using iterator = typename table_type::iterator; using const_iterator = typename table_type::const_iterator; +#if defined(BOOST_UNORDERED_ENABLE_STATS) + using stats = typename table_type::stats; +#endif + unordered_flat_set() : unordered_flat_set(0) {} explicit unordered_flat_set(size_type n, hasher const& h = hasher(), @@ -169,6 +174,7 @@ namespace boost { { } + template unordered_flat_set( concurrent_flat_set&& other) : table_(std::move(other.table_)) @@ -190,6 +196,13 @@ namespace boost { return *this; } + unordered_flat_set& operator=(std::initializer_list il) + { + this->clear(); + this->insert(il.begin(), il.end()); + return *this; + } + allocator_type get_allocator() const noexcept { return table_.get_allocator(); @@ -470,6 +483,14 @@ namespace boost { void reserve(size_type n) { table_.reserve(n); } +#if defined(BOOST_UNORDERED_ENABLE_STATS) + /// Stats + /// + stats get_stats() const { return table_.get_stats(); } + + void reset_stats() noexcept { table_.reset_stats(); } +#endif + /// Observers /// diff --git a/include/boost/unordered/unordered_flat_set_fwd.hpp b/include/boost/unordered/unordered_flat_set_fwd.hpp index d916d08..235d6fd 100644 --- a/include/boost/unordered/unordered_flat_set_fwd.hpp +++ b/include/boost/unordered/unordered_flat_set_fwd.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2022 Christian Mazakas +// Copyright (C) 2024 Braden Ganetsky // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,6 +14,10 @@ #include #include +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + namespace boost { namespace unordered { template , @@ -34,6 +39,15 @@ namespace boost { void swap(unordered_flat_set& lhs, unordered_flat_set& rhs) noexcept(noexcept(lhs.swap(rhs))); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class KeyEqual = std::equal_to > + using unordered_flat_set = boost::unordered::unordered_flat_set >; + } // namespace pmr +#endif } // namespace unordered using boost::unordered::unordered_flat_set; diff --git a/include/boost/unordered/unordered_node_map.hpp b/include/boost/unordered/unordered_node_map.hpp index 76ece41..701d1b7 100644 --- a/include/boost/unordered/unordered_node_map.hpp +++ b/include/boost/unordered/unordered_node_map.hpp @@ -1,4 +1,5 @@ // Copyright (C) 2022-2023 Christian Mazakas +// Copyright (C) 2024 Joaquin M Lopez Munoz // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,8 +9,8 @@ #include #pragma once -#include -#include +#include +#include #include #include #include @@ -32,45 +33,13 @@ namespace boost { #pragma warning(disable : 4714) /* marked as __forceinline not inlined */ #endif - namespace detail { - template - struct node_map_handle - : public detail::foa::node_handle_base - { - private: - using base_type = detail::foa::node_handle_base; - - using typename base_type::type_policy; - - template - friend class boost::unordered::unordered_node_map; - - public: - using key_type = typename TypePolicy::key_type; - using mapped_type = typename TypePolicy::mapped_type; - - constexpr node_map_handle() noexcept = default; - node_map_handle(node_map_handle&& nh) noexcept = default; - - node_map_handle& operator=(node_map_handle&&) noexcept = default; - - key_type& key() const - { - BOOST_ASSERT(!this->empty()); - return const_cast(this->data().first); - } - - mapped_type& mapped() const - { - BOOST_ASSERT(!this->empty()); - return const_cast(this->data().second); - } - }; - } // namespace detail - template class unordered_node_map { + template + friend class concurrent_node_map; + using map_types = detail::foa::node_map_types::void_pointer>; @@ -105,12 +74,16 @@ namespace boost { typename std::allocator_traits::const_pointer; using iterator = typename table_type::iterator; using const_iterator = typename table_type::const_iterator; - using node_type = detail::node_map_handle::template rebind_alloc< typename map_types::value_type>>; using insert_return_type = detail::foa::insert_return_type; +#if defined(BOOST_UNORDERED_ENABLE_STATS) + using stats = typename table_type::stats; +#endif + unordered_node_map() : unordered_node_map(0) {} explicit unordered_node_map(size_type n, hasher const& h = hasher(), @@ -212,6 +185,13 @@ namespace boost { { } + template + unordered_node_map( + concurrent_node_map&& other) + : table_(std::move(other.table_)) + { + } + ~unordered_node_map() = default; unordered_node_map& operator=(unordered_node_map const& other) @@ -227,6 +207,13 @@ namespace boost { return *this; } + unordered_node_map& operator=(std::initializer_list il) + { + this->clear(); + this->insert(il.begin(), il.end()); + return *this; + } + allocator_type get_allocator() const noexcept { return table_.get_allocator(); @@ -299,15 +286,17 @@ namespace boost { insert_return_type insert(node_type&& nh) { + using access = detail::foa::node_handle_access; + if (nh.empty()) { return {end(), false, node_type{}}; } BOOST_ASSERT(get_allocator() == nh.get_allocator()); - auto itp = table_.insert(std::move(nh.element())); + auto itp = table_.insert(std::move(access::element(nh))); if (itp.second) { - nh.reset(); + access::reset(nh); return {itp.first, true, node_type{}}; } else { return {itp.first, false, std::move(nh)}; @@ -316,15 +305,17 @@ namespace boost { iterator insert(const_iterator, node_type&& nh) { + using access = detail::foa::node_handle_access; + if (nh.empty()) { return end(); } BOOST_ASSERT(get_allocator() == nh.get_allocator()); - auto itp = table_.insert(std::move(nh.element())); + auto itp = table_.insert(std::move(access::element(nh))); if (itp.second) { - nh.reset(); + access::reset(nh); return itp.first; } else { return itp.first; @@ -499,7 +490,8 @@ namespace boost { BOOST_ASSERT(pos != end()); node_type nh; auto elem = table_.extract(pos); - nh.emplace(std::move(elem), get_allocator()); + detail::foa::node_handle_emplacer(nh)( + std::move(elem), get_allocator()); return nh; } @@ -745,6 +737,14 @@ namespace boost { void reserve(size_type n) { table_.reserve(n); } +#if defined(BOOST_UNORDERED_ENABLE_STATS) + /// Stats + /// + stats get_stats() const { return table_.get_stats(); } + + void reset_stats() noexcept { table_.reset_stats(); } +#endif + /// Observers /// diff --git a/include/boost/unordered/unordered_node_map_fwd.hpp b/include/boost/unordered/unordered_node_map_fwd.hpp index 4368053..28aa9ba 100644 --- a/include/boost/unordered/unordered_node_map_fwd.hpp +++ b/include/boost/unordered/unordered_node_map_fwd.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2022 Christian Mazakas +// Copyright (C) 2024 Braden Ganetsky // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,6 +14,10 @@ #include #include +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + namespace boost { namespace unordered { template , @@ -34,6 +39,16 @@ namespace boost { void swap(unordered_node_map& lhs, unordered_node_map& rhs) noexcept(noexcept(lhs.swap(rhs))); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class KeyEqual = std::equal_to > + using unordered_node_map = + boost::unordered::unordered_node_map > >; + } // namespace pmr +#endif } // namespace unordered using boost::unordered::unordered_node_map; diff --git a/include/boost/unordered/unordered_node_set.hpp b/include/boost/unordered/unordered_node_set.hpp index b87fb63..8cc23e4 100644 --- a/include/boost/unordered/unordered_node_set.hpp +++ b/include/boost/unordered/unordered_node_set.hpp @@ -1,4 +1,5 @@ // Copyright (C) 2022-2023 Christian Mazakas +// Copyright (C) 2024 Joaquin M Lopez Munoz // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -8,8 +9,9 @@ #include #pragma once +#include #include -#include +#include #include #include #include @@ -31,37 +33,12 @@ namespace boost { #pragma warning(disable : 4714) /* marked as __forceinline not inlined */ #endif - namespace detail { - template - struct node_set_handle - : public detail::foa::node_handle_base - { - private: - using base_type = detail::foa::node_handle_base; - - using typename base_type::type_policy; - - template - friend class boost::unordered::unordered_node_set; - - public: - using value_type = typename TypePolicy::value_type; - - constexpr node_set_handle() noexcept = default; - node_set_handle(node_set_handle&& nh) noexcept = default; - node_set_handle& operator=(node_set_handle&&) noexcept = default; - - value_type& value() const - { - BOOST_ASSERT(!this->empty()); - return const_cast(this->data()); - } - }; - } // namespace detail - template class unordered_node_set { + template + friend class concurrent_node_set; + using set_types = detail::foa::node_set_types::void_pointer>; @@ -95,12 +72,16 @@ namespace boost { typename std::allocator_traits::const_pointer; using iterator = typename table_type::iterator; using const_iterator = typename table_type::const_iterator; - using node_type = detail::node_set_handle::template rebind_alloc< typename set_types::value_type>>; using insert_return_type = detail::foa::insert_return_type; +#if defined(BOOST_UNORDERED_ENABLE_STATS) + using stats = typename table_type::stats; +#endif + unordered_node_set() : unordered_node_set(0) {} explicit unordered_node_set(size_type n, hasher const& h = hasher(), @@ -202,6 +183,13 @@ namespace boost { { } + template + unordered_node_set( + concurrent_node_set&& other) + : table_(std::move(other.table_)) + { + } + ~unordered_node_set() = default; unordered_node_set& operator=(unordered_node_set const& other) @@ -217,6 +205,13 @@ namespace boost { return *this; } + unordered_node_set& operator=(std::initializer_list il) + { + this->clear(); + this->insert(il.begin(), il.end()); + return *this; + } + allocator_type get_allocator() const noexcept { return table_.get_allocator(); @@ -304,15 +299,17 @@ namespace boost { insert_return_type insert(node_type&& nh) { + using access = detail::foa::node_handle_access; + if (nh.empty()) { return {end(), false, node_type{}}; } BOOST_ASSERT(get_allocator() == nh.get_allocator()); - auto itp = table_.insert(std::move(nh.element())); + auto itp = table_.insert(std::move(access::element(nh))); if (itp.second) { - nh.reset(); + access::reset(nh); return {itp.first, true, node_type{}}; } else { return {itp.first, false, std::move(nh)}; @@ -321,15 +318,17 @@ namespace boost { iterator insert(const_iterator, node_type&& nh) { + using access = detail::foa::node_handle_access; + if (nh.empty()) { return end(); } BOOST_ASSERT(get_allocator() == nh.get_allocator()); - auto itp = table_.insert(std::move(nh.element())); + auto itp = table_.insert(std::move(access::element(nh))); if (itp.second) { - nh.reset(); + access::reset(nh); return itp.first; } else { return itp.first; @@ -387,7 +386,8 @@ namespace boost { BOOST_ASSERT(pos != end()); node_type nh; auto elem = table_.extract(pos); - nh.emplace(std::move(elem), get_allocator()); + detail::foa::node_handle_emplacer(nh)( + std::move(elem), get_allocator()); return nh; } @@ -559,6 +559,14 @@ namespace boost { void reserve(size_type n) { table_.reserve(n); } +#if defined(BOOST_UNORDERED_ENABLE_STATS) + /// Stats + /// + stats get_stats() const { return table_.get_stats(); } + + void reset_stats() noexcept { table_.reset_stats(); } +#endif + /// Observers /// diff --git a/include/boost/unordered/unordered_node_set_fwd.hpp b/include/boost/unordered/unordered_node_set_fwd.hpp index ac68f7a..fb8a811 100644 --- a/include/boost/unordered/unordered_node_set_fwd.hpp +++ b/include/boost/unordered/unordered_node_set_fwd.hpp @@ -1,5 +1,6 @@ // Copyright (C) 2023 Christian Mazakas +// Copyright (C) 2024 Braden Ganetsky // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -13,6 +14,10 @@ #include #include +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE +#include +#endif + namespace boost { namespace unordered { template , @@ -34,6 +39,15 @@ namespace boost { void swap(unordered_node_set& lhs, unordered_node_set& rhs) noexcept(noexcept(lhs.swap(rhs))); + +#ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE + namespace pmr { + template , + class KeyEqual = std::equal_to > + using unordered_node_set = boost::unordered::unordered_node_set >; + } // namespace pmr +#endif } // namespace unordered using boost::unordered::unordered_node_set; diff --git a/include/boost/unordered/unordered_printers.hpp b/include/boost/unordered/unordered_printers.hpp new file mode 100644 index 0000000..cace173 --- /dev/null +++ b/include/boost/unordered/unordered_printers.hpp @@ -0,0 +1,414 @@ +// Copyright 2024 Braden Ganetsky +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +// Generated on 2024-08-25T17:48:54 + +#ifndef BOOST_UNORDERED_UNORDERED_PRINTERS_HPP +#define BOOST_UNORDERED_UNORDERED_PRINTERS_HPP + +#ifndef BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS +#if defined(__ELF__) +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Woverlength-strings" +#endif +__asm__(".pushsection \".debug_gdb_scripts\", \"MS\",@progbits,1\n" + ".ascii \"\\4gdb.inlined-script.BOOST_UNORDERED_UNORDERED_PRINTERS_HPP\\n\"\n" + ".ascii \"import gdb.printing\\n\"\n" + ".ascii \"import gdb.xmethod\\n\"\n" + ".ascii \"import re\\n\"\n" + ".ascii \"import math\\n\"\n" + + ".ascii \"class BoostUnorderedHelpers:\\n\"\n" + ".ascii \" def maybe_unwrap_atomic(n):\\n\"\n" + ".ascii \" if f\\\"{n.type.strip_typedefs()}\\\".startswith(\\\"std::atomic<\\\"):\\n\"\n" + ".ascii \" underlying_type = n.type.template_argument(0)\\n\"\n" + ".ascii \" return n.cast(underlying_type)\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" return n\\n\"\n" + + ".ascii \" def maybe_unwrap_foa_element(e):\\n\"\n" + ".ascii \" if f\\\"{e.type.strip_typedefs()}\\\".startswith(\\\"boost::unordered::detail::foa::element_type<\\\"):\\n\"\n" + ".ascii \" return e[\\\"p\\\"]\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" return e\\n\"\n" + + ".ascii \" def maybe_unwrap_reference(value):\\n\"\n" + ".ascii \" if value.type.code == gdb.TYPE_CODE_REF:\\n\"\n" + ".ascii \" return value.referenced_value()\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" return value\\n\"\n" + + ".ascii \" def countr_zero(n):\\n\"\n" + ".ascii \" for i in range(32):\\n\"\n" + ".ascii \" if (n & (1 << i)) != 0:\\n\"\n" + ".ascii \" return i\\n\"\n" + ".ascii \" return 32\\n\"\n" + + ".ascii \"class BoostUnorderedPointerCustomizationPoint:\\n\"\n" + ".ascii \" def __init__(self, any_ptr):\\n\"\n" + ".ascii \" vis = gdb.default_visualizer(any_ptr)\\n\"\n" + ".ascii \" if vis is None:\\n\"\n" + ".ascii \" self.to_address = lambda ptr: ptr\\n\"\n" + ".ascii \" self.next = lambda ptr, offset: ptr + offset\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" self.to_address = lambda ptr: ptr if (ptr.type.code == gdb.TYPE_CODE_PTR) else type(vis).boost_to_address(ptr)\\n\"\n" + ".ascii \" self.next = lambda ptr, offset: type(vis).boost_next(ptr, offset)\\n\"\n" + + ".ascii \"class BoostUnorderedFcaPrinter:\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)\\n\"\n" + ".ascii \" self.name = f\\\"{self.val.type.strip_typedefs()}\\\".split(\\\"<\\\")[0]\\n\"\n" + ".ascii \" self.name = self.name.replace(\\\"boost::unordered::\\\", \\\"boost::\\\")\\n\"\n" + ".ascii \" self.is_map = self.name.endswith(\\\"map\\\")\\n\"\n" + ".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"table_\\\"][\\\"buckets_\\\"][\\\"buckets\\\"])\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" size = self.val[\\\"table_\\\"][\\\"size_\\\"]\\n\"\n" + ".ascii \" return f\\\"{self.name} with {size} elements\\\"\\n\"\n" + + ".ascii \" def display_hint(self):\\n\"\n" + ".ascii \" return \\\"map\\\"\\n\"\n" + + ".ascii \" def children(self):\\n\"\n" + ".ascii \" def generator():\\n\"\n" + ".ascii \" grouped_buckets = self.val[\\\"table_\\\"][\\\"buckets_\\\"]\\n\"\n" + + ".ascii \" size = grouped_buckets[\\\"size_\\\"]\\n\"\n" + ".ascii \" buckets = grouped_buckets[\\\"buckets\\\"]\\n\"\n" + ".ascii \" bucket_index = 0\\n\"\n" + + ".ascii \" count = 0\\n\"\n" + ".ascii \" while bucket_index != size:\\n\"\n" + ".ascii \" current_bucket = self.cpo.next(self.cpo.to_address(buckets), bucket_index)\\n\"\n" + ".ascii \" node = self.cpo.to_address(current_bucket.dereference()[\\\"next\\\"])\\n\"\n" + ".ascii \" while node != 0:\\n\"\n" + ".ascii \" value = node.dereference()[\\\"buf\\\"][\\\"t_\\\"]\\n\"\n" + ".ascii \" if self.is_map:\\n\"\n" + ".ascii \" first = value[\\\"first\\\"]\\n\"\n" + ".ascii \" second = value[\\\"second\\\"]\\n\"\n" + ".ascii \" yield \\\"\\\", first\\n\"\n" + ".ascii \" yield \\\"\\\", second\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" yield \\\"\\\", count\\n\"\n" + ".ascii \" yield \\\"\\\", value\\n\"\n" + ".ascii \" count += 1\\n\"\n" + ".ascii \" node = self.cpo.to_address(node.dereference()[\\\"next\\\"])\\n\"\n" + ".ascii \" bucket_index += 1\\n\"\n" + + ".ascii \" return generator()\\n\"\n" + + ".ascii \"class BoostUnorderedFcaIteratorPrinter:\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + ".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"p\\\"])\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" if self.valid():\\n\"\n" + ".ascii \" value = self.cpo.to_address(self.val[\\\"p\\\"]).dereference()[\\\"buf\\\"][\\\"t_\\\"]\\n\"\n" + ".ascii \" return f\\\"iterator = {{ {value} }}\\\"\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" return \\\"iterator = { end iterator }\\\"\\n\"\n" + + ".ascii \" def valid(self):\\n\"\n" + ".ascii \" return (self.cpo.to_address(self.val[\\\"p\\\"]) != 0) and (self.cpo.to_address(self.val[\\\"itb\\\"][\\\"p\\\"]) != 0)\\n\"\n" + + ".ascii \"class BoostUnorderedFoaTableCoreCumulativeStatsPrinter:\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" return \\\"[stats]\\\"\\n\"\n" + + ".ascii \" def display_hint(self):\\n\"\n" + ".ascii \" return \\\"map\\\"\\n\"\n" + + ".ascii \" def children(self):\\n\"\n" + ".ascii \" def generator():\\n\"\n" + ".ascii \" members = [\\\"insertion\\\", \\\"successful_lookup\\\", \\\"unsuccessful_lookup\\\"]\\n\"\n" + ".ascii \" for member in members:\\n\"\n" + ".ascii \" yield \\\"\\\", member\\n\"\n" + ".ascii \" yield \\\"\\\", self.val[member]\\n\"\n" + ".ascii \" return generator()\\n\"\n" + + ".ascii \"class BoostUnorderedFoaCumulativeStatsPrinter:\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + ".ascii \" self.n = self.val[\\\"n\\\"]\\n\"\n" + ".ascii \" self.N = self.val.type.template_argument(0)\\n\"\n" + + ".ascii \" def display_hint(self):\\n\"\n" + ".ascii \" return \\\"map\\\"\\n\"\n" + + ".ascii \" def children(self):\\n\"\n" + ".ascii \" def generator():\\n\"\n" + ".ascii \" yield \\\"\\\", \\\"count\\\"\\n\"\n" + ".ascii \" yield \\\"\\\", self.n\\n\"\n" + + ".ascii \" sequence_stats_data = gdb.lookup_type(\\\"boost::unordered::detail::foa::sequence_stats_data\\\")\\n\"\n" + ".ascii \" data = self.val[\\\"data\\\"]\\n\"\n" + ".ascii \" arr = data.address.reinterpret_cast(sequence_stats_data.pointer())\\n\"\n" + ".ascii \" def build_string(idx):\\n\"\n" + ".ascii \" entry = arr[idx]\\n\"\n" + ".ascii \" avg = float(entry[\\\"m\\\"])\\n\"\n" + ".ascii \" var = float(entry[\\\"s\\\"] / self.n) if (self.n != 0) else 0.0\\n\"\n" + ".ascii \" dev = math.sqrt(var)\\n\"\n" + ".ascii \" return f\\\"{{avg = {avg}, var = {var}, dev = {dev}}}\\\"\\n\"\n" + + ".ascii \" if self.N > 0:\\n\"\n" + ".ascii \" yield \\\"\\\", \\\"probe_length\\\"\\n\"\n" + ".ascii \" yield \\\"\\\", build_string(0)\\n\"\n" + ".ascii \" if self.N > 1:\\n\"\n" + ".ascii \" yield \\\"\\\", \\\"num_comparisons\\\"\\n\"\n" + ".ascii \" yield \\\"\\\", build_string(1)\\n\"\n" + + ".ascii \" return generator()\\n\"\n" + + ".ascii \"class BoostUnorderedFoaPrinter:\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = BoostUnorderedHelpers.maybe_unwrap_reference(val)\\n\"\n" + ".ascii \" self.name = f\\\"{self.val.type.strip_typedefs()}\\\".split(\\\"<\\\")[0]\\n\"\n" + ".ascii \" self.name = self.name.replace(\\\"boost::unordered::\\\", \\\"boost::\\\")\\n\"\n" + ".ascii \" self.is_map = self.name.endswith(\\\"map\\\")\\n\"\n" + ".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"table_\\\"][\\\"arrays\\\"][\\\"groups_\\\"])\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" size = BoostUnorderedHelpers.maybe_unwrap_atomic(self.val[\\\"table_\\\"][\\\"size_ctrl\\\"][\\\"size\\\"])\\n\"\n" + ".ascii \" return f\\\"{self.name} with {size} elements\\\"\\n\"\n" + + ".ascii \" def display_hint(self):\\n\"\n" + ".ascii \" return \\\"map\\\"\\n\"\n" + + ".ascii \" def is_regular_layout(self, group):\\n\"\n" + ".ascii \" typename = group[\\\"m\\\"].type.strip_typedefs()\\n\"\n" + ".ascii \" array_size = typename.sizeof // typename.target().sizeof\\n\"\n" + ".ascii \" if array_size == 16:\\n\"\n" + ".ascii \" return True\\n\"\n" + ".ascii \" elif array_size == 2:\\n\"\n" + ".ascii \" return False\\n\"\n" + + ".ascii \" def match_occupied(self, group):\\n\"\n" + ".ascii \" m = group[\\\"m\\\"]\\n\"\n" + ".ascii \" at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b][\\\"n\\\"])\\n\"\n" + + ".ascii \" if self.is_regular_layout(group):\\n\"\n" + ".ascii \" bits = [1 << b for b in range(16) if at(b) == 0]\\n\"\n" + ".ascii \" return 0x7FFF & ~sum(bits)\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" xx = at(0) | at(1)\\n\"\n" + ".ascii \" yy = xx | (xx >> 32)\\n\"\n" + ".ascii \" return 0x7FFF & (yy | (yy >> 16))\\n\"\n" + + ".ascii \" def is_sentinel(self, group, pos):\\n\"\n" + ".ascii \" m = group[\\\"m\\\"]\\n\"\n" + ".ascii \" at = lambda b: BoostUnorderedHelpers.maybe_unwrap_atomic(m[b][\\\"n\\\"])\\n\"\n" + + ".ascii \" N = group[\\\"N\\\"]\\n\"\n" + ".ascii \" sentinel_ = group[\\\"sentinel_\\\"]\\n\"\n" + ".ascii \" if self.is_regular_layout(group):\\n\"\n" + ".ascii \" return pos == N-1 and at(N-1) == sentinel_\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" return pos == N-1 and (at(0) & 0x4000400040004000) == 0x4000 and (at(1) & 0x4000400040004000) == 0\\n\"\n" + + ".ascii \" def children(self):\\n\"\n" + ".ascii \" def generator():\\n\"\n" + ".ascii \" table = self.val[\\\"table_\\\"]\\n\"\n" + ".ascii \" groups = self.cpo.to_address(table[\\\"arrays\\\"][\\\"groups_\\\"])\\n\"\n" + ".ascii \" elements = self.cpo.to_address(table[\\\"arrays\\\"][\\\"elements_\\\"])\\n\"\n" + + ".ascii \" pc_ = groups.cast(gdb.lookup_type(\\\"unsigned char\\\").pointer())\\n\"\n" + ".ascii \" p_ = elements\\n\"\n" + ".ascii \" first_time = True\\n\"\n" + ".ascii \" mask = 0\\n\"\n" + ".ascii \" n0 = 0\\n\"\n" + ".ascii \" n = 0\\n\"\n" + + ".ascii \" count = 0\\n\"\n" + ".ascii \" while p_ != 0:\\n\"\n" + ".ascii \" # This if block mirrors the condition in the begin() call\\n\"\n" + ".ascii \" if (not first_time) or (self.match_occupied(groups.dereference()) & 1):\\n\"\n" + ".ascii \" pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(p_)\\n\"\n" + ".ascii \" value = self.cpo.to_address(pointer).dereference()\\n\"\n" + ".ascii \" if self.is_map:\\n\"\n" + ".ascii \" first = value[\\\"first\\\"]\\n\"\n" + ".ascii \" second = value[\\\"second\\\"]\\n\"\n" + ".ascii \" yield \\\"\\\", first\\n\"\n" + ".ascii \" yield \\\"\\\", second\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" yield \\\"\\\", count\\n\"\n" + ".ascii \" yield \\\"\\\", value\\n\"\n" + ".ascii \" count += 1\\n\"\n" + ".ascii \" first_time = False\\n\"\n" + + ".ascii \" n0 = pc_.cast(gdb.lookup_type(\\\"uintptr_t\\\")) % groups.dereference().type.sizeof\\n\"\n" + ".ascii \" pc_ = self.cpo.next(pc_, -n0)\\n\"\n" + + ".ascii \" mask = (self.match_occupied(pc_.cast(groups.type).dereference()) >> (n0+1)) << (n0+1)\\n\"\n" + ".ascii \" while mask == 0:\\n\"\n" + ".ascii \" pc_ = self.cpo.next(pc_, groups.dereference().type.sizeof)\\n\"\n" + ".ascii \" p_ = self.cpo.next(p_, groups.dereference()[\\\"N\\\"])\\n\"\n" + ".ascii \" mask = self.match_occupied(pc_.cast(groups.type).dereference())\\n\"\n" + + ".ascii \" n = BoostUnorderedHelpers.countr_zero(mask)\\n\"\n" + ".ascii \" if self.is_sentinel(pc_.cast(groups.type).dereference(), n):\\n\"\n" + ".ascii \" p_ = 0\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" pc_ = self.cpo.next(pc_, n)\\n\"\n" + ".ascii \" p_ = self.cpo.next(p_, n - n0)\\n\"\n" + + ".ascii \" return generator()\\n\"\n" + + ".ascii \"class BoostUnorderedFoaIteratorPrinter:\\n\"\n" + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + ".ascii \" self.cpo = BoostUnorderedPointerCustomizationPoint(self.val[\\\"p_\\\"])\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" if self.valid():\\n\"\n" + ".ascii \" element = self.cpo.to_address(self.val[\\\"p_\\\"])\\n\"\n" + ".ascii \" pointer = BoostUnorderedHelpers.maybe_unwrap_foa_element(element)\\n\"\n" + ".ascii \" value = self.cpo.to_address(pointer).dereference()\\n\"\n" + ".ascii \" return f\\\"iterator = {{ {value} }}\\\"\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" return \\\"iterator = { end iterator }\\\"\\n\"\n" + + ".ascii \" def valid(self):\\n\"\n" + ".ascii \" return (self.cpo.to_address(self.val[\\\"p_\\\"]) != 0) and (self.cpo.to_address(self.val[\\\"pc_\\\"]) != 0)\\n\"\n" + + ".ascii \"def boost_unordered_build_pretty_printer():\\n\"\n" + ".ascii \" pp = gdb.printing.RegexpCollectionPrettyPrinter(\\\"boost_unordered\\\")\\n\"\n" + ".ascii \" add_template_printer = lambda name, printer: pp.add_printer(name, f\\\"^{name}<.*>$\\\", printer)\\n\"\n" + ".ascii \" add_concrete_printer = lambda name, printer: pp.add_printer(name, f\\\"^{name}$\\\", printer)\\n\"\n" + + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_map\\\", BoostUnorderedFcaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_multimap\\\", BoostUnorderedFcaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_set\\\", BoostUnorderedFcaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_multiset\\\", BoostUnorderedFcaPrinter)\\n\"\n" + + ".ascii \" add_template_printer(\\\"boost::unordered::detail::iterator_detail::iterator\\\", BoostUnorderedFcaIteratorPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::detail::iterator_detail::c_iterator\\\", BoostUnorderedFcaIteratorPrinter)\\n\"\n" + + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_flat_map\\\", BoostUnorderedFoaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_flat_set\\\", BoostUnorderedFoaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_node_map\\\", BoostUnorderedFoaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::unordered_node_set\\\", BoostUnorderedFoaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::concurrent_flat_map\\\", BoostUnorderedFoaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::concurrent_flat_set\\\", BoostUnorderedFoaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::concurrent_node_map\\\", BoostUnorderedFoaPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::concurrent_node_set\\\", BoostUnorderedFoaPrinter)\\n\"\n" + + ".ascii \" add_template_printer(\\\"boost::unordered::detail::foa::table_iterator\\\", BoostUnorderedFoaIteratorPrinter)\\n\"\n" + + ".ascii \" add_concrete_printer(\\\"boost::unordered::detail::foa::table_core_cumulative_stats\\\", BoostUnorderedFoaTableCoreCumulativeStatsPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::detail::foa::cumulative_stats\\\", BoostUnorderedFoaCumulativeStatsPrinter)\\n\"\n" + ".ascii \" add_template_printer(\\\"boost::unordered::detail::foa::concurrent_cumulative_stats\\\", BoostUnorderedFoaCumulativeStatsPrinter)\\n\"\n" + + ".ascii \" return pp\\n\"\n" + + ".ascii \"gdb.printing.register_pretty_printer(gdb.current_objfile(), boost_unordered_build_pretty_printer())\\n\"\n" + + + + ".ascii \"# https://sourceware.org/gdb/current/onlinedocs/gdb.html/Writing-an-Xmethod.html\\n\"\n" + ".ascii \"class BoostUnorderedFoaGetStatsMethod(gdb.xmethod.XMethod):\\n\"\n" + ".ascii \" def __init__(self):\\n\"\n" + ".ascii \" gdb.xmethod.XMethod.__init__(self, \\\"get_stats\\\")\\n\"\n" + + ".ascii \" def get_worker(self, method_name):\\n\"\n" + ".ascii \" if method_name == \\\"get_stats\\\":\\n\"\n" + ".ascii \" return BoostUnorderedFoaGetStatsWorker()\\n\"\n" + + ".ascii \"class BoostUnorderedFoaGetStatsWorker(gdb.xmethod.XMethodWorker):\\n\"\n" + ".ascii \" def get_arg_types(self):\\n\"\n" + ".ascii \" return None\\n\"\n" + + ".ascii \" def get_result_type(self, obj):\\n\"\n" + ".ascii \" return gdb.lookup_type(\\\"boost::unordered::detail::foa::table_core_cumulative_stats\\\")\\n\"\n" + + ".ascii \" def __call__(self, obj):\\n\"\n" + ".ascii \" try:\\n\"\n" + ".ascii \" return obj[\\\"table_\\\"][\\\"cstats\\\"]\\n\"\n" + ".ascii \" except gdb.error:\\n\"\n" + ".ascii \" print(\\\"Error: Binary was compiled without stats. Recompile with `BOOST_UNORDERED_ENABLE_STATS` defined.\\\")\\n\"\n" + ".ascii \" return\\n\"\n" + + ".ascii \"class BoostUnorderedFoaMatcher(gdb.xmethod.XMethodMatcher):\\n\"\n" + ".ascii \" def __init__(self):\\n\"\n" + ".ascii \" gdb.xmethod.XMethodMatcher.__init__(self, 'BoostUnorderedFoaMatcher')\\n\"\n" + ".ascii \" self.methods = [BoostUnorderedFoaGetStatsMethod()]\\n\"\n" + + ".ascii \" def match(self, class_type, method_name):\\n\"\n" + ".ascii \" template_name = f\\\"{class_type.strip_typedefs()}\\\".split(\\\"<\\\")[0]\\n\"\n" + ".ascii \" regex = \\\"^boost::unordered::(unordered|concurrent)_(flat|node)_(map|set)$\\\"\\n\"\n" + ".ascii \" if not re.match(regex, template_name):\\n\"\n" + ".ascii \" return None\\n\"\n" + + ".ascii \" workers = []\\n\"\n" + ".ascii \" for method in self.methods:\\n\"\n" + ".ascii \" if method.enabled:\\n\"\n" + ".ascii \" worker = method.get_worker(method_name)\\n\"\n" + ".ascii \" if worker:\\n\"\n" + ".ascii \" workers.append(worker)\\n\"\n" + ".ascii \" return workers\\n\"\n" + + ".ascii \"gdb.xmethod.register_xmethod_matcher(None, BoostUnorderedFoaMatcher())\\n\"\n" + + + + ".ascii \"\\\"\\\"\\\" Fancy pointer support \\\"\\\"\\\"\\n\"\n" + + ".ascii \"\\\"\\\"\\\"\\n\"\n" + ".ascii \"To allow your own fancy pointer type to interact with Boost.Unordered GDB pretty-printers,\\n\"\n" + ".ascii \"create a pretty-printer for your own type with the following additional methods.\\n\"\n" + + ".ascii \"(Note, this is assuming the presence of a type alias `pointer` for the underlying\\n\"\n" + ".ascii \"raw pointer type, Substitute whichever name is applicable in your case.)\\n\"\n" + + ".ascii \"`boost_to_address(fancy_ptr)`\\n\"\n" + ".ascii \" * A static method, but `@staticmethod` is not required\\n\"\n" + ".ascii \" * Parameter `fancy_ptr` of type `gdb.Value`\\n\"\n" + ".ascii \" * Its `.type` will be your fancy pointer type\\n\"\n" + ".ascii \" * Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer\\n\"\n" + ".ascii \" * This method should be equivalent to calling `operator->()` on your fancy pointer in C++\\n\"\n" + + ".ascii \"`boost_next(raw_ptr, offset)`\\n\"\n" + ".ascii \" * Parameter `raw_ptr` of type `gdb.Value`\\n\"\n" + ".ascii \" * Its `.type` will be `pointer`\\n\"\n" + ".ascii \" * Parameter `offset`\\n\"\n" + ".ascii \" * Either has integer type, or is of type `gdb.Value` with an underlying integer\\n\"\n" + ".ascii \" * Returns a `gdb.Value` with the raw pointer equivalent to your fancy pointer, as if you did the following operations\\n\"\n" + ".ascii \" 1. Convert the incoming raw pointer to your fancy pointer\\n\"\n" + ".ascii \" 2. Use operator+= to add the offset to the fancy pointer\\n\"\n" + ".ascii \" 3. Convert back to the raw pointer\\n\"\n" + ".ascii \" * Note, you will not actually do these operations as stated. You will do equivalent lower-level operations that emulate having done the above\\n\"\n" + ".ascii \" * Ultimately, it will be as if you called `operator+()` on your fancy pointer in C++, but using only raw pointers\\n\"\n" + + ".ascii \"Example\\n\"\n" + ".ascii \"```\\n\"\n" + ".ascii \"class MyFancyPtrPrinter:\\n\"\n" + ".ascii \" ...\\n\"\n" + + ".ascii \" # Equivalent to `operator->()`\\n\"\n" + ".ascii \" def boost_to_address(fancy_ptr):\\n\"\n" + ".ascii \" ...\\n\"\n" + ".ascii \" return ...\\n\"\n" + + ".ascii \" # Equivalent to `operator+()`\\n\"\n" + ".ascii \" def boost_next(raw_ptr, offset):\\n\"\n" + ".ascii \" ...\\n\"\n" + ".ascii \" return ...\\n\"\n" + + ".ascii \" ...\\n\"\n" + ".ascii \"```\\n\"\n" + ".ascii \"\\\"\\\"\\\"\\n\"\n" + + ".byte 0\n" + ".popsection\n"); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#endif // defined(__ELF__) +#endif // !defined(BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS) + +#endif // !defined(BOOST_UNORDERED_UNORDERED_PRINTERS_HPP) diff --git a/make_unordered.sh b/make_unordered.sh index 32316da..05214a4 100755 --- a/make_unordered.sh +++ b/make_unordered.sh @@ -1,9 +1,7 @@ -#!/usr/bin/env bash - -wget https://boostorg.jfrog.io/artifactory/main/release/1.85.0/source/boost_1_85_0.tar.gz -tar -xvf boost_1_85_0.tar.gz +#wget https://boostorg.jfrog.io/artifactory/main/release/1.85.0/source/boost_1_87_0.tar.gz +#tar -xvf boost_1_87_0.tar.gz mkdir temp_boost -bcp --boost=boost_1_85_0 boost/unordered/unordered_flat_map.hpp boost/unordered/unordered_flat_set.hpp boost/unordered/unordered_node_map.hpp boost/unordered/unordered_node_set.hpp temp_boost +bcp --boost=boost_1_87_0 boost/unordered/unordered_flat_map.hpp boost/unordered/unordered_flat_set.hpp boost/unordered/unordered_node_map.hpp boost/unordered/unordered_node_set.hpp temp_boost cd temp_boost/boost rm version.hpp @@ -31,44 +29,46 @@ rm -rf detail rm -rf predef rm -rf mp11 -cp ~/boost_unordered/include/boost/minconfig.hpp . +cp ../../include/boost/minconfig.hpp . coan source -R '-DBOOST_WORKAROUND(a,b)=0' -DBOOST_HAS_PRAGMA_ONCE -UBOOST_NO_CXX11_DEFAULTED_FUNCTIONS -UBOOST_NO_CXX11_RVALUE_REFERENCES -UBOOST_NO_CXX11_VARIADIC_TEMPLATES -UBOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION . -find . -name '*.hpp' -exec sed -i 's///g' {} \; +find . -name '*.hpp' -exec gsed -i 's///g' {} \; -find . -name '*.hpp' -exec sed -i '/boost\/core\/allocator_access.hpp/d' {} \; -find . -name '*.hpp' -exec sed -i '/boost\/core\/allocator_traits.hpp/d' {} \; -find . -name '*.hpp' -exec sed -i '/boost\/core\/serialization.hpp/d' {} \; -find . -name '*.hpp' -exec sed -i '/boost\/core\/bit.hpp/d' {} \; -find . -name '*.hpp' -exec sed -i '/boost\/core\/pointer_traits.hpp/d' {} \; -find . -name '*.hpp' -exec sed -i '/boost\/core\/noncopyable.hpp/d' {} \; -find . -name '*.hpp' -exec sed -i '/boost\/config\/workaround.hpp/d' {} \; -find . -name '*.hpp' -exec sed -i '/boost\/cstdint.hpp/d' {} \; -find . -name '*.hpp' -exec sed -i '/>::construct(\1,\2);/gs' {} \; find . -name '*.hpp' -exec perl -pi -e 's|boost::allocator_destroy\((.*?), (.*)\)|std::allocator_traits>::destroy(\1, \2)|' {} \; diff --git a/test/helpers/equivalent.hpp b/test/helpers/equivalent.hpp index c9f06c6..bd27d8d 100644 --- a/test/helpers/equivalent.hpp +++ b/test/helpers/equivalent.hpp @@ -11,7 +11,8 @@ #include "./metafunctions.hpp" #include #include -#include +#include +#include namespace test { template diff --git a/test/helpers/helpers.hpp b/test/helpers/helpers.hpp index 146dea4..d421efa 100644 --- a/test/helpers/helpers.hpp +++ b/test/helpers/helpers.hpp @@ -51,6 +51,11 @@ namespace test { static_cast::difference_type>(x)); return it; } + + template + using is_map = + std::integral_constant::value>; } #endif diff --git a/test/helpers/unordered.hpp b/test/helpers/unordered.hpp index 613d083..fae0842 100644 --- a/test/helpers/unordered.hpp +++ b/test/helpers/unordered.hpp @@ -15,6 +15,8 @@ #include #include #else +#include +#include #endif #include "postfix.hpp" // clang-format on diff --git a/test/unordered/compile_tests.hpp b/test/unordered/compile_tests.hpp index 6925951..b0f49ab 100644 --- a/test/unordered/compile_tests.hpp +++ b/test/unordered/compile_tests.hpp @@ -18,6 +18,7 @@ #endif #include "../helpers/check_return_type.hpp" +#include #include #include #include @@ -150,7 +151,7 @@ template void container_test(X& r, T const&) X u5 = rvalue(a_const); a.swap(b); - boost::swap(a, b); + boost::core::invoke_swap(a, b); test::check_return_type::equals_ref(r = a); // Allocator @@ -237,7 +238,7 @@ template void unordered_destructible_test(X&) test::check_return_type::equals(a_const.cend()); a.swap(b); - boost::swap(a, b); + boost::core::invoke_swap(a, b); test::check_return_type::equals(a.size()); test::check_return_type::equals(a.max_size()); @@ -527,7 +528,7 @@ template void unordered_unique_test(X& r, T const& t) test::check_return_type::equals(insert_return.inserted); test::check_return_type::equals(insert_return.position); test::check_return_type::equals_ref(insert_return.node); - boost::swap(insert_return, insert_return2); + boost::core::invoke_swap(insert_return, insert_return2); #endif } diff --git a/test/unordered/extract_tests.cpp b/test/unordered/extract_tests.cpp index 4addb52..3c50542 100644 --- a/test/unordered/extract_tests.cpp +++ b/test/unordered/extract_tests.cpp @@ -126,7 +126,7 @@ namespace extract_tests { test::allocator1 >* test_set; boost::unordered_multiset >* test_multiset; - boost::unordered_flat_map >* test_map; boost::unordered_multimap >* test_multimap;