mirror of
https://github.com/greg7mdp/parallel-hashmap.git
synced 2026-08-29 08:34:39 +08:00
add more tests - they all pass on windows / vs2017
This commit is contained in:
Vendored
+26
-7
@@ -52,26 +52,45 @@ if (PHMAP_BUILD_TESTS)
|
||||
check_target(gtest_main)
|
||||
check_target(gmock)
|
||||
|
||||
# include_directories(${CMAKE_CURRENT_SOURCE_DIR}/parallel_hashmap)
|
||||
## ---------------- regular hash maps ----------------------------
|
||||
phmap_cc_test(NAME compressed_tuple SRCS "tests/compressed_tuple_test.cc"
|
||||
DEPS gmock_main)
|
||||
|
||||
phmap_cc_test(NAME container_memory SRCS "tests/container_memory_test.cc"
|
||||
DEPS gmock_main)
|
||||
|
||||
phmap_cc_test(NAME hash_policy_testing SRCS "tests/hash_policy_testing_test.cc"
|
||||
DEPS gmock_main)
|
||||
|
||||
phmap_cc_test(NAME node_hash_policy SRCS "tests/node_hash_policy_test.cc"
|
||||
DEPS gmock_main)
|
||||
|
||||
phmap_cc_test(NAME raw_hash_set SRCS "tests/raw_hash_set_test.cc"
|
||||
DEPS gmock_main)
|
||||
|
||||
phmap_cc_test(NAME raw_hash_set_allocator SRCS "tests/raw_hash_set_allocator_test.cc"
|
||||
DEPS gmock_main)
|
||||
|
||||
## ---------------- regular hash maps ----------------------------
|
||||
phmap_cc_test(NAME flat_hash_set SRCS "tests/flat_hash_set_test.cc"
|
||||
COPTS "-DUNORDERED_SET_CXX17" DEPS gmock_main)
|
||||
|
||||
phmap_cc_test(NAME flat_hash_map SRCS "tests/flat_hash_map_test.cc"
|
||||
DEPS gmock_main)
|
||||
|
||||
phmap_cc_test(NAME parallel_flat_hash_map SRCS "tests/parallel_flat_hash_map_test.cc"
|
||||
COPTS "-DUNORDERED_MAP_CXX17" DEPS gmock_main)
|
||||
|
||||
phmap_cc_test(NAME parallel_flat_hash_set SRCS "tests/parallel_flat_hash_set_test.cc"
|
||||
COPTS "-DUNORDERED_SET_CXX17" DEPS gmock_main)
|
||||
|
||||
phmap_cc_test(NAME node_hash_map SRCS "tests/node_hash_map_test.cc"
|
||||
DEPS gmock_main)
|
||||
|
||||
phmap_cc_test(NAME node_hash_set SRCS "tests/node_hash_set_test.cc"
|
||||
COPTS "-DUNORDERED_SET_CXX17" DEPS gmock_main)
|
||||
|
||||
## --------------- parallel hash maps -----------------------------------------------
|
||||
phmap_cc_test(NAME parallel_flat_hash_map SRCS "tests/parallel_flat_hash_map_test.cc"
|
||||
COPTS "-DUNORDERED_MAP_CXX17" DEPS gmock_main)
|
||||
|
||||
phmap_cc_test(NAME parallel_flat_hash_set SRCS "tests/parallel_flat_hash_set_test.cc"
|
||||
COPTS "-DUNORDERED_SET_CXX17" DEPS gmock_main)
|
||||
|
||||
phmap_cc_test(NAME parallel_node_hash_map SRCS "tests/parallel_node_hash_map_test.cc"
|
||||
DEPS gmock_main)
|
||||
|
||||
|
||||
Vendored
+11
-7
@@ -52,6 +52,10 @@
|
||||
#include "phmap_base.h"
|
||||
#include "phmap_utils.h"
|
||||
|
||||
#if PHMAP_HAVE_STD_STRING_VIEW
|
||||
#include <string_view>
|
||||
#endif
|
||||
|
||||
namespace phmap {
|
||||
namespace container_internal {
|
||||
|
||||
@@ -479,7 +483,7 @@ inline size_t GrowthToLowerboundCapacity(size_t growth)
|
||||
return growth + static_cast<size_t>((static_cast<int64_t>(growth) - 1) / 7);
|
||||
}
|
||||
|
||||
namespace debug {
|
||||
namespace hashtable_debug_internal {
|
||||
|
||||
// If it is a map, call get<0>().
|
||||
using std::get;
|
||||
@@ -522,7 +526,7 @@ struct HashtableDebugAccess
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace debug
|
||||
} // namespace hashtable_debug_internal
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// I N F O Z S T U B S
|
||||
@@ -565,7 +569,7 @@ public:
|
||||
|
||||
using DisposeCallback = void (*)(const HashtablezInfo&);
|
||||
DisposeCallback SetDisposeCallback(DisposeCallback f) {}
|
||||
int64_t Iterate(const std::function<void(const HashtablezInfo& stack)>& f) {}
|
||||
int64_t Iterate(const std::function<void(const HashtablezInfo& stack)>& f) { return 0; }
|
||||
};
|
||||
|
||||
void SetHashtablezEnabled(bool enabled) {}
|
||||
@@ -1526,7 +1530,7 @@ public:
|
||||
|
||||
private:
|
||||
template <class Container, typename Enabler>
|
||||
friend struct phmap::container_internal::debug::HashtableDebugAccess;
|
||||
friend struct phmap::container_internal::hashtable_debug_internal::HashtableDebugAccess;
|
||||
|
||||
struct FindElement
|
||||
{
|
||||
@@ -3027,7 +3031,7 @@ public:
|
||||
|
||||
private:
|
||||
template <class Container, typename Enabler>
|
||||
friend struct phmap::container_internal::debug::HashtableDebugAccess;
|
||||
friend struct phmap::container_internal::hashtable_debug_internal::HashtableDebugAccess;
|
||||
|
||||
struct FindElement
|
||||
{
|
||||
@@ -4092,7 +4096,7 @@ template <class T>
|
||||
using hash_default_eq = typename container_internal::HashEq<T>::Eq;
|
||||
|
||||
|
||||
namespace debug {
|
||||
namespace hashtable_debug_internal {
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// --------------------------------------------------------------------------
|
||||
@@ -4155,7 +4159,7 @@ struct HashtableDebugAccess<Set, phmap::void_t<typename Set::raw_hash_set>>
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace debug
|
||||
} // namespace hashtable_debug_internal
|
||||
} // namespace container_internal
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
Vendored
-10
@@ -2896,20 +2896,10 @@ struct InsertReturnType
|
||||
#include <sanitizer/asan_interface.h>
|
||||
#endif
|
||||
|
||||
//#include "absl/strings/str_cat.h"
|
||||
//#include "absl/types/span.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// span.h
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
//#include "absl/algorithm/algorithm.h"
|
||||
//#include "absl/base/internal/throw_delegate.h"
|
||||
//#include "absl/base/macros.h"
|
||||
//#include "absl/base/optimization.h"
|
||||
//#include "absl/base/port.h"
|
||||
//#include "absl/meta/type_traits.h"
|
||||
|
||||
namespace phmap {
|
||||
|
||||
template <typename T>
|
||||
|
||||
Vendored
+6
@@ -53,6 +53,10 @@
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
#define PHMAP_XSTR(x) PHMAP_STR(x)
|
||||
#define PHMAP_STR(x) #x
|
||||
#define PHMAP_VAR_NAME_VALUE(var) #var "=" PHMAP_STR(var)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Some sanity checks
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -297,6 +301,8 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// #pragma message(PHMAP_VAR_NAME_VALUE(_MSVC_LANG))
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1910 && \
|
||||
((defined(_MSVC_LANG) && _MSVC_LANG > 201402) || __cplusplus > 201402)
|
||||
// #define PHMAP_HAVE_STD_ANY 1
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
// 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 container_internal {
|
||||
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 container_internal
|
||||
} // namespace phmap
|
||||
@@ -0,0 +1,193 @@
|
||||
// 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 container_internal {
|
||||
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&& f, Arg&& 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&& f, Args&&... 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 container_internal
|
||||
} // namespace phmap
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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 container_internal {
|
||||
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 container_internal
|
||||
} // namespace phmap
|
||||
Vendored
+106
@@ -0,0 +1,106 @@
|
||||
// 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_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_
|
||||
#define PHMAP_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace phmap {
|
||||
namespace container_internal {
|
||||
|
||||
// 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::container_internal::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::container_internal::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::container_internal::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::container_internal::hashtable_debug_internal::
|
||||
HashtableDebugAccess<C>::LowerBoundAllocatedByteSize(num_elements);
|
||||
}
|
||||
|
||||
} // namespace container_internal
|
||||
} // namespace phmap
|
||||
|
||||
#endif // PHMAP_CONTAINER_INTERNAL_HASHTABLE_DEBUG_H_
|
||||
@@ -0,0 +1,65 @@
|
||||
// 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 container_internal {
|
||||
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* alloc, int value) {
|
||||
return new int(value);
|
||||
}
|
||||
|
||||
template <class Alloc>
|
||||
static void delete_element(Alloc* 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 container_internal
|
||||
} // namespace phmap
|
||||
@@ -0,0 +1,428 @@
|
||||
// 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 <limits>
|
||||
#include <scoped_allocator>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "parallel_hashmap/phmap.h"
|
||||
#include "tracked.h"
|
||||
|
||||
namespace phmap {
|
||||
namespace container_internal {
|
||||
namespace {
|
||||
|
||||
enum AllocSpec {
|
||||
kPropagateOnCopy = 1,
|
||||
kPropagateOnMove = 2,
|
||||
kPropagateOnSwap = 4,
|
||||
};
|
||||
|
||||
struct AllocState {
|
||||
size_t num_allocs = 0;
|
||||
std::set<void*> owned;
|
||||
};
|
||||
|
||||
template <class T,
|
||||
int Spec = kPropagateOnCopy | kPropagateOnMove | kPropagateOnSwap>
|
||||
class CheckedAlloc {
|
||||
public:
|
||||
template <class, int>
|
||||
friend class CheckedAlloc;
|
||||
|
||||
using value_type = T;
|
||||
|
||||
CheckedAlloc() {}
|
||||
explicit CheckedAlloc(size_t id) : id_(id) {}
|
||||
CheckedAlloc(const CheckedAlloc&) = default;
|
||||
CheckedAlloc& operator=(const CheckedAlloc&) = default;
|
||||
|
||||
template <class U>
|
||||
CheckedAlloc(const CheckedAlloc<U, Spec>& that)
|
||||
: id_(that.id_), state_(that.state_) {}
|
||||
|
||||
template <class U>
|
||||
struct rebind {
|
||||
using other = CheckedAlloc<U, Spec>;
|
||||
};
|
||||
|
||||
using propagate_on_container_copy_assignment =
|
||||
std::integral_constant<bool, (Spec & kPropagateOnCopy) != 0>;
|
||||
|
||||
using propagate_on_container_move_assignment =
|
||||
std::integral_constant<bool, (Spec & kPropagateOnMove) != 0>;
|
||||
|
||||
using propagate_on_container_swap =
|
||||
std::integral_constant<bool, (Spec & kPropagateOnSwap) != 0>;
|
||||
|
||||
CheckedAlloc select_on_container_copy_construction() const {
|
||||
if (Spec & kPropagateOnCopy) return *this;
|
||||
return {};
|
||||
}
|
||||
|
||||
T* allocate(size_t n) {
|
||||
T* ptr = std::allocator<T>().allocate(n);
|
||||
track_alloc(ptr);
|
||||
return ptr;
|
||||
}
|
||||
void deallocate(T* ptr, size_t n) {
|
||||
memset(ptr, 0, n * sizeof(T)); // The freed memory must be unpoisoned.
|
||||
track_dealloc(ptr);
|
||||
return std::allocator<T>().deallocate(ptr, n);
|
||||
}
|
||||
|
||||
friend bool operator==(const CheckedAlloc& a, const CheckedAlloc& b) {
|
||||
return a.id_ == b.id_;
|
||||
}
|
||||
friend bool operator!=(const CheckedAlloc& a, const CheckedAlloc& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
size_t num_allocs() const { return state_->num_allocs; }
|
||||
|
||||
void swap(CheckedAlloc& that) {
|
||||
using std::swap;
|
||||
swap(id_, that.id_);
|
||||
swap(state_, that.state_);
|
||||
}
|
||||
|
||||
friend void swap(CheckedAlloc& a, CheckedAlloc& b) { a.swap(b); }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& o, const CheckedAlloc& a) {
|
||||
return o << "alloc(" << a.id_ << ")";
|
||||
}
|
||||
|
||||
private:
|
||||
void track_alloc(void* ptr) {
|
||||
AllocState* state = state_.get();
|
||||
++state->num_allocs;
|
||||
if (!state->owned.insert(ptr).second)
|
||||
ADD_FAILURE() << *this << " got previously allocated memory: " << ptr;
|
||||
}
|
||||
void track_dealloc(void* ptr) {
|
||||
if (state_->owned.erase(ptr) != 1)
|
||||
ADD_FAILURE() << *this
|
||||
<< " deleting memory owned by another allocator: " << ptr;
|
||||
}
|
||||
|
||||
size_t id_ = std::numeric_limits<size_t>::max();
|
||||
|
||||
std::shared_ptr<AllocState> state_ = std::make_shared<AllocState>();
|
||||
};
|
||||
|
||||
struct Identity {
|
||||
int32_t operator()(int32_t v) const { return v; }
|
||||
};
|
||||
|
||||
struct Policy {
|
||||
using slot_type = Tracked<int32_t>;
|
||||
using init_type = Tracked<int32_t>;
|
||||
using key_type = int32_t;
|
||||
|
||||
template <class allocator_type, class... Args>
|
||||
static void construct(allocator_type* alloc, slot_type* slot,
|
||||
Args&&... args) {
|
||||
std::allocator_traits<allocator_type>::construct(
|
||||
*alloc, slot, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class allocator_type>
|
||||
static void destroy(allocator_type* alloc, slot_type* slot) {
|
||||
std::allocator_traits<allocator_type>::destroy(*alloc, slot);
|
||||
}
|
||||
|
||||
template <class allocator_type>
|
||||
static void transfer(allocator_type* alloc, slot_type* new_slot,
|
||||
slot_type* old_slot) {
|
||||
construct(alloc, new_slot, std::move(*old_slot));
|
||||
destroy(alloc, old_slot);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
static auto apply(F&& f, int32_t v) -> decltype(std::forward<F>(f)(v, v)) {
|
||||
return std::forward<F>(f)(v, v);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
static auto apply(F&& f, const slot_type& v)
|
||||
-> decltype(std::forward<F>(f)(v.val(), v)) {
|
||||
return std::forward<F>(f)(v.val(), v);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
static auto apply(F&& f, slot_type&& v)
|
||||
-> decltype(std::forward<F>(f)(v.val(), std::move(v))) {
|
||||
return std::forward<F>(f)(v.val(), std::move(v));
|
||||
}
|
||||
|
||||
static slot_type& element(slot_type* slot) { return *slot; }
|
||||
};
|
||||
|
||||
template <int Spec>
|
||||
struct PropagateTest : public ::testing::Test {
|
||||
using Alloc = CheckedAlloc<Tracked<int32_t>, Spec>;
|
||||
|
||||
using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, Alloc>;
|
||||
|
||||
PropagateTest() {
|
||||
EXPECT_EQ(a1, t1.get_allocator());
|
||||
EXPECT_NE(a2, t1.get_allocator());
|
||||
}
|
||||
|
||||
Alloc a1 = Alloc(1);
|
||||
Table t1 = Table(0, a1);
|
||||
Alloc a2 = Alloc(2);
|
||||
};
|
||||
|
||||
using PropagateOnAll =
|
||||
PropagateTest<kPropagateOnCopy | kPropagateOnMove | kPropagateOnSwap>;
|
||||
using NoPropagateOnCopy = PropagateTest<kPropagateOnMove | kPropagateOnSwap>;
|
||||
using NoPropagateOnMove = PropagateTest<kPropagateOnCopy | kPropagateOnSwap>;
|
||||
|
||||
TEST_F(PropagateOnAll, Empty) { EXPECT_EQ(0, a1.num_allocs()); }
|
||||
|
||||
TEST_F(PropagateOnAll, InsertAllocates) {
|
||||
auto it = t1.insert(0).first;
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(0, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(PropagateOnAll, InsertDecomposes) {
|
||||
auto it = t1.insert(0).first;
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(0, it->num_copies());
|
||||
|
||||
EXPECT_FALSE(t1.insert(0).second);
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(0, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(PropagateOnAll, RehashMoves) {
|
||||
auto it = t1.insert(0).first;
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
t1.rehash(2 * t1.capacity());
|
||||
EXPECT_EQ(2, a1.num_allocs());
|
||||
it = t1.find(0);
|
||||
EXPECT_EQ(1, it->num_moves());
|
||||
EXPECT_EQ(0, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(PropagateOnAll, CopyConstructor) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(t1);
|
||||
EXPECT_EQ(2, a1.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(1, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(NoPropagateOnCopy, CopyConstructor) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(t1);
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(1, u.get_allocator().num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(1, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(PropagateOnAll, CopyConstructorWithSameAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(t1, a1);
|
||||
EXPECT_EQ(2, a1.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(1, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(NoPropagateOnCopy, CopyConstructorWithSameAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(t1, a1);
|
||||
EXPECT_EQ(2, a1.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(1, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(PropagateOnAll, CopyConstructorWithDifferentAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(t1, a2);
|
||||
EXPECT_EQ(a2, u.get_allocator());
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(1, a2.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(1, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(NoPropagateOnCopy, CopyConstructorWithDifferentAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(t1, a2);
|
||||
EXPECT_EQ(a2, u.get_allocator());
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(1, a2.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(1, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(PropagateOnAll, MoveConstructor) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(std::move(t1));
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(0, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(NoPropagateOnMove, MoveConstructor) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(std::move(t1));
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(0, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(PropagateOnAll, MoveConstructorWithSameAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(std::move(t1), a1);
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(0, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(NoPropagateOnMove, MoveConstructorWithSameAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(std::move(t1), a1);
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(0, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(PropagateOnAll, MoveConstructorWithDifferentAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(std::move(t1), a2);
|
||||
it = u.find(0);
|
||||
EXPECT_EQ(a2, u.get_allocator());
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(1, a2.num_allocs());
|
||||
EXPECT_EQ(1, it->num_moves());
|
||||
EXPECT_EQ(0, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(NoPropagateOnMove, MoveConstructorWithDifferentAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(std::move(t1), a2);
|
||||
it = u.find(0);
|
||||
EXPECT_EQ(a2, u.get_allocator());
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(1, a2.num_allocs());
|
||||
EXPECT_EQ(1, it->num_moves());
|
||||
EXPECT_EQ(0, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(PropagateOnAll, CopyAssignmentWithSameAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(0, a1);
|
||||
u = t1;
|
||||
EXPECT_EQ(2, a1.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(1, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(NoPropagateOnCopy, CopyAssignmentWithSameAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(0, a1);
|
||||
u = t1;
|
||||
EXPECT_EQ(2, a1.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(1, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(PropagateOnAll, CopyAssignmentWithDifferentAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(0, a2);
|
||||
u = t1;
|
||||
EXPECT_EQ(a1, u.get_allocator());
|
||||
EXPECT_EQ(2, a1.num_allocs());
|
||||
EXPECT_EQ(0, a2.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(1, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(NoPropagateOnCopy, CopyAssignmentWithDifferentAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(0, a2);
|
||||
u = t1;
|
||||
EXPECT_EQ(a2, u.get_allocator());
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(1, a2.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(1, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(PropagateOnAll, MoveAssignmentWithSameAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(0, a1);
|
||||
u = std::move(t1);
|
||||
EXPECT_EQ(a1, u.get_allocator());
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(0, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(NoPropagateOnMove, MoveAssignmentWithSameAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(0, a1);
|
||||
u = std::move(t1);
|
||||
EXPECT_EQ(a1, u.get_allocator());
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(0, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(PropagateOnAll, MoveAssignmentWithDifferentAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(0, a2);
|
||||
u = std::move(t1);
|
||||
EXPECT_EQ(a1, u.get_allocator());
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(0, a2.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(0, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(NoPropagateOnMove, MoveAssignmentWithDifferentAlloc) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(0, a2);
|
||||
u = std::move(t1);
|
||||
it = u.find(0);
|
||||
EXPECT_EQ(a2, u.get_allocator());
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(1, a2.num_allocs());
|
||||
EXPECT_EQ(1, it->num_moves());
|
||||
EXPECT_EQ(0, it->num_copies());
|
||||
}
|
||||
|
||||
TEST_F(PropagateOnAll, Swap) {
|
||||
auto it = t1.insert(0).first;
|
||||
Table u(0, a2);
|
||||
u.swap(t1);
|
||||
EXPECT_EQ(a1, u.get_allocator());
|
||||
EXPECT_EQ(a2, t1.get_allocator());
|
||||
EXPECT_EQ(1, a1.num_allocs());
|
||||
EXPECT_EQ(0, a2.num_allocs());
|
||||
EXPECT_EQ(0, it->num_moves());
|
||||
EXPECT_EQ(0, it->num_copies());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace container_internal
|
||||
} // namespace phmap
|
||||
File diff suppressed because it is too large
Load Diff
Vendored
+5
-5
@@ -12,13 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_
|
||||
#define ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_
|
||||
#ifndef PHMAP_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_
|
||||
#define PHMAP_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_
|
||||
|
||||
#include <cstdlib>
|
||||
#include <ostream>
|
||||
|
||||
namespace absl {
|
||||
namespace phmap {
|
||||
namespace test_internal {
|
||||
|
||||
// A type that counts number of occurences of the type, the live occurrences of
|
||||
@@ -257,6 +257,6 @@ class MovableOnlyInstance : public BaseCountedInstance {
|
||||
};
|
||||
|
||||
} // namespace test_internal
|
||||
} // namespace absl
|
||||
} // namespace phmap
|
||||
|
||||
#endif // ABSL_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_
|
||||
#endif // PHMAP_CONTAINER_INTERNAL_TEST_INSTANCE_TRACKER_H_
|
||||
|
||||
Vendored
+41
-46
@@ -25,56 +25,51 @@ namespace container_internal {
|
||||
// A class that tracks its copies and moves so that it can be queried in tests.
|
||||
template <class T>
|
||||
class Tracked {
|
||||
public:
|
||||
Tracked() {}
|
||||
public:
|
||||
Tracked() {}
|
||||
// NOLINTNEXTLINE(runtime/explicit)
|
||||
Tracked(const T& val) : val_(val) {}
|
||||
Tracked(const Tracked& that)
|
||||
: val_(that.val_),
|
||||
num_moves_(that.num_moves_),
|
||||
num_copies_(that.num_copies_) {
|
||||
++(*num_copies_);
|
||||
}
|
||||
Tracked(Tracked&& that)
|
||||
: val_(std::move(that.val_)),
|
||||
num_moves_(std::move(that.num_moves_)),
|
||||
num_copies_(std::move(that.num_copies_)) {
|
||||
++(*num_moves_);
|
||||
}
|
||||
Tracked& operator=(const Tracked& that) {
|
||||
val_ = that.val_;
|
||||
num_moves_ = that.num_moves_;
|
||||
num_copies_ = that.num_copies_;
|
||||
++(*num_copies_);
|
||||
}
|
||||
Tracked& operator=(Tracked&& that) {
|
||||
val_ = std::move(that.val_);
|
||||
num_moves_ = std::move(that.num_moves_);
|
||||
num_copies_ = std::move(that.num_copies_);
|
||||
++(*num_moves_);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(runtime/explicit)
|
||||
Tracked(const T& val) : val_(val) {}
|
||||
const T& val() const { return val_; }
|
||||
|
||||
Tracked(const Tracked& that)
|
||||
: val_(that.val_),
|
||||
num_moves_(that.num_moves_),
|
||||
num_copies_(that.num_copies_) {
|
||||
++(*num_copies_);
|
||||
}
|
||||
friend bool operator==(const Tracked& a, const Tracked& b) {
|
||||
return a.val_ == b.val_;
|
||||
}
|
||||
friend bool operator!=(const Tracked& a, const Tracked& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
Tracked(Tracked&& that)
|
||||
: val_(std::move(that.val_)),
|
||||
num_moves_(std::move(that.num_moves_)),
|
||||
num_copies_(std::move(that.num_copies_)) {
|
||||
++(*num_moves_);
|
||||
}
|
||||
size_t num_copies() { return *num_copies_; }
|
||||
size_t num_moves() { return *num_moves_; }
|
||||
|
||||
Tracked& operator=(const Tracked& that) {
|
||||
val_ = that.val_;
|
||||
num_moves_ = that.num_moves_;
|
||||
num_copies_ = that.num_copies_;
|
||||
++(*num_copies_);
|
||||
}
|
||||
|
||||
Tracked& operator=(Tracked&& that) {
|
||||
val_ = std::move(that.val_);
|
||||
num_moves_ = std::move(that.num_moves_);
|
||||
num_copies_ = std::move(that.num_copies_);
|
||||
++(*num_moves_);
|
||||
}
|
||||
|
||||
const T& val() const { return val_; }
|
||||
|
||||
friend bool operator==(const Tracked& a, const Tracked& b) {
|
||||
return a.val_ == b.val_;
|
||||
}
|
||||
friend bool operator!=(const Tracked& a, const Tracked& b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
size_t num_copies() { return *num_copies_; }
|
||||
size_t num_moves() { return *num_moves_; }
|
||||
|
||||
private:
|
||||
T val_;
|
||||
std::shared_ptr<size_t> num_moves_ = std::make_shared<size_t>(0);
|
||||
std::shared_ptr<size_t> num_copies_ = std::make_shared<size_t>(0);
|
||||
private:
|
||||
T val_;
|
||||
std::shared_ptr<size_t> num_moves_ = std::make_shared<size_t>(0);
|
||||
std::shared_ptr<size_t> num_copies_ = std::make_shared<size_t>(0);
|
||||
};
|
||||
|
||||
} // namespace container_internal
|
||||
|
||||
Reference in New Issue
Block a user