mirror of
https://github.com/boostorg/unordered.git
synced 2025-07-29 19:07:15 +02:00
Sync from upstream.
This commit is contained in:
@ -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")
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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 =
|
||||
|
@ -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[]
|
||||
|
@ -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
|
||||
|
||||
|
16
doc/unordered/debuggability.adoc
Normal file
16
doc/unordered/debuggability.adoc
Normal 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 }`.
|
@ -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
|
||||
{
|
||||
|
@ -32,9 +32,14 @@ large changes in the returned hash code —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`.
|
||||
|
||||
|
380
extra/boost_unordered.natvis
Normal file
380
extra/boost_unordered.natvis
Normal 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<*>" Inheritable="false">
|
||||
<Intrinsic Name="active_idx" Expression="current_ & 1" />
|
||||
<Intrinsic Name="spare_idx" Expression="1 - active_idx()" />
|
||||
<Intrinsic Name="has_spare" Expression="(current_ & 2) != 0" />
|
||||
|
||||
<Intrinsic Name="hash" Expression="*reinterpret_cast<$T1*>(static_cast<function_pair::base1*>(&funcs_[idx].t_))">
|
||||
<Parameter Name="idx" Type="size_t" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="key_eq" Expression="*reinterpret_cast<$T2*>(static_cast<function_pair::base2*>(&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<*>" Inheritable="false">
|
||||
<Expand>
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="size" InitialValue="size_" />
|
||||
<Variable Name="bucket_index" InitialValue="0" />
|
||||
<Variable Name="current_bucket" InitialValue="&buckets[bucket_index]" />
|
||||
<Variable Name="node" InitialValue="current_bucket->next" />
|
||||
<Loop Condition="bucket_index != size">
|
||||
<Exec>current_bucket = &buckets[bucket_index]</Exec>
|
||||
<Exec>node = current_bucket->next</Exec>
|
||||
<Loop Condition="node != nullptr">
|
||||
<Item>node->buf.t_</Item>
|
||||
<Exec>node = node->next</Exec>
|
||||
</Loop>
|
||||
<Exec>++bucket_index</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="boost::unordered::detail::grouped_bucket_array<*>" Inheritable="false" IncludeView="MapHelper">
|
||||
<Expand>
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="size" InitialValue="size_" />
|
||||
<Variable Name="bucket_index" InitialValue="0" />
|
||||
<Variable Name="current_bucket" InitialValue="&buckets[bucket_index]" />
|
||||
<Variable Name="node" InitialValue="current_bucket->next" />
|
||||
<Loop Condition="bucket_index != size">
|
||||
<Exec>current_bucket = &buckets[bucket_index]</Exec>
|
||||
<Exec>node = current_bucket->next</Exec>
|
||||
<Loop Condition="node != nullptr">
|
||||
<Item Name="[{node->buf.t_.first}]">node->buf.t_</Item>
|
||||
<Exec>node = node->next</Exec>
|
||||
</Loop>
|
||||
<Exec>++bucket_index</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="boost::unordered::unordered_map<*>" Priority="Medium">
|
||||
<AlternativeType Name="boost::unordered::unordered_multimap<*>" />
|
||||
<AlternativeType Name="boost::unordered::unordered_set<*>" />
|
||||
<AlternativeType Name="boost::unordered::unordered_multiset<*>" />
|
||||
<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<table::functions*>(&table_)</ExpandedItem>
|
||||
<Item Name="[allocator]" ExcludeView="simple">*reinterpret_cast<table::bucket_array_type::node_allocator_type*>(&table_.buckets_)</Item>
|
||||
<ExpandedItem>table_.buckets_</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="boost::unordered::unordered_map<*>" Priority="MediumHigh" ExcludeView="ShowElementsByIndex">
|
||||
<AlternativeType Name="boost::unordered::unordered_multimap<*>" />
|
||||
<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<table::functions*>(&table_)</ExpandedItem>
|
||||
<Item Name="[allocator]" ExcludeView="simple">*reinterpret_cast<table::bucket_array_type::node_allocator_type*>(&table_.buckets_)</Item>
|
||||
<ExpandedItem>table_.buckets_,view(MapHelper)</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- FCA iterators -->
|
||||
|
||||
<Type Name="boost::unordered::detail::iterator_detail::iterator<*>" Inheritable="false">
|
||||
<AlternativeType Name="boost::unordered::detail::iterator_detail::c_iterator<*>" />
|
||||
<Intrinsic Name="valid" Expression="p != nullptr && itb.p != nullptr && itb.pbg != nullptr" />
|
||||
<DisplayString Condition="valid()">{p->buf.t_}</DisplayString>
|
||||
<DisplayString Condition="!valid()">{{ end iterator }}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="valid()">p->buf.t_</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- FOA and CFOA helpers -->
|
||||
|
||||
<Type Name="boost::unordered::detail::foa::element_type<*>" Priority="Medium" Inheritable="false">
|
||||
<DisplayString>{*p}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>*p</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="boost::unordered::detail::foa::element_type<std::pair<*>,*>" Priority="MediumHigh" Inheritable="false">
|
||||
<!-- Manually expand when holding a `std::pair`, otherwise the debugger complains about recursion-->
|
||||
<DisplayString>({p->first}, {p->second})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="first">p->first</Item>
|
||||
<Item Name="second">p->second</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="boost::unordered::detail::foa::plain_integral<*>" Inheritable="false">
|
||||
<Intrinsic Name="get" Expression="n" />
|
||||
</Type>
|
||||
|
||||
<Type Name="boost::unordered::detail::foa::atomic_integral<*>" Inheritable="false">
|
||||
<Intrinsic Name="get" Expression="n._Storage._Value" />
|
||||
</Type>
|
||||
|
||||
<Type Name="boost::unordered::detail::foa::group15<*>" Inheritable="false">
|
||||
<Intrinsic Name="check_bit" Expression="(m[b].get() == 0) ? (1 << b) : 0">
|
||||
<Parameter Name="b" Type="int" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="__match_occupied_regular_layout_true" Expression="0x7FFF & ~(
|
||||
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<uint32_t>(__match_occupied_x() | (__match_occupied_x() >> 32))" />
|
||||
<Intrinsic Name="__match_occupied_regular_layout_false" Expression="0x7FFF & (__match_occupied_y() | (__match_occupied_y() >> 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 && m[N-1].get() == sentinel_" >
|
||||
<Parameter Name="pos" Type="size_t" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="__is_sentinel_regular_layout_false" Expression="pos == N-1 && (m[0].get() & uint64_t(0x4000400040004000ull)) == uint64_t(0x4000ull) && (m[1].get() & 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<*>" Inheritable="true">
|
||||
<Intrinsic Name="bit_cast_to_double" Expression="*reinterpret_cast<double*>(&i)">
|
||||
<Parameter Name="i" Type="uint64_t" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="bit_cast_to_uint64_t" Expression="*reinterpret_cast<uint64_t*>(&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) >> 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<double>(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<*>" Inheritable="false">
|
||||
<AlternativeType Name="boost::unordered::detail::foa::concurrent_table<*>" />
|
||||
|
||||
<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->p">
|
||||
<Parameter Name="e" Type="element_type*" />
|
||||
</Intrinsic>
|
||||
|
||||
<Intrinsic Name="check_bit" Expression="(n & (1 << 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<unsigned char*>(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_ && !(arrays.groups_[0].match_occupied() & 0x1))">
|
||||
<Item>*p_</Item>
|
||||
</If>
|
||||
<Exec>first_time = false</Exec>
|
||||
|
||||
<Exec>n0 = reinterpret_cast<uintptr_t>(pc_) % sizeof(group_type)</Exec>
|
||||
<Exec>pc_ -= (ptrdiff_t)n0</Exec>
|
||||
|
||||
<Exec>mask = (reinterpret_cast<group_type*>(pc_)->match_occupied() >> (n0+1)) << (n0+1)</Exec>
|
||||
<Loop Condition="mask == 0">
|
||||
<Exec>pc_ += sizeof(group_type)</Exec>
|
||||
<Exec>p_ += group_type::N</Exec>
|
||||
<Exec>mask = reinterpret_cast<group_type*>(pc_)->match_occupied()</Exec>
|
||||
</Loop>
|
||||
|
||||
<Exec>n = countr_zero(mask)</Exec>
|
||||
<If Condition="reinterpret_cast<group_type*>(pc_)->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<*>" Inheritable="false" IncludeView="MapHelper">
|
||||
<AlternativeType Name="boost::unordered::detail::foa::concurrent_table<*>" />
|
||||
<Expand>
|
||||
<Item Name="[stats]" Optional="true">cstats</Item>
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="pc_" InitialValue="reinterpret_cast<unsigned char*>(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_ && !(arrays.groups_[0].match_occupied() & 0x1))">
|
||||
<Item Name="[{get_value(p_)->first}]">*p_</Item>
|
||||
</If>
|
||||
<Exec>first_time = false</Exec>
|
||||
|
||||
<Exec>n0 = reinterpret_cast<uintptr_t>(pc_) % sizeof(group_type)</Exec>
|
||||
<Exec>pc_ -= (ptrdiff_t)n0</Exec>
|
||||
|
||||
<Exec>mask = (reinterpret_cast<group_type*>(pc_)->match_occupied() >> (n0+1)) << (n0+1)</Exec>
|
||||
<Loop Condition="mask == 0">
|
||||
<Exec>pc_ += sizeof(group_type)</Exec>
|
||||
<Exec>p_ += group_type::N</Exec>
|
||||
<Exec>mask = reinterpret_cast<group_type*>(pc_)->match_occupied()</Exec>
|
||||
</Loop>
|
||||
|
||||
<Exec>n = countr_zero(mask)</Exec>
|
||||
<If Condition="reinterpret_cast<group_type*>(pc_)->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<*>" Priority="Medium">
|
||||
<AlternativeType Name="boost::unordered::unordered_flat_set<*>" />
|
||||
<AlternativeType Name="boost::unordered::unordered_node_map<*>" />
|
||||
<AlternativeType Name="boost::unordered::unordered_node_set<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_flat_map<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_flat_set<*>" />
|
||||
<DisplayString>{{ size={table_.size_ctrl.size} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[hash_function]" ExcludeView="simple">*reinterpret_cast<hasher*>(static_cast<table_type::super::base1*>(&table_))</Item>
|
||||
<Item Name="[key_eq]" ExcludeView="simple">*reinterpret_cast<key_equal*>(static_cast<table_type::super::base2*>(&table_))</Item>
|
||||
<Item Name="[allocator]" ExcludeView="simple">*reinterpret_cast<allocator_type*>(static_cast<table_type::super::base3*>(&table_))</Item>
|
||||
<ExpandedItem>table_</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="boost::unordered::unordered_flat_map<*>" Priority="MediumHigh" ExcludeView="ShowElementsByIndex">
|
||||
<AlternativeType Name="boost::unordered::unordered_node_map<*>" />
|
||||
<AlternativeType Name="boost::unordered::concurrent_flat_map<*>" />
|
||||
<DisplayString>{{ size={table_.size_ctrl.size} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[hash_function]" ExcludeView="simple">*reinterpret_cast<hasher*>(static_cast<table_type::super::base1*>(&table_))</Item>
|
||||
<Item Name="[key_eq]" ExcludeView="simple">*reinterpret_cast<key_equal*>(static_cast<table_type::super::base2*>(&table_))</Item>
|
||||
<Item Name="[allocator]" ExcludeView="simple">*reinterpret_cast<allocator_type*>(static_cast<table_type::super::base3*>(&table_))</Item>
|
||||
<ExpandedItem>table_,view(MapHelper)</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- FOA iterators -->
|
||||
|
||||
<Type Name="boost::unordered::detail::foa::table_iterator<*>" Inheritable="false">
|
||||
<Intrinsic Name="valid" Expression="p_ != nullptr && pc_ != nullptr" />
|
||||
<DisplayString Condition="valid()">{*p_}</DisplayString>
|
||||
<DisplayString Condition="!valid()">{{ end iterator }}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="valid()">*p_</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
</AutoVisualizer>
|
@ -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;
|
||||
|
@ -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{};
|
||||
|
@ -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();
|
||||
}
|
||||
|
Reference in New Issue
Block a user