Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
torch-sparse
Commits
2951b12d
Commit
2951b12d
authored
Jan 10, 2024
by
aiss
Browse files
push v0.6.18 version
parent
e8309f27
Changes
266
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
5365 additions
and
0 deletions
+5365
-0
third_party/parallel-hashmap/phmap.natvis
third_party/parallel-hashmap/phmap.natvis
+141
-0
third_party/parallel-hashmap/phmap_gdb.py
third_party/parallel-hashmap/phmap_gdb.py
+201
-0
third_party/parallel-hashmap/phmap_lldb.py
third_party/parallel-hashmap/phmap_lldb.py
+248
-0
third_party/parallel-hashmap/tests/btree_test.cc
third_party/parallel-hashmap/tests/btree_test.cc
+2376
-0
third_party/parallel-hashmap/tests/btree_test.h
third_party/parallel-hashmap/tests/btree_test.h
+490
-0
third_party/parallel-hashmap/tests/compressed_tuple_test.cc
third_party/parallel-hashmap/tests/compressed_tuple_test.cc
+201
-0
third_party/parallel-hashmap/tests/container_memory_test.cc
third_party/parallel-hashmap/tests/container_memory_test.cc
+193
-0
third_party/parallel-hashmap/tests/dump_load_test.cc
third_party/parallel-hashmap/tests/dump_load_test.cc
+65
-0
third_party/parallel-hashmap/tests/erase_if_test.cc
third_party/parallel-hashmap/tests/erase_if_test.cc
+52
-0
third_party/parallel-hashmap/tests/flat_hash_map_test.cc
third_party/parallel-hashmap/tests/flat_hash_map_test.cc
+286
-0
third_party/parallel-hashmap/tests/flat_hash_set_test.cc
third_party/parallel-hashmap/tests/flat_hash_set_test.cc
+133
-0
third_party/parallel-hashmap/tests/hash_generator_testing.h
third_party/parallel-hashmap/tests/hash_generator_testing.h
+218
-0
third_party/parallel-hashmap/tests/hash_policy_testing.h
third_party/parallel-hashmap/tests/hash_policy_testing.h
+195
-0
third_party/parallel-hashmap/tests/hash_policy_testing_test.cc
..._party/parallel-hashmap/tests/hash_policy_testing_test.cc
+44
-0
third_party/parallel-hashmap/tests/hashtable_debug.h
third_party/parallel-hashmap/tests/hashtable_debug.h
+106
-0
third_party/parallel-hashmap/tests/node_hash_map_test.cc
third_party/parallel-hashmap/tests/node_hash_map_test.cc
+225
-0
third_party/parallel-hashmap/tests/node_hash_policy_test.cc
third_party/parallel-hashmap/tests/node_hash_policy_test.cc
+65
-0
third_party/parallel-hashmap/tests/node_hash_set_test.cc
third_party/parallel-hashmap/tests/node_hash_set_test.cc
+110
-0
third_party/parallel-hashmap/tests/parallel_flat_hash_map_mutex_test.cc
...rallel-hashmap/tests/parallel_flat_hash_map_mutex_test.cc
+12
-0
third_party/parallel-hashmap/tests/parallel_flat_hash_map_test.cc
...rty/parallel-hashmap/tests/parallel_flat_hash_map_test.cc
+4
-0
No files found.
Too many changes to show.
To preserve performance only
266 of 266+
files are displayed.
Plain diff
Email patch
third_party/parallel-hashmap/phmap.natvis
0 → 100644
View file @
2951b12d
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer
xmlns=
"http://schemas.microsoft.com/vstudio/debugger/natvis/2010"
>
<!-- flat map/set -->
<Type
Name=
"phmap::flat_hash_set<*,*,*,*>"
>
<AlternativeType
Name=
"phmap::flat_hash_map<*,*,*,*,*>"
/>
<DisplayString>
{{size = {size_}}}
</DisplayString>
<Expand>
<CustomListItems
MaxItemsPerView=
"1000"
ExcludeView=
"Test"
>
<Variable
Name=
"ctrl"
InitialValue=
"ctrl_"
/>
<Variable
Name=
"slot"
InitialValue=
"slots_"
/>
<Variable
Name=
"ctrl_end"
InitialValue=
"ctrl_ + capacity_"
/>
<Variable
Name=
"slot_end"
InitialValue=
"slots_ + capacity_"
/>
<Size>
size_
</Size>
<Loop>
<Break
Condition=
"slot == slot_end"
/>
<If
Condition=
"*ctrl >= -1"
>
<Item>
*slot,na
</Item>
</If>
<Exec>
++slot
</Exec>
<Exec>
++ctrl
</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
<!-- node map/set - only difference is the **slot instead of *slot -->
<Type
Name=
"phmap::node_hash_set<*,*,*,*>"
>
<AlternativeType
Name=
"phmap::node_hash_map<*,*,*,*,*>"
/>
<DisplayString>
{{size = {size_}}}
</DisplayString>
<Expand>
<CustomListItems
MaxItemsPerView=
"1000"
ExcludeView=
"Test"
>
<Variable
Name=
"ctrl"
InitialValue=
"ctrl_"
/>
<Variable
Name=
"slot"
InitialValue=
"slots_"
/>
<Variable
Name=
"ctrl_end"
InitialValue=
"ctrl_ + capacity_"
/>
<Variable
Name=
"slot_end"
InitialValue=
"slots_ + capacity_"
/>
<Size>
size_
</Size>
<Loop>
<Break
Condition=
"slot == slot_end"
/>
<If
Condition=
"*ctrl >= -1"
>
<Item>
**slot,na
</Item>
</If>
<Exec>
++slot
</Exec>
<Exec>
++ctrl
</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
<Type
Name=
"phmap::priv::map_slot_type<*,*>"
>
<DisplayString>
{value}
</DisplayString>
</Type>
<!-- flat map iterators -->
<Type
Name=
"phmap::priv::raw_hash_set<*,*,*,*>::iterator"
>
<DisplayString
Condition=
"ctrl_ == 0"
>
unset
</DisplayString>
<DisplayString
Condition=
"!(*ctrl_ >= 0)"
>
end()
</DisplayString>
<DisplayString>
{*slot_,na}
</DisplayString>
</Type>
<!-- node map iterators - only difference is the **slot_ instead of * -->
<Type
Name=
"phmap::priv::raw_hash_set<phmap::priv::NodeHashSetPolicy<*>,*,*,*>::iterator"
>
<DisplayString
Condition=
"ctrl_ == 0"
>
unset
</DisplayString>
<DisplayString
Condition=
"!(*ctrl_ >= 0)"
>
end()
</DisplayString>
<DisplayString>
{**slot_,na}
</DisplayString>
</Type>
<!-- parallel flat/node set -->
<Type
Name=
"phmap::parallel_flat_hash_set<*,*,*,*,*,*>"
>
<AlternativeType
Name=
"phmap::parallel_node_hash_set<*,*,*,*,*,*>"
/>
<DisplayString>
{{size = ?}}
</DisplayString>
<Expand>
<CustomListItems
MaxItemsPerView=
"1000"
ExcludeView=
"Test"
>
<Variable
Name=
"idx"
InitialValue=
"0"
/>
<Variable
Name=
"maxidx"
InitialValue=
"$T5"
/>
<Variable
Name=
"ctrl"
InitialValue=
"sets_._Elems[0].set_.ctrl_"
/>
<Variable
Name=
"slot"
InitialValue=
"sets_._Elems[0].set_.slots_"
/>
<Variable
Name=
"ctrl_end"
InitialValue=
"sets_._Elems[0].set_.ctrl_"
/>
<Variable
Name=
"slot_end"
InitialValue=
"sets_._Elems[0].set_.slots_"
/>
<Exec>
maxidx = 2
<<
maxidx
</Exec>
<Loop>
<Break
Condition=
"idx == maxidx"
/>
<Exec>
ctrl = sets_._Elems[idx].set_.ctrl_
</Exec>
<Exec>
slot = sets_._Elems[idx].set_.slots_
</Exec>
<Exec>
ctrl_end = sets_._Elems[idx].set_.ctrl_ + sets_._Elems[idx].set_.capacity_
</Exec>
<Exec>
slot_end = sets_._Elems[idx].set_.slots_ + sets_._Elems[idx].set_.capacity_
</Exec>
<Loop>
<Break
Condition=
"slot == slot_end"
/>
<If
Condition=
"*ctrl >= -1"
>
<Item>
*slot,na
</Item>
</If>
<Exec>
++slot
</Exec>
<Exec>
++ctrl
</Exec>
</Loop>
<Exec>
++idx
</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
<!-- parallel flat/node map - only difference is $T6 instead of $T5 -->
<Type
Name=
"phmap::parallel_flat_hash_map<*,*,*,*,*,*,*>"
>
<AlternativeType
Name=
"phmap::parallel_node_hash_map<*,*,*,*,*,*,*>"
/>
<DisplayString>
{{size = ?}}
</DisplayString>
<Expand>
<CustomListItems
MaxItemsPerView=
"1000"
ExcludeView=
"Test"
>
<Variable
Name=
"idx"
InitialValue=
"0"
/>
<Variable
Name=
"maxidx"
InitialValue=
"$T6"
/>
<Variable
Name=
"ctrl"
InitialValue=
"sets_._Elems[0].set_.ctrl_"
/>
<Variable
Name=
"slot"
InitialValue=
"sets_._Elems[0].set_.slots_"
/>
<Variable
Name=
"ctrl_end"
InitialValue=
"sets_._Elems[0].set_.ctrl_"
/>
<Variable
Name=
"slot_end"
InitialValue=
"sets_._Elems[0].set_.slots_"
/>
<Exec>
maxidx = 2
<<
maxidx
</Exec>
<Loop>
<Break
Condition=
"idx == maxidx"
/>
<Exec>
ctrl = sets_._Elems[idx].set_.ctrl_
</Exec>
<Exec>
slot = sets_._Elems[idx].set_.slots_
</Exec>
<Exec>
ctrl_end = sets_._Elems[idx].set_.ctrl_ + sets_._Elems[idx].set_.capacity_
</Exec>
<Exec>
slot_end = sets_._Elems[idx].set_.slots_ + sets_._Elems[idx].set_.capacity_
</Exec>
<Loop>
<Break
Condition=
"slot == slot_end"
/>
<If
Condition=
"*ctrl >= -1"
>
<Item>
*slot,na
</Item>
</If>
<Exec>
++slot
</Exec>
<Exec>
++ctrl
</Exec>
</Loop>
<Exec>
++idx
</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
<Type
Name=
"phmap::priv::parallel_hash_set<*,*,*,*,*,*,*>::iterator"
>
<DisplayString>
{it_,na}
</DisplayString>
</Type>
</AutoVisualizer>
third_party/parallel-hashmap/phmap_gdb.py
0 → 100644
View file @
2951b12d
# Python GDB formatters for parallel-hashmap
# tested with GCC 10.2 / GDB 9.2
# to install it, ensure the script location is in the Python path
# and type the following command (or put it in $HOME/.gdbinit):
# python
# import phmap_gdb
# end
import
gdb.printing
def
counter
():
i
=
0
while
(
True
):
yield
str
(
i
)
i
+=
1
def
slot_iterator
(
base_obj
):
index
=
-
1
n_items
=
0
size
=
int
(
base_obj
[
"size_"
])
while
n_items
<
size
:
index
+=
1
if
int
(
base_obj
[
"ctrl_"
][
index
])
<
0
:
continue
n_items
+=
1
yield
base_obj
[
"slots_"
][
index
]
def
parallel_slot_iterator
(
base_obj
):
array
=
base_obj
[
"sets_"
]
array_len
=
int
(
array
.
type
.
template_argument
(
1
))
for
index
in
range
(
array_len
):
obj
=
array
[
"_M_elems"
][
index
][
"set_"
]
yield
from
slot_iterator
(
obj
)
def
flat_map_iterator
(
name
,
item
):
yield
(
next
(
name
),
item
[
"value"
][
"first"
])
yield
(
next
(
name
),
item
[
"value"
][
"second"
])
def
flat_set_iterator
(
name
,
item
):
yield
(
next
(
name
),
item
)
def
node_map_iterator
(
name
,
item
):
yield
(
next
(
name
),
item
.
dereference
()[
"first"
])
yield
(
next
(
name
),
item
.
dereference
()[
"second"
])
def
node_set_iterator
(
name
,
item
):
yield
(
next
(
name
),
item
.
dereference
())
def
traverse
(
iterator
,
slot_type_iterator
):
name
=
counter
()
for
item
in
iterator
:
yield
from
slot_type_iterator
(
name
,
item
)
def
parallel_size
(
parallel_hash_obj
):
array
=
parallel_hash_obj
[
"sets_"
]
array_len
=
int
(
array
.
type
.
template_argument
(
1
))
size
=
0
for
index
in
range
(
array_len
):
size
+=
array
[
"_M_elems"
][
index
][
"set_"
][
"size_"
]
return
size
class
FlatMapPrinter
:
def
__init__
(
self
,
val
):
self
.
val
=
val
def
children
(
self
):
return
traverse
(
slot_iterator
(
self
.
val
),
flat_map_iterator
)
def
to_string
(
self
):
return
f
"phmap::flat_hash_map with
{
int
(
self
.
val
[
'size_'
])
}
elements"
def
display_hint
(
self
):
return
"map"
class
FlatSetPrinter
:
def
__init__
(
self
,
val
):
self
.
val
=
val
def
children
(
self
):
return
traverse
(
slot_iterator
(
self
.
val
),
flat_set_iterator
)
def
to_string
(
self
):
return
f
"phmap::flat_hash_set with
{
int
(
self
.
val
[
'size_'
])
}
elements"
def
display_hint
(
self
):
return
"array"
class
NodeMapPrinter
:
def
__init__
(
self
,
val
):
self
.
val
=
val
def
children
(
self
):
return
traverse
(
slot_iterator
(
self
.
val
),
node_map_iterator
)
def
to_string
(
self
):
return
f
"phmap::node_hash_map with
{
int
(
self
.
val
[
'size_'
])
}
elements"
def
display_hint
(
self
):
return
"map"
class
NodeSetPrinter
:
def
__init__
(
self
,
val
):
self
.
val
=
val
def
children
(
self
):
return
traverse
(
slot_iterator
(
self
.
val
),
node_set_iterator
)
def
to_string
(
self
):
return
f
"phmap::node_hash_set with
{
int
(
self
.
val
[
'size_'
])
}
elements"
def
display_hint
(
self
):
return
"array"
class
ParallelFlatMapPrinter
:
def
__init__
(
self
,
val
):
self
.
val
=
val
def
children
(
self
):
return
traverse
(
parallel_slot_iterator
(
self
.
val
),
flat_map_iterator
)
def
to_string
(
self
):
return
f
"phmap::parallel_flat_hash_map with
{
parallel_size
(
self
.
val
)
}
elements"
def
display_hint
(
self
):
return
"map"
class
ParallelFlatSetPrinter
:
def
__init__
(
self
,
val
):
self
.
val
=
val
def
children
(
self
):
return
traverse
(
parallel_slot_iterator
(
self
.
val
),
flat_set_iterator
)
def
to_string
(
self
):
return
f
"phmap::parallel_flat_hash_set with
{
parallel_size
(
self
.
val
)
}
elements"
def
display_hint
(
self
):
return
"array"
class
ParallelNodeMapPrinter
:
def
__init__
(
self
,
val
):
self
.
val
=
val
def
children
(
self
):
return
traverse
(
parallel_slot_iterator
(
self
.
val
),
node_map_iterator
)
def
to_string
(
self
):
return
f
"phmap::parallel_node_hash_map with
{
parallel_size
(
self
.
val
)
}
elements"
def
display_hint
(
self
):
return
"map"
class
ParallelNodeSetPrinter
:
def
__init__
(
self
,
val
):
self
.
val
=
val
def
children
(
self
):
return
traverse
(
parallel_slot_iterator
(
self
.
val
),
node_set_iterator
)
def
to_string
(
self
):
return
f
"phmap::parallel_node_hash_set with
{
parallel_size
(
self
.
val
)
}
elements"
def
display_hint
(
self
):
return
"array"
def
build_pretty_printer
():
pp
=
gdb
.
printing
.
RegexpCollectionPrettyPrinter
(
"phmap"
)
pp
.
add_printer
(
'flat_hash_map'
,
'^phmap::flat_hash_map<.*>$'
,
FlatMapPrinter
)
pp
.
add_printer
(
'flat_hash_set'
,
'^phmap::flat_hash_set<.*>$'
,
FlatSetPrinter
)
pp
.
add_printer
(
'node_hash_map'
,
'^phmap::node_hash_map<.*>$'
,
NodeMapPrinter
)
pp
.
add_printer
(
'node_hash_set'
,
'^phmap::node_hash_set<.*>$'
,
NodeSetPrinter
)
pp
.
add_printer
(
'parallel_flat_hash_map'
,
'^phmap::parallel_flat_hash_map<.*>$'
,
ParallelFlatMapPrinter
)
pp
.
add_printer
(
'parallel_flat_hash_set'
,
'^phmap::parallel_flat_hash_set<.*>$'
,
ParallelFlatSetPrinter
)
pp
.
add_printer
(
'parallel_node_hash_map'
,
'^phmap::parallel_node_hash_map<.*>$'
,
ParallelNodeMapPrinter
)
pp
.
add_printer
(
'parallel_node_hash_set'
,
'^phmap::parallel_node_hash_set<.*>$'
,
ParallelNodeSetPrinter
)
return
pp
gdb
.
printing
.
register_pretty_printer
(
gdb
.
current_objfile
(),
build_pretty_printer
())
third_party/parallel-hashmap/phmap_lldb.py
0 → 100755
View file @
2951b12d
# Python lldb formatters for parallel-hashmap
# tested witch clang10 / lldb9 & 10
# to install it, type the following command or put it in $HOME/.lldbinit:
# command script import "PATH_TO_SCRIPT/lldb_phmap.py"
import
lldb
import
os
import
sys
import
re
_MAX_CHILDREN
=
250
_MAX_CTRL_INDEX
=
1_000
_MODULE_NAME
=
os
.
path
.
basename
(
__file__
).
split
(
"."
)[
0
]
def
_get_function_name
(
instance
=
None
):
"""Return the name of the calling function"""
class_name
=
f
"
{
type
(
instance
).
__name__
}
."
if
instance
else
""
return
class_name
+
sys
.
_getframe
(
1
).
f_code
.
co_name
class
flat_map_slot_type
:
CLASS_PATTERN
=
"^phmap::priv::raw_hash_set<phmap::priv::FlatHashMapPolicy.*>::slot_type$"
HAS_SUMMARY
=
True
IS_SYNTHETIC_PROVIDER
=
False
@
staticmethod
def
summary
(
valobj
,
_
):
try
:
valobj
=
valobj
.
GetChildMemberWithName
(
'value'
)
first
=
valobj
.
GetChildMemberWithName
(
'first'
).
GetSummary
()
if
not
first
:
first
=
"{...}"
second
=
valobj
.
GetChildMemberWithName
(
'second'
).
GetSummary
()
if
not
second
:
second
=
"{...}"
return
f
"{{
{
first
}
,
{
second
}
}}"
except
BaseException
as
ex
:
print
(
f
"
{
_get_function_name
()
}
->
{
ex
}
"
)
return
""
class
node_map_slot_type
:
CLASS_PATTERN
=
r
"phmap::priv::raw_hash_set<phmap::priv::NodeHashMapPolicy.*>::slot_type$"
HAS_SUMMARY
=
True
IS_SYNTHETIC_PROVIDER
=
False
@
staticmethod
def
summary
(
valobj
,
_
):
try
:
valobj
=
valobj
.
Dereference
()
first
=
valobj
.
GetChildMemberWithName
(
'first'
).
GetSummary
()
if
not
first
:
first
=
"{...}"
second
=
valobj
.
GetChildMemberWithName
(
'second'
).
GetSummary
()
if
not
second
:
second
=
"{...}"
return
f
"{{
{
first
}
,
{
second
}
}}"
except
BaseException
as
ex
:
print
(
f
"
{
_get_function_name
()
}
->
{
ex
}
"
)
return
"{?}"
class
node_set_slot_type
:
CLASS_PATTERN
=
r
"phmap::priv::raw_hash_set<phmap::priv::NodeHashSetPolicy.*>::slot_type$"
HAS_SUMMARY
=
True
IS_SYNTHETIC_PROVIDER
=
False
@
staticmethod
def
summary
(
valobj
,
_
):
try
:
summary
=
valobj
.
Dereference
().
GetSummary
()
if
not
summary
:
summary
=
"{...}"
return
summary
except
BaseException
as
ex
:
print
(
f
"
{
_get_function_name
()
}
->
{
ex
}
"
)
return
"{?}"
class
flat_hash_map_or_set
:
CLASS_PATTERN
=
"^phmap::flat_hash_(map|set)<.*>$"
HAS_SUMMARY
=
True
IS_SYNTHETIC_PROVIDER
=
True
@
staticmethod
def
summary
(
valobj
,
_
):
try
:
valobj
=
valobj
.
GetNonSyntheticValue
()
size
=
valobj
.
GetChildMemberWithName
(
'size_'
).
GetValueAsUnsigned
()
capacity
=
valobj
.
GetChildMemberWithName
(
'capacity_'
).
GetValueAsUnsigned
()
return
f
"size =
{
size
}
(capacity =
{
capacity
}
)"
except
BaseException
as
ex
:
print
(
f
"
{
_get_function_name
()
}
->
{
ex
}
"
)
return
"{?}"
def
__init__
(
self
,
valobj
,
_
):
self
.
valobj
=
valobj
self
.
slots_
=
self
.
slot_type
=
self
.
ctrl_
=
None
self
.
size_
=
self
.
capacity_
=
self
.
slot_size
=
0
def
num_children
(
self
):
return
min
(
self
.
size_
,
_MAX_CHILDREN
)
def
has_children
(
self
):
return
True
def
update
(
self
):
try
:
self
.
size_
=
self
.
valobj
.
GetChildMemberWithName
(
'size_'
).
GetValueAsUnsigned
()
self
.
capacity_
=
self
.
valobj
.
GetChildMemberWithName
(
'capacity_'
).
GetValueAsUnsigned
()
self
.
slots_
=
self
.
valobj
.
GetChildMemberWithName
(
"slots_"
)
self
.
slot_type
=
self
.
slots_
.
GetType
().
GetPointeeType
()
self
.
slot_size
=
self
.
slot_type
.
GetByteSize
()
self
.
ctrl_
=
self
.
valobj
.
GetChildMemberWithName
(
"ctrl_"
)
except
BaseException
as
ex
:
print
(
f
"
{
_get_function_name
(
self
)
}
->
{
ex
}
"
)
def
get_child_index
(
self
,
name
):
try
:
if
name
in
(
'size_'
,
'capacity_'
):
return
-
1
return
int
(
name
.
lstrip
(
'['
).
rstrip
(
']'
))
except
:
return
-
1
def
get_child_at_index
(
self
,
index
):
try
:
if
index
<
0
:
return
None
if
index
>=
self
.
size_
or
index
>=
_MAX_CHILDREN
:
return
None
real_idx
=
-
1
for
idx
in
range
(
min
(
self
.
capacity_
+
3
,
_MAX_CTRL_INDEX
)):
ctrl
=
self
.
ctrl_
.
GetChildAtIndex
(
idx
).
GetValueAsSigned
()
if
ctrl
>=
-
1
:
real_idx
+=
1
if
real_idx
==
index
:
return
self
.
slots_
.
CreateChildAtOffset
(
f
'[
{
index
}
]'
,
idx
*
self
.
slot_size
,
self
.
slot_type
)
except
BaseException
as
ex
:
print
(
f
"
{
_get_function_name
(
self
)
}
->
{
ex
}
"
)
return
None
class
parallel_flat_or_node_map_or_set
:
CLASS_PATTERN
=
"^phmap::parallel_(flat|node)_hash_(map|set)<.*>$"
HAS_SUMMARY
=
True
IS_SYNTHETIC_PROVIDER
=
True
REGEX_EXTRACT_ARRAY_SIZE
=
re
.
compile
(
r
"std::array\s*<.*,\s*(\d+)\s*>"
)
@
staticmethod
def
_get_size_and_capacity
(
valobj
):
try
:
valobj
=
valobj
.
GetNonSyntheticValue
()
sets
=
valobj
.
GetChildMemberWithName
(
'sets_'
)
# sets is an std::array<T, SIZE>.
# It's not possible to get the size of the array with templates parameters
# "set.GetType().GetTemplateArgumentType(1)" returns an "unsigned long" type but not the value
# so we must extract it with a regex
m
=
parallel_flat_or_node_map_or_set
.
REGEX_EXTRACT_ARRAY_SIZE
.
match
(
sets
.
GetType
().
GetName
())
n_buckets
=
int
(
m
.
group
(
1
))
# this is dependent on the implementation of the standard library
buckets
=
sets
.
GetChildMemberWithName
(
'_M_elems'
)
size
=
capacity
=
0
for
idx
in
range
(
n_buckets
):
bucket
=
buckets
.
GetChildAtIndex
(
idx
).
GetChildMemberWithName
(
'set_'
)
size
+=
bucket
.
GetChildMemberWithName
(
'size_'
).
GetValueAsUnsigned
()
capacity
+=
bucket
.
GetChildMemberWithName
(
'capacity_'
).
GetValueAsUnsigned
()
return
size
,
capacity
,
n_buckets
except
:
return
'?'
,
'?'
,
0
@
staticmethod
def
summary
(
valobj
,
_
):
size
,
capacity
,
_
=
parallel_flat_or_node_map_or_set
.
_get_size_and_capacity
(
valobj
)
return
f
"size =
{
size
}
(capacity =
{
capacity
}
)"
def
__init__
(
self
,
valobj
,
_
):
self
.
valobj
=
valobj
self
.
buckets
=
self
.
slot_type
=
None
self
.
size_
=
self
.
capacity_
=
self
.
n_buckets_
=
self
.
slot_type
=
self
.
ctrl_size
=
0
def
num_children
(
self
):
return
min
(
self
.
size_
,
_MAX_CHILDREN
)
def
has_children
(
self
):
return
True
def
update
(
self
):
try
:
self
.
size_
,
self
.
capacity_
,
self
.
n_buckets_
=
self
.
_get_size_and_capacity
(
self
.
valobj
)
self
.
buckets
=
self
.
valobj
.
GetChildMemberWithName
(
'sets_'
).
GetChildMemberWithName
(
'_M_elems'
)
bucket0
=
self
.
buckets
.
GetChildAtIndex
(
0
).
GetChildMemberWithName
(
'set_'
)
self
.
slot_type
=
bucket0
.
GetChildMemberWithName
(
'slots_'
).
GetType
().
GetPointeeType
()
self
.
slot_size
=
self
.
slot_type
.
GetByteSize
()
except
BaseException
as
ex
:
print
(
f
"
{
_get_function_name
(
self
)
}
->
{
ex
}
"
)
def
get_child_index
(
self
,
name
):
try
:
if
name
in
(
'sets_'
):
return
-
1
return
int
(
name
.
lstrip
(
'['
).
rstrip
(
']'
))
except
:
return
-
1
def
get_child_at_index
(
self
,
index
):
try
:
if
index
<
0
:
return
None
if
index
>=
self
.
size_
or
index
>=
_MAX_CHILDREN
:
return
None
real_idx
=
-
1
total_idx
=
0
for
idx
in
range
(
self
.
n_buckets_
):
bucket
=
self
.
buckets
.
GetChildAtIndex
(
idx
).
GetChildMemberWithName
(
'set_'
)
size
=
bucket
.
GetChildMemberWithName
(
"size_"
).
GetValueAsUnsigned
()
if
size
:
slots_
=
bucket
.
GetChildMemberWithName
(
"slots_"
)
ctrl_
=
bucket
.
GetChildMemberWithName
(
"ctrl_"
)
for
jdx
in
range
(
size
):
ctrl
=
ctrl_
.
GetChildAtIndex
(
jdx
).
GetValueAsSigned
()
if
ctrl
>=
-
1
:
real_idx
+=
1
if
real_idx
==
index
:
return
slots_
.
CreateChildAtOffset
(
f
'[
{
index
}
]'
,
jdx
*
self
.
slot_size
,
self
.
slot_type
)
total_idx
+=
size
if
total_idx
>
_MAX_CHILDREN
:
return
None
except
BaseException
as
ex
:
print
(
f
"
{
_get_function_name
(
self
)
}
->
{
ex
}
"
)
return
None
def
__lldb_init_module
(
debugger
,
internal_dict
):
for
sp
in
(
flat_map_slot_type
,
node_map_slot_type
,
node_set_slot_type
,
flat_hash_map_or_set
,
parallel_flat_or_node_map_or_set
,
):
if
sp
.
HAS_SUMMARY
:
debugger
.
HandleCommand
(
f
'type summary add --regex "
{
sp
.
CLASS_PATTERN
}
" --python-function
{
_MODULE_NAME
}
.
{
sp
.
__name__
}
.summary '
f
'--category phmap --expand'
)
if
sp
.
IS_SYNTHETIC_PROVIDER
:
debugger
.
HandleCommand
(
f
'type synthetic add --regex "
{
sp
.
CLASS_PATTERN
}
" --python-class
{
_MODULE_NAME
}
.
{
sp
.
__name__
}
'
f
'--category phmap'
)
debugger
.
HandleCommand
(
'type category enable phmap'
)
third_party/parallel-hashmap/tests/btree_test.cc
0 → 100644
View file @
2951b12d
// ---------------------------------------------------------------------------
// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Includes work from abseil-cpp (https://github.com/abseil/abseil-cpp)
// with modifications.
//
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ---------------------------------------------------------------------------
#include <numeric>
#include "btree_test.h"
#ifdef _MSC_VER
#pragma warning(disable: 4244 4100 4389)
#endif
namespace
phmap
{
namespace
test_internal
{
size_t
BaseCountedInstance
::
num_instances_
=
0
;
size_t
BaseCountedInstance
::
num_live_instances_
=
0
;
size_t
BaseCountedInstance
::
num_moves_
=
0
;
size_t
BaseCountedInstance
::
num_copies_
=
0
;
size_t
BaseCountedInstance
::
num_swaps_
=
0
;
size_t
BaseCountedInstance
::
num_comparisons_
=
0
;
}
// namespace test_internal
}
// namespace phmap\
static
const
size_t
test_values
=
10000
;
namespace
phmap
{
namespace
priv
{
namespace
{
using
::
phmap
::
test_internal
::
CopyableMovableInstance
;
using
::
phmap
::
test_internal
::
InstanceTracker
;
using
::
phmap
::
test_internal
::
MovableOnlyInstance
;
using
::
testing
::
ElementsAre
;
using
::
testing
::
ElementsAreArray
;
using
::
testing
::
IsEmpty
;
using
::
testing
::
Pair
;
#define PHMAP_INTERNAL_CHECK(condition, message) \
if (!(condition)) assert(0)
template
<
typename
T
,
typename
U
>
void
CheckPairEquals
(
const
T
&
x
,
const
U
&
y
)
{
PHMAP_INTERNAL_CHECK
(
x
==
y
,
"Values are unequal."
);
}
template
<
typename
T
,
typename
U
,
typename
V
,
typename
W
>
void
CheckPairEquals
(
const
std
::
pair
<
T
,
U
>
&
x
,
const
std
::
pair
<
V
,
W
>
&
y
)
{
CheckPairEquals
(
x
.
first
,
y
.
first
);
CheckPairEquals
(
x
.
second
,
y
.
second
);
}
}
// namespace
// The base class for a sorted associative container checker. TreeType is the
// container type to check and CheckerType is the container type to check
// against. TreeType is expected to be btree_{set,map,multiset,multimap} and
// CheckerType is expected to be {set,map,multiset,multimap}.
template
<
typename
TreeType
,
typename
CheckerType
>
class
base_checker
{
public:
using
key_type
=
typename
TreeType
::
key_type
;
using
value_type
=
typename
TreeType
::
value_type
;
using
key_compare
=
typename
TreeType
::
key_compare
;
using
pointer
=
typename
TreeType
::
pointer
;
using
const_pointer
=
typename
TreeType
::
const_pointer
;
using
reference
=
typename
TreeType
::
reference
;
using
const_reference
=
typename
TreeType
::
const_reference
;
using
size_type
=
typename
TreeType
::
size_type
;
using
difference_type
=
typename
TreeType
::
difference_type
;
using
iterator
=
typename
TreeType
::
iterator
;
using
const_iterator
=
typename
TreeType
::
const_iterator
;
using
reverse_iterator
=
typename
TreeType
::
reverse_iterator
;
using
const_reverse_iterator
=
typename
TreeType
::
const_reverse_iterator
;
public:
base_checker
()
:
const_tree_
(
tree_
)
{}
base_checker
(
const
base_checker
&
x
)
:
tree_
(
x
.
tree_
),
const_tree_
(
tree_
),
checker_
(
x
.
checker_
)
{}
template
<
typename
InputIterator
>
base_checker
(
InputIterator
b
,
InputIterator
e
)
:
tree_
(
b
,
e
),
const_tree_
(
tree_
),
checker_
(
b
,
e
)
{}
iterator
begin
()
{
return
tree_
.
begin
();
}
const_iterator
begin
()
const
{
return
tree_
.
begin
();
}
iterator
end
()
{
return
tree_
.
end
();
}
const_iterator
end
()
const
{
return
tree_
.
end
();
}
reverse_iterator
rbegin
()
{
return
tree_
.
rbegin
();
}
const_reverse_iterator
rbegin
()
const
{
return
tree_
.
rbegin
();
}
reverse_iterator
rend
()
{
return
tree_
.
rend
();
}
const_reverse_iterator
rend
()
const
{
return
tree_
.
rend
();
}
template
<
typename
IterType
,
typename
CheckerIterType
>
IterType
iter_check
(
IterType
tree_iter
,
CheckerIterType
checker_iter
)
const
{
if
(
tree_iter
==
tree_
.
end
())
{
PHMAP_INTERNAL_CHECK
(
checker_iter
==
checker_
.
end
(),
"Checker iterator not at end."
);
}
else
{
CheckPairEquals
(
*
tree_iter
,
*
checker_iter
);
}
return
tree_iter
;
}
template
<
typename
IterType
,
typename
CheckerIterType
>
IterType
riter_check
(
IterType
tree_iter
,
CheckerIterType
checker_iter
)
const
{
if
(
tree_iter
==
tree_
.
rend
())
{
PHMAP_INTERNAL_CHECK
(
checker_iter
==
checker_
.
rend
(),
"Checker iterator not at rend."
);
}
else
{
CheckPairEquals
(
*
tree_iter
,
*
checker_iter
);
}
return
tree_iter
;
}
void
value_check
(
const
value_type
&
x
)
{
typename
KeyOfValue
<
typename
TreeType
::
key_type
,
typename
TreeType
::
value_type
>::
type
key_of_value
;
const
key_type
&
key
=
key_of_value
(
x
);
CheckPairEquals
(
*
find
(
key
),
x
);
lower_bound
(
key
);
upper_bound
(
key
);
equal_range
(
key
);
contains
(
key
);
count
(
key
);
}
void
erase_check
(
const
key_type
&
key
)
{
EXPECT_FALSE
(
tree_
.
contains
(
key
));
EXPECT_EQ
(
tree_
.
find
(
key
),
const_tree_
.
end
());
EXPECT_FALSE
(
const_tree_
.
contains
(
key
));
EXPECT_EQ
(
const_tree_
.
find
(
key
),
tree_
.
end
());
EXPECT_EQ
(
tree_
.
equal_range
(
key
).
first
,
const_tree_
.
equal_range
(
key
).
second
);
}
iterator
lower_bound
(
const
key_type
&
key
)
{
return
iter_check
(
tree_
.
lower_bound
(
key
),
checker_
.
lower_bound
(
key
));
}
const_iterator
lower_bound
(
const
key_type
&
key
)
const
{
return
iter_check
(
tree_
.
lower_bound
(
key
),
checker_
.
lower_bound
(
key
));
}
iterator
upper_bound
(
const
key_type
&
key
)
{
return
iter_check
(
tree_
.
upper_bound
(
key
),
checker_
.
upper_bound
(
key
));
}
const_iterator
upper_bound
(
const
key_type
&
key
)
const
{
return
iter_check
(
tree_
.
upper_bound
(
key
),
checker_
.
upper_bound
(
key
));
}
std
::
pair
<
iterator
,
iterator
>
equal_range
(
const
key_type
&
key
)
{
std
::
pair
<
typename
CheckerType
::
iterator
,
typename
CheckerType
::
iterator
>
checker_res
=
checker_
.
equal_range
(
key
);
std
::
pair
<
iterator
,
iterator
>
tree_res
=
tree_
.
equal_range
(
key
);
iter_check
(
tree_res
.
first
,
checker_res
.
first
);
iter_check
(
tree_res
.
second
,
checker_res
.
second
);
return
tree_res
;
}
std
::
pair
<
const_iterator
,
const_iterator
>
equal_range
(
const
key_type
&
key
)
const
{
std
::
pair
<
typename
CheckerType
::
const_iterator
,
typename
CheckerType
::
const_iterator
>
checker_res
=
checker_
.
equal_range
(
key
);
std
::
pair
<
const_iterator
,
const_iterator
>
tree_res
=
tree_
.
equal_range
(
key
);
iter_check
(
tree_res
.
first
,
checker_res
.
first
);
iter_check
(
tree_res
.
second
,
checker_res
.
second
);
return
tree_res
;
}
iterator
find
(
const
key_type
&
key
)
{
return
iter_check
(
tree_
.
find
(
key
),
checker_
.
find
(
key
));
}
const_iterator
find
(
const
key_type
&
key
)
const
{
return
iter_check
(
tree_
.
find
(
key
),
checker_
.
find
(
key
));
}
bool
contains
(
const
key_type
&
key
)
const
{
return
find
(
key
)
!=
end
();
}
size_type
count
(
const
key_type
&
key
)
const
{
size_type
res
=
checker_
.
count
(
key
);
EXPECT_EQ
(
res
,
tree_
.
count
(
key
));
return
res
;
}
base_checker
&
operator
=
(
const
base_checker
&
x
)
{
tree_
=
x
.
tree_
;
checker_
=
x
.
checker_
;
return
*
this
;
}
int
erase
(
const
key_type
&
key
)
{
size_t
size
=
tree_
.
size
();
int
res
=
(
int
)
checker_
.
erase
(
key
);
EXPECT_EQ
(
res
,
tree_
.
count
(
key
));
EXPECT_EQ
(
res
,
tree_
.
erase
(
key
));
EXPECT_EQ
(
tree_
.
count
(
key
),
0
);
EXPECT_EQ
(
tree_
.
size
(),
size
-
res
);
erase_check
(
key
);
return
res
;
}
iterator
erase
(
iterator
iter
)
{
key_type
key
=
iter
.
key
();
size_t
size
=
tree_
.
size
();
size_t
count
=
tree_
.
count
(
key
);
auto
checker_iter
=
checker_
.
lower_bound
(
key
);
for
(
iterator
tmp
(
tree_
.
lower_bound
(
key
));
tmp
!=
iter
;
++
tmp
)
{
++
checker_iter
;
}
auto
checker_next
=
checker_iter
;
++
checker_next
;
checker_
.
erase
(
checker_iter
);
iter
=
tree_
.
erase
(
iter
);
EXPECT_EQ
(
tree_
.
size
(),
(
size_t
)
checker_
.
size
());
EXPECT_EQ
(
tree_
.
size
(),
size
-
1
);
EXPECT_EQ
(
tree_
.
count
(
key
),
count
-
1
);
if
(
count
==
1
)
{
erase_check
(
key
);
}
return
iter_check
(
iter
,
checker_next
);
}
void
erase
(
iterator
begin
,
iterator
end
)
{
size_t
size
=
tree_
.
size
();
int
count
=
std
::
distance
(
begin
,
end
);
auto
checker_begin
=
checker_
.
lower_bound
(
begin
.
key
());
for
(
iterator
tmp
(
tree_
.
lower_bound
(
begin
.
key
()));
tmp
!=
begin
;
++
tmp
)
{
++
checker_begin
;
}
auto
checker_end
=
end
==
tree_
.
end
()
?
checker_
.
end
()
:
checker_
.
lower_bound
(
end
.
key
());
if
(
end
!=
tree_
.
end
())
{
for
(
iterator
tmp
(
tree_
.
lower_bound
(
end
.
key
()));
tmp
!=
end
;
++
tmp
)
{
++
checker_end
;
}
}
checker_
.
erase
(
checker_begin
,
checker_end
);
tree_
.
erase
(
begin
,
end
);
EXPECT_EQ
(
tree_
.
size
(),
checker_
.
size
());
EXPECT_EQ
(
tree_
.
size
(),
size
-
count
);
}
void
clear
()
{
tree_
.
clear
();
checker_
.
clear
();
}
void
swap
(
base_checker
&
x
)
{
tree_
.
swap
(
x
.
tree_
);
checker_
.
swap
(
x
.
checker_
);
}
void
verify
()
const
{
tree_
.
verify
();
EXPECT_EQ
(
tree_
.
size
(),
checker_
.
size
());
// Move through the forward iterators using increment.
auto
checker_iter
=
checker_
.
begin
();
const_iterator
tree_iter
(
tree_
.
begin
());
for
(;
tree_iter
!=
tree_
.
end
();
++
tree_iter
,
++
checker_iter
)
{
CheckPairEquals
(
*
tree_iter
,
*
checker_iter
);
}
// Move through the forward iterators using decrement.
for
(
int
n
=
(
int
)
tree_
.
size
()
-
1
;
n
>=
0
;
--
n
)
{
iter_check
(
tree_iter
,
checker_iter
);
--
tree_iter
;
--
checker_iter
;
}
EXPECT_EQ
(
tree_iter
,
tree_
.
begin
());
EXPECT_EQ
(
checker_iter
,
checker_
.
begin
());
// Move through the reverse iterators using increment.
auto
checker_riter
=
checker_
.
rbegin
();
const_reverse_iterator
tree_riter
(
tree_
.
rbegin
());
for
(;
tree_riter
!=
tree_
.
rend
();
++
tree_riter
,
++
checker_riter
)
{
CheckPairEquals
(
*
tree_riter
,
*
checker_riter
);
}
// Move through the reverse iterators using decrement.
for
(
int
n
=
(
int
)
tree_
.
size
()
-
1
;
n
>=
0
;
--
n
)
{
riter_check
(
tree_riter
,
checker_riter
);
--
tree_riter
;
--
checker_riter
;
}
EXPECT_EQ
(
tree_riter
,
tree_
.
rbegin
());
EXPECT_EQ
(
checker_riter
,
checker_
.
rbegin
());
}
const
TreeType
&
tree
()
const
{
return
tree_
;
}
size_type
size
()
const
{
EXPECT_EQ
(
tree_
.
size
(),
checker_
.
size
());
return
tree_
.
size
();
}
size_type
max_size
()
const
{
return
tree_
.
max_size
();
}
bool
empty
()
const
{
EXPECT_EQ
(
tree_
.
empty
(),
checker_
.
empty
());
return
tree_
.
empty
();
}
protected:
TreeType
tree_
;
const
TreeType
&
const_tree_
;
CheckerType
checker_
;
};
namespace
{
// A checker for unique sorted associative containers. TreeType is expected to
// be btree_{set,map} and CheckerType is expected to be {set,map}.
template
<
typename
TreeType
,
typename
CheckerType
>
class
unique_checker
:
public
base_checker
<
TreeType
,
CheckerType
>
{
using
super_type
=
base_checker
<
TreeType
,
CheckerType
>
;
public:
using
iterator
=
typename
super_type
::
iterator
;
using
value_type
=
typename
super_type
::
value_type
;
public:
unique_checker
()
:
super_type
()
{}
unique_checker
(
const
unique_checker
&
x
)
:
super_type
(
x
)
{}
template
<
class
InputIterator
>
unique_checker
(
InputIterator
b
,
InputIterator
e
)
:
super_type
(
b
,
e
)
{}
unique_checker
&
operator
=
(
const
unique_checker
&
)
=
default
;
// Insertion routines.
std
::
pair
<
iterator
,
bool
>
insert
(
const
value_type
&
x
)
{
size_t
size
=
this
->
tree_
.
size
();
std
::
pair
<
typename
CheckerType
::
iterator
,
bool
>
checker_res
=
this
->
checker_
.
insert
(
x
);
std
::
pair
<
iterator
,
bool
>
tree_res
=
this
->
tree_
.
insert
(
x
);
CheckPairEquals
(
*
tree_res
.
first
,
*
checker_res
.
first
);
EXPECT_EQ
(
tree_res
.
second
,
checker_res
.
second
);
EXPECT_EQ
(
this
->
tree_
.
size
(),
this
->
checker_
.
size
());
EXPECT_EQ
(
this
->
tree_
.
size
(),
size
+
tree_res
.
second
);
return
tree_res
;
}
iterator
insert
(
iterator
position
,
const
value_type
&
x
)
{
size_t
size
=
this
->
tree_
.
size
();
std
::
pair
<
typename
CheckerType
::
iterator
,
bool
>
checker_res
=
this
->
checker_
.
insert
(
x
);
iterator
tree_res
=
this
->
tree_
.
insert
(
position
,
x
);
CheckPairEquals
(
*
tree_res
,
*
checker_res
.
first
);
EXPECT_EQ
(
this
->
tree_
.
size
(),
this
->
checker_
.
size
());
EXPECT_EQ
(
this
->
tree_
.
size
(),
size
+
checker_res
.
second
);
return
tree_res
;
}
template
<
typename
InputIterator
>
void
insert
(
InputIterator
b
,
InputIterator
e
)
{
for
(;
b
!=
e
;
++
b
)
{
insert
(
*
b
);
}
}
};
// A checker for multiple sorted associative containers. TreeType is expected
// to be btree_{multiset,multimap} and CheckerType is expected to be
// {multiset,multimap}.
template
<
typename
TreeType
,
typename
CheckerType
>
class
multi_checker
:
public
base_checker
<
TreeType
,
CheckerType
>
{
using
super_type
=
base_checker
<
TreeType
,
CheckerType
>
;
public:
using
iterator
=
typename
super_type
::
iterator
;
using
value_type
=
typename
super_type
::
value_type
;
public:
multi_checker
()
:
super_type
()
{}
multi_checker
(
const
multi_checker
&
x
)
:
super_type
(
x
)
{}
template
<
class
InputIterator
>
multi_checker
(
InputIterator
b
,
InputIterator
e
)
:
super_type
(
b
,
e
)
{}
multi_checker
&
operator
=
(
const
multi_checker
&
)
=
default
;
// Insertion routines.
iterator
insert
(
const
value_type
&
x
)
{
size_t
size
=
this
->
tree_
.
size
();
auto
checker_res
=
this
->
checker_
.
insert
(
x
);
iterator
tree_res
=
this
->
tree_
.
insert
(
x
);
CheckPairEquals
(
*
tree_res
,
*
checker_res
);
EXPECT_EQ
(
this
->
tree_
.
size
(),
this
->
checker_
.
size
());
EXPECT_EQ
(
this
->
tree_
.
size
(),
size
+
1
);
return
tree_res
;
}
iterator
insert
(
iterator
position
,
const
value_type
&
x
)
{
size_t
size
=
this
->
tree_
.
size
();
auto
checker_res
=
this
->
checker_
.
insert
(
x
);
iterator
tree_res
=
this
->
tree_
.
insert
(
position
,
x
);
CheckPairEquals
(
*
tree_res
,
*
checker_res
);
EXPECT_EQ
(
this
->
tree_
.
size
(),
this
->
checker_
.
size
());
EXPECT_EQ
(
this
->
tree_
.
size
(),
size
+
1
);
return
tree_res
;
}
template
<
typename
InputIterator
>
void
insert
(
InputIterator
b
,
InputIterator
e
)
{
for
(;
b
!=
e
;
++
b
)
{
insert
(
*
b
);
}
}
};
template
<
typename
T
,
typename
V
>
void
DoTest
(
const
char
*
name
,
T
*
b
,
const
std
::
vector
<
V
>
&
values
)
{
typename
KeyOfValue
<
typename
T
::
key_type
,
V
>::
type
key_of_value
;
T
&
mutable_b
=
*
b
;
const
T
&
const_b
=
*
b
;
// Test insert.
for
(
int
i
=
0
;
i
<
values
.
size
();
++
i
)
{
mutable_b
.
insert
(
values
[
i
]);
mutable_b
.
value_check
(
values
[
i
]);
}
ASSERT_EQ
(
mutable_b
.
size
(),
values
.
size
());
const_b
.
verify
();
// Test copy constructor.
T
b_copy
(
const_b
);
EXPECT_EQ
(
b_copy
.
size
(),
const_b
.
size
());
for
(
int
i
=
0
;
i
<
values
.
size
();
++
i
)
{
CheckPairEquals
(
*
b_copy
.
find
(
key_of_value
(
values
[
i
])),
values
[
i
]);
}
// Test range constructor.
T
b_range
(
const_b
.
begin
(),
const_b
.
end
());
EXPECT_EQ
(
b_range
.
size
(),
const_b
.
size
());
for
(
int
i
=
0
;
i
<
values
.
size
();
++
i
)
{
CheckPairEquals
(
*
b_range
.
find
(
key_of_value
(
values
[
i
])),
values
[
i
]);
}
// Test range insertion for values that already exist.
b_range
.
insert
(
b_copy
.
begin
(),
b_copy
.
end
());
b_range
.
verify
();
// Test range insertion for new values.
b_range
.
clear
();
b_range
.
insert
(
b_copy
.
begin
(),
b_copy
.
end
());
EXPECT_EQ
(
b_range
.
size
(),
b_copy
.
size
());
for
(
int
i
=
0
;
i
<
values
.
size
();
++
i
)
{
CheckPairEquals
(
*
b_range
.
find
(
key_of_value
(
values
[
i
])),
values
[
i
]);
}
// Test assignment to self. Nothing should change.
b_range
.
operator
=
(
b_range
);
EXPECT_EQ
(
b_range
.
size
(),
b_copy
.
size
());
// Test assignment of new values.
b_range
.
clear
();
b_range
=
b_copy
;
EXPECT_EQ
(
b_range
.
size
(),
b_copy
.
size
());
// Test swap.
b_range
.
clear
();
b_range
.
swap
(
b_copy
);
EXPECT_EQ
(
b_copy
.
size
(),
0
);
EXPECT_EQ
(
b_range
.
size
(),
const_b
.
size
());
for
(
int
i
=
0
;
i
<
values
.
size
();
++
i
)
{
CheckPairEquals
(
*
b_range
.
find
(
key_of_value
(
values
[
i
])),
values
[
i
]);
}
b_range
.
swap
(
b_copy
);
// Test non-member function swap.
swap
(
b_range
,
b_copy
);
EXPECT_EQ
(
b_copy
.
size
(),
0
);
EXPECT_EQ
(
b_range
.
size
(),
const_b
.
size
());
for
(
int
i
=
0
;
i
<
values
.
size
();
++
i
)
{
CheckPairEquals
(
*
b_range
.
find
(
key_of_value
(
values
[
i
])),
values
[
i
]);
}
swap
(
b_range
,
b_copy
);
// Test erase via values.
for
(
int
i
=
0
;
i
<
values
.
size
();
++
i
)
{
mutable_b
.
erase
(
key_of_value
(
values
[
i
]));
// Erasing a non-existent key should have no effect.
ASSERT_EQ
(
mutable_b
.
erase
(
key_of_value
(
values
[
i
])),
0
);
}
const_b
.
verify
();
EXPECT_EQ
(
const_b
.
size
(),
0
);
// Test erase via iterators.
mutable_b
=
b_copy
;
for
(
int
i
=
0
;
i
<
values
.
size
();
++
i
)
{
mutable_b
.
erase
(
mutable_b
.
find
(
key_of_value
(
values
[
i
])));
}
const_b
.
verify
();
EXPECT_EQ
(
const_b
.
size
(),
0
);
// Test insert with hint.
for
(
int
i
=
0
;
i
<
values
.
size
();
i
++
)
{
mutable_b
.
insert
(
mutable_b
.
upper_bound
(
key_of_value
(
values
[
i
])),
values
[
i
]);
}
const_b
.
verify
();
// Test range erase.
mutable_b
.
erase
(
mutable_b
.
begin
(),
mutable_b
.
end
());
EXPECT_EQ
(
mutable_b
.
size
(),
0
);
const_b
.
verify
();
// First half.
mutable_b
=
b_copy
;
typename
T
::
iterator
mutable_iter_end
=
mutable_b
.
begin
();
for
(
int
i
=
0
;
i
<
values
.
size
()
/
2
;
++
i
)
++
mutable_iter_end
;
mutable_b
.
erase
(
mutable_b
.
begin
(),
mutable_iter_end
);
EXPECT_EQ
(
mutable_b
.
size
(),
values
.
size
()
-
values
.
size
()
/
2
);
const_b
.
verify
();
// Second half.
mutable_b
=
b_copy
;
typename
T
::
iterator
mutable_iter_begin
=
mutable_b
.
begin
();
for
(
int
i
=
0
;
i
<
values
.
size
()
/
2
;
++
i
)
++
mutable_iter_begin
;
mutable_b
.
erase
(
mutable_iter_begin
,
mutable_b
.
end
());
EXPECT_EQ
(
mutable_b
.
size
(),
values
.
size
()
/
2
);
const_b
.
verify
();
// Second quarter.
mutable_b
=
b_copy
;
mutable_iter_begin
=
mutable_b
.
begin
();
for
(
int
i
=
0
;
i
<
values
.
size
()
/
4
;
++
i
)
++
mutable_iter_begin
;
mutable_iter_end
=
mutable_iter_begin
;
for
(
int
i
=
0
;
i
<
values
.
size
()
/
4
;
++
i
)
++
mutable_iter_end
;
mutable_b
.
erase
(
mutable_iter_begin
,
mutable_iter_end
);
EXPECT_EQ
(
mutable_b
.
size
(),
values
.
size
()
-
values
.
size
()
/
4
);
const_b
.
verify
();
mutable_b
.
clear
();
}
template
<
typename
T
>
void
ConstTest
()
{
using
value_type
=
typename
T
::
value_type
;
typename
KeyOfValue
<
typename
T
::
key_type
,
value_type
>::
type
key_of_value
;
T
mutable_b
;
const
T
&
const_b
=
mutable_b
;
// Insert a single value into the container and test looking it up.
value_type
value
=
Generator
<
value_type
>
(
2
)(
2
);
mutable_b
.
insert
(
value
);
EXPECT_TRUE
(
mutable_b
.
contains
(
key_of_value
(
value
)));
EXPECT_NE
(
mutable_b
.
find
(
key_of_value
(
value
)),
const_b
.
end
());
EXPECT_TRUE
(
const_b
.
contains
(
key_of_value
(
value
)));
EXPECT_NE
(
const_b
.
find
(
key_of_value
(
value
)),
mutable_b
.
end
());
EXPECT_EQ
(
*
const_b
.
lower_bound
(
key_of_value
(
value
)),
value
);
EXPECT_EQ
(
const_b
.
upper_bound
(
key_of_value
(
value
)),
const_b
.
end
());
EXPECT_EQ
(
*
const_b
.
equal_range
(
key_of_value
(
value
)).
first
,
value
);
// We can only create a non-const iterator from a non-const container.
typename
T
::
iterator
mutable_iter
(
mutable_b
.
begin
());
EXPECT_EQ
(
mutable_iter
,
const_b
.
begin
());
EXPECT_NE
(
mutable_iter
,
const_b
.
end
());
EXPECT_EQ
(
const_b
.
begin
(),
mutable_iter
);
EXPECT_NE
(
const_b
.
end
(),
mutable_iter
);
typename
T
::
reverse_iterator
mutable_riter
(
mutable_b
.
rbegin
());
EXPECT_EQ
(
mutable_riter
,
const_b
.
rbegin
());
EXPECT_NE
(
mutable_riter
,
const_b
.
rend
());
EXPECT_EQ
(
const_b
.
rbegin
(),
mutable_riter
);
EXPECT_NE
(
const_b
.
rend
(),
mutable_riter
);
// We can create a const iterator from a non-const iterator.
typename
T
::
const_iterator
const_iter
(
mutable_iter
);
EXPECT_EQ
(
const_iter
,
mutable_b
.
begin
());
EXPECT_NE
(
const_iter
,
mutable_b
.
end
());
EXPECT_EQ
(
mutable_b
.
begin
(),
const_iter
);
EXPECT_NE
(
mutable_b
.
end
(),
const_iter
);
typename
T
::
const_reverse_iterator
const_riter
(
mutable_riter
);
EXPECT_EQ
(
const_riter
,
mutable_b
.
rbegin
());
EXPECT_NE
(
const_riter
,
mutable_b
.
rend
());
EXPECT_EQ
(
mutable_b
.
rbegin
(),
const_riter
);
EXPECT_NE
(
mutable_b
.
rend
(),
const_riter
);
// Make sure various methods can be invoked on a const container.
const_b
.
verify
();
ASSERT_TRUE
(
!
const_b
.
empty
());
EXPECT_EQ
(
const_b
.
size
(),
1
);
EXPECT_GT
(
const_b
.
max_size
(),
0
);
EXPECT_TRUE
(
const_b
.
contains
(
key_of_value
(
value
)));
EXPECT_EQ
(
const_b
.
count
(
key_of_value
(
value
)),
1
);
}
template
<
typename
T
,
typename
C
>
void
BtreeTest
()
{
ConstTest
<
T
>
();
using
V
=
typename
remove_pair_const
<
typename
T
::
value_type
>::
type
;
const
std
::
vector
<
V
>
random_values
=
GenerateValuesWithSeed
<
V
>
(
test_values
,
4
*
test_values
,
testing
::
GTEST_FLAG
(
random_seed
));
unique_checker
<
T
,
C
>
container
;
// Test key insertion/deletion in sorted order.
std
::
vector
<
V
>
sorted_values
(
random_values
);
std
::
sort
(
sorted_values
.
begin
(),
sorted_values
.
end
());
DoTest
(
"sorted: "
,
&
container
,
sorted_values
);
// Test key insertion/deletion in reverse sorted order.
std
::
reverse
(
sorted_values
.
begin
(),
sorted_values
.
end
());
DoTest
(
"rsorted: "
,
&
container
,
sorted_values
);
// Test key insertion/deletion in random order.
DoTest
(
"random: "
,
&
container
,
random_values
);
}
template
<
typename
T
,
typename
C
>
void
BtreeMultiTest
()
{
ConstTest
<
T
>
();
using
V
=
typename
remove_pair_const
<
typename
T
::
value_type
>::
type
;
const
std
::
vector
<
V
>
random_values
=
GenerateValuesWithSeed
<
V
>
(
test_values
,
4
*
test_values
,
testing
::
GTEST_FLAG
(
random_seed
));
multi_checker
<
T
,
C
>
container
;
// Test keys in sorted order.
std
::
vector
<
V
>
sorted_values
(
random_values
);
std
::
sort
(
sorted_values
.
begin
(),
sorted_values
.
end
());
DoTest
(
"sorted: "
,
&
container
,
sorted_values
);
// Test keys in reverse sorted order.
std
::
reverse
(
sorted_values
.
begin
(),
sorted_values
.
end
());
DoTest
(
"rsorted: "
,
&
container
,
sorted_values
);
// Test keys in random order.
DoTest
(
"random: "
,
&
container
,
random_values
);
// Test keys in random order w/ duplicates.
std
::
vector
<
V
>
duplicate_values
(
random_values
);
duplicate_values
.
insert
(
duplicate_values
.
end
(),
random_values
.
begin
(),
random_values
.
end
());
DoTest
(
"duplicates:"
,
&
container
,
duplicate_values
);
// Test all identical keys.
std
::
vector
<
V
>
identical_values
(
100
);
std
::
fill
(
identical_values
.
begin
(),
identical_values
.
end
(),
Generator
<
V
>
(
2
)(
2
));
DoTest
(
"identical: "
,
&
container
,
identical_values
);
}
template
<
typename
T
>
struct
PropagatingCountingAlloc
:
public
CountingAllocator
<
T
>
{
using
propagate_on_container_copy_assignment
=
std
::
true_type
;
using
propagate_on_container_move_assignment
=
std
::
true_type
;
using
propagate_on_container_swap
=
std
::
true_type
;
using
Base
=
CountingAllocator
<
T
>
;
using
Base
::
Base
;
template
<
typename
U
>
explicit
PropagatingCountingAlloc
(
const
PropagatingCountingAlloc
<
U
>
&
other
)
:
Base
(
other
.
bytes_used_
)
{}
template
<
typename
U
>
struct
rebind
{
using
other
=
PropagatingCountingAlloc
<
U
>
;
};
};
template
<
typename
T
>
void
BtreeAllocatorTest
()
{
using
value_type
=
typename
T
::
value_type
;
int64_t
bytes1
=
0
,
bytes2
=
0
;
PropagatingCountingAlloc
<
T
>
allocator1
(
&
bytes1
);
PropagatingCountingAlloc
<
T
>
allocator2
(
&
bytes2
);
Generator
<
value_type
>
generator
(
1000
);
// Test that we allocate properly aligned memory. If we don't, then Layout
// will assert fail.
auto
unused1
=
allocator1
.
allocate
(
1
);
auto
unused2
=
allocator2
.
allocate
(
1
);
// Test copy assignment
{
T
b1
(
typename
T
::
key_compare
(),
allocator1
);
T
b2
(
typename
T
::
key_compare
(),
allocator2
);
int64_t
original_bytes1
=
bytes1
;
b1
.
insert
(
generator
(
0
));
EXPECT_GT
(
bytes1
,
original_bytes1
);
// This should propagate the allocator.
b1
=
b2
;
EXPECT_EQ
(
b1
.
size
(),
0
);
EXPECT_EQ
(
b2
.
size
(),
0
);
EXPECT_EQ
(
bytes1
,
original_bytes1
);
for
(
int
i
=
1
;
i
<
1000
;
i
++
)
{
b1
.
insert
(
generator
(
i
));
}
// We should have allocated out of allocator2.
EXPECT_GT
(
bytes2
,
bytes1
);
}
// Test move assignment
{
T
b1
(
typename
T
::
key_compare
(),
allocator1
);
T
b2
(
typename
T
::
key_compare
(),
allocator2
);
int64_t
original_bytes1
=
bytes1
;
b1
.
insert
(
generator
(
0
));
EXPECT_GT
(
bytes1
,
original_bytes1
);
// This should propagate the allocator.
b1
=
std
::
move
(
b2
);
EXPECT_EQ
(
b1
.
size
(),
0
);
EXPECT_EQ
(
bytes1
,
original_bytes1
);
for
(
int
i
=
1
;
i
<
1000
;
i
++
)
{
b1
.
insert
(
generator
(
i
));
}
// We should have allocated out of allocator2.
EXPECT_GT
(
bytes2
,
bytes1
);
}
// Test swap
{
T
b1
(
typename
T
::
key_compare
(),
allocator1
);
T
b2
(
typename
T
::
key_compare
(),
allocator2
);
int64_t
original_bytes1
=
bytes1
;
b1
.
insert
(
generator
(
0
));
EXPECT_GT
(
bytes1
,
original_bytes1
);
// This should swap the allocators.
swap
(
b1
,
b2
);
EXPECT_EQ
(
b1
.
size
(),
0
);
EXPECT_EQ
(
b2
.
size
(),
1
);
EXPECT_GT
(
bytes1
,
original_bytes1
);
for
(
int
i
=
1
;
i
<
1000
;
i
++
)
{
b1
.
insert
(
generator
(
i
));
}
// We should have allocated out of allocator2.
EXPECT_GT
(
bytes2
,
bytes1
);
}
allocator1
.
deallocate
(
unused1
,
1
);
allocator2
.
deallocate
(
unused2
,
1
);
}
template
<
typename
T
>
void
BtreeMapTest
()
{
using
value_type
=
typename
T
::
value_type
;
using
mapped_type
=
typename
T
::
mapped_type
;
mapped_type
m
=
Generator
<
mapped_type
>
(
0
)(
0
);
(
void
)
m
;
T
b
;
// Verify we can insert using operator[].
for
(
int
i
=
0
;
i
<
1000
;
i
++
)
{
value_type
v
=
Generator
<
value_type
>
(
1000
)(
i
);
b
[
v
.
first
]
=
v
.
second
;
}
EXPECT_EQ
(
b
.
size
(),
1000
);
// Test whether we can use the "->" operator on iterators and
// reverse_iterators. This stresses the btree_map_params::pair_pointer
// mechanism.
EXPECT_EQ
(
b
.
begin
()
->
first
,
Generator
<
value_type
>
(
1000
)(
0
).
first
);
EXPECT_EQ
(
b
.
begin
()
->
second
,
Generator
<
value_type
>
(
1000
)(
0
).
second
);
EXPECT_EQ
(
b
.
rbegin
()
->
first
,
Generator
<
value_type
>
(
1000
)(
999
).
first
);
EXPECT_EQ
(
b
.
rbegin
()
->
second
,
Generator
<
value_type
>
(
1000
)(
999
).
second
);
}
template
<
typename
T
>
void
BtreeMultiMapTest
()
{
using
mapped_type
=
typename
T
::
mapped_type
;
mapped_type
m
=
Generator
<
mapped_type
>
(
0
)(
0
);
(
void
)
m
;
}
template
<
typename
K
,
int
N
=
256
>
void
SetTest
()
{
EXPECT_EQ
(
sizeof
(
phmap
::
btree_set
<
K
>
),
2
*
sizeof
(
void
*
)
+
sizeof
(
typename
phmap
::
btree_set
<
K
>::
size_type
));
using
BtreeSet
=
phmap
::
btree_set
<
K
>
;
using
CountingBtreeSet
=
phmap
::
btree_set
<
K
,
std
::
less
<
K
>
,
PropagatingCountingAlloc
<
K
>>
;
BtreeTest
<
BtreeSet
,
std
::
set
<
K
>>
();
BtreeAllocatorTest
<
CountingBtreeSet
>
();
}
template
<
typename
K
,
int
N
=
256
>
void
MapTest
()
{
EXPECT_EQ
(
sizeof
(
phmap
::
btree_map
<
K
,
K
>
),
2
*
sizeof
(
void
*
)
+
sizeof
(
typename
phmap
::
btree_map
<
K
,
K
>::
size_type
));
using
BtreeMap
=
phmap
::
btree_map
<
K
,
K
>
;
using
CountingBtreeMap
=
phmap
::
btree_map
<
K
,
K
,
std
::
less
<
K
>
,
PropagatingCountingAlloc
<
std
::
pair
<
const
K
,
K
>>>
;
BtreeTest
<
BtreeMap
,
std
::
map
<
K
,
K
>>
();
BtreeAllocatorTest
<
CountingBtreeMap
>
();
BtreeMapTest
<
BtreeMap
>
();
}
TEST
(
Btree
,
set_int32
)
{
SetTest
<
int32_t
>
();
}
TEST
(
Btree
,
set_int64
)
{
SetTest
<
int64_t
>
();
}
TEST
(
Btree
,
set_string
)
{
SetTest
<
std
::
string
>
();
}
TEST
(
Btree
,
set_pair
)
{
SetTest
<
std
::
pair
<
int
,
int
>>
();
}
TEST
(
Btree
,
map_int32
)
{
MapTest
<
int32_t
>
();
}
TEST
(
Btree
,
map_int64
)
{
MapTest
<
int64_t
>
();
}
TEST
(
Btree
,
map_string
)
{
MapTest
<
std
::
string
>
();
}
TEST
(
Btree
,
map_pair
)
{
MapTest
<
std
::
pair
<
int
,
int
>>
();
}
template
<
typename
K
,
int
N
=
256
>
void
MultiSetTest
()
{
EXPECT_EQ
(
sizeof
(
phmap
::
btree_multiset
<
K
>
),
2
*
sizeof
(
void
*
)
+
sizeof
(
typename
phmap
::
btree_multiset
<
K
>::
size_type
));
using
BtreeMSet
=
phmap
::
btree_multiset
<
K
>
;
using
CountingBtreeMSet
=
phmap
::
btree_multiset
<
K
,
std
::
less
<
K
>
,
PropagatingCountingAlloc
<
K
>>
;
BtreeMultiTest
<
BtreeMSet
,
std
::
multiset
<
K
>>
();
BtreeAllocatorTest
<
CountingBtreeMSet
>
();
}
template
<
typename
K
,
int
N
=
256
>
void
MultiMapTest
()
{
EXPECT_EQ
(
sizeof
(
phmap
::
btree_multimap
<
K
,
K
>
),
2
*
sizeof
(
void
*
)
+
sizeof
(
typename
phmap
::
btree_multimap
<
K
,
K
>::
size_type
));
using
BtreeMMap
=
phmap
::
btree_multimap
<
K
,
K
>
;
using
CountingBtreeMMap
=
phmap
::
btree_multimap
<
K
,
K
,
std
::
less
<
K
>
,
PropagatingCountingAlloc
<
std
::
pair
<
const
K
,
K
>>>
;
BtreeMultiTest
<
BtreeMMap
,
std
::
multimap
<
K
,
K
>>
();
BtreeMultiMapTest
<
BtreeMMap
>
();
BtreeAllocatorTest
<
CountingBtreeMMap
>
();
}
TEST
(
Btree
,
multiset_int32
)
{
MultiSetTest
<
int32_t
>
();
}
TEST
(
Btree
,
multiset_int64
)
{
MultiSetTest
<
int64_t
>
();
}
TEST
(
Btree
,
multiset_string
)
{
MultiSetTest
<
std
::
string
>
();
}
TEST
(
Btree
,
multiset_pair
)
{
MultiSetTest
<
std
::
pair
<
int
,
int
>>
();
}
TEST
(
Btree
,
multimap_int32
)
{
MultiMapTest
<
int32_t
>
();
}
TEST
(
Btree
,
multimap_int64
)
{
MultiMapTest
<
int64_t
>
();
}
TEST
(
Btree
,
multimap_string
)
{
MultiMapTest
<
std
::
string
>
();
}
TEST
(
Btree
,
multimap_pair
)
{
MultiMapTest
<
std
::
pair
<
int
,
int
>>
();
}
struct
CompareIntToString
{
bool
operator
()(
const
std
::
string
&
a
,
const
std
::
string
&
b
)
const
{
return
a
<
b
;
}
bool
operator
()(
const
std
::
string
&
a
,
int
b
)
const
{
return
a
<
std
::
to_string
(
b
);
}
bool
operator
()(
int
a
,
const
std
::
string
&
b
)
const
{
return
std
::
to_string
(
a
)
<
b
;
}
using
is_transparent
=
void
;
};
struct
NonTransparentCompare
{
template
<
typename
T
,
typename
U
>
bool
operator
()(
const
T
&
t
,
const
U
&
u
)
const
{
// Treating all comparators as transparent can cause inefficiencies (see
// N3657 C++ proposal). Test that for comparators without 'is_transparent'
// alias (like this one), we do not attempt heterogeneous lookup.
EXPECT_TRUE
((
std
::
is_same
<
T
,
U
>
()));
return
t
<
u
;
}
};
template
<
typename
T
>
bool
CanEraseWithEmptyBrace
(
T
t
,
decltype
(
t
.
erase
({}))
*
)
{
return
true
;
}
template
<
typename
T
>
bool
CanEraseWithEmptyBrace
(
T
,
...)
{
return
false
;
}
template
<
typename
T
>
void
TestHeterogeneous
(
T
table
)
{
auto
lb
=
table
.
lower_bound
(
"3"
);
EXPECT_EQ
(
lb
,
table
.
lower_bound
(
3
));
EXPECT_NE
(
lb
,
table
.
lower_bound
(
4
));
EXPECT_EQ
(
lb
,
table
.
lower_bound
({
"3"
}));
EXPECT_NE
(
lb
,
table
.
lower_bound
({}));
auto
ub
=
table
.
upper_bound
(
"3"
);
EXPECT_EQ
(
ub
,
table
.
upper_bound
(
3
));
EXPECT_NE
(
ub
,
table
.
upper_bound
(
5
));
EXPECT_EQ
(
ub
,
table
.
upper_bound
({
"3"
}));
EXPECT_NE
(
ub
,
table
.
upper_bound
({}));
auto
er
=
table
.
equal_range
(
"3"
);
EXPECT_EQ
(
er
,
table
.
equal_range
(
3
));
EXPECT_NE
(
er
,
table
.
equal_range
(
4
));
EXPECT_EQ
(
er
,
table
.
equal_range
({
"3"
}));
EXPECT_NE
(
er
,
table
.
equal_range
({}));
auto
it
=
table
.
find
(
"3"
);
EXPECT_EQ
(
it
,
table
.
find
(
3
));
EXPECT_NE
(
it
,
table
.
find
(
4
));
EXPECT_EQ
(
it
,
table
.
find
({
"3"
}));
EXPECT_NE
(
it
,
table
.
find
({}));
EXPECT_TRUE
(
table
.
contains
(
3
));
EXPECT_FALSE
(
table
.
contains
(
4
));
EXPECT_TRUE
(
table
.
count
({
"3"
}));
EXPECT_FALSE
(
table
.
contains
({}));
EXPECT_EQ
(
1
,
table
.
count
(
3
));
EXPECT_EQ
(
0
,
table
.
count
(
4
));
EXPECT_EQ
(
1
,
table
.
count
({
"3"
}));
EXPECT_EQ
(
0
,
table
.
count
({}));
auto
copy
=
table
;
copy
.
erase
(
3
);
EXPECT_EQ
(
table
.
size
()
-
1
,
copy
.
size
());
copy
.
erase
(
4
);
EXPECT_EQ
(
table
.
size
()
-
1
,
copy
.
size
());
copy
.
erase
({
"5"
});
EXPECT_EQ
(
table
.
size
()
-
2
,
copy
.
size
());
EXPECT_FALSE
(
CanEraseWithEmptyBrace
(
table
,
nullptr
));
// Also run it with const T&.
if
(
std
::
is_class
<
T
>
())
TestHeterogeneous
<
const
T
&>
(
table
);
}
TEST
(
Btree
,
HeterogeneousLookup
)
{
TestHeterogeneous
(
btree_set
<
std
::
string
,
CompareIntToString
>
{
"1"
,
"3"
,
"5"
});
TestHeterogeneous
(
btree_map
<
std
::
string
,
int
,
CompareIntToString
>
{
{
"1"
,
1
},
{
"3"
,
3
},
{
"5"
,
5
}});
TestHeterogeneous
(
btree_multiset
<
std
::
string
,
CompareIntToString
>
{
"1"
,
"3"
,
"5"
});
TestHeterogeneous
(
btree_multimap
<
std
::
string
,
int
,
CompareIntToString
>
{
{
"1"
,
1
},
{
"3"
,
3
},
{
"5"
,
5
}});
// Only maps have .at()
btree_map
<
std
::
string
,
int
,
CompareIntToString
>
map
{
{
""
,
-
1
},
{
"1"
,
1
},
{
"3"
,
3
},
{
"5"
,
5
}};
EXPECT_EQ
(
1
,
map
.
at
(
1
));
EXPECT_EQ
(
3
,
map
.
at
({
"3"
}));
EXPECT_EQ
(
-
1
,
map
.
at
({}));
const
auto
&
cmap
=
map
;
EXPECT_EQ
(
1
,
cmap
.
at
(
1
));
EXPECT_EQ
(
3
,
cmap
.
at
({
"3"
}));
EXPECT_EQ
(
-
1
,
cmap
.
at
({}));
}
TEST
(
Btree
,
NoHeterogeneousLookupWithoutAlias
)
{
using
StringSet
=
phmap
::
btree_set
<
std
::
string
,
NonTransparentCompare
>
;
StringSet
s
;
ASSERT_TRUE
(
s
.
insert
(
"hello"
).
second
);
ASSERT_TRUE
(
s
.
insert
(
"world"
).
second
);
EXPECT_TRUE
(
s
.
end
()
==
s
.
find
(
"blah"
));
EXPECT_TRUE
(
s
.
begin
()
==
s
.
lower_bound
(
"hello"
));
EXPECT_EQ
(
1
,
s
.
count
(
"world"
));
EXPECT_TRUE
(
s
.
contains
(
"hello"
));
EXPECT_TRUE
(
s
.
contains
(
"world"
));
EXPECT_FALSE
(
s
.
contains
(
"blah"
));
using
StringMultiSet
=
phmap
::
btree_multiset
<
std
::
string
,
NonTransparentCompare
>
;
StringMultiSet
ms
;
ms
.
insert
(
"hello"
);
ms
.
insert
(
"world"
);
ms
.
insert
(
"world"
);
EXPECT_TRUE
(
ms
.
end
()
==
ms
.
find
(
"blah"
));
EXPECT_TRUE
(
ms
.
begin
()
==
ms
.
lower_bound
(
"hello"
));
EXPECT_EQ
(
2
,
ms
.
count
(
"world"
));
EXPECT_TRUE
(
ms
.
contains
(
"hello"
));
EXPECT_TRUE
(
ms
.
contains
(
"world"
));
EXPECT_FALSE
(
ms
.
contains
(
"blah"
));
}
TEST
(
Btree
,
DefaultTransparent
)
{
{
// `int` does not have a default transparent comparator.
// The input value is converted to key_type.
btree_set
<
int
>
s
=
{
1
};
double
d
=
1.1
;
EXPECT_EQ
(
s
.
begin
(),
s
.
find
(
d
));
EXPECT_TRUE
(
s
.
contains
(
d
));
}
{
// `std::string` has heterogeneous support.
btree_set
<
std
::
string
>
s
=
{
"A"
};
#if PHMAP_HAVE_STD_STRING_VIEW
EXPECT_EQ
(
s
.
begin
(),
s
.
find
(
std
::
string_view
(
"A"
)));
EXPECT_TRUE
(
s
.
contains
(
std
::
string_view
(
"A"
)));
#endif
}
}
class
StringLike
{
public:
StringLike
()
=
default
;
StringLike
(
const
char
*
s
)
:
s_
(
s
)
{
// NOLINT
++
constructor_calls_
;
}
bool
operator
<
(
const
StringLike
&
a
)
const
{
return
s_
<
a
.
s_
;
}
static
void
clear_constructor_call_count
()
{
constructor_calls_
=
0
;
}
static
int
constructor_calls
()
{
return
constructor_calls_
;
}
private:
static
int
constructor_calls_
;
std
::
string
s_
;
};
int
StringLike
::
constructor_calls_
=
0
;
TEST
(
Btree
,
HeterogeneousLookupDoesntDegradePerformance
)
{
using
StringSet
=
phmap
::
btree_set
<
StringLike
>
;
StringSet
s
;
for
(
int
i
=
0
;
i
<
100
;
++
i
)
{
ASSERT_TRUE
(
s
.
insert
(
std
::
to_string
(
i
).
c_str
()).
second
);
}
StringLike
::
clear_constructor_call_count
();
s
.
find
(
"50"
);
ASSERT_EQ
(
1
,
StringLike
::
constructor_calls
());
StringLike
::
clear_constructor_call_count
();
s
.
contains
(
"50"
);
ASSERT_EQ
(
1
,
StringLike
::
constructor_calls
());
StringLike
::
clear_constructor_call_count
();
s
.
count
(
"50"
);
ASSERT_EQ
(
1
,
StringLike
::
constructor_calls
());
StringLike
::
clear_constructor_call_count
();
s
.
lower_bound
(
"50"
);
ASSERT_EQ
(
1
,
StringLike
::
constructor_calls
());
StringLike
::
clear_constructor_call_count
();
s
.
upper_bound
(
"50"
);
ASSERT_EQ
(
1
,
StringLike
::
constructor_calls
());
StringLike
::
clear_constructor_call_count
();
s
.
equal_range
(
"50"
);
ASSERT_EQ
(
1
,
StringLike
::
constructor_calls
());
StringLike
::
clear_constructor_call_count
();
s
.
erase
(
"50"
);
ASSERT_EQ
(
1
,
StringLike
::
constructor_calls
());
}
// Verify that swapping btrees swaps the key comparison functors and that we can
// use non-default constructible comparators.
struct
SubstringLess
{
SubstringLess
()
=
delete
;
explicit
SubstringLess
(
int
length
)
:
n
(
length
)
{}
bool
operator
()(
const
std
::
string
&
a
,
const
std
::
string
&
b
)
const
{
#if PHMAP_HAVE_STD_STRING_VIEW
return
std
::
string_view
(
a
).
substr
(
0
,
n
)
<
std
::
string_view
(
b
).
substr
(
0
,
n
);
#else
return
a
.
substr
(
0
,
n
)
<
b
.
substr
(
0
,
n
);
#endif
}
int
n
;
};
TEST
(
Btree
,
SwapKeyCompare
)
{
using
SubstringSet
=
phmap
::
btree_set
<
std
::
string
,
SubstringLess
>
;
SubstringSet
s1
(
SubstringLess
(
1
),
SubstringSet
::
allocator_type
());
SubstringSet
s2
(
SubstringLess
(
2
),
SubstringSet
::
allocator_type
());
ASSERT_TRUE
(
s1
.
insert
(
"a"
).
second
);
ASSERT_FALSE
(
s1
.
insert
(
"aa"
).
second
);
ASSERT_TRUE
(
s2
.
insert
(
"a"
).
second
);
ASSERT_TRUE
(
s2
.
insert
(
"aa"
).
second
);
ASSERT_FALSE
(
s2
.
insert
(
"aaa"
).
second
);
swap
(
s1
,
s2
);
ASSERT_TRUE
(
s1
.
insert
(
"b"
).
second
);
ASSERT_TRUE
(
s1
.
insert
(
"bb"
).
second
);
ASSERT_FALSE
(
s1
.
insert
(
"bbb"
).
second
);
ASSERT_TRUE
(
s2
.
insert
(
"b"
).
second
);
ASSERT_FALSE
(
s2
.
insert
(
"bb"
).
second
);
}
TEST
(
Btree
,
UpperBoundRegression
)
{
// Regress a bug where upper_bound would default-construct a new key_compare
// instead of copying the existing one.
using
SubstringSet
=
phmap
::
btree_set
<
std
::
string
,
SubstringLess
>
;
SubstringSet
my_set
(
SubstringLess
(
3
));
my_set
.
insert
(
"aab"
);
my_set
.
insert
(
"abb"
);
// We call upper_bound("aaa"). If this correctly uses the length 3
// comparator, aaa < aab < abb, so we should get aab as the result.
// If it instead uses the default-constructed length 2 comparator,
// aa == aa < ab, so we'll get abb as our result.
SubstringSet
::
iterator
it
=
my_set
.
upper_bound
(
"aaa"
);
ASSERT_TRUE
(
it
!=
my_set
.
end
());
EXPECT_EQ
(
"aab"
,
*
it
);
}
TEST
(
Btree
,
Comparison
)
{
const
int
kSetSize
=
1201
;
phmap
::
btree_set
<
int64_t
>
my_set
;
for
(
int
i
=
0
;
i
<
kSetSize
;
++
i
)
{
my_set
.
insert
(
i
);
}
phmap
::
btree_set
<
int64_t
>
my_set_copy
(
my_set
);
EXPECT_TRUE
(
my_set_copy
==
my_set
);
EXPECT_TRUE
(
my_set
==
my_set_copy
);
EXPECT_FALSE
(
my_set_copy
!=
my_set
);
EXPECT_FALSE
(
my_set
!=
my_set_copy
);
my_set
.
insert
(
kSetSize
);
EXPECT_FALSE
(
my_set_copy
==
my_set
);
EXPECT_FALSE
(
my_set
==
my_set_copy
);
EXPECT_TRUE
(
my_set_copy
!=
my_set
);
EXPECT_TRUE
(
my_set
!=
my_set_copy
);
my_set
.
erase
(
kSetSize
-
1
);
EXPECT_FALSE
(
my_set_copy
==
my_set
);
EXPECT_FALSE
(
my_set
==
my_set_copy
);
EXPECT_TRUE
(
my_set_copy
!=
my_set
);
EXPECT_TRUE
(
my_set
!=
my_set_copy
);
phmap
::
btree_map
<
std
::
string
,
int64_t
>
my_map
;
for
(
int
i
=
0
;
i
<
kSetSize
;
++
i
)
{
my_map
[
std
::
string
(
i
,
'a'
)]
=
i
;
}
phmap
::
btree_map
<
std
::
string
,
int64_t
>
my_map_copy
(
my_map
);
EXPECT_TRUE
(
my_map_copy
==
my_map
);
EXPECT_TRUE
(
my_map
==
my_map_copy
);
EXPECT_FALSE
(
my_map_copy
!=
my_map
);
EXPECT_FALSE
(
my_map
!=
my_map_copy
);
++
my_map_copy
[
std
::
string
(
7
,
'a'
)];
EXPECT_FALSE
(
my_map_copy
==
my_map
);
EXPECT_FALSE
(
my_map
==
my_map_copy
);
EXPECT_TRUE
(
my_map_copy
!=
my_map
);
EXPECT_TRUE
(
my_map
!=
my_map_copy
);
my_map_copy
=
my_map
;
my_map
[
"hello"
]
=
kSetSize
;
EXPECT_FALSE
(
my_map_copy
==
my_map
);
EXPECT_FALSE
(
my_map
==
my_map_copy
);
EXPECT_TRUE
(
my_map_copy
!=
my_map
);
EXPECT_TRUE
(
my_map
!=
my_map_copy
);
my_map
.
erase
(
std
::
string
(
kSetSize
-
1
,
'a'
));
EXPECT_FALSE
(
my_map_copy
==
my_map
);
EXPECT_FALSE
(
my_map
==
my_map_copy
);
EXPECT_TRUE
(
my_map_copy
!=
my_map
);
EXPECT_TRUE
(
my_map
!=
my_map_copy
);
}
TEST
(
Btree
,
RangeCtorSanity
)
{
std
::
vector
<
int
>
ivec
;
ivec
.
push_back
(
1
);
std
::
map
<
int
,
int
>
imap
;
imap
.
insert
(
std
::
make_pair
(
1
,
2
));
phmap
::
btree_multiset
<
int
>
tmset
(
ivec
.
begin
(),
ivec
.
end
());
phmap
::
btree_multimap
<
int
,
int
>
tmmap
(
imap
.
begin
(),
imap
.
end
());
phmap
::
btree_set
<
int
>
tset
(
ivec
.
begin
(),
ivec
.
end
());
phmap
::
btree_map
<
int
,
int
>
tmap
(
imap
.
begin
(),
imap
.
end
());
EXPECT_EQ
(
1
,
tmset
.
size
());
EXPECT_EQ
(
1
,
tmmap
.
size
());
EXPECT_EQ
(
1
,
tset
.
size
());
EXPECT_EQ
(
1
,
tmap
.
size
());
}
TEST
(
Btree
,
BtreeMapCanHoldMoveOnlyTypes
)
{
phmap
::
btree_map
<
std
::
string
,
std
::
unique_ptr
<
std
::
string
>>
m
;
std
::
unique_ptr
<
std
::
string
>
&
v
=
m
[
"A"
];
EXPECT_TRUE
(
v
==
nullptr
);
v
.
reset
(
new
std
::
string
(
"X"
));
auto
iter
=
m
.
find
(
"A"
);
EXPECT_EQ
(
"X"
,
*
iter
->
second
);
}
TEST
(
Btree
,
InitializerListConstructor
)
{
phmap
::
btree_set
<
std
::
string
>
set
({
"a"
,
"b"
});
EXPECT_EQ
(
set
.
count
(
"a"
),
1
);
EXPECT_EQ
(
set
.
count
(
"b"
),
1
);
phmap
::
btree_multiset
<
int
>
mset
({
1
,
1
,
4
});
EXPECT_EQ
(
mset
.
count
(
1
),
2
);
EXPECT_EQ
(
mset
.
count
(
4
),
1
);
phmap
::
btree_map
<
int
,
int
>
map
({{
1
,
5
},
{
2
,
10
}});
EXPECT_EQ
(
map
[
1
],
5
);
EXPECT_EQ
(
map
[
2
],
10
);
phmap
::
btree_multimap
<
int
,
int
>
mmap
({{
1
,
5
},
{
1
,
10
}});
auto
range
=
mmap
.
equal_range
(
1
);
auto
it
=
range
.
first
;
ASSERT_NE
(
it
,
range
.
second
);
EXPECT_EQ
(
it
->
second
,
5
);
ASSERT_NE
(
++
it
,
range
.
second
);
EXPECT_EQ
(
it
->
second
,
10
);
EXPECT_EQ
(
++
it
,
range
.
second
);
}
TEST
(
Btree
,
InitializerListInsert
)
{
phmap
::
btree_set
<
std
::
string
>
set
;
set
.
insert
({
"a"
,
"b"
});
EXPECT_EQ
(
set
.
count
(
"a"
),
1
);
EXPECT_EQ
(
set
.
count
(
"b"
),
1
);
phmap
::
btree_multiset
<
int
>
mset
;
mset
.
insert
({
1
,
1
,
4
});
EXPECT_EQ
(
mset
.
count
(
1
),
2
);
EXPECT_EQ
(
mset
.
count
(
4
),
1
);
phmap
::
btree_map
<
int
,
int
>
map
;
map
.
insert
({{
1
,
5
},
{
2
,
10
}});
// Test that inserting one element using an initializer list also works.
map
.
insert
({
3
,
15
});
EXPECT_EQ
(
map
[
1
],
5
);
EXPECT_EQ
(
map
[
2
],
10
);
EXPECT_EQ
(
map
[
3
],
15
);
phmap
::
btree_multimap
<
int
,
int
>
mmap
;
mmap
.
insert
({{
1
,
5
},
{
1
,
10
}});
auto
range
=
mmap
.
equal_range
(
1
);
auto
it
=
range
.
first
;
ASSERT_NE
(
it
,
range
.
second
);
EXPECT_EQ
(
it
->
second
,
5
);
ASSERT_NE
(
++
it
,
range
.
second
);
EXPECT_EQ
(
it
->
second
,
10
);
EXPECT_EQ
(
++
it
,
range
.
second
);
}
template
<
typename
Compare
,
typename
K
>
void
AssertKeyCompareToAdapted
()
{
using
Adapted
=
typename
key_compare_to_adapter
<
Compare
>::
type
;
static_assert
(
!
std
::
is_same
<
Adapted
,
Compare
>::
value
,
"key_compare_to_adapter should have adapted this comparator."
);
static_assert
(
std
::
is_same
<
phmap
::
weak_ordering
,
phmap
::
invoke_result_t
<
Adapted
,
const
K
&
,
const
K
&>>::
value
,
"Adapted comparator should be a key-compare-to comparator."
);
}
template
<
typename
Compare
,
typename
K
>
void
AssertKeyCompareToNotAdapted
()
{
using
Unadapted
=
typename
key_compare_to_adapter
<
Compare
>::
type
;
static_assert
(
std
::
is_same
<
Unadapted
,
Compare
>::
value
,
"key_compare_to_adapter shouldn't have adapted this comparator."
);
static_assert
(
std
::
is_same
<
bool
,
phmap
::
invoke_result_t
<
Unadapted
,
const
K
&
,
const
K
&>>::
value
,
"Un-adapted comparator should return bool."
);
}
TEST
(
Btree
,
KeyCompareToAdapter
)
{
AssertKeyCompareToAdapted
<
std
::
less
<
std
::
string
>
,
std
::
string
>
();
AssertKeyCompareToAdapted
<
std
::
greater
<
std
::
string
>
,
std
::
string
>
();
#if PHMAP_HAVE_STD_STRING_VIEW
AssertKeyCompareToAdapted
<
std
::
less
<
std
::
string_view
>
,
std
::
string_view
>
();
AssertKeyCompareToAdapted
<
std
::
greater
<
std
::
string_view
>
,
std
::
string_view
>
();
#endif
AssertKeyCompareToNotAdapted
<
std
::
less
<
int
>
,
int
>
();
AssertKeyCompareToNotAdapted
<
std
::
greater
<
int
>
,
int
>
();
}
TEST
(
Btree
,
RValueInsert
)
{
InstanceTracker
tracker
;
phmap
::
btree_set
<
MovableOnlyInstance
>
set
;
set
.
insert
(
MovableOnlyInstance
(
1
));
set
.
insert
(
MovableOnlyInstance
(
3
));
MovableOnlyInstance
two
(
2
);
set
.
insert
(
set
.
find
(
MovableOnlyInstance
(
3
)),
std
::
move
(
two
));
auto
it
=
set
.
find
(
MovableOnlyInstance
(
2
));
ASSERT_NE
(
it
,
set
.
end
());
ASSERT_NE
(
++
it
,
set
.
end
());
EXPECT_EQ
(
it
->
value
(),
3
);
phmap
::
btree_multiset
<
MovableOnlyInstance
>
mset
;
MovableOnlyInstance
zero
(
0
);
MovableOnlyInstance
zero2
(
0
);
mset
.
insert
(
std
::
move
(
zero
));
mset
.
insert
(
mset
.
find
(
MovableOnlyInstance
(
0
)),
std
::
move
(
zero2
));
EXPECT_EQ
(
mset
.
count
(
MovableOnlyInstance
(
0
)),
2
);
phmap
::
btree_map
<
int
,
MovableOnlyInstance
>
map
;
std
::
pair
<
const
int
,
MovableOnlyInstance
>
p1
=
{
1
,
MovableOnlyInstance
(
5
)};
std
::
pair
<
const
int
,
MovableOnlyInstance
>
p2
=
{
2
,
MovableOnlyInstance
(
10
)};
std
::
pair
<
const
int
,
MovableOnlyInstance
>
p3
=
{
3
,
MovableOnlyInstance
(
15
)};
map
.
insert
(
std
::
move
(
p1
));
map
.
insert
(
std
::
move
(
p3
));
map
.
insert
(
map
.
find
(
3
),
std
::
move
(
p2
));
ASSERT_NE
(
map
.
find
(
2
),
map
.
end
());
EXPECT_EQ
(
map
.
find
(
2
)
->
second
.
value
(),
10
);
phmap
::
btree_multimap
<
int
,
MovableOnlyInstance
>
mmap
;
std
::
pair
<
const
int
,
MovableOnlyInstance
>
p4
=
{
1
,
MovableOnlyInstance
(
5
)};
std
::
pair
<
const
int
,
MovableOnlyInstance
>
p5
=
{
1
,
MovableOnlyInstance
(
10
)};
mmap
.
insert
(
std
::
move
(
p4
));
mmap
.
insert
(
mmap
.
find
(
1
),
std
::
move
(
p5
));
auto
range
=
mmap
.
equal_range
(
1
);
auto
it1
=
range
.
first
;
ASSERT_NE
(
it1
,
range
.
second
);
EXPECT_EQ
(
it1
->
second
.
value
(),
10
);
ASSERT_NE
(
++
it1
,
range
.
second
);
EXPECT_EQ
(
it1
->
second
.
value
(),
5
);
EXPECT_EQ
(
++
it1
,
range
.
second
);
EXPECT_EQ
(
tracker
.
copies
(),
0
);
EXPECT_EQ
(
tracker
.
swaps
(),
0
);
}
}
// namespace
class
BtreeNodePeer
{
public:
// Yields the size of a leaf node with a specific number of values.
template
<
typename
ValueType
>
constexpr
static
size_t
GetTargetNodeSize
(
size_t
target_values_per_node
)
{
return
btree_node
<
set_params
<
ValueType
,
std
::
less
<
ValueType
>
,
std
::
allocator
<
ValueType
>
,
/*TargetNodeSize=*/
256
,
// This parameter isn't used here.
/*Multi=*/
false
>>::
SizeWithNValues
(
target_values_per_node
);
}
// Yields the number of values in a (non-root) leaf node for this set.
template
<
typename
Set
>
constexpr
static
size_t
GetNumValuesPerNode
()
{
return
btree_node
<
typename
Set
::
params_type
>::
kNodeValues
;
}
};
namespace
{
// A btree set with a specific number of values per node.
template
<
typename
Key
,
int
TargetValuesPerNode
,
typename
Cmp
=
std
::
less
<
Key
>
>
class
SizedBtreeSet
:
public
btree_set_container
<
btree
<
set_params
<
Key
,
Cmp
,
std
::
allocator
<
Key
>
,
BtreeNodePeer
::
GetTargetNodeSize
<
Key
>
(
TargetValuesPerNode
),
/*Multi=*/
false
>>>
{
using
Base
=
typename
SizedBtreeSet
::
btree_set_container
;
public:
SizedBtreeSet
()
{}
using
Base
::
Base
;
};
template
<
typename
Set
>
void
ExpectOperationCounts
(
const
int
expected_moves
,
const
int
expected_comparisons
,
const
std
::
vector
<
int
>
&
values
,
InstanceTracker
*
tracker
,
Set
*
set
)
{
for
(
const
int
v
:
values
)
set
->
insert
(
MovableOnlyInstance
(
v
));
set
->
clear
();
EXPECT_EQ
(
tracker
->
moves
(),
expected_moves
);
EXPECT_EQ
(
tracker
->
comparisons
(),
expected_comparisons
);
EXPECT_EQ
(
tracker
->
copies
(),
0
);
EXPECT_EQ
(
tracker
->
swaps
(),
0
);
tracker
->
ResetCopiesMovesSwaps
();
}
// Note: when the values in this test change, it is expected to have an impact
// on performance.
TEST
(
Btree
,
MovesComparisonsCopiesSwapsTracking
)
{
InstanceTracker
tracker
;
// Note: this is minimum number of values per node.
SizedBtreeSet
<
MovableOnlyInstance
,
/*TargetValuesPerNode=*/
3
>
set3
;
// Note: this is the default number of values per node for a set of int32s
// (with 64-bit pointers).
SizedBtreeSet
<
MovableOnlyInstance
,
/*TargetValuesPerNode=*/
61
>
set61
;
SizedBtreeSet
<
MovableOnlyInstance
,
/*TargetValuesPerNode=*/
100
>
set100
;
// Don't depend on flags for random values because then the expectations will
// fail if the flags change.
std
::
vector
<
int
>
values
=
GenerateValuesWithSeed
<
int
>
(
10000
,
1
<<
22
,
/*seed=*/
23
);
EXPECT_EQ
(
BtreeNodePeer
::
GetNumValuesPerNode
<
decltype
(
set3
)
>
(),
3
);
EXPECT_EQ
(
BtreeNodePeer
::
GetNumValuesPerNode
<
decltype
(
set61
)
>
(),
61
);
EXPECT_EQ
(
BtreeNodePeer
::
GetNumValuesPerNode
<
decltype
(
set100
)
>
(),
100
);
PHMAP_IF_CONSTEXPR
(
sizeof
(
void
*
)
==
8
)
{
EXPECT_EQ
(
BtreeNodePeer
::
GetNumValuesPerNode
<
phmap
::
btree_set
<
int32_t
>>
(),
BtreeNodePeer
::
GetNumValuesPerNode
<
decltype
(
set61
)
>
());
}
// Test key insertion/deletion in random order.
ExpectOperationCounts
(
45281
,
132551
,
values
,
&
tracker
,
&
set3
);
ExpectOperationCounts
(
386718
,
129807
,
values
,
&
tracker
,
&
set61
);
ExpectOperationCounts
(
586761
,
130310
,
values
,
&
tracker
,
&
set100
);
// Test key insertion/deletion in sorted order.
std
::
sort
(
values
.
begin
(),
values
.
end
());
ExpectOperationCounts
(
26638
,
92134
,
values
,
&
tracker
,
&
set3
);
ExpectOperationCounts
(
20208
,
87757
,
values
,
&
tracker
,
&
set61
);
ExpectOperationCounts
(
20124
,
96583
,
values
,
&
tracker
,
&
set100
);
// Test key insertion/deletion in reverse sorted order.
std
::
reverse
(
values
.
begin
(),
values
.
end
());
ExpectOperationCounts
(
49951
,
119325
,
values
,
&
tracker
,
&
set3
);
ExpectOperationCounts
(
338813
,
118266
,
values
,
&
tracker
,
&
set61
);
ExpectOperationCounts
(
534529
,
125279
,
values
,
&
tracker
,
&
set100
);
}
struct
MovableOnlyInstanceThreeWayCompare
{
phmap
::
weak_ordering
operator
()(
const
MovableOnlyInstance
&
a
,
const
MovableOnlyInstance
&
b
)
const
{
return
a
.
compare
(
b
);
}
};
// Note: when the values in this test change, it is expected to have an impact
// on performance.
TEST
(
Btree
,
MovesComparisonsCopiesSwapsTrackingThreeWayCompare
)
{
InstanceTracker
tracker
;
// Note: this is minimum number of values per node.
SizedBtreeSet
<
MovableOnlyInstance
,
/*TargetValuesPerNode=*/
3
,
MovableOnlyInstanceThreeWayCompare
>
set3
;
// Note: this is the default number of values per node for a set of int32s
// (with 64-bit pointers).
SizedBtreeSet
<
MovableOnlyInstance
,
/*TargetValuesPerNode=*/
61
,
MovableOnlyInstanceThreeWayCompare
>
set61
;
SizedBtreeSet
<
MovableOnlyInstance
,
/*TargetValuesPerNode=*/
100
,
MovableOnlyInstanceThreeWayCompare
>
set100
;
// Don't depend on flags for random values because then the expectations will
// fail if the flags change.
std
::
vector
<
int
>
values
=
GenerateValuesWithSeed
<
int
>
(
10000
,
1
<<
22
,
/*seed=*/
23
);
EXPECT_EQ
(
BtreeNodePeer
::
GetNumValuesPerNode
<
decltype
(
set3
)
>
(),
3
);
EXPECT_EQ
(
BtreeNodePeer
::
GetNumValuesPerNode
<
decltype
(
set61
)
>
(),
61
);
EXPECT_EQ
(
BtreeNodePeer
::
GetNumValuesPerNode
<
decltype
(
set100
)
>
(),
100
);
PHMAP_IF_CONSTEXPR
(
sizeof
(
void
*
)
==
8
)
{
EXPECT_EQ
(
BtreeNodePeer
::
GetNumValuesPerNode
<
phmap
::
btree_set
<
int32_t
>>
(),
BtreeNodePeer
::
GetNumValuesPerNode
<
decltype
(
set61
)
>
());
}
// Test key insertion/deletion in random order.
ExpectOperationCounts
(
45281
,
122560
,
values
,
&
tracker
,
&
set3
);
ExpectOperationCounts
(
386718
,
119816
,
values
,
&
tracker
,
&
set61
);
ExpectOperationCounts
(
586761
,
120319
,
values
,
&
tracker
,
&
set100
);
// Test key insertion/deletion in sorted order.
std
::
sort
(
values
.
begin
(),
values
.
end
());
ExpectOperationCounts
(
26638
,
92134
,
values
,
&
tracker
,
&
set3
);
ExpectOperationCounts
(
20208
,
87757
,
values
,
&
tracker
,
&
set61
);
ExpectOperationCounts
(
20124
,
96583
,
values
,
&
tracker
,
&
set100
);
// Test key insertion/deletion in reverse sorted order.
std
::
reverse
(
values
.
begin
(),
values
.
end
());
ExpectOperationCounts
(
49951
,
109326
,
values
,
&
tracker
,
&
set3
);
ExpectOperationCounts
(
338813
,
108267
,
values
,
&
tracker
,
&
set61
);
ExpectOperationCounts
(
534529
,
115280
,
values
,
&
tracker
,
&
set100
);
}
struct
NoDefaultCtor
{
int
num
;
explicit
NoDefaultCtor
(
int
i
)
:
num
(
i
)
{}
friend
bool
operator
<
(
const
NoDefaultCtor
&
a
,
const
NoDefaultCtor
&
b
)
{
return
a
.
num
<
b
.
num
;
}
};
TEST
(
Btree
,
BtreeMapCanHoldNoDefaultCtorTypes
)
{
phmap
::
btree_map
<
NoDefaultCtor
,
NoDefaultCtor
>
m
;
for
(
int
i
=
1
;
i
<=
99
;
++
i
)
{
SCOPED_TRACE
(
i
);
EXPECT_TRUE
(
m
.
emplace
(
NoDefaultCtor
(
i
),
NoDefaultCtor
(
100
-
i
)).
second
);
}
EXPECT_FALSE
(
m
.
emplace
(
NoDefaultCtor
(
78
),
NoDefaultCtor
(
0
)).
second
);
auto
iter99
=
m
.
find
(
NoDefaultCtor
(
99
));
ASSERT_NE
(
iter99
,
m
.
end
());
EXPECT_EQ
(
iter99
->
second
.
num
,
1
);
auto
iter1
=
m
.
find
(
NoDefaultCtor
(
1
));
ASSERT_NE
(
iter1
,
m
.
end
());
EXPECT_EQ
(
iter1
->
second
.
num
,
99
);
auto
iter50
=
m
.
find
(
NoDefaultCtor
(
50
));
ASSERT_NE
(
iter50
,
m
.
end
());
EXPECT_EQ
(
iter50
->
second
.
num
,
50
);
auto
iter25
=
m
.
find
(
NoDefaultCtor
(
25
));
ASSERT_NE
(
iter25
,
m
.
end
());
EXPECT_EQ
(
iter25
->
second
.
num
,
75
);
}
TEST
(
Btree
,
BtreeMultimapCanHoldNoDefaultCtorTypes
)
{
phmap
::
btree_multimap
<
NoDefaultCtor
,
NoDefaultCtor
>
m
;
for
(
int
i
=
1
;
i
<=
99
;
++
i
)
{
SCOPED_TRACE
(
i
);
m
.
emplace
(
NoDefaultCtor
(
i
),
NoDefaultCtor
(
100
-
i
));
}
auto
iter99
=
m
.
find
(
NoDefaultCtor
(
99
));
ASSERT_NE
(
iter99
,
m
.
end
());
EXPECT_EQ
(
iter99
->
second
.
num
,
1
);
auto
iter1
=
m
.
find
(
NoDefaultCtor
(
1
));
ASSERT_NE
(
iter1
,
m
.
end
());
EXPECT_EQ
(
iter1
->
second
.
num
,
99
);
auto
iter50
=
m
.
find
(
NoDefaultCtor
(
50
));
ASSERT_NE
(
iter50
,
m
.
end
());
EXPECT_EQ
(
iter50
->
second
.
num
,
50
);
auto
iter25
=
m
.
find
(
NoDefaultCtor
(
25
));
ASSERT_NE
(
iter25
,
m
.
end
());
EXPECT_EQ
(
iter25
->
second
.
num
,
75
);
}
TEST
(
Btree
,
MapAt
)
{
phmap
::
btree_map
<
int
,
int
>
map
=
{{
1
,
2
},
{
2
,
4
}};
EXPECT_EQ
(
map
.
at
(
1
),
2
);
EXPECT_EQ
(
map
.
at
(
2
),
4
);
map
.
at
(
2
)
=
8
;
const
phmap
::
btree_map
<
int
,
int
>
&
const_map
=
map
;
EXPECT_EQ
(
const_map
.
at
(
1
),
2
);
EXPECT_EQ
(
const_map
.
at
(
2
),
8
);
#ifdef PHMAP_HAVE_EXCEPTIONS
EXPECT_THROW
(
map
.
at
(
3
),
std
::
out_of_range
);
#else
EXPECT_DEATH
(
map
.
at
(
3
),
"phmap::btree_map::at"
);
#endif
}
TEST
(
Btree
,
BtreeMultisetEmplace
)
{
const
int
value_to_insert
=
123456
;
phmap
::
btree_multiset
<
int
>
s
;
auto
iter
=
s
.
emplace
(
value_to_insert
);
ASSERT_NE
(
iter
,
s
.
end
());
EXPECT_EQ
(
*
iter
,
value_to_insert
);
auto
iter2
=
s
.
emplace
(
value_to_insert
);
EXPECT_NE
(
iter2
,
iter
);
ASSERT_NE
(
iter2
,
s
.
end
());
EXPECT_EQ
(
*
iter2
,
value_to_insert
);
auto
result
=
s
.
equal_range
(
value_to_insert
);
EXPECT_EQ
(
std
::
distance
(
result
.
first
,
result
.
second
),
2
);
}
TEST
(
Btree
,
BtreeMultisetEmplaceHint
)
{
const
int
value_to_insert
=
123456
;
phmap
::
btree_multiset
<
int
>
s
;
auto
iter
=
s
.
emplace
(
value_to_insert
);
ASSERT_NE
(
iter
,
s
.
end
());
EXPECT_EQ
(
*
iter
,
value_to_insert
);
auto
emplace_iter
=
s
.
emplace_hint
(
iter
,
value_to_insert
);
EXPECT_NE
(
emplace_iter
,
iter
);
ASSERT_NE
(
emplace_iter
,
s
.
end
());
EXPECT_EQ
(
*
emplace_iter
,
value_to_insert
);
}
TEST
(
Btree
,
BtreeMultimapEmplace
)
{
const
int
key_to_insert
=
123456
;
const
char
value0
[]
=
"a"
;
phmap
::
btree_multimap
<
int
,
std
::
string
>
s
;
auto
iter
=
s
.
emplace
(
key_to_insert
,
value0
);
ASSERT_NE
(
iter
,
s
.
end
());
EXPECT_EQ
(
iter
->
first
,
key_to_insert
);
EXPECT_EQ
(
iter
->
second
,
value0
);
const
char
value1
[]
=
"b"
;
auto
iter2
=
s
.
emplace
(
key_to_insert
,
value1
);
EXPECT_NE
(
iter2
,
iter
);
ASSERT_NE
(
iter2
,
s
.
end
());
EXPECT_EQ
(
iter2
->
first
,
key_to_insert
);
EXPECT_EQ
(
iter2
->
second
,
value1
);
auto
result
=
s
.
equal_range
(
key_to_insert
);
EXPECT_EQ
(
std
::
distance
(
result
.
first
,
result
.
second
),
2
);
}
TEST
(
Btree
,
BtreeMultimapEmplaceHint
)
{
const
int
key_to_insert
=
123456
;
const
char
value0
[]
=
"a"
;
phmap
::
btree_multimap
<
int
,
std
::
string
>
s
;
auto
iter
=
s
.
emplace
(
key_to_insert
,
value0
);
ASSERT_NE
(
iter
,
s
.
end
());
EXPECT_EQ
(
iter
->
first
,
key_to_insert
);
EXPECT_EQ
(
iter
->
second
,
value0
);
const
char
value1
[]
=
"b"
;
auto
emplace_iter
=
s
.
emplace_hint
(
iter
,
key_to_insert
,
value1
);
EXPECT_NE
(
emplace_iter
,
iter
);
ASSERT_NE
(
emplace_iter
,
s
.
end
());
EXPECT_EQ
(
emplace_iter
->
first
,
key_to_insert
);
EXPECT_EQ
(
emplace_iter
->
second
,
value1
);
}
TEST
(
Btree
,
ConstIteratorAccessors
)
{
phmap
::
btree_set
<
int
>
set
;
for
(
int
i
=
0
;
i
<
100
;
++
i
)
{
set
.
insert
(
i
);
}
auto
it
=
set
.
cbegin
();
auto
r_it
=
set
.
crbegin
();
for
(
int
i
=
0
;
i
<
100
;
++
i
,
++
it
,
++
r_it
)
{
ASSERT_EQ
(
*
it
,
i
);
ASSERT_EQ
(
*
r_it
,
99
-
i
);
}
EXPECT_EQ
(
it
,
set
.
cend
());
EXPECT_EQ
(
r_it
,
set
.
crend
());
}
#if 0
TEST(Btree, StrSplitCompatible) {
const phmap::btree_set<std::string> split_set = phmap::StrSplit("a,b,c", ',');
const phmap::btree_set<std::string> expected_set = {"a", "b", "c"};
EXPECT_EQ(split_set, expected_set);
}
#endif
// We can't use EXPECT_EQ/etc. to compare phmap::weak_ordering because they
// convert literal 0 to int and phmap::weak_ordering can only be compared with
// literal 0. Defining this function allows for avoiding ClangTidy warnings.
bool
Identity
(
const
bool
b
)
{
return
b
;
}
TEST
(
Btree
,
ValueComp
)
{
phmap
::
btree_set
<
int
>
s
;
EXPECT_TRUE
(
s
.
value_comp
()(
1
,
2
));
EXPECT_FALSE
(
s
.
value_comp
()(
2
,
2
));
EXPECT_FALSE
(
s
.
value_comp
()(
2
,
1
));
phmap
::
btree_map
<
int
,
int
>
m1
;
EXPECT_TRUE
(
m1
.
value_comp
()(
std
::
make_pair
(
1
,
0
),
std
::
make_pair
(
2
,
0
)));
EXPECT_FALSE
(
m1
.
value_comp
()(
std
::
make_pair
(
2
,
0
),
std
::
make_pair
(
2
,
0
)));
EXPECT_FALSE
(
m1
.
value_comp
()(
std
::
make_pair
(
2
,
0
),
std
::
make_pair
(
1
,
0
)));
phmap
::
btree_map
<
std
::
string
,
int
>
m2
;
EXPECT_TRUE
(
Identity
(
m2
.
value_comp
()(
std
::
make_pair
(
"a"
,
0
),
std
::
make_pair
(
"b"
,
0
))
<
0
));
EXPECT_TRUE
(
Identity
(
m2
.
value_comp
()(
std
::
make_pair
(
"b"
,
0
),
std
::
make_pair
(
"b"
,
0
))
==
0
));
EXPECT_TRUE
(
Identity
(
m2
.
value_comp
()(
std
::
make_pair
(
"b"
,
0
),
std
::
make_pair
(
"a"
,
0
))
>
0
));
}
TEST
(
Btree
,
DefaultConstruction
)
{
phmap
::
btree_set
<
int
>
s
;
phmap
::
btree_map
<
int
,
int
>
m
;
phmap
::
btree_multiset
<
int
>
ms
;
phmap
::
btree_multimap
<
int
,
int
>
mm
;
EXPECT_TRUE
(
s
.
empty
());
EXPECT_TRUE
(
m
.
empty
());
EXPECT_TRUE
(
ms
.
empty
());
EXPECT_TRUE
(
mm
.
empty
());
}
#if 0
TEST(Btree, SwissTableHashable) {
static constexpr int kValues = 10000;
std::vector<int> values(kValues);
std::iota(values.begin(), values.end(), 0);
std::vector<std::pair<int, int>> map_values;
for (int v : values) map_values.emplace_back(v, -v);
using set = phmap::btree_set<int>;
EXPECT_TRUE(phmap::VerifyTypeImplementsPhmapHashCorrectly({
set{},
set{1},
set{2},
set{1, 2},
set{2, 1},
set(values.begin(), values.end()),
set(values.rbegin(), values.rend()),
}));
using mset = phmap::btree_multiset<int>;
EXPECT_TRUE(phmap::VerifyTypeImplementsPhmapHashCorrectly({
mset{},
mset{1},
mset{1, 1},
mset{2},
mset{2, 2},
mset{1, 2},
mset{1, 1, 2},
mset{1, 2, 2},
mset{1, 1, 2, 2},
mset(values.begin(), values.end()),
mset(values.rbegin(), values.rend()),
}));
using map = phmap::btree_map<int, int>;
EXPECT_TRUE(phmap::VerifyTypeImplementsPhmapHashCorrectly({
map{},
map{{1, 0}},
map{{1, 1}},
map{{2, 0}},
map{{2, 2}},
map{{1, 0}, {2, 1}},
map(map_values.begin(), map_values.end()),
map(map_values.rbegin(), map_values.rend()),
}));
using mmap = phmap::btree_multimap<int, int>;
EXPECT_TRUE(phmap::VerifyTypeImplementsPhmapHashCorrectly({
mmap{},
mmap{{1, 0}},
mmap{{1, 1}},
mmap{{1, 0}, {1, 1}},
mmap{{1, 1}, {1, 0}},
mmap{{2, 0}},
mmap{{2, 2}},
mmap{{1, 0}, {2, 1}},
mmap(map_values.begin(), map_values.end()),
mmap(map_values.rbegin(), map_values.rend()),
}));
}
#endif
TEST
(
Btree
,
ComparableSet
)
{
phmap
::
btree_set
<
int
>
s1
=
{
1
,
2
};
phmap
::
btree_set
<
int
>
s2
=
{
2
,
3
};
EXPECT_LT
(
s1
,
s2
);
EXPECT_LE
(
s1
,
s2
);
EXPECT_LE
(
s1
,
s1
);
EXPECT_GT
(
s2
,
s1
);
EXPECT_GE
(
s2
,
s1
);
EXPECT_GE
(
s1
,
s1
);
}
TEST
(
Btree
,
ComparableSetsDifferentLength
)
{
phmap
::
btree_set
<
int
>
s1
=
{
1
,
2
};
phmap
::
btree_set
<
int
>
s2
=
{
1
,
2
,
3
};
EXPECT_LT
(
s1
,
s2
);
EXPECT_LE
(
s1
,
s2
);
EXPECT_GT
(
s2
,
s1
);
EXPECT_GE
(
s2
,
s1
);
}
TEST
(
Btree
,
ComparableMultiset
)
{
phmap
::
btree_multiset
<
int
>
s1
=
{
1
,
2
};
phmap
::
btree_multiset
<
int
>
s2
=
{
2
,
3
};
EXPECT_LT
(
s1
,
s2
);
EXPECT_LE
(
s1
,
s2
);
EXPECT_LE
(
s1
,
s1
);
EXPECT_GT
(
s2
,
s1
);
EXPECT_GE
(
s2
,
s1
);
EXPECT_GE
(
s1
,
s1
);
}
TEST
(
Btree
,
ComparableMap
)
{
phmap
::
btree_map
<
int
,
int
>
s1
=
{{
1
,
2
}};
phmap
::
btree_map
<
int
,
int
>
s2
=
{{
2
,
3
}};
EXPECT_LT
(
s1
,
s2
);
EXPECT_LE
(
s1
,
s2
);
EXPECT_LE
(
s1
,
s1
);
EXPECT_GT
(
s2
,
s1
);
EXPECT_GE
(
s2
,
s1
);
EXPECT_GE
(
s1
,
s1
);
}
TEST
(
Btree
,
ComparableMultimap
)
{
phmap
::
btree_multimap
<
int
,
int
>
s1
=
{{
1
,
2
}};
phmap
::
btree_multimap
<
int
,
int
>
s2
=
{{
2
,
3
}};
EXPECT_LT
(
s1
,
s2
);
EXPECT_LE
(
s1
,
s2
);
EXPECT_LE
(
s1
,
s1
);
EXPECT_GT
(
s2
,
s1
);
EXPECT_GE
(
s2
,
s1
);
EXPECT_GE
(
s1
,
s1
);
}
TEST
(
Btree
,
ComparableSetWithCustomComparator
)
{
// As specified by
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf section
// [container.requirements.general].12, ordering associative containers always
// uses default '<' operator
// - even if otherwise the container uses custom functor.
phmap
::
btree_set
<
int
,
std
::
greater
<
int
>>
s1
=
{
1
,
2
};
phmap
::
btree_set
<
int
,
std
::
greater
<
int
>>
s2
=
{
2
,
3
};
EXPECT_LT
(
s1
,
s2
);
EXPECT_LE
(
s1
,
s2
);
EXPECT_LE
(
s1
,
s1
);
EXPECT_GT
(
s2
,
s1
);
EXPECT_GE
(
s2
,
s1
);
EXPECT_GE
(
s1
,
s1
);
}
TEST
(
Btree
,
EraseReturnsIterator
)
{
phmap
::
btree_set
<
int
>
set
=
{
1
,
2
,
3
,
4
,
5
};
auto
result_it
=
set
.
erase
(
set
.
begin
(),
set
.
find
(
3
));
EXPECT_EQ
(
result_it
,
set
.
find
(
3
));
result_it
=
set
.
erase
(
set
.
find
(
5
));
EXPECT_EQ
(
result_it
,
set
.
end
());
}
TEST
(
Btree
,
ExtractAndInsertNodeHandleSet
)
{
phmap
::
btree_set
<
int
>
src1
=
{
1
,
2
,
3
,
4
,
5
};
auto
nh
=
src1
.
extract
(
src1
.
find
(
3
));
EXPECT_THAT
(
src1
,
ElementsAre
(
1
,
2
,
4
,
5
));
phmap
::
btree_set
<
int
>
other
;
phmap
::
btree_set
<
int
>::
insert_return_type
res
=
other
.
insert
(
std
::
move
(
nh
));
EXPECT_THAT
(
other
,
ElementsAre
(
3
));
EXPECT_EQ
(
res
.
position
,
other
.
find
(
3
));
EXPECT_TRUE
(
res
.
inserted
);
EXPECT_TRUE
(
res
.
node
.
empty
());
phmap
::
btree_set
<
int
>
src2
=
{
3
,
4
};
nh
=
src2
.
extract
(
src2
.
find
(
3
));
EXPECT_THAT
(
src2
,
ElementsAre
(
4
));
res
=
other
.
insert
(
std
::
move
(
nh
));
EXPECT_THAT
(
other
,
ElementsAre
(
3
));
EXPECT_EQ
(
res
.
position
,
other
.
find
(
3
));
EXPECT_FALSE
(
res
.
inserted
);
ASSERT_FALSE
(
res
.
node
.
empty
());
EXPECT_EQ
(
res
.
node
.
value
(),
3
);
}
template
<
typename
Set
>
void
TestExtractWithTrackingForSet
()
{
InstanceTracker
tracker
;
{
Set
s
;
// Add enough elements to make sure we test internal nodes too.
const
size_t
kSize
=
1000
;
while
(
s
.
size
()
<
kSize
)
{
s
.
insert
(
MovableOnlyInstance
(
s
.
size
()));
}
for
(
int
i
=
0
;
i
<
kSize
;
++
i
)
{
// Extract with key
auto
nh
=
s
.
extract
(
MovableOnlyInstance
(
i
));
EXPECT_EQ
(
s
.
size
(),
kSize
-
1
);
EXPECT_EQ
(
nh
.
value
().
value
(),
i
);
// Insert with node
s
.
insert
(
std
::
move
(
nh
));
EXPECT_EQ
(
s
.
size
(),
kSize
);
// Extract with iterator
auto
it
=
s
.
find
(
MovableOnlyInstance
(
i
));
nh
=
s
.
extract
(
it
);
EXPECT_EQ
(
s
.
size
(),
kSize
-
1
);
EXPECT_EQ
(
nh
.
value
().
value
(),
i
);
// Insert with node and hint
s
.
insert
(
s
.
begin
(),
std
::
move
(
nh
));
EXPECT_EQ
(
s
.
size
(),
kSize
);
}
}
EXPECT_EQ
(
0
,
tracker
.
instances
());
}
template
<
typename
Map
>
void
TestExtractWithTrackingForMap
()
{
InstanceTracker
tracker
;
{
Map
m
;
// Add enough elements to make sure we test internal nodes too.
const
size_t
kSize
=
1000
;
while
(
m
.
size
()
<
kSize
)
{
m
.
insert
(
{
CopyableMovableInstance
(
m
.
size
()),
MovableOnlyInstance
(
m
.
size
())});
}
for
(
int
i
=
0
;
i
<
kSize
;
++
i
)
{
// Extract with key
auto
nh
=
m
.
extract
(
CopyableMovableInstance
(
i
));
EXPECT_EQ
(
m
.
size
(),
kSize
-
1
);
EXPECT_EQ
(
nh
.
key
().
value
(),
i
);
EXPECT_EQ
(
nh
.
mapped
().
value
(),
i
);
// Insert with node
m
.
insert
(
std
::
move
(
nh
));
EXPECT_EQ
(
m
.
size
(),
kSize
);
// Extract with iterator
auto
it
=
m
.
find
(
CopyableMovableInstance
(
i
));
nh
=
m
.
extract
(
it
);
EXPECT_EQ
(
m
.
size
(),
kSize
-
1
);
EXPECT_EQ
(
nh
.
key
().
value
(),
i
);
EXPECT_EQ
(
nh
.
mapped
().
value
(),
i
);
// Insert with node and hint
m
.
insert
(
m
.
begin
(),
std
::
move
(
nh
));
EXPECT_EQ
(
m
.
size
(),
kSize
);
}
}
EXPECT_EQ
(
0
,
tracker
.
instances
());
}
TEST
(
Btree
,
ExtractTracking
)
{
TestExtractWithTrackingForSet
<
phmap
::
btree_set
<
MovableOnlyInstance
>>
();
TestExtractWithTrackingForSet
<
phmap
::
btree_multiset
<
MovableOnlyInstance
>>
();
TestExtractWithTrackingForMap
<
phmap
::
btree_map
<
CopyableMovableInstance
,
MovableOnlyInstance
>>
();
TestExtractWithTrackingForMap
<
phmap
::
btree_multimap
<
CopyableMovableInstance
,
MovableOnlyInstance
>>
();
}
TEST
(
Btree
,
ExtractAndInsertNodeHandleMultiSet
)
{
phmap
::
btree_multiset
<
int
>
src1
=
{
1
,
2
,
3
,
3
,
4
,
5
};
auto
nh
=
src1
.
extract
(
src1
.
find
(
3
));
EXPECT_THAT
(
src1
,
ElementsAre
(
1
,
2
,
3
,
4
,
5
));
phmap
::
btree_multiset
<
int
>
other
;
auto
res
=
other
.
insert
(
std
::
move
(
nh
));
EXPECT_THAT
(
other
,
ElementsAre
(
3
));
EXPECT_EQ
(
res
,
other
.
find
(
3
));
phmap
::
btree_multiset
<
int
>
src2
=
{
3
,
4
};
nh
=
src2
.
extract
(
src2
.
find
(
3
));
EXPECT_THAT
(
src2
,
ElementsAre
(
4
));
res
=
other
.
insert
(
std
::
move
(
nh
));
EXPECT_THAT
(
other
,
ElementsAre
(
3
,
3
));
EXPECT_EQ
(
res
,
++
other
.
find
(
3
));
}
TEST
(
Btree
,
ExtractAndInsertNodeHandleMap
)
{
phmap
::
btree_map
<
int
,
int
>
src1
=
{{
1
,
2
},
{
3
,
4
},
{
5
,
6
}};
auto
nh
=
src1
.
extract
(
src1
.
find
(
3
));
EXPECT_THAT
(
src1
,
ElementsAre
(
Pair
(
1
,
2
),
Pair
(
5
,
6
)));
phmap
::
btree_map
<
int
,
int
>
other
;
phmap
::
btree_map
<
int
,
int
>::
insert_return_type
res
=
other
.
insert
(
std
::
move
(
nh
));
EXPECT_THAT
(
other
,
ElementsAre
(
Pair
(
3
,
4
)));
EXPECT_EQ
(
res
.
position
,
other
.
find
(
3
));
EXPECT_TRUE
(
res
.
inserted
);
EXPECT_TRUE
(
res
.
node
.
empty
());
phmap
::
btree_map
<
int
,
int
>
src2
=
{{
3
,
6
}};
nh
=
src2
.
extract
(
src2
.
find
(
3
));
EXPECT_TRUE
(
src2
.
empty
());
res
=
other
.
insert
(
std
::
move
(
nh
));
EXPECT_THAT
(
other
,
ElementsAre
(
Pair
(
3
,
4
)));
EXPECT_EQ
(
res
.
position
,
other
.
find
(
3
));
EXPECT_FALSE
(
res
.
inserted
);
ASSERT_FALSE
(
res
.
node
.
empty
());
EXPECT_EQ
(
res
.
node
.
key
(),
3
);
EXPECT_EQ
(
res
.
node
.
mapped
(),
6
);
}
TEST
(
Btree
,
ExtractAndInsertNodeHandleMultiMap
)
{
phmap
::
btree_multimap
<
int
,
int
>
src1
=
{{
1
,
2
},
{
3
,
4
},
{
5
,
6
}};
auto
nh
=
src1
.
extract
(
src1
.
find
(
3
));
EXPECT_THAT
(
src1
,
ElementsAre
(
Pair
(
1
,
2
),
Pair
(
5
,
6
)));
phmap
::
btree_multimap
<
int
,
int
>
other
;
auto
res
=
other
.
insert
(
std
::
move
(
nh
));
EXPECT_THAT
(
other
,
ElementsAre
(
Pair
(
3
,
4
)));
EXPECT_EQ
(
res
,
other
.
find
(
3
));
phmap
::
btree_multimap
<
int
,
int
>
src2
=
{{
3
,
6
}};
nh
=
src2
.
extract
(
src2
.
find
(
3
));
EXPECT_TRUE
(
src2
.
empty
());
res
=
other
.
insert
(
std
::
move
(
nh
));
EXPECT_THAT
(
other
,
ElementsAre
(
Pair
(
3
,
4
),
Pair
(
3
,
6
)));
EXPECT_EQ
(
res
,
++
other
.
begin
());
}
// For multisets, insert with hint also affects correctness because we need to
// insert immediately before the hint if possible.
struct
InsertMultiHintData
{
int
key
;
int
not_key
;
bool
operator
==
(
const
InsertMultiHintData
other
)
const
{
return
key
==
other
.
key
&&
not_key
==
other
.
not_key
;
}
};
struct
InsertMultiHintDataKeyCompare
{
using
is_transparent
=
void
;
bool
operator
()(
const
InsertMultiHintData
a
,
const
InsertMultiHintData
b
)
const
{
return
a
.
key
<
b
.
key
;
}
bool
operator
()(
const
int
a
,
const
InsertMultiHintData
b
)
const
{
return
a
<
b
.
key
;
}
bool
operator
()(
const
InsertMultiHintData
a
,
const
int
b
)
const
{
return
a
.
key
<
b
;
}
};
TEST
(
Btree
,
InsertHintNodeHandle
)
{
// For unique sets, insert with hint is just a performance optimization.
// Test that insert works correctly when the hint is right or wrong.
{
phmap
::
btree_set
<
int
>
src
=
{
1
,
2
,
3
,
4
,
5
};
auto
nh
=
src
.
extract
(
src
.
find
(
3
));
EXPECT_THAT
(
src
,
ElementsAre
(
1
,
2
,
4
,
5
));
phmap
::
btree_set
<
int
>
other
=
{
0
,
100
};
// Test a correct hint.
auto
it
=
other
.
insert
(
other
.
lower_bound
(
3
),
std
::
move
(
nh
));
EXPECT_THAT
(
other
,
ElementsAre
(
0
,
3
,
100
));
EXPECT_EQ
(
it
,
other
.
find
(
3
));
nh
=
src
.
extract
(
src
.
find
(
5
));
// Test an incorrect hint.
it
=
other
.
insert
(
other
.
end
(),
std
::
move
(
nh
));
EXPECT_THAT
(
other
,
ElementsAre
(
0
,
3
,
5
,
100
));
EXPECT_EQ
(
it
,
other
.
find
(
5
));
}
phmap
::
btree_multiset
<
InsertMultiHintData
,
InsertMultiHintDataKeyCompare
>
src
=
{{
1
,
2
},
{
3
,
4
},
{
3
,
5
}};
auto
nh
=
src
.
extract
(
src
.
lower_bound
(
3
));
EXPECT_EQ
(
nh
.
value
(),
(
InsertMultiHintData
{
3
,
4
}));
phmap
::
btree_multiset
<
InsertMultiHintData
,
InsertMultiHintDataKeyCompare
>
other
=
{{
3
,
1
},
{
3
,
2
},
{
3
,
3
}};
auto
it
=
other
.
insert
(
--
other
.
end
(),
std
::
move
(
nh
));
EXPECT_THAT
(
other
,
ElementsAre
(
InsertMultiHintData
{
3
,
1
},
InsertMultiHintData
{
3
,
2
},
InsertMultiHintData
{
3
,
4
},
InsertMultiHintData
{
3
,
3
}));
EXPECT_EQ
(
it
,
--
(
--
other
.
end
()));
nh
=
src
.
extract
(
src
.
find
(
3
));
EXPECT_EQ
(
nh
.
value
(),
(
InsertMultiHintData
{
3
,
5
}));
it
=
other
.
insert
(
other
.
begin
(),
std
::
move
(
nh
));
EXPECT_THAT
(
other
,
ElementsAre
(
InsertMultiHintData
{
3
,
5
},
InsertMultiHintData
{
3
,
1
},
InsertMultiHintData
{
3
,
2
},
InsertMultiHintData
{
3
,
4
},
InsertMultiHintData
{
3
,
3
}));
EXPECT_EQ
(
it
,
other
.
begin
());
}
struct
IntCompareToCmp
{
phmap
::
weak_ordering
operator
()(
int
a
,
int
b
)
const
{
if
(
a
<
b
)
return
phmap
::
weak_ordering
::
less
;
if
(
a
>
b
)
return
phmap
::
weak_ordering
::
greater
;
return
phmap
::
weak_ordering
::
equivalent
;
}
};
TEST
(
Btree
,
MergeIntoUniqueContainers
)
{
phmap
::
btree_set
<
int
,
IntCompareToCmp
>
src1
=
{
1
,
2
,
3
};
phmap
::
btree_multiset
<
int
>
src2
=
{
3
,
4
,
4
,
5
};
phmap
::
btree_set
<
int
>
dst
;
dst
.
merge
(
src1
);
EXPECT_TRUE
(
src1
.
empty
());
EXPECT_THAT
(
dst
,
ElementsAre
(
1
,
2
,
3
));
dst
.
merge
(
src2
);
EXPECT_THAT
(
src2
,
ElementsAre
(
3
,
4
));
EXPECT_THAT
(
dst
,
ElementsAre
(
1
,
2
,
3
,
4
,
5
));
}
TEST
(
Btree
,
MergeIntoUniqueContainersWithCompareTo
)
{
phmap
::
btree_set
<
int
,
IntCompareToCmp
>
src1
=
{
1
,
2
,
3
};
phmap
::
btree_multiset
<
int
>
src2
=
{
3
,
4
,
4
,
5
};
phmap
::
btree_set
<
int
,
IntCompareToCmp
>
dst
;
dst
.
merge
(
src1
);
EXPECT_TRUE
(
src1
.
empty
());
EXPECT_THAT
(
dst
,
ElementsAre
(
1
,
2
,
3
));
dst
.
merge
(
src2
);
EXPECT_THAT
(
src2
,
ElementsAre
(
3
,
4
));
EXPECT_THAT
(
dst
,
ElementsAre
(
1
,
2
,
3
,
4
,
5
));
}
TEST
(
Btree
,
MergeIntoMultiContainers
)
{
phmap
::
btree_set
<
int
,
IntCompareToCmp
>
src1
=
{
1
,
2
,
3
};
phmap
::
btree_multiset
<
int
>
src2
=
{
3
,
4
,
4
,
5
};
phmap
::
btree_multiset
<
int
>
dst
;
dst
.
merge
(
src1
);
EXPECT_TRUE
(
src1
.
empty
());
EXPECT_THAT
(
dst
,
ElementsAre
(
1
,
2
,
3
));
dst
.
merge
(
src2
);
EXPECT_TRUE
(
src2
.
empty
());
EXPECT_THAT
(
dst
,
ElementsAre
(
1
,
2
,
3
,
3
,
4
,
4
,
5
));
}
TEST
(
Btree
,
MergeIntoMultiContainersWithCompareTo
)
{
phmap
::
btree_set
<
int
,
IntCompareToCmp
>
src1
=
{
1
,
2
,
3
};
phmap
::
btree_multiset
<
int
>
src2
=
{
3
,
4
,
4
,
5
};
phmap
::
btree_multiset
<
int
,
IntCompareToCmp
>
dst
;
dst
.
merge
(
src1
);
EXPECT_TRUE
(
src1
.
empty
());
EXPECT_THAT
(
dst
,
ElementsAre
(
1
,
2
,
3
));
dst
.
merge
(
src2
);
EXPECT_TRUE
(
src2
.
empty
());
EXPECT_THAT
(
dst
,
ElementsAre
(
1
,
2
,
3
,
3
,
4
,
4
,
5
));
}
TEST
(
Btree
,
MergeIntoMultiMapsWithDifferentComparators
)
{
phmap
::
btree_map
<
int
,
int
,
IntCompareToCmp
>
src1
=
{{
1
,
1
},
{
2
,
2
},
{
3
,
3
}};
phmap
::
btree_multimap
<
int
,
int
,
std
::
greater
<
int
>>
src2
=
{
{
5
,
5
},
{
4
,
1
},
{
4
,
4
},
{
3
,
2
}};
phmap
::
btree_multimap
<
int
,
int
>
dst
;
dst
.
merge
(
src1
);
EXPECT_TRUE
(
src1
.
empty
());
EXPECT_THAT
(
dst
,
ElementsAre
(
Pair
(
1
,
1
),
Pair
(
2
,
2
),
Pair
(
3
,
3
)));
dst
.
merge
(
src2
);
EXPECT_TRUE
(
src2
.
empty
());
EXPECT_THAT
(
dst
,
ElementsAre
(
Pair
(
1
,
1
),
Pair
(
2
,
2
),
Pair
(
3
,
3
),
Pair
(
3
,
2
),
Pair
(
4
,
1
),
Pair
(
4
,
4
),
Pair
(
5
,
5
)));
}
struct
KeyCompareToWeakOrdering
{
template
<
typename
T
>
phmap
::
weak_ordering
operator
()(
const
T
&
a
,
const
T
&
b
)
const
{
return
a
<
b
?
phmap
::
weak_ordering
::
less
:
a
==
b
?
phmap
::
weak_ordering
::
equivalent
:
phmap
::
weak_ordering
::
greater
;
}
};
struct
KeyCompareToStrongOrdering
{
template
<
typename
T
>
phmap
::
strong_ordering
operator
()(
const
T
&
a
,
const
T
&
b
)
const
{
return
a
<
b
?
phmap
::
strong_ordering
::
less
:
a
==
b
?
phmap
::
strong_ordering
::
equal
:
phmap
::
strong_ordering
::
greater
;
}
};
TEST
(
Btree
,
UserProvidedKeyCompareToComparators
)
{
phmap
::
btree_set
<
int
,
KeyCompareToWeakOrdering
>
weak_set
=
{
1
,
2
,
3
};
EXPECT_TRUE
(
weak_set
.
contains
(
2
));
EXPECT_FALSE
(
weak_set
.
contains
(
4
));
phmap
::
btree_set
<
int
,
KeyCompareToStrongOrdering
>
strong_set
=
{
1
,
2
,
3
};
EXPECT_TRUE
(
strong_set
.
contains
(
2
));
EXPECT_FALSE
(
strong_set
.
contains
(
4
));
}
TEST
(
Btree
,
TryEmplaceBasicTest
)
{
phmap
::
btree_map
<
int
,
std
::
string
>
m
;
// Should construct a std::string from the literal.
m
.
try_emplace
(
1
,
"one"
);
EXPECT_EQ
(
1
,
m
.
size
());
// Try other std::string constructors and const lvalue key.
const
int
key
(
42
);
m
.
try_emplace
(
key
,
3
,
'a'
);
m
.
try_emplace
(
2
,
std
::
string
(
"two"
));
EXPECT_TRUE
(
std
::
is_sorted
(
m
.
begin
(),
m
.
end
()));
EXPECT_THAT
(
m
,
ElementsAreArray
(
std
::
vector
<
std
::
pair
<
int
,
std
::
string
>>
{
{
1
,
"one"
},
{
2
,
"two"
},
{
42
,
"aaa"
}}));
}
TEST
(
Btree
,
TryEmplaceWithHintWorks
)
{
// Use a counting comparator here to verify that hint is used.
int
calls
=
0
;
auto
cmp
=
[
&
calls
](
int
x
,
int
y
)
{
++
calls
;
return
x
<
y
;
};
using
Cmp
=
decltype
(
cmp
);
phmap
::
btree_map
<
int
,
int
,
Cmp
>
m
(
cmp
);
for
(
int
i
=
0
;
i
<
128
;
++
i
)
{
m
.
emplace
(
i
,
i
);
}
// Sanity check for the comparator
calls
=
0
;
m
.
emplace
(
127
,
127
);
EXPECT_GE
(
calls
,
4
);
// Try with begin hint:
calls
=
0
;
auto
it
=
m
.
try_emplace
(
m
.
begin
(),
-
1
,
-
1
);
EXPECT_EQ
(
129
,
m
.
size
());
EXPECT_EQ
(
it
,
m
.
begin
());
EXPECT_LE
(
calls
,
2
);
// Try with end hint:
calls
=
0
;
std
::
pair
<
int
,
int
>
pair1024
=
{
1024
,
1024
};
it
=
m
.
try_emplace
(
m
.
end
(),
pair1024
.
first
,
pair1024
.
second
);
EXPECT_EQ
(
130
,
m
.
size
());
EXPECT_EQ
(
it
,
--
m
.
end
());
EXPECT_LE
(
calls
,
2
);
// Try value already present, bad hint; ensure no duplicate added:
calls
=
0
;
it
=
m
.
try_emplace
(
m
.
end
(),
16
,
17
);
EXPECT_EQ
(
130
,
m
.
size
());
EXPECT_GE
(
calls
,
4
);
EXPECT_EQ
(
it
,
m
.
find
(
16
));
// Try value already present, hint points directly to it:
calls
=
0
;
it
=
m
.
try_emplace
(
it
,
16
,
17
);
EXPECT_EQ
(
130
,
m
.
size
());
EXPECT_LE
(
calls
,
2
);
EXPECT_EQ
(
it
,
m
.
find
(
16
));
m
.
erase
(
2
);
EXPECT_EQ
(
129
,
m
.
size
());
auto
hint
=
m
.
find
(
3
);
// Try emplace in the middle of two other elements.
calls
=
0
;
m
.
try_emplace
(
hint
,
2
,
2
);
EXPECT_EQ
(
130
,
m
.
size
());
EXPECT_LE
(
calls
,
2
);
EXPECT_TRUE
(
std
::
is_sorted
(
m
.
begin
(),
m
.
end
()));
}
TEST
(
Btree
,
TryEmplaceWithBadHint
)
{
phmap
::
btree_map
<
int
,
int
>
m
=
{{
1
,
1
},
{
9
,
9
}};
// Bad hint (too small), should still emplace:
auto
it
=
m
.
try_emplace
(
m
.
begin
(),
2
,
2
);
EXPECT_EQ
(
it
,
++
m
.
begin
());
EXPECT_THAT
(
m
,
ElementsAreArray
(
std
::
vector
<
std
::
pair
<
int
,
int
>>
{{
1
,
1
},
{
2
,
2
},
{
9
,
9
}}));
// Bad hint, too large this time:
it
=
m
.
try_emplace
(
++
(
++
m
.
begin
()),
0
,
0
);
EXPECT_EQ
(
it
,
m
.
begin
());
EXPECT_THAT
(
m
,
ElementsAreArray
(
std
::
vector
<
std
::
pair
<
int
,
int
>>
{
{
0
,
0
},
{
1
,
1
},
{
2
,
2
},
{
9
,
9
}}));
}
TEST
(
Btree
,
TryEmplaceMaintainsSortedOrder
)
{
phmap
::
btree_map
<
int
,
std
::
string
>
m
;
std
::
pair
<
int
,
std
::
string
>
pair5
=
{
5
,
"five"
};
// Test both lvalue & rvalue emplace.
m
.
try_emplace
(
10
,
"ten"
);
m
.
try_emplace
(
pair5
.
first
,
pair5
.
second
);
EXPECT_EQ
(
2
,
m
.
size
());
EXPECT_TRUE
(
std
::
is_sorted
(
m
.
begin
(),
m
.
end
()));
int
int100
{
100
};
m
.
try_emplace
(
int100
,
"hundred"
);
m
.
try_emplace
(
1
,
"one"
);
EXPECT_EQ
(
4
,
m
.
size
());
EXPECT_TRUE
(
std
::
is_sorted
(
m
.
begin
(),
m
.
end
()));
}
TEST
(
Btree
,
TryEmplaceWithHintAndNoValueArgsWorks
)
{
phmap
::
btree_map
<
int
,
int
>
m
;
m
.
try_emplace
(
m
.
end
(),
1
);
EXPECT_EQ
(
0
,
m
[
1
]);
}
TEST
(
Btree
,
TryEmplaceWithHintAndMultipleValueArgsWorks
)
{
phmap
::
btree_map
<
int
,
std
::
string
>
m
;
m
.
try_emplace
(
m
.
end
(),
1
,
10
,
'a'
);
EXPECT_EQ
(
std
::
string
(
10
,
'a'
),
m
[
1
]);
}
TEST
(
Btree
,
MoveAssignmentAllocatorPropagation
)
{
InstanceTracker
tracker
;
int64_t
bytes1
=
0
,
bytes2
=
0
;
PropagatingCountingAlloc
<
MovableOnlyInstance
>
allocator1
(
&
bytes1
);
PropagatingCountingAlloc
<
MovableOnlyInstance
>
allocator2
(
&
bytes2
);
std
::
less
<
MovableOnlyInstance
>
cmp
;
// Test propagating allocator_type.
{
phmap
::
btree_set
<
MovableOnlyInstance
,
std
::
less
<
MovableOnlyInstance
>
,
PropagatingCountingAlloc
<
MovableOnlyInstance
>>
set1
(
cmp
,
allocator1
),
set2
(
cmp
,
allocator2
);
for
(
int
i
=
0
;
i
<
100
;
++
i
)
set1
.
insert
(
MovableOnlyInstance
(
i
));
tracker
.
ResetCopiesMovesSwaps
();
set2
=
std
::
move
(
set1
);
EXPECT_EQ
(
tracker
.
moves
(),
0
);
}
// Test non-propagating allocator_type with equal allocators.
{
phmap
::
btree_set
<
MovableOnlyInstance
,
std
::
less
<
MovableOnlyInstance
>
,
CountingAllocator
<
MovableOnlyInstance
>>
set1
(
cmp
,
allocator1
),
set2
(
cmp
,
allocator1
);
for
(
int
i
=
0
;
i
<
100
;
++
i
)
set1
.
insert
(
MovableOnlyInstance
(
i
));
tracker
.
ResetCopiesMovesSwaps
();
set2
=
std
::
move
(
set1
);
EXPECT_EQ
(
tracker
.
moves
(),
0
);
}
// Test non-propagating allocator_type with different allocators.
{
phmap
::
btree_set
<
MovableOnlyInstance
,
std
::
less
<
MovableOnlyInstance
>
,
CountingAllocator
<
MovableOnlyInstance
>>
set1
(
cmp
,
allocator1
),
set2
(
cmp
,
allocator2
);
for
(
int
i
=
0
;
i
<
100
;
++
i
)
set1
.
insert
(
MovableOnlyInstance
(
i
));
tracker
.
ResetCopiesMovesSwaps
();
set2
=
std
::
move
(
set1
);
EXPECT_GE
(
tracker
.
moves
(),
100
);
}
}
TEST
(
Btree
,
EmptyTree
)
{
phmap
::
btree_set
<
int
>
s
;
EXPECT_TRUE
(
s
.
empty
());
EXPECT_EQ
(
s
.
size
(),
0
);
EXPECT_GT
(
s
.
max_size
(),
0
);
}
bool
IsEven
(
int
k
)
{
return
k
%
2
==
0
;
}
TEST
(
Btree
,
EraseIf
)
{
// Test that erase_if works with all the container types and supports lambdas.
{
phmap
::
btree_set
<
int
>
s
=
{
1
,
3
,
5
,
6
,
100
};
erase_if
(
s
,
[](
int
k
)
{
return
k
>
3
;
});
EXPECT_THAT
(
s
,
ElementsAre
(
1
,
3
));
}
{
phmap
::
btree_multiset
<
int
>
s
=
{
1
,
3
,
3
,
5
,
6
,
6
,
100
};
erase_if
(
s
,
[](
int
k
)
{
return
k
<=
3
;
});
EXPECT_THAT
(
s
,
ElementsAre
(
5
,
6
,
6
,
100
));
}
{
phmap
::
btree_map
<
int
,
int
>
m
=
{{
1
,
1
},
{
3
,
3
},
{
6
,
6
},
{
100
,
100
}};
erase_if
(
m
,
[](
std
::
pair
<
const
int
,
int
>
kv
)
{
return
kv
.
first
>
3
;
});
EXPECT_THAT
(
m
,
ElementsAre
(
Pair
(
1
,
1
),
Pair
(
3
,
3
)));
}
{
phmap
::
btree_multimap
<
int
,
int
>
m
=
{{
1
,
1
},
{
3
,
3
},
{
3
,
6
},
{
6
,
6
},
{
6
,
7
},
{
100
,
6
}};
erase_if
(
m
,
[](
std
::
pair
<
const
int
,
int
>
kv
)
{
return
kv
.
second
==
6
;
});
EXPECT_THAT
(
m
,
ElementsAre
(
Pair
(
1
,
1
),
Pair
(
3
,
3
),
Pair
(
6
,
7
)));
}
// Test that erasing all elements from a large set works and test support for
// function pointers.
{
phmap
::
btree_set
<
int
>
s
;
for
(
int
i
=
0
;
i
<
1000
;
++
i
)
s
.
insert
(
2
*
i
);
erase_if
(
s
,
IsEven
);
EXPECT_THAT
(
s
,
IsEmpty
());
}
// Test that erase_if supports other format of function pointers.
{
phmap
::
btree_set
<
int
>
s
=
{
1
,
3
,
5
,
6
,
100
};
erase_if
(
s
,
&
IsEven
);
EXPECT_THAT
(
s
,
ElementsAre
(
1
,
3
,
5
));
}
}
}
// namespace
}
// namespace priv
}
// namespace phmap
third_party/parallel-hashmap/tests/btree_test.h
0 → 100644
View file @
2951b12d
// ---------------------------------------------------------------------------
// Copyright (c) 2019, Gregory Popovitch - greg7mdp@gmail.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Includes work from abseil-cpp (https://github.com/abseil/abseil-cpp)
// with modifications.
//
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ---------------------------------------------------------------------------
#ifndef PHMAP_CONTAINER_BTREE_TEST_H_
#define PHMAP_CONTAINER_BTREE_TEST_H_
#include <algorithm>
#include <cassert>
#include <random>
#include <string>
#include <utility>
#include <vector>
#include <cstdint>
#include <map>
#include <memory>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <cstdlib>
#include <ostream>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "parallel_hashmap/btree.h"
#include "parallel_hashmap/phmap.h"
namespace
phmap
{
namespace
test_internal
{
// A type that counts number of occurrences of the type, the live occurrences of
// the type, as well as the number of copies, moves, swaps, and comparisons that
// have occurred on the type. This is used as a base class for the copyable,
// copyable+movable, and movable types below that are used in actual tests. Use
// InstanceTracker in tests to track the number of instances.
class
BaseCountedInstance
{
public:
explicit
BaseCountedInstance
(
size_t
x
)
:
value_
(
x
)
{
++
num_instances_
;
++
num_live_instances_
;
}
BaseCountedInstance
(
const
BaseCountedInstance
&
x
)
:
value_
(
x
.
value_
),
is_live_
(
x
.
is_live_
)
{
++
num_instances_
;
if
(
is_live_
)
++
num_live_instances_
;
++
num_copies_
;
}
BaseCountedInstance
(
BaseCountedInstance
&&
x
)
:
value_
(
x
.
value_
),
is_live_
(
x
.
is_live_
)
{
x
.
is_live_
=
false
;
++
num_instances_
;
++
num_moves_
;
}
~
BaseCountedInstance
()
{
--
num_instances_
;
if
(
is_live_
)
--
num_live_instances_
;
}
BaseCountedInstance
&
operator
=
(
const
BaseCountedInstance
&
x
)
{
value_
=
x
.
value_
;
if
(
is_live_
)
--
num_live_instances_
;
is_live_
=
x
.
is_live_
;
if
(
is_live_
)
++
num_live_instances_
;
++
num_copies_
;
return
*
this
;
}
BaseCountedInstance
&
operator
=
(
BaseCountedInstance
&&
x
)
{
value_
=
x
.
value_
;
if
(
is_live_
)
--
num_live_instances_
;
is_live_
=
x
.
is_live_
;
x
.
is_live_
=
false
;
++
num_moves_
;
return
*
this
;
}
bool
operator
==
(
const
BaseCountedInstance
&
x
)
const
{
++
num_comparisons_
;
return
value_
==
x
.
value_
;
}
bool
operator
!=
(
const
BaseCountedInstance
&
x
)
const
{
++
num_comparisons_
;
return
value_
!=
x
.
value_
;
}
bool
operator
<
(
const
BaseCountedInstance
&
x
)
const
{
++
num_comparisons_
;
return
value_
<
x
.
value_
;
}
bool
operator
>
(
const
BaseCountedInstance
&
x
)
const
{
++
num_comparisons_
;
return
value_
>
x
.
value_
;
}
bool
operator
<=
(
const
BaseCountedInstance
&
x
)
const
{
++
num_comparisons_
;
return
value_
<=
x
.
value_
;
}
bool
operator
>=
(
const
BaseCountedInstance
&
x
)
const
{
++
num_comparisons_
;
return
value_
>=
x
.
value_
;
}
phmap
::
weak_ordering
compare
(
const
BaseCountedInstance
&
x
)
const
{
++
num_comparisons_
;
return
value_
<
x
.
value_
?
phmap
::
weak_ordering
::
less
:
value_
==
x
.
value_
?
phmap
::
weak_ordering
::
equivalent
:
phmap
::
weak_ordering
::
greater
;
}
size_t
value
()
const
{
if
(
!
is_live_
)
std
::
abort
();
return
value_
;
}
friend
std
::
ostream
&
operator
<<
(
std
::
ostream
&
o
,
const
BaseCountedInstance
&
v
)
{
return
o
<<
"[value:"
<<
v
.
value
()
<<
"]"
;
}
// Implementation of efficient swap() that counts swaps.
static
void
SwapImpl
(
BaseCountedInstance
&
lhs
,
// NOLINT(runtime/references)
BaseCountedInstance
&
rhs
)
{
// NOLINT(runtime/references)
using
std
::
swap
;
swap
(
lhs
.
value_
,
rhs
.
value_
);
swap
(
lhs
.
is_live_
,
rhs
.
is_live_
);
++
BaseCountedInstance
::
num_swaps_
;
}
private:
friend
class
InstanceTracker
;
size_t
value_
;
// Indicates if the value is live, ie it hasn't been moved away from.
bool
is_live_
=
true
;
// Number of instances.
static
size_t
num_instances_
;
// Number of live instances (those that have not been moved away from.)
static
size_t
num_live_instances_
;
// Number of times that BaseCountedInstance objects were moved.
static
size_t
num_moves_
;
// Number of times that BaseCountedInstance objects were copied.
static
size_t
num_copies_
;
// Number of times that BaseCountedInstance objects were swapped.
static
size_t
num_swaps_
;
// Number of times that BaseCountedInstance objects were compared.
static
size_t
num_comparisons_
;
};
// Helper to track the BaseCountedInstance instance counters. Expects that the
// number of instances and live_instances are the same when it is constructed
// and when it is destructed.
class
InstanceTracker
{
public:
InstanceTracker
()
:
start_instances_
(
BaseCountedInstance
::
num_instances_
),
start_live_instances_
(
BaseCountedInstance
::
num_live_instances_
)
{
ResetCopiesMovesSwaps
();
}
~
InstanceTracker
()
{
if
(
instances
()
!=
0
)
std
::
abort
();
if
(
live_instances
()
!=
0
)
std
::
abort
();
}
// Returns the number of BaseCountedInstance instances both containing valid
// values and those moved away from compared to when the InstanceTracker was
// constructed
size_t
instances
()
const
{
return
BaseCountedInstance
::
num_instances_
-
start_instances_
;
}
// Returns the number of live BaseCountedInstance instances compared to when
// the InstanceTracker was constructed
size_t
live_instances
()
const
{
return
BaseCountedInstance
::
num_live_instances_
-
start_live_instances_
;
}
// Returns the number of moves on BaseCountedInstance objects since
// construction or since the last call to ResetCopiesMovesSwaps().
size_t
moves
()
const
{
return
BaseCountedInstance
::
num_moves_
-
start_moves_
;
}
// Returns the number of copies on BaseCountedInstance objects since
// construction or the last call to ResetCopiesMovesSwaps().
size_t
copies
()
const
{
return
BaseCountedInstance
::
num_copies_
-
start_copies_
;
}
// Returns the number of swaps on BaseCountedInstance objects since
// construction or the last call to ResetCopiesMovesSwaps().
size_t
swaps
()
const
{
return
BaseCountedInstance
::
num_swaps_
-
start_swaps_
;
}
// Returns the number of comparisons on BaseCountedInstance objects since
// construction or the last call to ResetCopiesMovesSwaps().
size_t
comparisons
()
const
{
return
BaseCountedInstance
::
num_comparisons_
-
start_comparisons_
;
}
// Resets the base values for moves, copies, comparisons, and swaps to the
// current values, so that subsequent Get*() calls for moves, copies,
// comparisons, and swaps will compare to the situation at the point of this
// call.
void
ResetCopiesMovesSwaps
()
{
start_moves_
=
BaseCountedInstance
::
num_moves_
;
start_copies_
=
BaseCountedInstance
::
num_copies_
;
start_swaps_
=
BaseCountedInstance
::
num_swaps_
;
start_comparisons_
=
BaseCountedInstance
::
num_comparisons_
;
}
private:
size_t
start_instances_
;
size_t
start_live_instances_
;
size_t
start_moves_
;
size_t
start_copies_
;
size_t
start_swaps_
;
size_t
start_comparisons_
;
};
// Copyable, not movable.
class
CopyableOnlyInstance
:
public
BaseCountedInstance
{
public:
explicit
CopyableOnlyInstance
(
size_t
x
)
:
BaseCountedInstance
(
x
)
{}
CopyableOnlyInstance
(
const
CopyableOnlyInstance
&
rhs
)
=
default
;
CopyableOnlyInstance
&
operator
=
(
const
CopyableOnlyInstance
&
rhs
)
=
default
;
friend
void
swap
(
CopyableOnlyInstance
&
lhs
,
CopyableOnlyInstance
&
rhs
)
{
BaseCountedInstance
::
SwapImpl
(
lhs
,
rhs
);
}
static
bool
supports_move
()
{
return
false
;
}
};
// Copyable and movable.
class
CopyableMovableInstance
:
public
BaseCountedInstance
{
public:
explicit
CopyableMovableInstance
(
size_t
x
)
:
BaseCountedInstance
(
x
)
{}
CopyableMovableInstance
(
const
CopyableMovableInstance
&
rhs
)
=
default
;
CopyableMovableInstance
(
CopyableMovableInstance
&&
rhs
)
=
default
;
CopyableMovableInstance
&
operator
=
(
const
CopyableMovableInstance
&
rhs
)
=
default
;
CopyableMovableInstance
&
operator
=
(
CopyableMovableInstance
&&
rhs
)
=
default
;
friend
void
swap
(
CopyableMovableInstance
&
lhs
,
CopyableMovableInstance
&
rhs
)
{
BaseCountedInstance
::
SwapImpl
(
lhs
,
rhs
);
}
static
bool
supports_move
()
{
return
true
;
}
};
// Only movable, not default-constructible.
class
MovableOnlyInstance
:
public
BaseCountedInstance
{
public:
explicit
MovableOnlyInstance
(
size_t
x
)
:
BaseCountedInstance
(
x
)
{}
MovableOnlyInstance
(
MovableOnlyInstance
&&
other
)
=
default
;
MovableOnlyInstance
&
operator
=
(
MovableOnlyInstance
&&
other
)
=
default
;
friend
void
swap
(
MovableOnlyInstance
&
lhs
,
MovableOnlyInstance
&
rhs
)
{
BaseCountedInstance
::
SwapImpl
(
lhs
,
rhs
);
}
static
bool
supports_move
()
{
return
true
;
}
};
}
// namespace test_internal
namespace
priv
{
// Like remove_const but propagates the removal through std::pair.
template
<
typename
T
>
struct
remove_pair_const
{
using
type
=
typename
std
::
remove_const
<
T
>::
type
;
};
template
<
typename
T
,
typename
U
>
struct
remove_pair_const
<
std
::
pair
<
T
,
U
>
>
{
using
type
=
std
::
pair
<
typename
remove_pair_const
<
T
>::
type
,
typename
remove_pair_const
<
U
>::
type
>
;
};
// Utility class to provide an accessor for a key given a value. The default
// behavior is to treat the value as a pair and return the first element.
template
<
typename
K
,
typename
V
>
struct
KeyOfValue
{
struct
type
{
const
K
&
operator
()(
const
V
&
p
)
const
{
return
p
.
first
;
}
};
};
// Partial specialization of KeyOfValue class for when the key and value are
// the same type such as in set<> and btree_set<>.
template
<
typename
K
>
struct
KeyOfValue
<
K
,
K
>
{
struct
type
{
const
K
&
operator
()(
const
K
&
k
)
const
{
return
k
;
}
};
};
inline
char
*
GenerateDigits
(
char
buf
[
16
],
unsigned
val
,
unsigned
maxval
)
{
assert
(
val
<=
maxval
);
constexpr
unsigned
kBase
=
64
;
// avoid integer division.
unsigned
p
=
15
;
buf
[
p
--
]
=
0
;
while
(
maxval
>
0
)
{
buf
[
p
--
]
=
' '
+
(
val
%
kBase
);
val
/=
kBase
;
maxval
/=
kBase
;
}
return
buf
+
p
+
1
;
}
template
<
typename
K
>
struct
Generator
{
int
maxval
;
explicit
Generator
(
int
m
)
:
maxval
(
m
)
{}
K
operator
()(
int
i
)
const
{
assert
(
i
<=
maxval
);
return
K
(
i
);
}
};
#if 0
template <>
struct Generator<phmap::Time> {
int maxval;
explicit Generator(int m) : maxval(m) {}
phmap::Time operator()(int i) const { return phmap::FromUnixMillis(i); }
};
#endif
template
<
>
struct
Generator
<
std
::
string
>
{
int
maxval
;
explicit
Generator
(
int
m
)
:
maxval
(
m
)
{}
std
::
string
operator
()(
int
i
)
const
{
char
buf
[
16
];
return
GenerateDigits
(
buf
,
i
,
maxval
);
}
};
template
<
typename
T
,
typename
U
>
struct
Generator
<
std
::
pair
<
T
,
U
>
>
{
Generator
<
typename
remove_pair_const
<
T
>::
type
>
tgen
;
Generator
<
typename
remove_pair_const
<
U
>::
type
>
ugen
;
explicit
Generator
(
int
m
)
:
tgen
(
m
),
ugen
(
m
)
{}
std
::
pair
<
T
,
U
>
operator
()(
int
i
)
const
{
return
std
::
make_pair
(
tgen
(
i
),
ugen
(
i
));
}
};
// Generate n values for our tests and benchmarks. Value range is [0, maxval].
inline
std
::
vector
<
int
>
GenerateNumbersWithSeed
(
int
n
,
int
maxval
,
int
seed
)
{
// NOTE: Some tests rely on generated numbers not changing between test runs.
// We use std::minstd_rand0 because it is well-defined, but don't use
// std::uniform_int_distribution because platforms use different algorithms.
std
::
minstd_rand0
rng
(
seed
);
std
::
vector
<
int
>
values
;
phmap
::
flat_hash_set
<
int
>
unique_values
;
if
(
values
.
size
()
<
n
)
{
for
(
size_t
i
=
values
.
size
();
i
<
(
size_t
)
n
;
i
++
)
{
int
value
;
do
{
value
=
static_cast
<
int
>
(
rng
())
%
(
maxval
+
1
);
}
while
(
!
unique_values
.
insert
(
value
).
second
);
values
.
push_back
(
value
);
}
}
return
values
;
}
// Generates n values in the range [0, maxval].
template
<
typename
V
>
std
::
vector
<
V
>
GenerateValuesWithSeed
(
int
n
,
int
maxval
,
int
seed
)
{
const
std
::
vector
<
int
>
nums
=
GenerateNumbersWithSeed
(
n
,
maxval
,
seed
);
Generator
<
V
>
gen
(
maxval
);
std
::
vector
<
V
>
vec
;
vec
.
reserve
(
n
);
for
(
int
i
=
0
;
i
<
n
;
i
++
)
{
vec
.
push_back
(
gen
(
nums
[
i
]));
}
return
vec
;
}
}
// namespace priv
namespace
priv
{
// This is a stateful allocator, but the state lives outside of the
// allocator (in whatever test is using the allocator). This is odd
// but helps in tests where the allocator is propagated into nested
// containers - that chain of allocators uses the same state and is
// thus easier to query for aggregate allocation information.
template
<
typename
T
>
class
CountingAllocator
:
public
std
::
allocator
<
T
>
{
public:
using
Alloc
=
std
::
allocator
<
T
>
;
using
AllocTraits
=
typename
std
::
allocator_traits
<
Alloc
>
;
using
pointer
=
typename
AllocTraits
::
pointer
;
using
size_type
=
typename
AllocTraits
::
size_type
;
CountingAllocator
()
:
bytes_used_
(
nullptr
)
{}
explicit
CountingAllocator
(
int64_t
*
b
)
:
bytes_used_
(
b
)
{}
template
<
typename
U
>
CountingAllocator
(
const
CountingAllocator
<
U
>&
x
)
:
Alloc
(
x
),
bytes_used_
(
x
.
bytes_used_
)
{}
pointer
allocate
(
size_type
n
,
std
::
allocator_traits
<
std
::
allocator
<
void
>>::
const_pointer
hint
=
nullptr
)
{
assert
(
bytes_used_
!=
nullptr
);
*
bytes_used_
+=
n
*
sizeof
(
T
);
return
AllocTraits
::
allocate
(
*
this
,
n
,
hint
);
}
void
deallocate
(
pointer
p
,
size_type
n
)
{
AllocTraits
::
deallocate
(
*
this
,
p
,
n
);
assert
(
bytes_used_
!=
nullptr
);
*
bytes_used_
-=
n
*
sizeof
(
T
);
}
template
<
typename
U
>
class
rebind
{
public:
using
other
=
CountingAllocator
<
U
>
;
};
friend
bool
operator
==
(
const
CountingAllocator
&
a
,
const
CountingAllocator
&
b
)
{
return
a
.
bytes_used_
==
b
.
bytes_used_
;
}
friend
bool
operator
!=
(
const
CountingAllocator
&
a
,
const
CountingAllocator
&
b
)
{
return
!
(
a
==
b
);
}
int64_t
*
bytes_used_
;
};
}
// namespace priv
}
// namespace phmap
#endif // PHMAP_CONTAINER_BTREE_TEST_H_
third_party/parallel-hashmap/tests/compressed_tuple_test.cc
0 → 100644
View file @
2951b12d
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "parallel_hashmap/phmap.h"
#include <memory>
#include <string>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace
phmap
{
namespace
priv
{
namespace
{
enum
class
CallType
{
kConstRef
,
kConstMove
};
template
<
int
>
struct
Empty
{
constexpr
CallType
value
()
const
&
{
return
CallType
::
kConstRef
;
}
constexpr
CallType
value
()
const
&&
{
return
CallType
::
kConstMove
;
}
};
template
<
typename
T
>
struct
NotEmpty
{
T
value
;
};
template
<
typename
T
,
typename
U
>
struct
TwoValues
{
T
value1
;
U
value2
;
};
TEST
(
CompressedTupleTest
,
Sizeof
)
{
EXPECT_EQ
(
sizeof
(
int
),
sizeof
(
CompressedTuple
<
int
>
));
EXPECT_EQ
(
sizeof
(
int
),
sizeof
(
CompressedTuple
<
int
,
Empty
<
0
>>
));
EXPECT_EQ
(
sizeof
(
int
),
sizeof
(
CompressedTuple
<
int
,
Empty
<
0
>
,
Empty
<
1
>>
));
EXPECT_EQ
(
sizeof
(
int
),
sizeof
(
CompressedTuple
<
int
,
Empty
<
0
>
,
Empty
<
1
>
,
Empty
<
2
>>
));
EXPECT_EQ
(
sizeof
(
TwoValues
<
int
,
double
>
),
sizeof
(
CompressedTuple
<
int
,
NotEmpty
<
double
>>
));
EXPECT_EQ
(
sizeof
(
TwoValues
<
int
,
double
>
),
sizeof
(
CompressedTuple
<
int
,
Empty
<
0
>
,
NotEmpty
<
double
>>
));
EXPECT_EQ
(
sizeof
(
TwoValues
<
int
,
double
>
),
sizeof
(
CompressedTuple
<
int
,
Empty
<
0
>
,
NotEmpty
<
double
>
,
Empty
<
1
>>
));
}
TEST
(
CompressedTupleTest
,
Access
)
{
struct
S
{
std
::
string
x
;
};
CompressedTuple
<
int
,
Empty
<
0
>
,
S
>
x
(
7
,
{},
S
{
"ABC"
});
EXPECT_EQ
(
sizeof
(
x
),
sizeof
(
TwoValues
<
int
,
S
>
));
EXPECT_EQ
(
7
,
x
.
get
<
0
>
());
EXPECT_EQ
(
"ABC"
,
x
.
get
<
2
>
().
x
);
}
TEST
(
CompressedTupleTest
,
NonClasses
)
{
CompressedTuple
<
int
,
const
char
*>
x
(
7
,
"ABC"
);
EXPECT_EQ
(
7
,
x
.
get
<
0
>
());
EXPECT_STREQ
(
"ABC"
,
x
.
get
<
1
>
());
}
TEST
(
CompressedTupleTest
,
MixClassAndNonClass
)
{
CompressedTuple
<
int
,
const
char
*
,
Empty
<
0
>
,
NotEmpty
<
double
>>
x
(
7
,
"ABC"
,
{},
{
1.25
});
struct
Mock
{
int
v
;
const
char
*
p
;
double
d
;
};
EXPECT_EQ
(
sizeof
(
x
),
sizeof
(
Mock
));
EXPECT_EQ
(
7
,
x
.
get
<
0
>
());
EXPECT_STREQ
(
"ABC"
,
x
.
get
<
1
>
());
EXPECT_EQ
(
1.25
,
x
.
get
<
3
>
().
value
);
}
TEST
(
CompressedTupleTest
,
Nested
)
{
CompressedTuple
<
int
,
CompressedTuple
<
int
>
,
CompressedTuple
<
int
,
CompressedTuple
<
int
>>>
x
(
1
,
CompressedTuple
<
int
>
(
2
),
CompressedTuple
<
int
,
CompressedTuple
<
int
>>
(
3
,
CompressedTuple
<
int
>
(
4
)));
EXPECT_EQ
(
1
,
x
.
get
<
0
>
());
EXPECT_EQ
(
2
,
x
.
get
<
1
>
().
get
<
0
>
());
EXPECT_EQ
(
3
,
x
.
get
<
2
>
().
get
<
0
>
());
EXPECT_EQ
(
4
,
x
.
get
<
2
>
().
get
<
1
>
().
get
<
0
>
());
CompressedTuple
<
Empty
<
0
>
,
Empty
<
0
>
,
CompressedTuple
<
Empty
<
0
>
,
CompressedTuple
<
Empty
<
0
>>>>
y
;
std
::
set
<
Empty
<
0
>*>
empties
{
&
y
.
get
<
0
>
(),
&
y
.
get
<
1
>
(),
&
y
.
get
<
2
>
().
get
<
0
>
(),
&
y
.
get
<
2
>
().
get
<
1
>
().
get
<
0
>
()};
#ifdef _MSC_VER
// MSVC has a bug where many instances of the same base class are layed out in
// the same address when using __declspec(empty_bases).
// This will be fixed in a future version of MSVC.
int
expected
=
1
;
#else
int
expected
=
4
;
#endif
EXPECT_EQ
(
expected
,
sizeof
(
y
));
EXPECT_EQ
(
expected
,
empties
.
size
());
EXPECT_EQ
(
sizeof
(
y
),
sizeof
(
Empty
<
0
>
)
*
empties
.
size
());
EXPECT_EQ
(
4
*
sizeof
(
char
),
sizeof
(
CompressedTuple
<
CompressedTuple
<
char
,
char
>
,
CompressedTuple
<
char
,
char
>>
));
EXPECT_TRUE
(
(
std
::
is_empty
<
CompressedTuple
<
CompressedTuple
<
Empty
<
0
>>
,
CompressedTuple
<
Empty
<
1
>>>>::
value
));
}
TEST
(
CompressedTupleTest
,
Reference
)
{
int
i
=
7
;
std
::
string
s
=
"Very long std::string that goes in the heap"
;
CompressedTuple
<
int
,
int
&
,
std
::
string
,
std
::
string
&>
x
(
i
,
i
,
s
,
s
);
// Sanity check. We should have not moved from `s`
EXPECT_EQ
(
s
,
"Very long std::string that goes in the heap"
);
EXPECT_EQ
(
x
.
get
<
0
>
(),
x
.
get
<
1
>
());
EXPECT_NE
(
&
x
.
get
<
0
>
(),
&
x
.
get
<
1
>
());
EXPECT_EQ
(
&
x
.
get
<
1
>
(),
&
i
);
EXPECT_EQ
(
x
.
get
<
2
>
(),
x
.
get
<
3
>
());
EXPECT_NE
(
&
x
.
get
<
2
>
(),
&
x
.
get
<
3
>
());
EXPECT_EQ
(
&
x
.
get
<
3
>
(),
&
s
);
}
TEST
(
CompressedTupleTest
,
NoElements
)
{
CompressedTuple
<>
x
;
static_cast
<
void
>
(
x
);
// Silence -Wunused-variable.
EXPECT_TRUE
(
std
::
is_empty
<
CompressedTuple
<>>::
value
);
}
TEST
(
CompressedTupleTest
,
MoveOnlyElements
)
{
CompressedTuple
<
std
::
unique_ptr
<
std
::
string
>>
str_tup
(
phmap
::
make_unique
<
std
::
string
>
(
"str"
));
CompressedTuple
<
CompressedTuple
<
std
::
unique_ptr
<
std
::
string
>>
,
std
::
unique_ptr
<
int
>>
x
(
std
::
move
(
str_tup
),
phmap
::
make_unique
<
int
>
(
5
));
EXPECT_EQ
(
*
x
.
get
<
0
>
().
get
<
0
>
(),
"str"
);
EXPECT_EQ
(
*
x
.
get
<
1
>
(),
5
);
std
::
unique_ptr
<
std
::
string
>
x0
=
std
::
move
(
x
.
get
<
0
>
()).
get
<
0
>
();
std
::
unique_ptr
<
int
>
x1
=
std
::
move
(
x
).
get
<
1
>
();
EXPECT_EQ
(
*
x0
,
"str"
);
EXPECT_EQ
(
*
x1
,
5
);
}
TEST
(
CompressedTupleTest
,
Constexpr
)
{
constexpr
CompressedTuple
<
int
,
double
,
CompressedTuple
<
int
>
,
Empty
<
0
>>
x
(
7
,
1.25
,
CompressedTuple
<
int
>
(
5
),
{});
constexpr
int
x0
=
x
.
get
<
0
>
();
constexpr
double
x1
=
x
.
get
<
1
>
();
constexpr
int
x2
=
x
.
get
<
2
>
().
get
<
0
>
();
constexpr
CallType
x3
=
x
.
get
<
3
>
().
value
();
EXPECT_EQ
(
x0
,
7
);
EXPECT_EQ
(
x1
,
1.25
);
EXPECT_EQ
(
x2
,
5
);
EXPECT_EQ
(
x3
,
CallType
::
kConstRef
);
#if defined(__clang__)
// An apparent bug in earlier versions of gcc claims these are ambiguous.
constexpr
int
x2m
=
std
::
move
(
x
.
get
<
2
>
()).
get
<
0
>
();
constexpr
CallType
x3m
=
std
::
move
(
x
).
get
<
3
>
().
value
();
EXPECT_EQ
(
x2m
,
5
);
EXPECT_EQ
(
x3m
,
CallType
::
kConstMove
);
#endif
}
#if defined(__clang__) || defined(__GNUC__)
TEST
(
CompressedTupleTest
,
EmptyFinalClass
)
{
struct
S
final
{
int
f
()
const
{
return
5
;
}
};
CompressedTuple
<
S
>
x
;
EXPECT_EQ
(
x
.
get
<
0
>
().
f
(),
5
);
}
#endif
}
// namespace
}
// namespace priv
}
// namespace phmap
third_party/parallel-hashmap/tests/container_memory_test.cc
0 → 100644
View file @
2951b12d
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "parallel_hashmap/phmap.h"
#include <cstdint>
#include <tuple>
#include <utility>
#if PHMAP_HAVE_STD_STRING_VIEW
#include <string_view>
#endif
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace
phmap
{
namespace
priv
{
namespace
{
using
::
testing
::
Pair
;
TEST
(
Memory
,
AlignmentLargerThanBase
)
{
std
::
allocator
<
int8_t
>
alloc
;
void
*
mem
=
Allocate
<
2
>
(
&
alloc
,
3
);
EXPECT_EQ
(
0
,
reinterpret_cast
<
uintptr_t
>
(
mem
)
%
2
);
memcpy
(
mem
,
"abc"
,
3
);
Deallocate
<
2
>
(
&
alloc
,
mem
,
3
);
}
TEST
(
Memory
,
AlignmentSmallerThanBase
)
{
std
::
allocator
<
int64_t
>
alloc
;
void
*
mem
=
Allocate
<
2
>
(
&
alloc
,
3
);
EXPECT_EQ
(
0
,
reinterpret_cast
<
uintptr_t
>
(
mem
)
%
2
);
memcpy
(
mem
,
"abc"
,
3
);
Deallocate
<
2
>
(
&
alloc
,
mem
,
3
);
}
class
Fixture
:
public
::
testing
::
Test
{
using
Alloc
=
std
::
allocator
<
std
::
string
>
;
public:
Fixture
()
{
ptr_
=
std
::
allocator_traits
<
Alloc
>::
allocate
(
*
alloc
(),
1
);
}
~
Fixture
()
override
{
std
::
allocator_traits
<
Alloc
>::
destroy
(
*
alloc
(),
ptr_
);
std
::
allocator_traits
<
Alloc
>::
deallocate
(
*
alloc
(),
ptr_
,
1
);
}
std
::
string
*
ptr
()
{
return
ptr_
;
}
Alloc
*
alloc
()
{
return
&
alloc_
;
}
private:
Alloc
alloc_
;
std
::
string
*
ptr_
;
};
TEST_F
(
Fixture
,
ConstructNoArgs
)
{
ConstructFromTuple
(
alloc
(),
ptr
(),
std
::
forward_as_tuple
());
EXPECT_EQ
(
*
ptr
(),
""
);
}
TEST_F
(
Fixture
,
ConstructOneArg
)
{
ConstructFromTuple
(
alloc
(),
ptr
(),
std
::
forward_as_tuple
(
"abcde"
));
EXPECT_EQ
(
*
ptr
(),
"abcde"
);
}
TEST_F
(
Fixture
,
ConstructTwoArg
)
{
ConstructFromTuple
(
alloc
(),
ptr
(),
std
::
forward_as_tuple
(
5
,
'a'
));
EXPECT_EQ
(
*
ptr
(),
"aaaaa"
);
}
TEST
(
PairArgs
,
NoArgs
)
{
EXPECT_THAT
(
PairArgs
(),
Pair
(
std
::
forward_as_tuple
(),
std
::
forward_as_tuple
()));
}
TEST
(
PairArgs
,
TwoArgs
)
{
EXPECT_EQ
(
std
::
make_pair
(
std
::
forward_as_tuple
(
1
),
std
::
forward_as_tuple
(
'A'
)),
PairArgs
(
1
,
'A'
));
}
TEST
(
PairArgs
,
Pair
)
{
EXPECT_EQ
(
std
::
make_pair
(
std
::
forward_as_tuple
(
1
),
std
::
forward_as_tuple
(
'A'
)),
PairArgs
(
std
::
make_pair
(
1
,
'A'
)));
}
TEST
(
PairArgs
,
Piecewise
)
{
EXPECT_EQ
(
std
::
make_pair
(
std
::
forward_as_tuple
(
1
),
std
::
forward_as_tuple
(
'A'
)),
PairArgs
(
std
::
piecewise_construct
,
std
::
forward_as_tuple
(
1
),
std
::
forward_as_tuple
(
'A'
)));
}
#if PHMAP_HAVE_STD_STRING_VIEW
TEST
(
WithConstructed
,
Simple
)
{
EXPECT_EQ
(
1
,
WithConstructed
<
std
::
string_view
>
(
std
::
make_tuple
(
std
::
string
(
"a"
)),
[](
std
::
string_view
str
)
{
return
str
.
size
();
}));
}
#endif
template
<
class
F
,
class
Arg
>
decltype
(
DecomposeValue
(
std
::
declval
<
F
>
(),
std
::
declval
<
Arg
>
()))
DecomposeValueImpl
(
int
,
F
&&
f
,
Arg
&&
arg
)
{
return
DecomposeValue
(
std
::
forward
<
F
>
(
f
),
std
::
forward
<
Arg
>
(
arg
));
}
template
<
class
F
,
class
Arg
>
const
char
*
DecomposeValueImpl
(
char
,
F
&&
,
Arg
&&
)
{
return
"not decomposable"
;
}
template
<
class
F
,
class
Arg
>
decltype
(
DecomposeValueImpl
(
0
,
std
::
declval
<
F
>
(),
std
::
declval
<
Arg
>
()))
TryDecomposeValue
(
F
&&
f
,
Arg
&&
arg
)
{
return
DecomposeValueImpl
(
0
,
std
::
forward
<
F
>
(
f
),
std
::
forward
<
Arg
>
(
arg
));
}
TEST
(
DecomposeValue
,
Decomposable
)
{
auto
f
=
[](
const
int
&
x
,
int
&&
y
)
{
EXPECT_EQ
(
&
x
,
&
y
);
EXPECT_EQ
(
42
,
x
);
return
'A'
;
};
EXPECT_EQ
(
'A'
,
TryDecomposeValue
(
f
,
42
));
}
TEST
(
DecomposeValue
,
NotDecomposable
)
{
auto
f
=
[](
void
*
)
{
ADD_FAILURE
()
<<
"Must not be called"
;
return
'A'
;
};
EXPECT_STREQ
(
"not decomposable"
,
TryDecomposeValue
(
f
,
42
));
}
template
<
class
F
,
class
...
Args
>
decltype
(
DecomposePair
(
std
::
declval
<
F
>
(),
std
::
declval
<
Args
>
()...))
DecomposePairImpl
(
int
,
F
&&
f
,
Args
&&
...
args
)
{
return
DecomposePair
(
std
::
forward
<
F
>
(
f
),
std
::
forward
<
Args
>
(
args
)...);
}
template
<
class
F
,
class
...
Args
>
const
char
*
DecomposePairImpl
(
char
,
F
&&
,
Args
&&
...
)
{
return
"not decomposable"
;
}
template
<
class
F
,
class
...
Args
>
decltype
(
DecomposePairImpl
(
0
,
std
::
declval
<
F
>
(),
std
::
declval
<
Args
>
()...))
TryDecomposePair
(
F
&&
f
,
Args
&&
...
args
)
{
return
DecomposePairImpl
(
0
,
std
::
forward
<
F
>
(
f
),
std
::
forward
<
Args
>
(
args
)...);
}
TEST
(
DecomposePair
,
Decomposable
)
{
auto
f
=
[](
const
int
&
x
,
std
::
piecewise_construct_t
,
std
::
tuple
<
int
&&>
k
,
std
::
tuple
<
double
>&&
v
)
{
EXPECT_EQ
(
&
x
,
&
std
::
get
<
0
>
(
k
));
EXPECT_EQ
(
42
,
x
);
EXPECT_EQ
(
0.5
,
std
::
get
<
0
>
(
v
));
return
'A'
;
};
EXPECT_EQ
(
'A'
,
TryDecomposePair
(
f
,
42
,
0.5
));
EXPECT_EQ
(
'A'
,
TryDecomposePair
(
f
,
std
::
make_pair
(
42
,
0.5
)));
EXPECT_EQ
(
'A'
,
TryDecomposePair
(
f
,
std
::
piecewise_construct
,
std
::
make_tuple
(
42
),
std
::
make_tuple
(
0.5
)));
}
TEST
(
DecomposePair
,
NotDecomposable
)
{
auto
f
=
[](...)
{
ADD_FAILURE
()
<<
"Must not be called"
;
return
'A'
;
};
EXPECT_STREQ
(
"not decomposable"
,
TryDecomposePair
(
f
));
EXPECT_STREQ
(
"not decomposable"
,
TryDecomposePair
(
f
,
std
::
piecewise_construct
,
std
::
make_tuple
(),
std
::
make_tuple
(
0.5
)));
}
}
// namespace
}
// namespace priv
}
// namespace phmap
third_party/parallel-hashmap/tests/dump_load_test.cc
0 → 100644
View file @
2951b12d
#include <vector>
#include "gtest/gtest.h"
#include "parallel_hashmap/phmap_dump.h"
namespace
phmap
{
namespace
priv
{
namespace
{
TEST
(
DumpLoad
,
FlatHashSet_uint32
)
{
phmap
::
flat_hash_set
<
uint32_t
>
st1
=
{
1991
,
1202
};
{
phmap
::
BinaryOutputArchive
ar_out
(
"./dump.data"
);
EXPECT_TRUE
(
st1
.
phmap_dump
(
ar_out
));
}
phmap
::
flat_hash_set
<
uint32_t
>
st2
;
{
phmap
::
BinaryInputArchive
ar_in
(
"./dump.data"
);
EXPECT_TRUE
(
st2
.
phmap_load
(
ar_in
));
}
EXPECT_TRUE
(
st1
==
st2
);
}
TEST
(
DumpLoad
,
FlatHashMap_uint64_uint32
)
{
phmap
::
flat_hash_map
<
uint64_t
,
uint32_t
>
mp1
=
{
{
78731
,
99
},
{
13141
,
299
},
{
2651
,
101
}
};
{
phmap
::
BinaryOutputArchive
ar_out
(
"./dump.data"
);
EXPECT_TRUE
(
mp1
.
phmap_dump
(
ar_out
));
}
phmap
::
flat_hash_map
<
uint64_t
,
uint32_t
>
mp2
;
{
phmap
::
BinaryInputArchive
ar_in
(
"./dump.data"
);
EXPECT_TRUE
(
mp2
.
phmap_load
(
ar_in
));
}
EXPECT_TRUE
(
mp1
==
mp2
);
}
TEST
(
DumpLoad
,
ParallelFlatHashMap_uint64_uint32
)
{
phmap
::
parallel_flat_hash_map
<
uint64_t
,
uint32_t
>
mp1
=
{
{
99
,
299
},
{
992
,
2991
},
{
299
,
1299
}
};
{
phmap
::
BinaryOutputArchive
ar_out
(
"./dump.data"
);
EXPECT_TRUE
(
mp1
.
phmap_dump
(
ar_out
));
}
phmap
::
parallel_flat_hash_map
<
uint64_t
,
uint32_t
>
mp2
;
{
phmap
::
BinaryInputArchive
ar_in
(
"./dump.data"
);
EXPECT_TRUE
(
mp2
.
phmap_load
(
ar_in
));
}
EXPECT_TRUE
(
mp1
==
mp2
);
}
}
}
}
third_party/parallel-hashmap/tests/erase_if_test.cc
0 → 100644
View file @
2951b12d
#include <vector>
#include "gtest/gtest.h"
#include "parallel_hashmap/phmap.h"
namespace
phmap
{
namespace
priv
{
namespace
{
TEST
(
EraseIf
,
FlatHashSet_uint32
)
{
phmap
::
flat_hash_set
<
uint32_t
>
st1
=
{
3
,
6
,
7
,
9
};
auto
num_erased
=
erase_if
(
st1
,
[](
const
uint32_t
&
v
)
{
return
v
>=
7
;
});
EXPECT_TRUE
(
num_erased
==
2
);
phmap
::
flat_hash_set
<
uint32_t
>
st2
=
{
0
,
2
,
3
,
6
};
num_erased
=
erase_if
(
st2
,
[](
const
uint32_t
&
v
)
{
return
v
<=
2
;
});
EXPECT_TRUE
(
num_erased
==
2
);
EXPECT_TRUE
(
st1
==
st2
);
}
TEST
(
EraseIf
,
FlatHashMap_uint64_uint32
)
{
using
map
=
phmap
::
flat_hash_map
<
uint32_t
,
uint32_t
>
;
map
st1
=
{
{
3
,
0
},
{
6
,
0
},
{
7
,
0
},
{
9
,
0
}
};
auto
num_erased
=
erase_if
(
st1
,
[](
const
map
::
value_type
&
v
)
{
return
v
.
first
>=
7
;
});
EXPECT_TRUE
(
num_erased
==
2
);
map
st2
=
{
{
0
,
0
},
{
2
,
0
},
{
3
,
0
},
{
6
,
0
}
};
num_erased
=
erase_if
(
st2
,
[](
const
map
::
value_type
&
v
)
{
return
v
.
first
<=
2
;
});
EXPECT_TRUE
(
num_erased
==
2
);
EXPECT_TRUE
(
st1
==
st2
);
}
TEST
(
EraseIf
,
ParallelFlatHashMap_uint64_uint32
)
{
using
map
=
phmap
::
parallel_flat_hash_map
<
uint32_t
,
uint32_t
>
;
map
st1
=
{
{
3
,
0
},
{
6
,
0
},
{
7
,
0
},
{
9
,
0
}
};
auto
num_erased
=
erase_if
(
st1
,
[](
const
map
::
value_type
&
v
)
{
return
v
.
first
>=
7
;
});
EXPECT_TRUE
(
num_erased
==
2
);
map
st2
=
{
{
0
,
0
},
{
2
,
0
},
{
3
,
0
},
{
6
,
0
}
};
num_erased
=
erase_if
(
st2
,
[](
const
map
::
value_type
&
v
)
{
return
v
.
first
<=
2
;
});
EXPECT_TRUE
(
num_erased
==
2
);
EXPECT_TRUE
(
st1
==
st2
);
}
}
}
}
third_party/parallel-hashmap/tests/flat_hash_map_test.cc
0 → 100644
View file @
2951b12d
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIS_HASH_MAP
#define THIS_HASH_MAP flat_hash_map
#define THIS_TEST_NAME FlatHashMap
#define ORIG_FLAT_HASH_MAP 1
#endif
#ifndef THIS_EXTRA_TPL_PARAMS
#define THIS_EXTRA_TPL_PARAMS
#endif
#include "parallel_hashmap/phmap.h"
#if defined(PHMAP_HAVE_STD_ANY)
#include <any>
#endif
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4710 4711)
#endif
#include "hash_generator_testing.h"
#include "unordered_map_constructor_test.h"
#include "unordered_map_lookup_test.h"
#include "unordered_map_members_test.h"
#include "unordered_map_modifiers_test.h"
#ifdef _MSC_VER
#pragma warning(pop)
#endif
namespace
phmap
{
namespace
priv
{
namespace
{
using
::
phmap
::
priv
::
hash_internal
::
Enum
;
using
::
phmap
::
priv
::
hash_internal
::
EnumClass
;
using
::
testing
::
_
;
using
::
testing
::
Pair
;
using
::
testing
::
UnorderedElementsAre
;
template
<
class
K
,
class
V
>
using
Map
=
THIS_HASH_MAP
<
K
,
V
,
StatefulTestingHash
,
StatefulTestingEqual
,
Alloc
<
std
::
pair
<
const
K
,
V
>>
THIS_EXTRA_TPL_PARAMS
>
;
template
<
class
K
,
class
V
,
class
H
=
phmap
::
priv
::
hash_default_hash
<
K
>,
class
Eq
=
phmap
::
priv
::
hash_default_eq
<
K
>
,
class
Alloc
=
phmap
::
priv
::
Allocator
<
phmap
::
priv
::
Pair
<
const
K
,
V
>>>
using
ThisMap
=
THIS_HASH_MAP
<
K
,
V
,
H
,
Eq
,
Alloc
THIS_EXTRA_TPL_PARAMS
>
;
static_assert
(
!
std
::
is_standard_layout
<
NonStandardLayout
>
(),
""
);
using
MapTypes
=
::
testing
::
Types
<
Map
<
int
,
int
>
,
Map
<
std
::
string
,
int
>
,
Map
<
Enum
,
std
::
string
>
,
Map
<
EnumClass
,
int
>
,
Map
<
int
,
NonStandardLayout
>
,
Map
<
NonStandardLayout
,
int
>>
;
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
ConstructorTest
,
MapTypes
);
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
LookupTest
,
MapTypes
);
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
MembersTest
,
MapTypes
);
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
ModifiersTest
,
MapTypes
);
TEST
(
THIS_TEST_NAME
,
StandardLayout
)
{
struct
Int
{
explicit
Int
(
size_t
val
)
:
value
(
val
)
{}
Int
()
:
value
(
0
)
{
ADD_FAILURE
();
}
Int
(
const
Int
&
other
)
:
value
(
other
.
value
)
{
ADD_FAILURE
();
}
Int
(
Int
&&
)
=
default
;
bool
operator
==
(
const
Int
&
other
)
const
{
return
value
==
other
.
value
;
}
size_t
value
;
};
static_assert
(
std
::
is_standard_layout
<
Int
>
(),
""
);
struct
Hash
{
size_t
operator
()(
const
Int
&
obj
)
const
{
return
obj
.
value
;
}
};
// Verify that neither the key nor the value get default-constructed or
// copy-constructed.
{
ThisMap
<
Int
,
Int
,
Hash
>
m
;
m
.
try_emplace
(
Int
(
1
),
Int
(
2
));
m
.
try_emplace
(
Int
(
3
),
Int
(
4
));
m
.
erase
(
Int
(
1
));
m
.
rehash
(
2
*
m
.
bucket_count
());
}
{
ThisMap
<
Int
,
Int
,
Hash
>
m
;
m
.
try_emplace
(
Int
(
1
),
Int
(
2
));
m
.
try_emplace
(
Int
(
3
),
Int
(
4
));
m
.
erase
(
Int
(
1
));
m
.
clear
();
}
}
// gcc becomes unhappy if this is inside the method, so pull it out here.
struct
balast
{};
TEST
(
THIS_TEST_NAME
,
IteratesMsan
)
{
// Because SwissTable randomizes on pointer addresses, we keep old tables
// around to ensure we don't reuse old memory.
std
::
vector
<
ThisMap
<
int
,
balast
>>
garbage
;
for
(
int
i
=
0
;
i
<
100
;
++
i
)
{
ThisMap
<
int
,
balast
>
t
;
for
(
int
j
=
0
;
j
<
100
;
++
j
)
{
t
[
j
];
for
(
const
auto
&
p
:
t
)
EXPECT_THAT
(
p
,
Pair
(
_
,
_
));
}
garbage
.
push_back
(
std
::
move
(
t
));
}
}
// Demonstration of the "Lazy Key" pattern. This uses heterogeneous insert to
// avoid creating expensive key elements when the item is already present in the
// map.
struct
LazyInt
{
explicit
LazyInt
(
size_t
val
,
int
*
tracker_
)
:
value
(
val
),
tracker
(
tracker_
)
{}
explicit
operator
size_t
()
const
{
++*
tracker
;
return
value
;
}
size_t
value
;
int
*
tracker
;
};
struct
Hash
{
using
is_transparent
=
void
;
int
*
tracker
;
size_t
operator
()(
size_t
obj
)
const
{
++*
tracker
;
return
obj
;
}
size_t
operator
()(
const
LazyInt
&
obj
)
const
{
++*
tracker
;
return
obj
.
value
;
}
};
struct
Eq
{
using
is_transparent
=
void
;
bool
operator
()(
size_t
lhs
,
size_t
rhs
)
const
{
return
lhs
==
rhs
;
}
bool
operator
()(
size_t
lhs
,
const
LazyInt
&
rhs
)
const
{
return
lhs
==
rhs
.
value
;
}
};
TEST
(
THIS_TEST_NAME
,
PtrKet
)
{
using
H
=
ThisMap
<
void
*
,
bool
>
;
H
hash
;
int
a
,
b
;
hash
.
insert
(
H
::
value_type
(
&
a
,
true
));
hash
.
insert
(
H
::
value_type
(
&
b
,
false
));
}
TEST
(
THIS_TEST_NAME
,
LazyKeyPattern
)
{
// hashes are only guaranteed in opt mode, we use assertions to track internal
// state that can cause extra calls to hash.
int
conversions
=
0
;
int
hashes
=
0
;
ThisMap
<
size_t
,
size_t
,
Hash
,
Eq
>
m
(
0
,
Hash
{
&
hashes
});
m
.
reserve
(
3
);
m
[
LazyInt
(
1
,
&
conversions
)]
=
1
;
EXPECT_THAT
(
m
,
UnorderedElementsAre
(
Pair
(
1
,
1
)));
EXPECT_EQ
(
conversions
,
1
);
#ifdef NDEBUG
EXPECT_EQ
(
hashes
,
1
);
#endif
m
[
LazyInt
(
1
,
&
conversions
)]
=
2
;
EXPECT_THAT
(
m
,
UnorderedElementsAre
(
Pair
(
1
,
2
)));
EXPECT_EQ
(
conversions
,
1
);
#ifdef NDEBUG
EXPECT_EQ
(
hashes
,
2
);
#endif
m
.
try_emplace
(
LazyInt
(
2
,
&
conversions
),
3
);
EXPECT_THAT
(
m
,
UnorderedElementsAre
(
Pair
(
1
,
2
),
Pair
(
2
,
3
)));
EXPECT_EQ
(
conversions
,
2
);
#if defined(NDEBUG) && ORIG_FLAT_HASH_MAP
// for parallel maps, the reserve(3) above is not sufficient to guarantee that a submap will not resize and therefore rehash
EXPECT_EQ
(
hashes
,
3
);
#endif
m
.
try_emplace
(
LazyInt
(
2
,
&
conversions
),
4
);
EXPECT_THAT
(
m
,
UnorderedElementsAre
(
Pair
(
1
,
2
),
Pair
(
2
,
3
)));
EXPECT_EQ
(
conversions
,
2
);
#if defined(NDEBUG) && ORIG_FLAT_HASH_MAP
// for parallel maps, the reserve(3) above is not sufficient to guarantee that a submap will not resize and therefore rehash
EXPECT_EQ
(
hashes
,
4
);
#endif
}
TEST
(
THIS_TEST_NAME
,
BitfieldArgument
)
{
union
{
int
n
:
1
;
};
n
=
0
;
ThisMap
<
int
,
int
>
m
;
m
.
erase
(
n
);
m
.
count
(
n
);
m
.
prefetch
(
n
);
m
.
find
(
n
);
m
.
contains
(
n
);
m
.
equal_range
(
n
);
m
.
insert_or_assign
(
n
,
n
);
m
.
insert_or_assign
(
m
.
end
(),
n
,
n
);
m
.
try_emplace
(
n
);
m
.
try_emplace
(
m
.
end
(),
n
);
m
.
at
(
n
);
m
[
n
];
}
TEST
(
THIS_TEST_NAME
,
MergeExtractInsert
)
{
// We can't test mutable keys, or non-copyable keys with ThisMap.
// Test that the nodes have the proper API.
ThisMap
<
int
,
int
>
m
=
{{
1
,
7
},
{
2
,
9
}};
auto
node
=
m
.
extract
(
1
);
EXPECT_TRUE
(
node
);
EXPECT_EQ
(
node
.
key
(),
1
);
EXPECT_EQ
(
node
.
mapped
(),
7
);
EXPECT_THAT
(
m
,
UnorderedElementsAre
(
Pair
(
2
,
9
)));
node
.
mapped
()
=
17
;
m
.
insert
(
std
::
move
(
node
));
EXPECT_THAT
(
m
,
UnorderedElementsAre
(
Pair
(
1
,
17
),
Pair
(
2
,
9
)));
}
#if 0 && !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__) && defined(PHMAP_HAVE_STD_ANY)
TEST(THIS_TEST_NAME, Any) {
ThisMap<int, std::any> m;
m.emplace(1, 7);
auto it = m.find(1);
ASSERT_NE(it, m.end());
EXPECT_EQ(7, std::any_cast<int>(it->second));
m.emplace(std::piecewise_construct, std::make_tuple(2), std::make_tuple(8));
it = m.find(2);
ASSERT_NE(it, m.end());
EXPECT_EQ(8, std::any_cast<int>(it->second));
m.emplace(std::piecewise_construct, std::make_tuple(3),
std::make_tuple(std::any(9)));
it = m.find(3);
ASSERT_NE(it, m.end());
EXPECT_EQ(9, std::any_cast<int>(it->second));
struct H {
size_t operator()(const std::any&) const { return 0; }
};
struct E {
bool operator()(const std::any&, const std::any&) const { return true; }
};
ThisMap<std::any, int, H, E> m2;
m2.emplace(1, 7);
auto it2 = m2.find(1);
ASSERT_NE(it2, m2.end());
EXPECT_EQ(7, it2->second);
}
#endif // __ANDROID__
}
// namespace
}
// namespace priv
}
// namespace phmap
third_party/parallel-hashmap/tests/flat_hash_set_test.cc
0 → 100644
View file @
2951b12d
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIS_HASH_SET
#define THIS_HASH_SET flat_hash_set
#define THIS_TEST_NAME FlatHashSet
#endif
#include "parallel_hashmap/phmap.h"
#include <vector>
#include "hash_generator_testing.h"
#include "unordered_set_constructor_test.h"
#include "unordered_set_lookup_test.h"
#include "unordered_set_members_test.h"
#include "unordered_set_modifiers_test.h"
namespace
phmap
{
namespace
priv
{
namespace
{
using
::
phmap
::
priv
::
hash_internal
::
Enum
;
using
::
phmap
::
priv
::
hash_internal
::
EnumClass
;
using
::
testing
::
Pointee
;
using
::
testing
::
UnorderedElementsAre
;
using
::
testing
::
UnorderedElementsAreArray
;
template
<
class
T
>
using
Set
=
phmap
::
THIS_HASH_SET
<
T
,
StatefulTestingHash
,
StatefulTestingEqual
,
Alloc
<
T
>>
;
using
SetTypes
=
::
testing
::
Types
<
Set
<
int
>
,
Set
<
std
::
string
>
,
Set
<
Enum
>
,
Set
<
EnumClass
>>
;
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
ConstructorTest
,
SetTypes
);
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
LookupTest
,
SetTypes
);
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
MembersTest
,
SetTypes
);
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
ModifiersTest
,
SetTypes
);
#if PHMAP_HAVE_STD_STRING_VIEW
TEST
(
THIS_TEST_NAME
,
EmplaceString
)
{
std
::
vector
<
std
::
string
>
v
=
{
"a"
,
"b"
};
phmap
::
THIS_HASH_SET
<
std
::
string_view
>
hs
(
v
.
begin
(),
v
.
end
());
//EXPECT_THAT(hs, UnorderedElementsAreArray(v));
}
#endif
TEST
(
THIS_TEST_NAME
,
BitfieldArgument
)
{
union
{
int
n
:
1
;
};
n
=
0
;
phmap
::
THIS_HASH_SET
<
int
>
s
=
{
n
};
s
.
insert
(
n
);
s
.
insert
(
s
.
end
(),
n
);
s
.
insert
({
n
});
s
.
erase
(
n
);
s
.
count
(
n
);
s
.
prefetch
(
n
);
s
.
find
(
n
);
s
.
contains
(
n
);
s
.
equal_range
(
n
);
}
TEST
(
THIS_TEST_NAME
,
MergeExtractInsert
)
{
struct
Hash
{
size_t
operator
()(
const
std
::
unique_ptr
<
int
>&
p
)
const
{
return
*
p
;
}
};
struct
Eq
{
bool
operator
()(
const
std
::
unique_ptr
<
int
>&
a
,
const
std
::
unique_ptr
<
int
>&
b
)
const
{
return
*
a
==
*
b
;
}
};
phmap
::
THIS_HASH_SET
<
std
::
unique_ptr
<
int
>
,
Hash
,
Eq
>
set1
,
set2
;
set1
.
insert
(
phmap
::
make_unique
<
int
>
(
7
));
set1
.
insert
(
phmap
::
make_unique
<
int
>
(
17
));
set2
.
insert
(
phmap
::
make_unique
<
int
>
(
7
));
set2
.
insert
(
phmap
::
make_unique
<
int
>
(
19
));
EXPECT_THAT
(
set1
,
UnorderedElementsAre
(
Pointee
(
7
),
Pointee
(
17
)));
EXPECT_THAT
(
set2
,
UnorderedElementsAre
(
Pointee
(
7
),
Pointee
(
19
)));
set1
.
merge
(
set2
);
EXPECT_THAT
(
set1
,
UnorderedElementsAre
(
Pointee
(
7
),
Pointee
(
17
),
Pointee
(
19
)));
EXPECT_THAT
(
set2
,
UnorderedElementsAre
(
Pointee
(
7
)));
auto
node
=
set1
.
extract
(
phmap
::
make_unique
<
int
>
(
7
));
EXPECT_TRUE
(
node
);
EXPECT_THAT
(
node
.
value
(),
Pointee
(
7
));
EXPECT_THAT
(
set1
,
UnorderedElementsAre
(
Pointee
(
17
),
Pointee
(
19
)));
auto
insert_result
=
set2
.
insert
(
std
::
move
(
node
));
EXPECT_FALSE
(
node
);
EXPECT_FALSE
(
insert_result
.
inserted
);
EXPECT_TRUE
(
insert_result
.
node
);
EXPECT_THAT
(
insert_result
.
node
.
value
(),
Pointee
(
7
));
EXPECT_EQ
(
**
insert_result
.
position
,
7
);
EXPECT_NE
(
insert_result
.
position
->
get
(),
insert_result
.
node
.
value
().
get
());
EXPECT_THAT
(
set2
,
UnorderedElementsAre
(
Pointee
(
7
)));
node
=
set1
.
extract
(
phmap
::
make_unique
<
int
>
(
17
));
EXPECT_TRUE
(
node
);
EXPECT_THAT
(
node
.
value
(),
Pointee
(
17
));
EXPECT_THAT
(
set1
,
UnorderedElementsAre
(
Pointee
(
19
)));
node
.
value
()
=
phmap
::
make_unique
<
int
>
(
23
);
insert_result
=
set2
.
insert
(
std
::
move
(
node
));
EXPECT_FALSE
(
node
);
EXPECT_TRUE
(
insert_result
.
inserted
);
EXPECT_FALSE
(
insert_result
.
node
);
EXPECT_EQ
(
**
insert_result
.
position
,
23
);
EXPECT_THAT
(
set2
,
UnorderedElementsAre
(
Pointee
(
7
),
Pointee
(
23
)));
}
}
// namespace
}
// namespace priv
}
// namespace phmap
third_party/parallel-hashmap/tests/hash_generator_testing.h
0 → 100644
View file @
2951b12d
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Generates random values for testing. Specialized only for the few types we
// care about.
#ifndef PHMAP_PRIV_HASH_GENERATOR_TESTING_H_
#define PHMAP_PRIV_HASH_GENERATOR_TESTING_H_
#include <stdint.h>
#include <algorithm>
#include <iosfwd>
#include <random>
#include <tuple>
#include <type_traits>
#include <utility>
#include <string>
#include <deque>
#include <functional>
#if PHMAP_HAVE_STD_STRING_VIEW
#include <string_view>
#endif
#include "hash_policy_testing.h"
namespace
phmap
{
namespace
priv
{
namespace
hash_internal
{
namespace
generator_internal
{
template
<
class
Container
,
class
=
void
>
struct
IsMap
:
std
::
false_type
{};
template
<
class
Map
>
struct
IsMap
<
Map
,
phmap
::
void_t
<
typename
Map
::
mapped_type
>>
:
std
::
true_type
{};
}
// namespace generator_internal
namespace
{
class
RandomDeviceSeedSeq
{
public:
using
result_type
=
typename
std
::
random_device
::
result_type
;
template
<
class
Iterator
>
void
generate
(
Iterator
start
,
Iterator
end
)
{
while
(
start
!=
end
)
{
*
start
=
gen_
();
++
start
;
}
}
private:
std
::
random_device
gen_
;
};
}
// namespace
std
::
mt19937_64
*
GetSharedRng
();
// declaration
std
::
mt19937_64
*
GetSharedRng
()
{
RandomDeviceSeedSeq
seed_seq
;
static
auto
*
rng
=
new
std
::
mt19937_64
(
seed_seq
);
return
rng
;
}
enum
Enum
{
kEnumEmpty
,
kEnumDeleted
,
};
enum
class
EnumClass
:
uint64_t
{
kEmpty
,
kDeleted
,
};
inline
std
::
ostream
&
operator
<<
(
std
::
ostream
&
o
,
const
EnumClass
&
ec
)
{
return
o
<<
static_cast
<
uint64_t
>
(
ec
);
}
template
<
class
T
,
class
E
=
void
>
struct
Generator
;
template
<
class
T
>
struct
Generator
<
T
,
typename
std
::
enable_if
<
std
::
is_integral
<
T
>::
value
>::
type
>
{
T
operator
()()
const
{
std
::
uniform_int_distribution
<
T
>
dist
;
return
dist
(
*
GetSharedRng
());
}
};
template
<
>
struct
Generator
<
Enum
>
{
Enum
operator
()()
const
{
std
::
uniform_int_distribution
<
typename
std
::
underlying_type
<
Enum
>::
type
>
dist
;
while
(
true
)
{
auto
variate
=
dist
(
*
GetSharedRng
());
if
(
variate
!=
kEnumEmpty
&&
variate
!=
kEnumDeleted
)
return
static_cast
<
Enum
>
(
variate
);
}
}
};
template
<
>
struct
Generator
<
EnumClass
>
{
EnumClass
operator
()()
const
{
std
::
uniform_int_distribution
<
typename
std
::
underlying_type
<
EnumClass
>::
type
>
dist
;
while
(
true
)
{
EnumClass
variate
=
static_cast
<
EnumClass
>
(
dist
(
*
GetSharedRng
()));
if
(
variate
!=
EnumClass
::
kEmpty
&&
variate
!=
EnumClass
::
kDeleted
)
return
static_cast
<
EnumClass
>
(
variate
);
}
}
};
template
<
>
struct
Generator
<
std
::
string
>
{
std
::
string
operator
()()
const
{
// NOLINTNEXTLINE(runtime/int)
std
::
uniform_int_distribution
<
short
>
chars
(
0x20
,
0x7E
);
std
::
string
res
;
res
.
resize
(
32
);
std
::
generate
(
res
.
begin
(),
res
.
end
(),
[
&
]()
{
return
(
char
)
chars
(
*
GetSharedRng
());
});
return
res
;
}
};
#if PHMAP_HAVE_STD_STRING_VIEW
template
<
>
struct
Generator
<
std
::
string_view
>
{
std
::
string_view
operator
()()
const
{
static
auto
*
arena
=
new
std
::
deque
<
std
::
string
>
();
// NOLINTNEXTLINE(runtime/int)
std
::
uniform_int_distribution
<
short
>
chars
(
0x20
,
0x7E
);
arena
->
emplace_back
();
auto
&
res
=
arena
->
back
();
res
.
resize
(
32
);
std
::
generate
(
res
.
begin
(),
res
.
end
(),
[
&
]()
{
return
(
char
)
chars
(
*
GetSharedRng
());
});
return
res
;
}
};
#endif
template
<
>
struct
Generator
<
NonStandardLayout
>
{
NonStandardLayout
operator
()()
const
{
return
NonStandardLayout
(
Generator
<
std
::
string
>
()());
}
};
template
<
class
K
,
class
V
>
struct
Generator
<
std
::
pair
<
K
,
V
>>
{
std
::
pair
<
K
,
V
>
operator
()()
const
{
return
std
::
pair
<
K
,
V
>
(
Generator
<
typename
std
::
decay
<
K
>::
type
>
()(),
Generator
<
typename
std
::
decay
<
V
>::
type
>
()());
}
};
template
<
class
...
Ts
>
struct
Generator
<
std
::
tuple
<
Ts
...
>>
{
std
::
tuple
<
Ts
...
>
operator
()()
const
{
return
std
::
tuple
<
Ts
...
>
(
Generator
<
typename
std
::
decay
<
Ts
>::
type
>
()()...);
}
};
template
<
class
U
>
struct
Generator
<
U
,
phmap
::
void_t
<
decltype
(
std
::
declval
<
U
&>
().
key
()),
decltype
(
std
::
declval
<
U
&>
().
value
())
>>
:
Generator
<
std
::
pair
<
typename
std
::
decay
<
decltype
(
std
::
declval
<
U
&>
().
key
())
>::
type
,
typename
std
::
decay
<
decltype
(
std
::
declval
<
U
&>
().
value
())
>::
type
>>
{};
template
<
class
Container
>
using
GeneratedType
=
decltype
(
std
::
declval
<
const
Generator
<
typename
std
::
conditional
<
generator_internal
::
IsMap
<
Container
>::
value
,
typename
Container
::
value_type
,
typename
Container
::
key_type
>::
type
>&>
()());
}
// namespace hash_internal
}
// namespace priv
}
// namespace phmap
namespace
std
{
using
phmap
::
priv
::
hash_internal
::
EnumClass
;
using
phmap
::
priv
::
hash_internal
::
Enum
;
template
<
>
struct
hash
<
EnumClass
>
{
std
::
size_t
operator
()(
EnumClass
const
&
p
)
const
{
return
(
std
::
size_t
)
p
;
}
};
template
<
>
struct
hash
<
Enum
>
{
std
::
size_t
operator
()(
Enum
const
&
p
)
const
{
return
(
std
::
size_t
)
p
;
}
};
}
#endif // PHMAP_PRIV_HASH_GENERATOR_TESTING_H_
third_party/parallel-hashmap/tests/hash_policy_testing.h
0 → 100644
View file @
2951b12d
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Utilities to help tests verify that hash tables properly handle stateful
// allocators and hash functions.
#ifndef PHMAP_PRIV_HASH_POLICY_TESTING_H_
#define PHMAP_PRIV_HASH_POLICY_TESTING_H_
#include <cstdlib>
#include <limits>
#include <memory>
#include <ostream>
#include <type_traits>
#include <utility>
#include <vector>
namespace
phmap
{
namespace
priv
{
namespace
hash_testing_internal
{
template
<
class
Derived
>
struct
WithId
{
WithId
()
:
id_
(
next_id
<
Derived
>
())
{}
WithId
(
const
WithId
&
that
)
:
id_
(
that
.
id_
)
{}
WithId
(
WithId
&&
that
)
:
id_
(
that
.
id_
)
{
that
.
id_
=
0
;
}
WithId
&
operator
=
(
const
WithId
&
that
)
{
id_
=
that
.
id_
;
return
*
this
;
}
WithId
&
operator
=
(
WithId
&&
that
)
{
id_
=
that
.
id_
;
that
.
id_
=
0
;
return
*
this
;
}
size_t
id
()
const
{
return
id_
;
}
friend
bool
operator
==
(
const
WithId
&
a
,
const
WithId
&
b
)
{
return
a
.
id_
==
b
.
id_
;
}
friend
bool
operator
!=
(
const
WithId
&
a
,
const
WithId
&
b
)
{
return
!
(
a
==
b
);
}
protected:
explicit
WithId
(
size_t
id
)
:
id_
(
id
)
{}
private:
size_t
id_
;
template
<
class
T
>
static
size_t
next_id
()
{
// 0 is reserved for moved from state.
static
size_t
gId
=
1
;
return
gId
++
;
}
};
}
// namespace hash_testing_internal
struct
NonStandardLayout
{
NonStandardLayout
()
{}
explicit
NonStandardLayout
(
std
::
string
s
)
:
value
(
std
::
move
(
s
))
{}
virtual
~
NonStandardLayout
()
{}
friend
bool
operator
==
(
const
NonStandardLayout
&
a
,
const
NonStandardLayout
&
b
)
{
return
a
.
value
==
b
.
value
;
}
friend
bool
operator
!=
(
const
NonStandardLayout
&
a
,
const
NonStandardLayout
&
b
)
{
return
a
.
value
!=
b
.
value
;
}
template
<
typename
H
>
friend
H
AbslHashValue
(
H
h
,
const
NonStandardLayout
&
v
)
{
return
H
::
combine
(
std
::
move
(
h
),
v
.
value
);
}
std
::
string
value
;
};
struct
StatefulTestingHash
:
phmap
::
priv
::
hash_testing_internal
::
WithId
<
StatefulTestingHash
>
{
template
<
class
T
>
size_t
operator
()(
const
T
&
t
)
const
{
return
phmap
::
Hash
<
T
>
{}(
t
);
}
};
struct
StatefulTestingEqual
:
phmap
::
priv
::
hash_testing_internal
::
WithId
<
StatefulTestingEqual
>
{
template
<
class
T
,
class
U
>
bool
operator
()(
const
T
&
t
,
const
U
&
u
)
const
{
return
t
==
u
;
}
};
// It is expected that Alloc() == Alloc() for all allocators so we cannot use
// WithId base. We need to explicitly assign ids.
template
<
class
T
=
int
>
struct
Alloc
:
std
::
allocator
<
T
>
{
using
propagate_on_container_swap
=
std
::
true_type
;
// Using old paradigm for this to ensure compatibility.
explicit
Alloc
(
size_t
id
=
0
)
:
id_
(
id
)
{}
Alloc
(
const
Alloc
&
)
=
default
;
Alloc
&
operator
=
(
const
Alloc
&
)
=
default
;
template
<
class
U
>
Alloc
(
const
Alloc
<
U
>&
that
)
:
std
::
allocator
<
T
>
(
that
),
id_
(
that
.
id
())
{}
template
<
class
U
>
struct
rebind
{
using
other
=
Alloc
<
U
>
;
};
size_t
id
()
const
{
return
id_
;
}
friend
bool
operator
==
(
const
Alloc
&
a
,
const
Alloc
&
b
)
{
return
a
.
id_
==
b
.
id_
;
}
friend
bool
operator
!=
(
const
Alloc
&
a
,
const
Alloc
&
b
)
{
return
!
(
a
==
b
);
}
private:
size_t
id_
=
(
std
::
numeric_limits
<
size_t
>::
max
)();
};
template
<
class
Map
>
auto
items
(
const
Map
&
m
)
->
std
::
vector
<
std
::
pair
<
typename
Map
::
key_type
,
typename
Map
::
mapped_type
>>
{
using
std
::
get
;
std
::
vector
<
std
::
pair
<
typename
Map
::
key_type
,
typename
Map
::
mapped_type
>>
res
;
res
.
reserve
(
m
.
size
());
for
(
const
auto
&
v
:
m
)
res
.
emplace_back
(
get
<
0
>
(
v
),
get
<
1
>
(
v
));
return
res
;
}
template
<
class
Set
>
auto
keys
(
const
Set
&
s
)
->
std
::
vector
<
typename
std
::
decay
<
typename
Set
::
key_type
>::
type
>
{
std
::
vector
<
typename
std
::
decay
<
typename
Set
::
key_type
>::
type
>
res
;
res
.
reserve
(
s
.
size
());
for
(
const
auto
&
v
:
s
)
res
.
emplace_back
(
v
);
return
res
;
}
}
// namespace priv
}
// namespace phmap
namespace
std
{
// inject specialization of std::hash for NonStandardLayout into namespace std
// ----------------------------------------------------------------
template
<
>
struct
hash
<
phmap
::
priv
::
NonStandardLayout
>
{
std
::
size_t
operator
()(
phmap
::
priv
::
NonStandardLayout
const
&
p
)
const
{
return
std
::
hash
<
std
::
string
>
()(
p
.
value
);
}
};
}
// PHMAP_UNORDERED_SUPPORTS_ALLOC_CTORS is false for glibcxx versions
// where the unordered containers are missing certain constructors that
// take allocator arguments. This test is defined ad-hoc for the platforms
// we care about (notably Crosstool 17) because libstdcxx's useless
// versioning scheme precludes a more principled solution.
// From GCC-4.9 Changelog: (src: https://gcc.gnu.org/gcc-4.9/changes.html)
// "the unordered associative containers in <unordered_map> and <unordered_set>
// meet the allocator-aware container requirements;"
#if (defined(__GLIBCXX__) && __GLIBCXX__ <= 20140425 ) || \
( __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9 ))
#define PHMAP_UNORDERED_SUPPORTS_ALLOC_CTORS 0
#else
#define PHMAP_UNORDERED_SUPPORTS_ALLOC_CTORS 1
#endif
#endif // PHMAP_PRIV_HASH_POLICY_TESTING_H_
third_party/parallel-hashmap/tests/hash_policy_testing_test.cc
0 → 100644
View file @
2951b12d
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "parallel_hashmap/phmap.h"
#include "hash_policy_testing.h"
#include "gtest/gtest.h"
namespace
phmap
{
namespace
priv
{
namespace
{
TEST
(
_
,
Hash
)
{
StatefulTestingHash
h1
;
EXPECT_EQ
(
1
,
h1
.
id
());
StatefulTestingHash
h2
;
EXPECT_EQ
(
2
,
h2
.
id
());
StatefulTestingHash
h1c
(
h1
);
EXPECT_EQ
(
1
,
h1c
.
id
());
StatefulTestingHash
h2m
(
std
::
move
(
h2
));
EXPECT_EQ
(
2
,
h2m
.
id
());
EXPECT_EQ
(
0
,
h2
.
id
());
StatefulTestingHash
h3
;
EXPECT_EQ
(
3
,
h3
.
id
());
h3
=
StatefulTestingHash
();
EXPECT_EQ
(
4
,
h3
.
id
());
h3
=
std
::
move
(
h1
);
EXPECT_EQ
(
1
,
h3
.
id
());
}
}
// namespace
}
// namespace priv
}
// namespace phmap
third_party/parallel-hashmap/tests/hashtable_debug.h
0 → 100644
View file @
2951b12d
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// This library provides APIs to debug the probing behavior of hash tables.
//
// In general, the probing behavior is a black box for users and only the
// side effects can be measured in the form of performance differences.
// These APIs give a glimpse on the actual behavior of the probing algorithms in
// these hashtables given a specified hash function and a set of elements.
//
// The probe count distribution can be used to assess the quality of the hash
// function for that particular hash table. Note that a hash function that
// performs well in one hash table implementation does not necessarily performs
// well in a different one.
//
// This library supports std::unordered_{set,map}, dense_hash_{set,map} and
// phmap::{flat,node,string}_hash_{set,map}.
#ifndef PHMAP_PRIV_HASHTABLE_DEBUG_H_
#define PHMAP_PRIV_HASHTABLE_DEBUG_H_
#include <cstddef>
#include <algorithm>
#include <type_traits>
#include <vector>
namespace
phmap
{
namespace
priv
{
// Returns the number of probes required to lookup `key`. Returns 0 for a
// search with no collisions. Higher values mean more hash collisions occurred;
// however, the exact meaning of this number varies according to the container
// type.
template
<
typename
C
>
size_t
GetHashtableDebugNumProbes
(
const
C
&
c
,
const
typename
C
::
key_type
&
key
)
{
return
phmap
::
priv
::
hashtable_debug_internal
::
HashtableDebugAccess
<
C
>::
GetNumProbes
(
c
,
key
);
}
// Gets a histogram of the number of probes for each elements in the container.
// The sum of all the values in the vector is equal to container.size().
template
<
typename
C
>
std
::
vector
<
size_t
>
GetHashtableDebugNumProbesHistogram
(
const
C
&
container
)
{
std
::
vector
<
size_t
>
v
;
for
(
auto
it
=
container
.
begin
();
it
!=
container
.
end
();
++
it
)
{
size_t
num_probes
=
GetHashtableDebugNumProbes
(
container
,
phmap
::
priv
::
hashtable_debug_internal
::
GetKey
<
C
>
(
*
it
,
0
));
v
.
resize
((
std
::
max
)(
v
.
size
(),
num_probes
+
1
));
v
[
num_probes
]
++
;
}
return
v
;
}
struct
HashtableDebugProbeSummary
{
size_t
total_elements
;
size_t
total_num_probes
;
double
mean
;
};
// Gets a summary of the probe count distribution for the elements in the
// container.
template
<
typename
C
>
HashtableDebugProbeSummary
GetHashtableDebugProbeSummary
(
const
C
&
container
)
{
auto
probes
=
GetHashtableDebugNumProbesHistogram
(
container
);
HashtableDebugProbeSummary
summary
=
{};
for
(
size_t
i
=
0
;
i
<
probes
.
size
();
++
i
)
{
summary
.
total_elements
+=
probes
[
i
];
summary
.
total_num_probes
+=
probes
[
i
]
*
i
;
}
summary
.
mean
=
1.0
*
summary
.
total_num_probes
/
summary
.
total_elements
;
return
summary
;
}
// Returns the number of bytes requested from the allocator by the container
// and not freed.
template
<
typename
C
>
size_t
AllocatedByteSize
(
const
C
&
c
)
{
return
phmap
::
priv
::
hashtable_debug_internal
::
HashtableDebugAccess
<
C
>::
AllocatedByteSize
(
c
);
}
// Returns a tight lower bound for AllocatedByteSize(c) where `c` is of type `C`
// and `c.size()` is equal to `num_elements`.
template
<
typename
C
>
size_t
LowerBoundAllocatedByteSize
(
size_t
num_elements
)
{
return
phmap
::
priv
::
hashtable_debug_internal
::
HashtableDebugAccess
<
C
>::
LowerBoundAllocatedByteSize
(
num_elements
);
}
}
// namespace priv
}
// namespace phmap
#endif // PHMAP_PRIV_HASHTABLE_DEBUG_H_
third_party/parallel-hashmap/tests/node_hash_map_test.cc
0 → 100644
View file @
2951b12d
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIS_HASH_MAP
#define THIS_HASH_MAP node_hash_map
#define THIS_TEST_NAME NodeHashMap
#endif
#include "parallel_hashmap/phmap.h"
#include "tracked.h"
#include "unordered_map_constructor_test.h"
#include "unordered_map_lookup_test.h"
#include "unordered_map_members_test.h"
#include "unordered_map_modifiers_test.h"
namespace
phmap
{
namespace
priv
{
namespace
{
using
::
testing
::
Field
;
using
::
testing
::
Pair
;
using
::
testing
::
UnorderedElementsAre
;
using
MapTypes
=
::
testing
::
Types
<
phmap
::
THIS_HASH_MAP
<
int
,
int
,
StatefulTestingHash
,
StatefulTestingEqual
,
Alloc
<
std
::
pair
<
const
int
,
int
>>>
,
phmap
::
THIS_HASH_MAP
<
std
::
string
,
std
::
string
,
StatefulTestingHash
,
StatefulTestingEqual
,
Alloc
<
std
::
pair
<
const
std
::
string
,
std
::
string
>>>>
;
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
ConstructorTest
,
MapTypes
);
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
LookupTest
,
MapTypes
);
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
MembersTest
,
MapTypes
);
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
ModifiersTest
,
MapTypes
);
using
M
=
phmap
::
THIS_HASH_MAP
<
std
::
string
,
Tracked
<
int
>>
;
TEST
(
THIS_TEST_NAME
,
Emplace
)
{
M
m
;
Tracked
<
int
>
t
(
53
);
m
.
emplace
(
"a"
,
t
);
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
1
,
t
.
num_copies
());
m
.
emplace
(
std
::
string
(
"a"
),
t
);
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
1
,
t
.
num_copies
());
std
::
string
a
(
"a"
);
m
.
emplace
(
a
,
t
);
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
1
,
t
.
num_copies
());
const
std
::
string
ca
(
"a"
);
m
.
emplace
(
a
,
t
);
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
1
,
t
.
num_copies
());
m
.
emplace
(
std
::
make_pair
(
"a"
,
t
));
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
2
,
t
.
num_copies
());
m
.
emplace
(
std
::
make_pair
(
std
::
string
(
"a"
),
t
));
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
3
,
t
.
num_copies
());
std
::
pair
<
std
::
string
,
Tracked
<
int
>>
p
(
"a"
,
t
);
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
4
,
t
.
num_copies
());
m
.
emplace
(
p
);
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
4
,
t
.
num_copies
());
const
std
::
pair
<
std
::
string
,
Tracked
<
int
>>
cp
(
"a"
,
t
);
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
5
,
t
.
num_copies
());
m
.
emplace
(
cp
);
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
5
,
t
.
num_copies
());
std
::
pair
<
const
std
::
string
,
Tracked
<
int
>>
pc
(
"a"
,
t
);
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
6
,
t
.
num_copies
());
m
.
emplace
(
pc
);
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
6
,
t
.
num_copies
());
const
std
::
pair
<
const
std
::
string
,
Tracked
<
int
>>
cpc
(
"a"
,
t
);
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
7
,
t
.
num_copies
());
m
.
emplace
(
cpc
);
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
7
,
t
.
num_copies
());
m
.
emplace
(
std
::
piecewise_construct
,
std
::
forward_as_tuple
(
"a"
),
std
::
forward_as_tuple
(
t
));
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
7
,
t
.
num_copies
());
m
.
emplace
(
std
::
piecewise_construct
,
std
::
forward_as_tuple
(
std
::
string
(
"a"
)),
std
::
forward_as_tuple
(
t
));
ASSERT_EQ
(
0
,
t
.
num_moves
());
ASSERT_EQ
(
7
,
t
.
num_copies
());
}
TEST
(
THIS_TEST_NAME
,
AssignRecursive
)
{
struct
Tree
{
// Verify that unordered_map<K, IncompleteType> can be instantiated.
phmap
::
THIS_HASH_MAP
<
int
,
Tree
>
children
;
};
Tree
root
;
const
Tree
&
child
=
root
.
children
.
emplace
().
first
->
second
;
// Verify that `lhs = rhs` doesn't read rhs after clearing lhs.
root
=
child
;
}
TEST
(
FlatHashMap
,
MoveOnlyKey
)
{
struct
Key
{
Key
()
=
default
;
Key
(
Key
&&
)
=
default
;
Key
&
operator
=
(
Key
&&
)
=
default
;
};
struct
Eq
{
bool
operator
()(
const
Key
&
,
const
Key
&
)
const
{
return
true
;
}
};
struct
Hash
{
size_t
operator
()(
const
Key
&
)
const
{
return
0
;
}
};
phmap
::
THIS_HASH_MAP
<
Key
,
int
,
Hash
,
Eq
>
m
;
m
[
Key
()];
}
struct
NonMovableKey
{
explicit
NonMovableKey
(
int
i_
)
:
i
(
i_
)
{}
NonMovableKey
(
NonMovableKey
&&
)
=
delete
;
int
i
;
};
struct
NonMovableKeyHash
{
using
is_transparent
=
void
;
size_t
operator
()(
const
NonMovableKey
&
k
)
const
{
return
k
.
i
;
}
size_t
operator
()(
int
k
)
const
{
return
k
;
}
};
struct
NonMovableKeyEq
{
using
is_transparent
=
void
;
bool
operator
()(
const
NonMovableKey
&
a
,
const
NonMovableKey
&
b
)
const
{
return
a
.
i
==
b
.
i
;
}
bool
operator
()(
const
NonMovableKey
&
a
,
int
b
)
const
{
return
a
.
i
==
b
;
}
};
TEST
(
THIS_TEST_NAME
,
MergeExtractInsert
)
{
phmap
::
THIS_HASH_MAP
<
NonMovableKey
,
int
,
NonMovableKeyHash
,
NonMovableKeyEq
>
set1
,
set2
;
set1
.
emplace
(
std
::
piecewise_construct
,
std
::
make_tuple
(
7
),
std
::
make_tuple
(
-
7
));
set1
.
emplace
(
std
::
piecewise_construct
,
std
::
make_tuple
(
17
),
std
::
make_tuple
(
-
17
));
set2
.
emplace
(
std
::
piecewise_construct
,
std
::
make_tuple
(
7
),
std
::
make_tuple
(
-
70
));
set2
.
emplace
(
std
::
piecewise_construct
,
std
::
make_tuple
(
19
),
std
::
make_tuple
(
-
190
));
auto
Elem
=
[](
int
key
,
int
value
)
{
return
Pair
(
Field
(
&
NonMovableKey
::
i
,
key
),
value
);
};
EXPECT_THAT
(
set1
,
UnorderedElementsAre
(
Elem
(
7
,
-
7
),
Elem
(
17
,
-
17
)));
EXPECT_THAT
(
set2
,
UnorderedElementsAre
(
Elem
(
7
,
-
70
),
Elem
(
19
,
-
190
)));
// NonMovableKey is neither copyable nor movable. We should still be able to
// move nodes around.
static_assert
(
!
std
::
is_move_constructible
<
NonMovableKey
>::
value
,
""
);
set1
.
merge
(
set2
);
EXPECT_THAT
(
set1
,
UnorderedElementsAre
(
Elem
(
7
,
-
7
),
Elem
(
17
,
-
17
),
Elem
(
19
,
-
190
)));
EXPECT_THAT
(
set2
,
UnorderedElementsAre
(
Elem
(
7
,
-
70
)));
auto
node
=
set1
.
extract
(
7
);
EXPECT_TRUE
(
node
);
EXPECT_EQ
(
node
.
key
().
i
,
7
);
EXPECT_EQ
(
node
.
mapped
(),
-
7
);
EXPECT_THAT
(
set1
,
UnorderedElementsAre
(
Elem
(
17
,
-
17
),
Elem
(
19
,
-
190
)));
auto
insert_result
=
set2
.
insert
(
std
::
move
(
node
));
EXPECT_FALSE
(
node
);
EXPECT_FALSE
(
insert_result
.
inserted
);
EXPECT_TRUE
(
insert_result
.
node
);
EXPECT_EQ
(
insert_result
.
node
.
key
().
i
,
7
);
EXPECT_EQ
(
insert_result
.
node
.
mapped
(),
-
7
);
EXPECT_THAT
(
*
insert_result
.
position
,
Elem
(
7
,
-
70
));
EXPECT_THAT
(
set2
,
UnorderedElementsAre
(
Elem
(
7
,
-
70
)));
node
=
set1
.
extract
(
17
);
EXPECT_TRUE
(
node
);
EXPECT_EQ
(
node
.
key
().
i
,
17
);
EXPECT_EQ
(
node
.
mapped
(),
-
17
);
EXPECT_THAT
(
set1
,
UnorderedElementsAre
(
Elem
(
19
,
-
190
)));
node
.
mapped
()
=
23
;
insert_result
=
set2
.
insert
(
std
::
move
(
node
));
EXPECT_FALSE
(
node
);
EXPECT_TRUE
(
insert_result
.
inserted
);
EXPECT_FALSE
(
insert_result
.
node
);
EXPECT_THAT
(
*
insert_result
.
position
,
Elem
(
17
,
23
));
EXPECT_THAT
(
set2
,
UnorderedElementsAre
(
Elem
(
7
,
-
70
),
Elem
(
17
,
23
)));
}
}
// namespace
}
// namespace priv
}
// namespace phmap
third_party/parallel-hashmap/tests/node_hash_policy_test.cc
0 → 100644
View file @
2951b12d
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include "parallel_hashmap/phmap.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace
phmap
{
namespace
priv
{
namespace
{
using
::
testing
::
Pointee
;
struct
Policy
:
node_hash_policy
<
int
&
,
Policy
>
{
using
key_type
=
int
;
using
init_type
=
int
;
template
<
class
Alloc
>
static
int
*
new_element
(
Alloc
*
,
int
value
)
{
return
new
int
(
value
);
}
template
<
class
Alloc
>
static
void
delete_element
(
Alloc
*
,
int
*
elem
)
{
delete
elem
;
}
};
using
NodePolicy
=
hash_policy_traits
<
Policy
>
;
struct
NodeTest
:
::
testing
::
Test
{
std
::
allocator
<
int
>
alloc
;
int
n
=
53
;
int
*
a
=
&
n
;
};
TEST_F
(
NodeTest
,
ConstructDestroy
)
{
NodePolicy
::
construct
(
&
alloc
,
&
a
,
42
);
EXPECT_THAT
(
a
,
Pointee
(
42
));
NodePolicy
::
destroy
(
&
alloc
,
&
a
);
}
TEST_F
(
NodeTest
,
transfer
)
{
int
s
=
42
;
int
*
b
=
&
s
;
NodePolicy
::
transfer
(
&
alloc
,
&
a
,
&
b
);
EXPECT_EQ
(
&
s
,
a
);
}
}
// namespace
}
// namespace priv
}
// namespace phmap
third_party/parallel-hashmap/tests/node_hash_set_test.cc
0 → 100644
View file @
2951b12d
// Copyright 2018 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIS_HASH_SET
#define THIS_HASH_SET node_hash_set
#define THIS_TEST_NAME NodeHashSet
#endif
#include "parallel_hashmap/phmap.h"
#include "unordered_set_constructor_test.h"
#include "unordered_set_lookup_test.h"
#include "unordered_set_members_test.h"
#include "unordered_set_modifiers_test.h"
namespace
phmap
{
namespace
priv
{
namespace
{
using
::
phmap
::
priv
::
hash_internal
::
Enum
;
using
::
phmap
::
priv
::
hash_internal
::
EnumClass
;
using
::
testing
::
Pointee
;
using
::
testing
::
UnorderedElementsAre
;
using
SetTypes
=
::
testing
::
Types
<
THIS_HASH_SET
<
int
,
StatefulTestingHash
,
StatefulTestingEqual
,
Alloc
<
int
>>
,
THIS_HASH_SET
<
std
::
string
,
StatefulTestingHash
,
StatefulTestingEqual
,
Alloc
<
std
::
string
>>
,
THIS_HASH_SET
<
Enum
,
StatefulTestingHash
,
StatefulTestingEqual
,
Alloc
<
Enum
>>
,
THIS_HASH_SET
<
EnumClass
,
StatefulTestingHash
,
StatefulTestingEqual
,
Alloc
<
EnumClass
>>>
;
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
ConstructorTest
,
SetTypes
);
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
LookupTest
,
SetTypes
);
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
MembersTest
,
SetTypes
);
INSTANTIATE_TYPED_TEST_SUITE_P
(
THIS_TEST_NAME
,
ModifiersTest
,
SetTypes
);
TEST
(
THIS_TEST_NAME
,
MoveableNotCopyableCompiles
)
{
THIS_HASH_SET
<
std
::
unique_ptr
<
void
*>>
t
;
THIS_HASH_SET
<
std
::
unique_ptr
<
void
*>>
u
;
u
=
std
::
move
(
t
);
}
TEST
(
THIS_TEST_NAME
,
MergeExtractInsert
)
{
struct
Hash
{
size_t
operator
()(
const
std
::
unique_ptr
<
int
>&
p
)
const
{
return
*
p
;
}
};
struct
Eq
{
bool
operator
()(
const
std
::
unique_ptr
<
int
>&
a
,
const
std
::
unique_ptr
<
int
>&
b
)
const
{
return
*
a
==
*
b
;
}
};
phmap
::
THIS_HASH_SET
<
std
::
unique_ptr
<
int
>
,
Hash
,
Eq
>
set1
,
set2
;
set1
.
insert
(
phmap
::
make_unique
<
int
>
(
7
));
set1
.
insert
(
phmap
::
make_unique
<
int
>
(
17
));
set2
.
insert
(
phmap
::
make_unique
<
int
>
(
7
));
set2
.
insert
(
phmap
::
make_unique
<
int
>
(
19
));
EXPECT_THAT
(
set1
,
UnorderedElementsAre
(
Pointee
(
7
),
Pointee
(
17
)));
EXPECT_THAT
(
set2
,
UnorderedElementsAre
(
Pointee
(
7
),
Pointee
(
19
)));
set1
.
merge
(
set2
);
EXPECT_THAT
(
set1
,
UnorderedElementsAre
(
Pointee
(
7
),
Pointee
(
17
),
Pointee
(
19
)));
EXPECT_THAT
(
set2
,
UnorderedElementsAre
(
Pointee
(
7
)));
auto
node
=
set1
.
extract
(
phmap
::
make_unique
<
int
>
(
7
));
EXPECT_TRUE
(
node
);
EXPECT_THAT
(
node
.
value
(),
Pointee
(
7
));
EXPECT_THAT
(
set1
,
UnorderedElementsAre
(
Pointee
(
17
),
Pointee
(
19
)));
auto
insert_result
=
set2
.
insert
(
std
::
move
(
node
));
EXPECT_FALSE
(
node
);
EXPECT_FALSE
(
insert_result
.
inserted
);
EXPECT_TRUE
(
insert_result
.
node
);
EXPECT_THAT
(
insert_result
.
node
.
value
(),
Pointee
(
7
));
EXPECT_EQ
(
**
insert_result
.
position
,
7
);
EXPECT_NE
(
insert_result
.
position
->
get
(),
insert_result
.
node
.
value
().
get
());
EXPECT_THAT
(
set2
,
UnorderedElementsAre
(
Pointee
(
7
)));
node
=
set1
.
extract
(
phmap
::
make_unique
<
int
>
(
17
));
EXPECT_TRUE
(
node
);
EXPECT_THAT
(
node
.
value
(),
Pointee
(
17
));
EXPECT_THAT
(
set1
,
UnorderedElementsAre
(
Pointee
(
19
)));
node
.
value
()
=
phmap
::
make_unique
<
int
>
(
23
);
insert_result
=
set2
.
insert
(
std
::
move
(
node
));
EXPECT_FALSE
(
node
);
EXPECT_TRUE
(
insert_result
.
inserted
);
EXPECT_FALSE
(
insert_result
.
node
);
EXPECT_EQ
(
**
insert_result
.
position
,
23
);
EXPECT_THAT
(
set2
,
UnorderedElementsAre
(
Pointee
(
7
),
Pointee
(
23
)));
}
}
// namespace
}
// namespace priv
}
// namespace phmap
third_party/parallel-hashmap/tests/parallel_flat_hash_map_mutex_test.cc
0 → 100644
View file @
2951b12d
#define THIS_HASH_MAP parallel_flat_hash_map
#define THIS_TEST_NAME ParallelFlatHashMap
#if 1
#define THIS_EXTRA_TPL_PARAMS , 4, std::mutex
#else
#include <boost/thread/locks.hpp>
#include <boost/thread/shared_mutex.hpp>
#define THIS_EXTRA_TPL_PARAMS , 4, boost::upgrade_mutex
#endif
#include "parallel_hash_map_test.cc"
third_party/parallel-hashmap/tests/parallel_flat_hash_map_test.cc
0 → 100644
View file @
2951b12d
#define THIS_HASH_MAP parallel_flat_hash_map
#define THIS_TEST_NAME ParallelFlatHashMap
#include "parallel_hash_map_test.cc"
Prev
1
…
9
10
11
12
13
14
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment