forked from boostorg/unordered
Merge pull request #249 from boostorg/feature/natvis
Implement natvis visualizations
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")
|
||||
|
@ -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[]
|
||||
|
@ -11,6 +11,7 @@
|
||||
* 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 }`.
|
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;
|
||||
|
Reference in New Issue
Block a user