00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include <limits>
00016 #include <scoped_allocator>
00017
00018 #include "gtest/gtest.h"
00019 #include "absl/container/internal/raw_hash_set.h"
00020 #include "absl/container/internal/tracked.h"
00021
00022 namespace absl {
00023 namespace container_internal {
00024 namespace {
00025
00026 enum AllocSpec {
00027 kPropagateOnCopy = 1,
00028 kPropagateOnMove = 2,
00029 kPropagateOnSwap = 4,
00030 };
00031
00032 struct AllocState {
00033 size_t num_allocs = 0;
00034 std::set<void*> owned;
00035 };
00036
00037 template <class T,
00038 int Spec = kPropagateOnCopy | kPropagateOnMove | kPropagateOnSwap>
00039 class CheckedAlloc {
00040 public:
00041 template <class, int>
00042 friend class CheckedAlloc;
00043
00044 using value_type = T;
00045
00046 CheckedAlloc() {}
00047 explicit CheckedAlloc(size_t id) : id_(id) {}
00048 CheckedAlloc(const CheckedAlloc&) = default;
00049 CheckedAlloc& operator=(const CheckedAlloc&) = default;
00050
00051 template <class U>
00052 CheckedAlloc(const CheckedAlloc<U, Spec>& that)
00053 : id_(that.id_), state_(that.state_) {}
00054
00055 template <class U>
00056 struct rebind {
00057 using other = CheckedAlloc<U, Spec>;
00058 };
00059
00060 using propagate_on_container_copy_assignment =
00061 std::integral_constant<bool, (Spec & kPropagateOnCopy) != 0>;
00062
00063 using propagate_on_container_move_assignment =
00064 std::integral_constant<bool, (Spec & kPropagateOnMove) != 0>;
00065
00066 using propagate_on_container_swap =
00067 std::integral_constant<bool, (Spec & kPropagateOnSwap) != 0>;
00068
00069 CheckedAlloc select_on_container_copy_construction() const {
00070 if (Spec & kPropagateOnCopy) return *this;
00071 return {};
00072 }
00073
00074 T* allocate(size_t n) {
00075 T* ptr = std::allocator<T>().allocate(n);
00076 track_alloc(ptr);
00077 return ptr;
00078 }
00079 void deallocate(T* ptr, size_t n) {
00080 memset(ptr, 0, n * sizeof(T));
00081 track_dealloc(ptr);
00082 return std::allocator<T>().deallocate(ptr, n);
00083 }
00084
00085 friend bool operator==(const CheckedAlloc& a, const CheckedAlloc& b) {
00086 return a.id_ == b.id_;
00087 }
00088 friend bool operator!=(const CheckedAlloc& a, const CheckedAlloc& b) {
00089 return !(a == b);
00090 }
00091
00092 size_t num_allocs() const { return state_->num_allocs; }
00093
00094 void swap(CheckedAlloc& that) {
00095 using std::swap;
00096 swap(id_, that.id_);
00097 swap(state_, that.state_);
00098 }
00099
00100 friend void swap(CheckedAlloc& a, CheckedAlloc& b) { a.swap(b); }
00101
00102 friend std::ostream& operator<<(std::ostream& o, const CheckedAlloc& a) {
00103 return o << "alloc(" << a.id_ << ")";
00104 }
00105
00106 private:
00107 void track_alloc(void* ptr) {
00108 AllocState* state = state_.get();
00109 ++state->num_allocs;
00110 if (!state->owned.insert(ptr).second)
00111 ADD_FAILURE() << *this << " got previously allocated memory: " << ptr;
00112 }
00113 void track_dealloc(void* ptr) {
00114 if (state_->owned.erase(ptr) != 1)
00115 ADD_FAILURE() << *this
00116 << " deleting memory owned by another allocator: " << ptr;
00117 }
00118
00119 size_t id_ = std::numeric_limits<size_t>::max();
00120
00121 std::shared_ptr<AllocState> state_ = std::make_shared<AllocState>();
00122 };
00123
00124 struct Identity {
00125 int32_t operator()(int32_t v) const { return v; }
00126 };
00127
00128 struct Policy {
00129 using slot_type = Tracked<int32_t>;
00130 using init_type = Tracked<int32_t>;
00131 using key_type = int32_t;
00132
00133 template <class allocator_type, class... Args>
00134 static void construct(allocator_type* alloc, slot_type* slot,
00135 Args&&... args) {
00136 std::allocator_traits<allocator_type>::construct(
00137 *alloc, slot, std::forward<Args>(args)...);
00138 }
00139
00140 template <class allocator_type>
00141 static void destroy(allocator_type* alloc, slot_type* slot) {
00142 std::allocator_traits<allocator_type>::destroy(*alloc, slot);
00143 }
00144
00145 template <class allocator_type>
00146 static void transfer(allocator_type* alloc, slot_type* new_slot,
00147 slot_type* old_slot) {
00148 construct(alloc, new_slot, std::move(*old_slot));
00149 destroy(alloc, old_slot);
00150 }
00151
00152 template <class F>
00153 static auto apply(F&& f, int32_t v) -> decltype(std::forward<F>(f)(v, v)) {
00154 return std::forward<F>(f)(v, v);
00155 }
00156
00157 template <class F>
00158 static auto apply(F&& f, const slot_type& v)
00159 -> decltype(std::forward<F>(f)(v.val(), v)) {
00160 return std::forward<F>(f)(v.val(), v);
00161 }
00162
00163 template <class F>
00164 static auto apply(F&& f, slot_type&& v)
00165 -> decltype(std::forward<F>(f)(v.val(), std::move(v))) {
00166 return std::forward<F>(f)(v.val(), std::move(v));
00167 }
00168
00169 static slot_type& element(slot_type* slot) { return *slot; }
00170 };
00171
00172 template <int Spec>
00173 struct PropagateTest : public ::testing::Test {
00174 using Alloc = CheckedAlloc<Tracked<int32_t>, Spec>;
00175
00176 using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, Alloc>;
00177
00178 PropagateTest() {
00179 EXPECT_EQ(a1, t1.get_allocator());
00180 EXPECT_NE(a2, t1.get_allocator());
00181 }
00182
00183 Alloc a1 = Alloc(1);
00184 Table t1 = Table(0, a1);
00185 Alloc a2 = Alloc(2);
00186 };
00187
00188 using PropagateOnAll =
00189 PropagateTest<kPropagateOnCopy | kPropagateOnMove | kPropagateOnSwap>;
00190 using NoPropagateOnCopy = PropagateTest<kPropagateOnMove | kPropagateOnSwap>;
00191 using NoPropagateOnMove = PropagateTest<kPropagateOnCopy | kPropagateOnSwap>;
00192
00193 TEST_F(PropagateOnAll, Empty) { EXPECT_EQ(0, a1.num_allocs()); }
00194
00195 TEST_F(PropagateOnAll, InsertAllocates) {
00196 auto it = t1.insert(0).first;
00197 EXPECT_EQ(1, a1.num_allocs());
00198 EXPECT_EQ(0, it->num_moves());
00199 EXPECT_EQ(0, it->num_copies());
00200 }
00201
00202 TEST_F(PropagateOnAll, InsertDecomposes) {
00203 auto it = t1.insert(0).first;
00204 EXPECT_EQ(1, a1.num_allocs());
00205 EXPECT_EQ(0, it->num_moves());
00206 EXPECT_EQ(0, it->num_copies());
00207
00208 EXPECT_FALSE(t1.insert(0).second);
00209 EXPECT_EQ(1, a1.num_allocs());
00210 EXPECT_EQ(0, it->num_moves());
00211 EXPECT_EQ(0, it->num_copies());
00212 }
00213
00214 TEST_F(PropagateOnAll, RehashMoves) {
00215 auto it = t1.insert(0).first;
00216 EXPECT_EQ(0, it->num_moves());
00217 t1.rehash(2 * t1.capacity());
00218 EXPECT_EQ(2, a1.num_allocs());
00219 it = t1.find(0);
00220 EXPECT_EQ(1, it->num_moves());
00221 EXPECT_EQ(0, it->num_copies());
00222 }
00223
00224 TEST_F(PropagateOnAll, CopyConstructor) {
00225 auto it = t1.insert(0).first;
00226 Table u(t1);
00227 EXPECT_EQ(2, a1.num_allocs());
00228 EXPECT_EQ(0, it->num_moves());
00229 EXPECT_EQ(1, it->num_copies());
00230 }
00231
00232 TEST_F(NoPropagateOnCopy, CopyConstructor) {
00233 auto it = t1.insert(0).first;
00234 Table u(t1);
00235 EXPECT_EQ(1, a1.num_allocs());
00236 EXPECT_EQ(1, u.get_allocator().num_allocs());
00237 EXPECT_EQ(0, it->num_moves());
00238 EXPECT_EQ(1, it->num_copies());
00239 }
00240
00241 TEST_F(PropagateOnAll, CopyConstructorWithSameAlloc) {
00242 auto it = t1.insert(0).first;
00243 Table u(t1, a1);
00244 EXPECT_EQ(2, a1.num_allocs());
00245 EXPECT_EQ(0, it->num_moves());
00246 EXPECT_EQ(1, it->num_copies());
00247 }
00248
00249 TEST_F(NoPropagateOnCopy, CopyConstructorWithSameAlloc) {
00250 auto it = t1.insert(0).first;
00251 Table u(t1, a1);
00252 EXPECT_EQ(2, a1.num_allocs());
00253 EXPECT_EQ(0, it->num_moves());
00254 EXPECT_EQ(1, it->num_copies());
00255 }
00256
00257 TEST_F(PropagateOnAll, CopyConstructorWithDifferentAlloc) {
00258 auto it = t1.insert(0).first;
00259 Table u(t1, a2);
00260 EXPECT_EQ(a2, u.get_allocator());
00261 EXPECT_EQ(1, a1.num_allocs());
00262 EXPECT_EQ(1, a2.num_allocs());
00263 EXPECT_EQ(0, it->num_moves());
00264 EXPECT_EQ(1, it->num_copies());
00265 }
00266
00267 TEST_F(NoPropagateOnCopy, CopyConstructorWithDifferentAlloc) {
00268 auto it = t1.insert(0).first;
00269 Table u(t1, a2);
00270 EXPECT_EQ(a2, u.get_allocator());
00271 EXPECT_EQ(1, a1.num_allocs());
00272 EXPECT_EQ(1, a2.num_allocs());
00273 EXPECT_EQ(0, it->num_moves());
00274 EXPECT_EQ(1, it->num_copies());
00275 }
00276
00277 TEST_F(PropagateOnAll, MoveConstructor) {
00278 auto it = t1.insert(0).first;
00279 Table u(std::move(t1));
00280 EXPECT_EQ(1, a1.num_allocs());
00281 EXPECT_EQ(0, it->num_moves());
00282 EXPECT_EQ(0, it->num_copies());
00283 }
00284
00285 TEST_F(NoPropagateOnMove, MoveConstructor) {
00286 auto it = t1.insert(0).first;
00287 Table u(std::move(t1));
00288 EXPECT_EQ(1, a1.num_allocs());
00289 EXPECT_EQ(0, it->num_moves());
00290 EXPECT_EQ(0, it->num_copies());
00291 }
00292
00293 TEST_F(PropagateOnAll, MoveConstructorWithSameAlloc) {
00294 auto it = t1.insert(0).first;
00295 Table u(std::move(t1), a1);
00296 EXPECT_EQ(1, a1.num_allocs());
00297 EXPECT_EQ(0, it->num_moves());
00298 EXPECT_EQ(0, it->num_copies());
00299 }
00300
00301 TEST_F(NoPropagateOnMove, MoveConstructorWithSameAlloc) {
00302 auto it = t1.insert(0).first;
00303 Table u(std::move(t1), a1);
00304 EXPECT_EQ(1, a1.num_allocs());
00305 EXPECT_EQ(0, it->num_moves());
00306 EXPECT_EQ(0, it->num_copies());
00307 }
00308
00309 TEST_F(PropagateOnAll, MoveConstructorWithDifferentAlloc) {
00310 auto it = t1.insert(0).first;
00311 Table u(std::move(t1), a2);
00312 it = u.find(0);
00313 EXPECT_EQ(a2, u.get_allocator());
00314 EXPECT_EQ(1, a1.num_allocs());
00315 EXPECT_EQ(1, a2.num_allocs());
00316 EXPECT_EQ(1, it->num_moves());
00317 EXPECT_EQ(0, it->num_copies());
00318 }
00319
00320 TEST_F(NoPropagateOnMove, MoveConstructorWithDifferentAlloc) {
00321 auto it = t1.insert(0).first;
00322 Table u(std::move(t1), a2);
00323 it = u.find(0);
00324 EXPECT_EQ(a2, u.get_allocator());
00325 EXPECT_EQ(1, a1.num_allocs());
00326 EXPECT_EQ(1, a2.num_allocs());
00327 EXPECT_EQ(1, it->num_moves());
00328 EXPECT_EQ(0, it->num_copies());
00329 }
00330
00331 TEST_F(PropagateOnAll, CopyAssignmentWithSameAlloc) {
00332 auto it = t1.insert(0).first;
00333 Table u(0, a1);
00334 u = t1;
00335 EXPECT_EQ(2, a1.num_allocs());
00336 EXPECT_EQ(0, it->num_moves());
00337 EXPECT_EQ(1, it->num_copies());
00338 }
00339
00340 TEST_F(NoPropagateOnCopy, CopyAssignmentWithSameAlloc) {
00341 auto it = t1.insert(0).first;
00342 Table u(0, a1);
00343 u = t1;
00344 EXPECT_EQ(2, a1.num_allocs());
00345 EXPECT_EQ(0, it->num_moves());
00346 EXPECT_EQ(1, it->num_copies());
00347 }
00348
00349 TEST_F(PropagateOnAll, CopyAssignmentWithDifferentAlloc) {
00350 auto it = t1.insert(0).first;
00351 Table u(0, a2);
00352 u = t1;
00353 EXPECT_EQ(a1, u.get_allocator());
00354 EXPECT_EQ(2, a1.num_allocs());
00355 EXPECT_EQ(0, a2.num_allocs());
00356 EXPECT_EQ(0, it->num_moves());
00357 EXPECT_EQ(1, it->num_copies());
00358 }
00359
00360 TEST_F(NoPropagateOnCopy, CopyAssignmentWithDifferentAlloc) {
00361 auto it = t1.insert(0).first;
00362 Table u(0, a2);
00363 u = t1;
00364 EXPECT_EQ(a2, u.get_allocator());
00365 EXPECT_EQ(1, a1.num_allocs());
00366 EXPECT_EQ(1, a2.num_allocs());
00367 EXPECT_EQ(0, it->num_moves());
00368 EXPECT_EQ(1, it->num_copies());
00369 }
00370
00371 TEST_F(PropagateOnAll, MoveAssignmentWithSameAlloc) {
00372 auto it = t1.insert(0).first;
00373 Table u(0, a1);
00374 u = std::move(t1);
00375 EXPECT_EQ(a1, u.get_allocator());
00376 EXPECT_EQ(1, a1.num_allocs());
00377 EXPECT_EQ(0, it->num_moves());
00378 EXPECT_EQ(0, it->num_copies());
00379 }
00380
00381 TEST_F(NoPropagateOnMove, MoveAssignmentWithSameAlloc) {
00382 auto it = t1.insert(0).first;
00383 Table u(0, a1);
00384 u = std::move(t1);
00385 EXPECT_EQ(a1, u.get_allocator());
00386 EXPECT_EQ(1, a1.num_allocs());
00387 EXPECT_EQ(0, it->num_moves());
00388 EXPECT_EQ(0, it->num_copies());
00389 }
00390
00391 TEST_F(PropagateOnAll, MoveAssignmentWithDifferentAlloc) {
00392 auto it = t1.insert(0).first;
00393 Table u(0, a2);
00394 u = std::move(t1);
00395 EXPECT_EQ(a1, u.get_allocator());
00396 EXPECT_EQ(1, a1.num_allocs());
00397 EXPECT_EQ(0, a2.num_allocs());
00398 EXPECT_EQ(0, it->num_moves());
00399 EXPECT_EQ(0, it->num_copies());
00400 }
00401
00402 TEST_F(NoPropagateOnMove, MoveAssignmentWithDifferentAlloc) {
00403 auto it = t1.insert(0).first;
00404 Table u(0, a2);
00405 u = std::move(t1);
00406 it = u.find(0);
00407 EXPECT_EQ(a2, u.get_allocator());
00408 EXPECT_EQ(1, a1.num_allocs());
00409 EXPECT_EQ(1, a2.num_allocs());
00410 EXPECT_EQ(1, it->num_moves());
00411 EXPECT_EQ(0, it->num_copies());
00412 }
00413
00414 TEST_F(PropagateOnAll, Swap) {
00415 auto it = t1.insert(0).first;
00416 Table u(0, a2);
00417 u.swap(t1);
00418 EXPECT_EQ(a1, u.get_allocator());
00419 EXPECT_EQ(a2, t1.get_allocator());
00420 EXPECT_EQ(1, a1.num_allocs());
00421 EXPECT_EQ(0, a2.num_allocs());
00422 EXPECT_EQ(0, it->num_moves());
00423 EXPECT_EQ(0, it->num_copies());
00424 }
00425
00426 }
00427 }
00428 }