Sync from upstream.

This commit is contained in:
Rene Rivera
2024-06-02 13:50:31 -05:00
13 changed files with 493 additions and 18 deletions

View File

@ -23,6 +23,15 @@ target_link_libraries(boost_unordered
Boost::throw_exception
)
if(CMAKE_VERSION VERSION_GREATER 3.18 AND CMAKE_GENERATOR MATCHES "Visual Studio")
file(GLOB_RECURSE boost_unordered_IDEFILES CONFIGURE_DEPENDS include/*.hpp)
source_group(TREE ${PROJECT_SOURCE_DIR}/include FILES ${boost_unordered_IDEFILES} PREFIX "Header Files")
list(APPEND boost_unordered_IDEFILES extra/boost_unordered.natvis)
target_sources(boost_unordered PRIVATE ${boost_unordered_IDEFILES})
endif()
target_compile_features(boost_unordered INTERFACE cxx_std_11)
if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")

View File

@ -1,5 +1,5 @@
// Copyright 2021 Peter Dimov.
// Copyright 2023 Joaquin M Lopez Munoz.
// Copyright 2023-2024 Joaquin M Lopez Munoz.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
@ -25,6 +25,7 @@
#include <iostream>
#include <iomanip>
#include <chrono>
#include <type_traits>
using namespace std::chrono_literals;
@ -341,7 +342,7 @@ template<> struct fnv1a_hash_impl<64>
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits >
{
using is_avalanching = void;
using is_avalanching = std::true_type;
};
template<class K, class V> using std_unordered_map_fnv1a =

View File

@ -18,6 +18,7 @@
#include <iostream>
#include <iomanip>
#include <chrono>
#include <type_traits>
using namespace std::chrono_literals;
@ -262,7 +263,7 @@ template<> struct fnv1a_hash_impl<64>
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits >
{
using is_avalanching = void;
using is_avalanching = std::true_type;
};
template<class K, class V> using boost_unordered_flat_map_fnv1a =
@ -272,7 +273,7 @@ template<class K, class V> using boost_unordered_flat_map_fnv1a =
struct slightly_bad_hash
{
using is_avalanching = void;
using is_avalanching = std::true_type;
std::size_t operator()( std::string const& s ) const
{
@ -295,7 +296,7 @@ template<class K, class V> using boost_unordered_flat_map_slightly_bad_hash =
struct bad_hash
{
using is_avalanching = void;
using is_avalanching = std::true_type;
std::size_t operator()( std::string const& s ) const
{

View File

@ -1,5 +1,5 @@
// Copyright 2021 Peter Dimov.
// Copyright 2023 Joaquin M Lopez Munoz.
// Copyright 2023-2024 Joaquin M Lopez Munoz.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
@ -26,6 +26,7 @@
#include <iostream>
#include <iomanip>
#include <chrono>
#include <type_traits>
using namespace std::chrono_literals;
@ -342,7 +343,7 @@ template<> struct fnv1a_hash_impl<64>
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits >
{
using is_avalanching = void;
using is_avalanching = std::true_type;
};
template<class K, class V> using std_unordered_map_fnv1a =

View File

@ -18,6 +18,7 @@ include::unordered/concurrent.adoc[]
include::unordered/hash_quality.adoc[]
include::unordered/compliance.adoc[]
include::unordered/structures.adoc[]
include::unordered/debuggability.adoc[]
include::unordered/benchmarks.adoc[]
include::unordered/rationale.adoc[]
include::unordered/ref.adoc[]

View File

@ -10,6 +10,8 @@
* Added container `pmr` aliases when header `<memory_resource>` is available. The alias `boost::unordered::pmr::[container]` refers to `boost::unordered::[container]` with a `std::pmr::polymorphic_allocator` allocator type.
* Equipped open-addressing and concurrent containers to internally calculate and provide statistical metrics affected by the quality of the hash function. This functionality is enabled by the global macro `BOOST_UNORDERED_ENABLE_STATS`.
* Avalanching hash functions must now be marked via an `is_avalanching` typedef with an embedded `value` constant set to `true` (typically, defining `is_avalanching` as `std::true_type`). `using is_avalanching = void` is deprecated but allowed for backwards compatibility.
* Added Visual Studio Natvis framework custom visualizations for containers and iterators. This works for all containers with an allocator using raw pointers. In this release, containers and iterators are not supported if their allocator uses fancy pointers. This may be addressed in later releases.
== Release 1.85.0

View File

@ -0,0 +1,16 @@
[#debuggability]
:idprefix: debuggability_
= Debuggability
== Visual Studio Natvis
All containers and iterators have custom visualizations in the Natvis framework, as long as their allocator uses regular raw pointers. Any container or iterator with an allocator using fancy pointers does not have a custom visualization right now.
The visualizations mirror those for the standard unordered containers. A container has a maximum of 100 elements displayed at once. Each set element has its item name listed as `[i]`, where `i` is the index in the display, starting at `0`. Each map element has its item name listed as `[\{key-display}]` by default. For example, if the first element is the pair `("abc", 1)`, the item name will be `["abc"]`. This behaviour can be overridden by using the view "ShowElementsByIndex", which switches the map display behaviour to name the elements by index. This same view name is used in the standard unordered containers.
By default, the closed-addressing containers will show the `[hash_function]` and `[key_eq]`, the `[spare_hash_function]` and `[spare_key_eq]` if applicable, the `[allocator]`, and the elements. Using the view "detailed" adds the `[bucket_count]` and `[max_load_factor]`. Conversely, using the view "simple" shows only the elements, with no other items present.
By default, the open-addressing containers will show the `[hash_function]`, `[key_eq]`, `[allocator]`, and the elements. Using the view "simple" shows only the elements, with no other items present. Both the SIMD and the non-SIMD implementations are viewable through the Natvis framework.
Iterators are displayed similarly to their standard counterparts. An iterator is displayed as though it were the element that it points to. An end iterator is simply displayed as `\{ end iterator }`.

View File

@ -33,7 +33,7 @@ follows:
----
struct my_string_hash_function
{
using is_avalanching = void; // instruct Boost.Unordered to not use post-mixing
using is_avalanching = std::true_type; // instruct Boost.Unordered to not use post-mixing
std::size_t operator()(const std::string& x) const
{

View File

@ -32,9 +32,14 @@ large changes in the returned hash code &#8212;ideally, flipping one bit in the
the input value results in each bit of the hash code flipping with probability 50%. Approaching
this property is critical for the proper behavior of open-addressing hash containers.
`hash_is_avalanching<Hash>::value` is `true` if `Hash::is_avalanching` is a valid type,
and `false` otherwise.
Users can then declare a hash function `Hash` as avalanching either by embedding an `is_avalanching` typedef
`hash_is_avalanching<Hash>::value` is:
* `false` if `Hash::is_avalanching` is not present,
* `Hash::is_avalanching::value` if this is present and convertible at compile time to a `bool`,
* `true` if `Hash::is_avalanching` is `void` (this usage is deprecated),
* ill-formed otherwise.
Users can then declare a hash function `Hash` as avalanching either by embedding an appropriate `is_avalanching` typedef
into the definition of `Hash`, or directly by specializing `hash_is_avalanching<Hash>` to a class with
an embedded compile-time constant `value` set to `true`.

View File

@ -0,0 +1,380 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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)
-->
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<!-- FCA containers -->
<Type Name="boost::unordered::detail::functions&lt;*&gt;" Inheritable="false">
<Intrinsic Name="active_idx" Expression="current_ &amp; 1" />
<Intrinsic Name="spare_idx" Expression="1 - active_idx()" />
<Intrinsic Name="has_spare" Expression="(current_ &amp; 2) != 0" />
<Intrinsic Name="hash" Expression="*reinterpret_cast&lt;$T1*&gt;(static_cast&lt;function_pair::base1*&gt;(&amp;funcs_[idx].t_))">
<Parameter Name="idx" Type="size_t" />
</Intrinsic>
<Intrinsic Name="key_eq" Expression="*reinterpret_cast&lt;$T2*&gt;(static_cast&lt;function_pair::base2*&gt;(&amp;funcs_[idx].t_))">
<Parameter Name="idx" Type="size_t" />
</Intrinsic>
<Expand>
<Item Name="[hash_function]">hash(active_idx())</Item>
<Item Name="[key_eq]">key_eq(active_idx())</Item>
<Item Name="[spare_hash_function]" Condition="has_spare()">hash(spare_idx())</Item>
<Item Name="[spare_key_eq]" Condition="has_spare()">key_eq(spare_idx())</Item>
</Expand>
</Type>
<Type Name="boost::unordered::detail::grouped_bucket_array&lt;*&gt;" Inheritable="false">
<Expand>
<CustomListItems MaxItemsPerView="100">
<Variable Name="size" InitialValue="size_" />
<Variable Name="bucket_index" InitialValue="0" />
<Variable Name="current_bucket" InitialValue="&amp;buckets[bucket_index]" />
<Variable Name="node" InitialValue="current_bucket->next" />
<Loop Condition="bucket_index != size">
<Exec>current_bucket = &amp;buckets[bucket_index]</Exec>
<Exec>node = current_bucket-&gt;next</Exec>
<Loop Condition="node != nullptr">
<Item>node-&gt;buf.t_</Item>
<Exec>node = node-&gt;next</Exec>
</Loop>
<Exec>++bucket_index</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
<Type Name="boost::unordered::detail::grouped_bucket_array&lt;*&gt;" Inheritable="false" IncludeView="MapHelper">
<Expand>
<CustomListItems MaxItemsPerView="100">
<Variable Name="size" InitialValue="size_" />
<Variable Name="bucket_index" InitialValue="0" />
<Variable Name="current_bucket" InitialValue="&amp;buckets[bucket_index]" />
<Variable Name="node" InitialValue="current_bucket->next" />
<Loop Condition="bucket_index != size">
<Exec>current_bucket = &amp;buckets[bucket_index]</Exec>
<Exec>node = current_bucket-&gt;next</Exec>
<Loop Condition="node != nullptr">
<Item Name="[{node-&gt;buf.t_.first}]">node-&gt;buf.t_</Item>
<Exec>node = node-&gt;next</Exec>
</Loop>
<Exec>++bucket_index</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
<Type Name="boost::unordered::unordered_map&lt;*&gt;" Priority="Medium">
<AlternativeType Name="boost::unordered::unordered_multimap&lt;*&gt;" />
<AlternativeType Name="boost::unordered::unordered_set&lt;*&gt;" />
<AlternativeType Name="boost::unordered::unordered_multiset&lt;*&gt;" />
<DisplayString>{{ size={table_.size_} }}</DisplayString>
<Expand>
<Item Name="[bucket_count]" IncludeView="detailed">table_.buckets_.size_</Item>
<Item Name="[max_load_factor]" IncludeView="detailed">table_.mlf_</Item>
<ExpandedItem ExcludeView="simple">*reinterpret_cast&lt;table::functions*&gt;(&amp;table_)</ExpandedItem>
<Item Name="[allocator]" ExcludeView="simple">*reinterpret_cast&lt;table::bucket_array_type::node_allocator_type*&gt;(&amp;table_.buckets_)</Item>
<ExpandedItem>table_.buckets_</ExpandedItem>
</Expand>
</Type>
<Type Name="boost::unordered::unordered_map&lt;*&gt;" Priority="MediumHigh" ExcludeView="ShowElementsByIndex">
<AlternativeType Name="boost::unordered::unordered_multimap&lt;*&gt;" />
<DisplayString>{{ size={table_.size_} }}</DisplayString>
<Expand>
<Item Name="[bucket_count]" IncludeView="detailed">table_.buckets_.size_</Item>
<Item Name="[max_load_factor]" IncludeView="detailed">table_.mlf_</Item>
<ExpandedItem ExcludeView="simple">*reinterpret_cast&lt;table::functions*&gt;(&amp;table_)</ExpandedItem>
<Item Name="[allocator]" ExcludeView="simple">*reinterpret_cast&lt;table::bucket_array_type::node_allocator_type*&gt;(&amp;table_.buckets_)</Item>
<ExpandedItem>table_.buckets_,view(MapHelper)</ExpandedItem>
</Expand>
</Type>
<!-- FCA iterators -->
<Type Name="boost::unordered::detail::iterator_detail::iterator&lt;*&gt;" Inheritable="false">
<AlternativeType Name="boost::unordered::detail::iterator_detail::c_iterator&lt;*&gt;" />
<Intrinsic Name="valid" Expression="p != nullptr &amp;&amp; itb.p != nullptr &amp;&amp; itb.pbg != nullptr" />
<DisplayString Condition="valid()">{p-&gt;buf.t_}</DisplayString>
<DisplayString Condition="!valid()">{{ end iterator }}</DisplayString>
<Expand>
<ExpandedItem Condition="valid()">p-&gt;buf.t_</ExpandedItem>
</Expand>
</Type>
<!-- FOA and CFOA helpers -->
<Type Name="boost::unordered::detail::foa::element_type&lt;*&gt;" Priority="Medium" Inheritable="false">
<DisplayString>{*p}</DisplayString>
<Expand>
<ExpandedItem>*p</ExpandedItem>
</Expand>
</Type>
<Type Name="boost::unordered::detail::foa::element_type&lt;std::pair&lt;*&gt;,*&gt;" Priority="MediumHigh" Inheritable="false">
<!-- Manually expand when holding a `std::pair`, otherwise the debugger complains about recursion-->
<DisplayString>({p-&gt;first}, {p-&gt;second})</DisplayString>
<Expand>
<Item Name="first">p-&gt;first</Item>
<Item Name="second">p-&gt;second</Item>
</Expand>
</Type>
<Type Name="boost::unordered::detail::foa::plain_integral&lt;*&gt;" Inheritable="false">
<Intrinsic Name="get" Expression="n" />
</Type>
<Type Name="boost::unordered::detail::foa::atomic_integral&lt;*&gt;" Inheritable="false">
<Intrinsic Name="get" Expression="n._Storage._Value" />
</Type>
<Type Name="boost::unordered::detail::foa::group15&lt;*&gt;" Inheritable="false">
<Intrinsic Name="check_bit" Expression="(m[b].get() == 0) ? (1 &lt;&lt; b) : 0">
<Parameter Name="b" Type="int" />
</Intrinsic>
<Intrinsic Name="__match_occupied_regular_layout_true" Expression="0x7FFF &amp; ~(
check_bit( 0) + check_bit( 1) + check_bit( 2) + check_bit( 3) +
check_bit( 4) + check_bit( 5) + check_bit( 6) + check_bit( 7) +
check_bit( 8) + check_bit( 9) + check_bit(10) + check_bit(11) +
check_bit(12) + check_bit(13) + check_bit(14) + check_bit(15)
)" />
<Intrinsic Name="__match_occupied_x" Expression="m[0].get() | m[1].get()" />
<Intrinsic Name="__match_occupied_y" Expression="static_cast&lt;uint32_t&gt;(__match_occupied_x() | (__match_occupied_x() &gt;&gt; 32))" />
<Intrinsic Name="__match_occupied_regular_layout_false" Expression="0x7FFF &amp; (__match_occupied_y() | (__match_occupied_y() &gt;&gt; 16))" />
<Intrinsic Name="match_occupied" Expression="regular_layout ? __match_occupied_regular_layout_true() : __match_occupied_regular_layout_false()" />
<Intrinsic Name="__is_sentinel_regular_layout_true" Expression="pos == N-1 &amp;&amp; m[N-1].get() == sentinel_" >
<Parameter Name="pos" Type="size_t" />
</Intrinsic>
<Intrinsic Name="__is_sentinel_regular_layout_false" Expression="pos == N-1 &amp;&amp; (m[0].get() &amp; uint64_t(0x4000400040004000ull)) == uint64_t(0x4000ull) &amp;&amp; (m[1].get() &amp; uint64_t(0x4000400040004000ull))==0" >
<Parameter Name="pos" Type="size_t" />
</Intrinsic>
<Intrinsic Name="is_sentinel" Expression="regular_layout ? __is_sentinel_regular_layout_true(pos) : __is_sentinel_regular_layout_false(pos)" >
<Parameter Name="pos" Type="size_t" />
</Intrinsic>
</Type>
<!-- FOA and CFOA stats -->
<Type Name="boost::unordered::detail::foa::table_core_cumulative_stats" Inheritable="false">
<DisplayString>stats</DisplayString>
<Expand>
<Item Name="[insertion]">insertion</Item>
<Item Name="[successful_lookup]">successful_lookup</Item>
<Item Name="[unsuccessful_lookup]">unsuccessful_lookup</Item>
</Expand>
</Type>
<Type Name="boost::unordered::detail::cumulative_stats&lt;*&gt;" Inheritable="true">
<Intrinsic Name="bit_cast_to_double" Expression="*reinterpret_cast&lt;double*&gt;(&amp;i)">
<Parameter Name="i" Type="uint64_t" />
</Intrinsic>
<Intrinsic Name="bit_cast_to_uint64_t" Expression="*reinterpret_cast&lt;uint64_t*&gt;(&amp;d)">
<Parameter Name="d" Type="double" />
</Intrinsic>
<!-- https://en.wikipedia.org/wiki/Fast_inverse_square_root#Magic_number -->
<Intrinsic Name="__inv_sqrt_init" Expression="bit_cast_to_double(0x5FE6EB50C7B537A9ull - (bit_cast_to_uint64_t(x) &gt;&gt; 1))">
<Parameter Name="x" Type="double" />
</Intrinsic>
<Intrinsic Name="__inv_sqrt_iter" Expression="0.5 * f * (3 - x * f * f)">
<Parameter Name="x" Type="double" />
<Parameter Name="f" Type="double" />
</Intrinsic>
<Intrinsic Name="inv_sqrt" Expression="__inv_sqrt_iter(x, __inv_sqrt_iter(x, __inv_sqrt_iter(x, __inv_sqrt_iter(x, __inv_sqrt_init(x)))))">
<Parameter Name="x" Type="double" />
</Intrinsic>
<Intrinsic Name="average" Expression="data[idx].m">
<Parameter Name="idx" Type="size_t" />
</Intrinsic>
<Intrinsic Name="variance" Expression="n != 0 ? data[idx].s / static_cast&lt;double&gt;(n) : 0.0">
<Parameter Name="idx" Type="size_t" />
</Intrinsic>
<Intrinsic Name="deviation" Expression="variance(idx) == 0 ? 0.0 : 1/inv_sqrt(variance(idx))">
<Parameter Name="idx" Type="size_t" />
</Intrinsic>
<DisplayString>{{ count = {n} }}</DisplayString>
<Expand>
<Item Name="[count]">n</Item>
<Synthetic Name="[probe_length]" Condition="$T1 > 0">
<DisplayString>{{ average = {average(0)} }}</DisplayString>
<Expand>
<Item Name="[average]">average(0)</Item>
<Item Name="[variance]">variance(0)</Item>
<Item Name="[deviation]">deviation(0)</Item>
</Expand>
</Synthetic>
<Synthetic Name="[num_comparisons]" Condition="$T1 > 1">
<DisplayString>{{ average = {average(1)} }}</DisplayString>
<Expand>
<Item Name="[average]">average(1)</Item>
<Item Name="[variance]">variance(1)</Item>
<Item Name="[deviation]">deviation(1)</Item>
</Expand>
</Synthetic>
</Expand>
</Type>
<!-- FOA and CFOA containers -->
<Type Name="boost::unordered::detail::foa::table&lt;*&gt;" Inheritable="false">
<AlternativeType Name="boost::unordered::detail::foa::concurrent_table&lt;*&gt;" />
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e">
<Parameter Name="e" Type="value_type*" />
</Intrinsic>
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e-&gt;p">
<Parameter Name="e" Type="element_type*" />
</Intrinsic>
<Intrinsic Name="check_bit" Expression="(n &amp; (1 &lt;&lt; i)) != 0">
<Parameter Name="n" Type="unsigned int" />
<Parameter Name="i" Type="unsigned int" />
</Intrinsic>
<Intrinsic Name="countr_zero" Expression="
check_bit(n, 0) ? 0 : check_bit(n, 1) ? 1 : check_bit(n, 2) ? 2 : check_bit(n, 3) ? 3 :
check_bit(n, 4) ? 4 : check_bit(n, 5) ? 5 : check_bit(n, 6) ? 6 : check_bit(n, 7) ? 7 :
check_bit(n, 8) ? 8 : check_bit(n, 9) ? 9 : check_bit(n, 10) ? 10 : check_bit(n, 11) ? 11 :
check_bit(n, 12) ? 12 : check_bit(n, 13) ? 13 : check_bit(n, 14) ? 14 : check_bit(n, 15) ? 15 :
check_bit(n, 16) ? 16 : check_bit(n, 17) ? 17 : check_bit(n, 18) ? 18 : check_bit(n, 19) ? 19 :
check_bit(n, 20) ? 20 : check_bit(n, 21) ? 21 : check_bit(n, 22) ? 22 : check_bit(n, 23) ? 23 :
check_bit(n, 24) ? 24 : check_bit(n, 25) ? 25 : check_bit(n, 26) ? 26 : check_bit(n, 27) ? 27 :
check_bit(n, 28) ? 28 : check_bit(n, 29) ? 29 : check_bit(n, 30) ? 30 : check_bit(n, 31) ? 31 : 32
">
<Parameter Name="n" Type="unsigned int" />
</Intrinsic>
<Expand>
<Item Name="[stats]" Optional="true">cstats</Item>
<CustomListItems MaxItemsPerView="100">
<Variable Name="pc_" InitialValue="reinterpret_cast&lt;unsigned char*&gt;(arrays.groups_)" />
<Variable Name="p_" InitialValue="arrays.elements_" />
<Variable Name="first_time" InitialValue="true" />
<Variable Name="mask" InitialValue="(int)0" />
<Variable Name="n0" InitialValue="(size_t)0" />
<Variable Name="n" InitialValue="(unsigned int)0" />
<Loop Condition="p_ != nullptr">
<!-- This if block mirrors the condition in the begin() call -->
<If Condition="!first_time || !(arrays.elements_ &amp;&amp; !(arrays.groups_[0].match_occupied() &amp; 0x1))">
<Item>*p_</Item>
</If>
<Exec>first_time = false</Exec>
<Exec>n0 = reinterpret_cast&lt;uintptr_t&gt;(pc_) % sizeof(group_type)</Exec>
<Exec>pc_ -= (ptrdiff_t)n0</Exec>
<Exec>mask = (reinterpret_cast&lt;group_type*&gt;(pc_)-&gt;match_occupied() &gt;&gt; (n0+1)) &lt;&lt; (n0+1)</Exec>
<Loop Condition="mask == 0">
<Exec>pc_ += sizeof(group_type)</Exec>
<Exec>p_ += group_type::N</Exec>
<Exec>mask = reinterpret_cast&lt;group_type*&gt;(pc_)-&gt;match_occupied()</Exec>
</Loop>
<Exec>n = countr_zero(mask)</Exec>
<If Condition="reinterpret_cast&lt;group_type*&gt;(pc_)-&gt;is_sentinel(n)">
<Exec>p_ = nullptr</Exec>
</If>
<Else>
<Exec>pc_ += (ptrdiff_t)n</Exec>
<Exec>p_ -= (ptrdiff_t)n0</Exec>
<Exec>p_ += (ptrdiff_t)n</Exec>
</Else>
</Loop>
</CustomListItems>
</Expand>
</Type>
<Type Name="boost::unordered::detail::foa::table&lt;*&gt;" Inheritable="false" IncludeView="MapHelper">
<AlternativeType Name="boost::unordered::detail::foa::concurrent_table&lt;*&gt;" />
<Expand>
<Item Name="[stats]" Optional="true">cstats</Item>
<CustomListItems MaxItemsPerView="100">
<Variable Name="pc_" InitialValue="reinterpret_cast&lt;unsigned char*&gt;(arrays.groups_)" />
<Variable Name="p_" InitialValue="arrays.elements_" />
<Variable Name="first_time" InitialValue="true" />
<Variable Name="mask" InitialValue="(int)0" />
<Variable Name="n0" InitialValue="(size_t)0" />
<Variable Name="n" InitialValue="(unsigned int)0" />
<Loop Condition="p_ != nullptr">
<!-- This if block mirrors the condition in the begin() call -->
<If Condition="!first_time || !(arrays.elements_ &amp;&amp; !(arrays.groups_[0].match_occupied() &amp; 0x1))">
<Item Name="[{get_value(p_)-&gt;first}]">*p_</Item>
</If>
<Exec>first_time = false</Exec>
<Exec>n0 = reinterpret_cast&lt;uintptr_t&gt;(pc_) % sizeof(group_type)</Exec>
<Exec>pc_ -= (ptrdiff_t)n0</Exec>
<Exec>mask = (reinterpret_cast&lt;group_type*&gt;(pc_)-&gt;match_occupied() &gt;&gt; (n0+1)) &lt;&lt; (n0+1)</Exec>
<Loop Condition="mask == 0">
<Exec>pc_ += sizeof(group_type)</Exec>
<Exec>p_ += group_type::N</Exec>
<Exec>mask = reinterpret_cast&lt;group_type*&gt;(pc_)-&gt;match_occupied()</Exec>
</Loop>
<Exec>n = countr_zero(mask)</Exec>
<If Condition="reinterpret_cast&lt;group_type*&gt;(pc_)-&gt;is_sentinel(n)">
<Exec>p_ = nullptr</Exec>
</If>
<Else>
<Exec>pc_ += (ptrdiff_t)n</Exec>
<Exec>p_ -= (ptrdiff_t)n0</Exec>
<Exec>p_ += (ptrdiff_t)n</Exec>
</Else>
</Loop>
</CustomListItems>
</Expand>
</Type>
<Type Name="boost::unordered::unordered_flat_map&lt;*&gt;" Priority="Medium">
<AlternativeType Name="boost::unordered::unordered_flat_set&lt;*&gt;" />
<AlternativeType Name="boost::unordered::unordered_node_map&lt;*&gt;" />
<AlternativeType Name="boost::unordered::unordered_node_set&lt;*&gt;" />
<AlternativeType Name="boost::unordered::concurrent_flat_map&lt;*&gt;" />
<AlternativeType Name="boost::unordered::concurrent_flat_set&lt;*&gt;" />
<DisplayString>{{ size={table_.size_ctrl.size} }}</DisplayString>
<Expand>
<Item Name="[hash_function]" ExcludeView="simple">*reinterpret_cast&lt;hasher*&gt;(static_cast&lt;table_type::super::base1*&gt;(&amp;table_))</Item>
<Item Name="[key_eq]" ExcludeView="simple">*reinterpret_cast&lt;key_equal*&gt;(static_cast&lt;table_type::super::base2*&gt;(&amp;table_))</Item>
<Item Name="[allocator]" ExcludeView="simple">*reinterpret_cast&lt;allocator_type*&gt;(static_cast&lt;table_type::super::base3*&gt;(&amp;table_))</Item>
<ExpandedItem>table_</ExpandedItem>
</Expand>
</Type>
<Type Name="boost::unordered::unordered_flat_map&lt;*&gt;" Priority="MediumHigh" ExcludeView="ShowElementsByIndex">
<AlternativeType Name="boost::unordered::unordered_node_map&lt;*&gt;" />
<AlternativeType Name="boost::unordered::concurrent_flat_map&lt;*&gt;" />
<DisplayString>{{ size={table_.size_ctrl.size} }}</DisplayString>
<Expand>
<Item Name="[hash_function]" ExcludeView="simple">*reinterpret_cast&lt;hasher*&gt;(static_cast&lt;table_type::super::base1*&gt;(&amp;table_))</Item>
<Item Name="[key_eq]" ExcludeView="simple">*reinterpret_cast&lt;key_equal*&gt;(static_cast&lt;table_type::super::base2*&gt;(&amp;table_))</Item>
<Item Name="[allocator]" ExcludeView="simple">*reinterpret_cast&lt;allocator_type*&gt;(static_cast&lt;table_type::super::base3*&gt;(&amp;table_))</Item>
<ExpandedItem>table_,view(MapHelper)</ExpandedItem>
</Expand>
</Type>
<!-- FOA iterators -->
<Type Name="boost::unordered::detail::foa::table_iterator&lt;*&gt;" Inheritable="false">
<Intrinsic Name="valid" Expression="p_ != nullptr &amp;&amp; pc_ != nullptr" />
<DisplayString Condition="valid()">{*p_}</DisplayString>
<DisplayString Condition="!valid()">{{ end iterator }}</DisplayString>
<Expand>
<ExpandedItem Condition="valid()">*p_</ExpandedItem>
</Expand>
</Type>
</AutoVisualizer>

View File

@ -1413,6 +1413,12 @@ __declspec(empty_bases) /* activate EBO with multiple inheritance */
table_core:empty_value<Hash,0>,empty_value<Pred,1>,empty_value<Allocator,2>
{
private:
// These `baseN` aliases are for debugger visualizations, like natvis
using base1=empty_value<Hash,0>;
using base2=empty_value<Pred,1>;
using base3=empty_value<Allocator,2>;
public:
using type_policy=TypePolicy;
using group_type=Group;

View File

@ -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<typename Hash,typename=void>
struct hash_is_avalanching_impl: std::false_type{};
struct hash_is_avalanching_impl:std::false_type{};
template<typename IsAvalanching>
struct avalanching_value
{
static constexpr bool value=IsAvalanching::value;
};
/* may be explicitly marked as BOOST_DEPRECATED in the future */
template<> struct avalanching_value<void>
{
static constexpr bool value=true;
};
template<typename Hash>
struct hash_is_avalanching_impl<Hash,
boost::unordered::detail::void_t<typename Hash::is_avalanching> >:
std::true_type{};
struct hash_is_avalanching_impl<
Hash,
boost::unordered::detail::void_t<typename Hash::is_avalanching>
>:std::integral_constant<
bool,
avalanching_value<typename Hash::is_avalanching>::value
>{};
template<typename Hash>
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<Hash,
* when actual characterization differs from default.
*/
/* hash_is_avalanching<Hash>::value is true when the type Hash::is_avalanching
* is present, false otherwise.
/* hash_is_avalanching<Hash>::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<typename Hash>
struct hash_is_avalanching: detail::hash_is_avalanching_impl<Hash>::type{};

View File

@ -1,4 +1,5 @@
// Copyright 2022 Peter Dimov
// Copyright 2024 Joaquin M Lopez Munoz
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
@ -26,6 +27,7 @@ namespace unordered
#include <boost/unordered/hash_traits.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#include <type_traits>
struct X1
{
@ -36,6 +38,27 @@ struct X2
typedef void is_avalanching;
};
struct X4
{
using is_avalanching = std::false_type;
};
struct X5
{
using is_avalanching = std::true_type;
};
struct X6
{
using is_avalanching = boost::false_type;
};
struct X7
{
using is_avalanching = boost::true_type;
};
int main()
{
using boost::unordered::hash_is_avalanching;
@ -43,6 +66,10 @@ int main()
BOOST_TEST_TRAIT_FALSE((hash_is_avalanching<X1>));
BOOST_TEST_TRAIT_TRUE((hash_is_avalanching<X2>));
BOOST_TEST_TRAIT_TRUE((hash_is_avalanching<X3>));
BOOST_TEST_TRAIT_FALSE((hash_is_avalanching<X4>));
BOOST_TEST_TRAIT_TRUE((hash_is_avalanching<X5>));
BOOST_TEST_TRAIT_FALSE((hash_is_avalanching<X6>));
BOOST_TEST_TRAIT_TRUE((hash_is_avalanching<X7>));
return boost::report_errors();
}