• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <cstddef>
16 #include <cstdint>
17 #include <functional>
18 #include <limits>
19 #include <memory>
20 #include <ostream>
21 #include <set>
22 #include <type_traits>
23 #include <utility>
24 
25 #include "gmock/gmock.h"
26 #include "gtest/gtest.h"
27 #include "absl/base/config.h"
28 #include "absl/container/internal/container_memory.h"
29 #include "absl/container/internal/raw_hash_set.h"
30 #include "absl/container/internal/tracked.h"
31 
32 namespace absl {
33 ABSL_NAMESPACE_BEGIN
34 namespace container_internal {
35 namespace {
36 using ::testing::AnyOf;
37 
38 enum AllocSpec {
39   kPropagateOnCopy = 1,
40   kPropagateOnMove = 2,
41   kPropagateOnSwap = 4,
42 };
43 
44 struct AllocState {
45   size_t num_allocs = 0;
46   std::set<void*> owned;
47 };
48 
49 template <class T,
50           int Spec = kPropagateOnCopy | kPropagateOnMove | kPropagateOnSwap>
51 class CheckedAlloc {
52  public:
53   template <class, int>
54   friend class CheckedAlloc;
55 
56   using value_type = T;
57 
CheckedAlloc()58   CheckedAlloc() {}
CheckedAlloc(size_t id)59   explicit CheckedAlloc(size_t id) : id_(id) {}
60   CheckedAlloc(const CheckedAlloc&) = default;
61   CheckedAlloc& operator=(const CheckedAlloc&) = default;
62 
63   template <class U>
CheckedAlloc(const CheckedAlloc<U,Spec> & that)64   CheckedAlloc(const CheckedAlloc<U, Spec>& that)
65       : id_(that.id_), state_(that.state_) {}
66 
67   template <class U>
68   struct rebind {
69     using other = CheckedAlloc<U, Spec>;
70   };
71 
72   using propagate_on_container_copy_assignment =
73       std::integral_constant<bool, (Spec & kPropagateOnCopy) != 0>;
74 
75   using propagate_on_container_move_assignment =
76       std::integral_constant<bool, (Spec & kPropagateOnMove) != 0>;
77 
78   using propagate_on_container_swap =
79       std::integral_constant<bool, (Spec & kPropagateOnSwap) != 0>;
80 
select_on_container_copy_construction() const81   CheckedAlloc select_on_container_copy_construction() const {
82     if (Spec & kPropagateOnCopy) return *this;
83     return {};
84   }
85 
allocate(size_t n)86   T* allocate(size_t n) {
87     T* ptr = std::allocator<T>().allocate(n);
88     track_alloc(ptr);
89     return ptr;
90   }
deallocate(T * ptr,size_t n)91   void deallocate(T* ptr, size_t n) {
92     memset(ptr, 0, n * sizeof(T));  // The freed memory must be unpoisoned.
93     track_dealloc(ptr);
94     return std::allocator<T>().deallocate(ptr, n);
95   }
96 
operator ==(const CheckedAlloc & a,const CheckedAlloc & b)97   friend bool operator==(const CheckedAlloc& a, const CheckedAlloc& b) {
98     return a.id_ == b.id_;
99   }
operator !=(const CheckedAlloc & a,const CheckedAlloc & b)100   friend bool operator!=(const CheckedAlloc& a, const CheckedAlloc& b) {
101     return !(a == b);
102   }
103 
num_allocs() const104   size_t num_allocs() const { return state_->num_allocs; }
105 
swap(CheckedAlloc & that)106   void swap(CheckedAlloc& that) {
107     using std::swap;
108     swap(id_, that.id_);
109     swap(state_, that.state_);
110   }
111 
swap(CheckedAlloc & a,CheckedAlloc & b)112   friend void swap(CheckedAlloc& a, CheckedAlloc& b) { a.swap(b); }
113 
operator <<(std::ostream & o,const CheckedAlloc & a)114   friend std::ostream& operator<<(std::ostream& o, const CheckedAlloc& a) {
115     return o << "alloc(" << a.id_ << ")";
116   }
117 
118  private:
track_alloc(void * ptr)119   void track_alloc(void* ptr) {
120     AllocState* state = state_.get();
121     ++state->num_allocs;
122     if (!state->owned.insert(ptr).second)
123       ADD_FAILURE() << *this << " got previously allocated memory: " << ptr;
124   }
track_dealloc(void * ptr)125   void track_dealloc(void* ptr) {
126     if (state_->owned.erase(ptr) != 1)
127       ADD_FAILURE() << *this
128                     << " deleting memory owned by another allocator: " << ptr;
129   }
130 
131   size_t id_ = std::numeric_limits<size_t>::max();
132 
133   std::shared_ptr<AllocState> state_ = std::make_shared<AllocState>();
134 };
135 
136 struct Identity {
operator ()absl::container_internal::__anon9b0bfa100111::Identity137   size_t operator()(int32_t v) const { return static_cast<size_t>(v); }
138 };
139 
140 struct Policy {
141   using slot_type = Tracked<int32_t>;
142   using init_type = Tracked<int32_t>;
143   using key_type = int32_t;
144 
145   template <class allocator_type, class... Args>
constructabsl::container_internal::__anon9b0bfa100111::Policy146   static void construct(allocator_type* alloc, slot_type* slot,
147                         Args&&... args) {
148     std::allocator_traits<allocator_type>::construct(
149         *alloc, slot, std::forward<Args>(args)...);
150   }
151 
152   template <class allocator_type>
destroyabsl::container_internal::__anon9b0bfa100111::Policy153   static void destroy(allocator_type* alloc, slot_type* slot) {
154     std::allocator_traits<allocator_type>::destroy(*alloc, slot);
155   }
156 
157   template <class allocator_type>
transferabsl::container_internal::__anon9b0bfa100111::Policy158   static void transfer(allocator_type* alloc, slot_type* new_slot,
159                        slot_type* old_slot) {
160     construct(alloc, new_slot, std::move(*old_slot));
161     destroy(alloc, old_slot);
162   }
163 
164   template <class F>
applyabsl::container_internal::__anon9b0bfa100111::Policy165   static auto apply(F&& f, int32_t v) -> decltype(std::forward<F>(f)(v, v)) {
166     return std::forward<F>(f)(v, v);
167   }
168 
169   template <class F>
applyabsl::container_internal::__anon9b0bfa100111::Policy170   static auto apply(F&& f, const slot_type& v)
171       -> decltype(std::forward<F>(f)(v.val(), v)) {
172     return std::forward<F>(f)(v.val(), v);
173   }
174 
175   template <class F>
applyabsl::container_internal::__anon9b0bfa100111::Policy176   static auto apply(F&& f, slot_type&& v)
177       -> decltype(std::forward<F>(f)(v.val(), std::move(v))) {
178     return std::forward<F>(f)(v.val(), std::move(v));
179   }
180 
elementabsl::container_internal::__anon9b0bfa100111::Policy181   static slot_type& element(slot_type* slot) { return *slot; }
182 
183   template <class Hash>
get_hash_slot_fnabsl::container_internal::__anon9b0bfa100111::Policy184   static constexpr HashSlotFn get_hash_slot_fn() {
185     return nullptr;
186   }
187 };
188 
189 template <int Spec>
190 struct PropagateTest : public ::testing::Test {
191   using Alloc = CheckedAlloc<Tracked<int32_t>, Spec>;
192 
193   using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, Alloc>;
194 
PropagateTestabsl::container_internal::__anon9b0bfa100111::PropagateTest195   PropagateTest() {
196     EXPECT_EQ(a1, t1.get_allocator());
197     EXPECT_NE(a2, t1.get_allocator());
198   }
199 
200   Alloc a1 = Alloc(1);
201   Table t1 = Table(0, a1);
202   Alloc a2 = Alloc(2);
203 };
204 
205 using PropagateOnAll =
206     PropagateTest<kPropagateOnCopy | kPropagateOnMove | kPropagateOnSwap>;
207 using NoPropagateOnCopy = PropagateTest<kPropagateOnMove | kPropagateOnSwap>;
208 using NoPropagateOnMove = PropagateTest<kPropagateOnCopy | kPropagateOnSwap>;
209 
TEST_F(PropagateOnAll,Empty)210 TEST_F(PropagateOnAll, Empty) { EXPECT_EQ(0, a1.num_allocs()); }
211 
TEST_F(PropagateOnAll,InsertAllocates)212 TEST_F(PropagateOnAll, InsertAllocates) {
213   auto it = t1.insert(0).first;
214   EXPECT_EQ(1, a1.num_allocs());
215   EXPECT_EQ(0, it->num_moves());
216   EXPECT_EQ(0, it->num_copies());
217 }
218 
TEST_F(PropagateOnAll,InsertDecomposes)219 TEST_F(PropagateOnAll, InsertDecomposes) {
220   auto it = t1.insert(0).first;
221   EXPECT_EQ(1, a1.num_allocs());
222   EXPECT_EQ(0, it->num_moves());
223   EXPECT_EQ(0, it->num_copies());
224 
225   EXPECT_FALSE(t1.insert(0).second);
226   EXPECT_EQ(1, a1.num_allocs());
227   EXPECT_EQ(0, it->num_moves());
228   EXPECT_EQ(0, it->num_copies());
229 }
230 
TEST_F(PropagateOnAll,RehashMoves)231 TEST_F(PropagateOnAll, RehashMoves) {
232   auto it = t1.insert(0).first;
233   EXPECT_EQ(0, it->num_moves());
234   t1.rehash(2 * t1.capacity());
235   EXPECT_EQ(2, a1.num_allocs());
236   it = t1.find(0);
237   EXPECT_EQ(1, it->num_moves());
238   EXPECT_EQ(0, it->num_copies());
239 }
240 
TEST_F(PropagateOnAll,CopyConstructor)241 TEST_F(PropagateOnAll, CopyConstructor) {
242   auto it = t1.insert(0).first;
243   Table u(t1);
244   EXPECT_EQ(2, a1.num_allocs());
245   EXPECT_EQ(0, it->num_moves());
246   EXPECT_EQ(1, it->num_copies());
247 }
248 
TEST_F(NoPropagateOnCopy,CopyConstructor)249 TEST_F(NoPropagateOnCopy, CopyConstructor) {
250   auto it = t1.insert(0).first;
251   Table u(t1);
252   EXPECT_EQ(1, a1.num_allocs());
253   EXPECT_EQ(1, u.get_allocator().num_allocs());
254   EXPECT_EQ(0, it->num_moves());
255   EXPECT_EQ(1, it->num_copies());
256 }
257 
TEST_F(PropagateOnAll,CopyConstructorWithSameAlloc)258 TEST_F(PropagateOnAll, CopyConstructorWithSameAlloc) {
259   auto it = t1.insert(0).first;
260   Table u(t1, a1);
261   EXPECT_EQ(2, a1.num_allocs());
262   EXPECT_EQ(0, it->num_moves());
263   EXPECT_EQ(1, it->num_copies());
264 }
265 
TEST_F(NoPropagateOnCopy,CopyConstructorWithSameAlloc)266 TEST_F(NoPropagateOnCopy, CopyConstructorWithSameAlloc) {
267   auto it = t1.insert(0).first;
268   Table u(t1, a1);
269   EXPECT_EQ(2, a1.num_allocs());
270   EXPECT_EQ(0, it->num_moves());
271   EXPECT_EQ(1, it->num_copies());
272 }
273 
TEST_F(PropagateOnAll,CopyConstructorWithDifferentAlloc)274 TEST_F(PropagateOnAll, CopyConstructorWithDifferentAlloc) {
275   auto it = t1.insert(0).first;
276   Table u(t1, a2);
277   EXPECT_EQ(a2, u.get_allocator());
278   EXPECT_EQ(1, a1.num_allocs());
279   EXPECT_EQ(1, a2.num_allocs());
280   EXPECT_EQ(0, it->num_moves());
281   EXPECT_EQ(1, it->num_copies());
282 }
283 
TEST_F(NoPropagateOnCopy,CopyConstructorWithDifferentAlloc)284 TEST_F(NoPropagateOnCopy, CopyConstructorWithDifferentAlloc) {
285   auto it = t1.insert(0).first;
286   Table u(t1, a2);
287   EXPECT_EQ(a2, u.get_allocator());
288   EXPECT_EQ(1, a1.num_allocs());
289   EXPECT_EQ(1, a2.num_allocs());
290   EXPECT_EQ(0, it->num_moves());
291   EXPECT_EQ(1, it->num_copies());
292 }
293 
TEST_F(PropagateOnAll,MoveConstructor)294 TEST_F(PropagateOnAll, MoveConstructor) {
295   t1.insert(0);
296   Table u(std::move(t1));
297   auto it = u.begin();
298   EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
299   EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
300   EXPECT_EQ(0, it->num_copies());
301 }
302 
TEST_F(NoPropagateOnMove,MoveConstructor)303 TEST_F(NoPropagateOnMove, MoveConstructor) {
304   t1.insert(0);
305   Table u(std::move(t1));
306   auto it = u.begin();
307   EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
308   EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
309   EXPECT_EQ(0, it->num_copies());
310 }
311 
TEST_F(PropagateOnAll,MoveConstructorWithSameAlloc)312 TEST_F(PropagateOnAll, MoveConstructorWithSameAlloc) {
313   t1.insert(0);
314   Table u(std::move(t1), a1);
315   auto it = u.begin();
316   EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
317   EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
318   EXPECT_EQ(0, it->num_copies());
319 }
320 
TEST_F(NoPropagateOnMove,MoveConstructorWithSameAlloc)321 TEST_F(NoPropagateOnMove, MoveConstructorWithSameAlloc) {
322   t1.insert(0);
323   Table u(std::move(t1), a1);
324   auto it = u.begin();
325   EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
326   EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
327   EXPECT_EQ(0, it->num_copies());
328 }
329 
TEST_F(PropagateOnAll,MoveConstructorWithDifferentAlloc)330 TEST_F(PropagateOnAll, MoveConstructorWithDifferentAlloc) {
331   auto it = t1.insert(0).first;
332   Table u(std::move(t1), a2);
333   it = u.find(0);
334   EXPECT_EQ(a2, u.get_allocator());
335   EXPECT_EQ(1, a1.num_allocs());
336   EXPECT_THAT(a2.num_allocs(), AnyOf(1, 2));
337   EXPECT_THAT(it->num_moves(), AnyOf(1, 2));
338   EXPECT_EQ(0, it->num_copies());
339 }
340 
TEST_F(NoPropagateOnMove,MoveConstructorWithDifferentAlloc)341 TEST_F(NoPropagateOnMove, MoveConstructorWithDifferentAlloc) {
342   auto it = t1.insert(0).first;
343   Table u(std::move(t1), a2);
344   it = u.find(0);
345   EXPECT_EQ(a2, u.get_allocator());
346   EXPECT_EQ(1, a1.num_allocs());
347   EXPECT_THAT(a2.num_allocs(), AnyOf(1, 2));
348   EXPECT_THAT(it->num_moves(), AnyOf(1, 2));
349   EXPECT_EQ(0, it->num_copies());
350 }
351 
TEST_F(PropagateOnAll,CopyAssignmentWithSameAlloc)352 TEST_F(PropagateOnAll, CopyAssignmentWithSameAlloc) {
353   auto it = t1.insert(0).first;
354   Table u(0, a1);
355   u = t1;
356   EXPECT_THAT(a1.num_allocs(), AnyOf(2, 3));
357   EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
358   EXPECT_EQ(1, it->num_copies());
359 }
360 
TEST_F(NoPropagateOnCopy,CopyAssignmentWithSameAlloc)361 TEST_F(NoPropagateOnCopy, CopyAssignmentWithSameAlloc) {
362   auto it = t1.insert(0).first;
363   Table u(0, a1);
364   u = t1;
365   EXPECT_THAT(a1.num_allocs(), AnyOf(2, 3));
366   EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
367   EXPECT_EQ(1, it->num_copies());
368 }
369 
TEST_F(PropagateOnAll,CopyAssignmentWithDifferentAlloc)370 TEST_F(PropagateOnAll, CopyAssignmentWithDifferentAlloc) {
371   auto it = t1.insert(0).first;
372   Table u(0, a2);
373   u = t1;
374   EXPECT_EQ(a1, u.get_allocator());
375   EXPECT_THAT(a1.num_allocs(), AnyOf(2, 3));
376   EXPECT_EQ(0, a2.num_allocs());
377   EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
378   EXPECT_EQ(1, it->num_copies());
379 }
380 
TEST_F(NoPropagateOnCopy,CopyAssignmentWithDifferentAlloc)381 TEST_F(NoPropagateOnCopy, CopyAssignmentWithDifferentAlloc) {
382   auto it = t1.insert(0).first;
383   Table u(0, a2);
384   u = t1;
385   EXPECT_EQ(a2, u.get_allocator());
386   EXPECT_EQ(1, a1.num_allocs());
387   EXPECT_THAT(a2.num_allocs(), AnyOf(1, 2));
388   EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
389   EXPECT_EQ(1, it->num_copies());
390 }
391 
TEST_F(PropagateOnAll,MoveAssignmentWithSameAlloc)392 TEST_F(PropagateOnAll, MoveAssignmentWithSameAlloc) {
393   t1.insert(0);
394   Table u(0, a1);
395   u = std::move(t1);
396   auto it = u.begin();
397   EXPECT_EQ(a1, u.get_allocator());
398   EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
399   EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
400   EXPECT_EQ(0, it->num_copies());
401 }
402 
TEST_F(NoPropagateOnMove,MoveAssignmentWithSameAlloc)403 TEST_F(NoPropagateOnMove, MoveAssignmentWithSameAlloc) {
404   t1.insert(0);
405   Table u(0, a1);
406   u = std::move(t1);
407   auto it = u.begin();
408   EXPECT_EQ(a1, u.get_allocator());
409   EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
410   EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
411   EXPECT_EQ(0, it->num_copies());
412 }
413 
TEST_F(PropagateOnAll,MoveAssignmentWithDifferentAlloc)414 TEST_F(PropagateOnAll, MoveAssignmentWithDifferentAlloc) {
415   t1.insert(0);
416   Table u(0, a2);
417   u = std::move(t1);
418   auto it = u.begin();
419   EXPECT_EQ(a1, u.get_allocator());
420   EXPECT_THAT(a1.num_allocs(), AnyOf(1, 2));
421   EXPECT_EQ(0, a2.num_allocs());
422   EXPECT_THAT(it->num_moves(), AnyOf(0, 1));
423   EXPECT_EQ(0, it->num_copies());
424 }
425 
TEST_F(NoPropagateOnMove,MoveAssignmentWithDifferentAlloc)426 TEST_F(NoPropagateOnMove, MoveAssignmentWithDifferentAlloc) {
427   t1.insert(0);
428   Table u(0, a2);
429   u = std::move(t1);
430   auto it = u.find(0);
431   EXPECT_EQ(a2, u.get_allocator());
432   EXPECT_EQ(1, a1.num_allocs());
433   EXPECT_THAT(a2.num_allocs(), AnyOf(1, 2));
434   EXPECT_THAT(it->num_moves(), AnyOf(1, 2));
435   EXPECT_EQ(0, it->num_copies());
436 }
437 
TEST_F(PropagateOnAll,Swap)438 TEST_F(PropagateOnAll, Swap) {
439   auto it = t1.insert(0).first;
440   Table u(0, a2);
441   u.swap(t1);
442   EXPECT_EQ(a1, u.get_allocator());
443   EXPECT_EQ(a2, t1.get_allocator());
444   EXPECT_EQ(1, a1.num_allocs());
445   EXPECT_EQ(0, a2.num_allocs());
446   EXPECT_EQ(0, it->num_moves());
447   EXPECT_EQ(0, it->num_copies());
448 }
449 
450 // This allocator is similar to std::pmr::polymorphic_allocator.
451 // Note the disabled assignment.
452 template <class T>
453 class PAlloc {
454   template <class>
455   friend class PAlloc;
456 
457  public:
458   // types
459   using value_type = T;
460 
461   PAlloc() noexcept = default;
PAlloc(size_t id)462   explicit PAlloc(size_t id) noexcept : id_(id) {}
463   PAlloc(const PAlloc&) noexcept = default;
464   PAlloc& operator=(const PAlloc&) noexcept = delete;
465 
466   template <class U>
PAlloc(const PAlloc<U> & that)467   PAlloc(const PAlloc<U>& that) noexcept : id_(that.id_) {}  // NOLINT
468 
469   template <class U>
470   struct rebind {
471     using other = PAlloc<U>;
472   };
473 
select_on_container_copy_construction() const474   constexpr PAlloc select_on_container_copy_construction() const { return {}; }
475 
476   // public member functions
allocate(size_t)477   T* allocate(size_t) { return new T; }
deallocate(T * p,size_t)478   void deallocate(T* p, size_t) { delete p; }
479 
operator ==(const PAlloc & a,const PAlloc & b)480   friend bool operator==(const PAlloc& a, const PAlloc& b) {
481     return a.id_ == b.id_;
482   }
operator !=(const PAlloc & a,const PAlloc & b)483   friend bool operator!=(const PAlloc& a, const PAlloc& b) { return !(a == b); }
484 
485  private:
486   size_t id_ = std::numeric_limits<size_t>::max();
487 };
488 
TEST(NoPropagateDeletedAssignment,CopyConstruct)489 TEST(NoPropagateDeletedAssignment, CopyConstruct) {
490   using PA = PAlloc<char>;
491   using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, PA>;
492 
493   Table t1(PA{1}), t2(t1);
494   EXPECT_EQ(t1.get_allocator(), PA(1));
495   EXPECT_EQ(t2.get_allocator(), PA());
496 }
497 
TEST(NoPropagateDeletedAssignment,CopyAssignment)498 TEST(NoPropagateDeletedAssignment, CopyAssignment) {
499   using PA = PAlloc<char>;
500   using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, PA>;
501 
502   Table t1(PA{1}), t2(PA{2});
503   t1 = t2;
504   EXPECT_EQ(t1.get_allocator(), PA(1));
505   EXPECT_EQ(t2.get_allocator(), PA(2));
506 }
507 
TEST(NoPropagateDeletedAssignment,MoveAssignment)508 TEST(NoPropagateDeletedAssignment, MoveAssignment) {
509   using PA = PAlloc<char>;
510   using Table = raw_hash_set<Policy, Identity, std::equal_to<int32_t>, PA>;
511 
512   Table t1(PA{1}), t2(PA{2});
513   t1 = std::move(t2);
514   EXPECT_EQ(t1.get_allocator(), PA(1));
515 }
516 
517 }  // namespace
518 }  // namespace container_internal
519 ABSL_NAMESPACE_END
520 }  // namespace absl
521