proxygen
F14Policy.h
Go to the documentation of this file.
1 /*
2  * Copyright 2017-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <memory>
20 #include <type_traits>
21 #include <utility>
22 
23 #include <folly/Memory.h>
24 #include <folly/Portability.h>
25 #include <folly/Unit.h>
28 #include <folly/hash/Hash.h>
29 #include <folly/lang/Align.h>
30 #include <folly/lang/SafeAssert.h>
31 #include <folly/memory/Malloc.h>
32 
33 #if FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
34 
35 namespace folly {
36 namespace f14 {
37 namespace detail {
38 
39 template <typename Ptr>
40 using NonConstPtr = typename std::pointer_traits<Ptr>::template rebind<
41  std::remove_const_t<typename std::pointer_traits<Ptr>::element_type>>;
42 
43 template <typename KeyType, typename MappedType>
44 using MapValueType = std::pair<KeyType const, MappedType>;
45 
46 template <typename KeyType, typename MappedTypeOrVoid>
47 using SetOrMapValueType = std::conditional_t<
49  KeyType,
50  MapValueType<KeyType, MappedTypeOrVoid>>;
51 
52 // Used to enable EBO for Hasher, KeyEqual, and Alloc. std::tuple of
53 // all empty objects is empty in libstdc++ but not libc++.
54 template <
55  char Tag,
56  typename T,
58 struct ObjectHolder {
59  T value_;
60 
61  template <typename... Args>
62  ObjectHolder(Args&&... args) : value_{std::forward<Args>(args)...} {}
63 
64  T& operator*() {
65  return value_;
66  }
67  T const& operator*() const {
68  return value_;
69  }
70 };
71 
72 template <char Tag, typename T>
73 struct ObjectHolder<Tag, T, true> : private T {
74  template <typename... Args>
75  ObjectHolder(Args&&... args) : T{std::forward<Args>(args)...} {}
76 
77  T& operator*() {
78  return *this;
79  }
80  T const& operator*() const {
81  return *this;
82  }
83 };
84 
85 // Policy provides the functionality of hasher, key_equal, and
86 // allocator_type. In addition, it can add indirection to the values
87 // contained in the base table by defining a non-trivial value() method.
88 //
89 // To facilitate stateful implementations it is guaranteed that there
90 // will be a 1:1 relationship between BaseTable and Policy instance:
91 // policies will only be copied when their owning table is copied, and
92 // they will only be moved when their owning table is moved.
93 //
94 // Key equality will have the user-supplied search key as its first
95 // argument and the table contents as its second. Heterogeneous lookup
96 // should be handled on the first argument.
97 //
98 // Item is the data stored inline in the hash table's chunks. The policy
99 // controls how this is mapped to the corresponding Value.
100 //
101 // The policies defined in this file work for either set or map types.
102 // Most of the functionality is identical. A few methods detect the
103 // collection type by checking to see if MappedType is void, and then use
104 // SFINAE to select the appropriate implementation.
105 template <
106  typename KeyType,
107  typename MappedTypeOrVoid,
108  typename HasherOrVoid,
109  typename KeyEqualOrVoid,
110  typename AllocOrVoid,
111  typename ItemType>
112 struct BasePolicy
113  : private ObjectHolder<
114  'H',
115  Defaulted<HasherOrVoid, DefaultHasher<KeyType>>>,
116  private ObjectHolder<
117  'E',
118  Defaulted<KeyEqualOrVoid, DefaultKeyEqual<KeyType>>>,
119  private ObjectHolder<
120  'A',
121  Defaulted<
122  AllocOrVoid,
123  DefaultAlloc<SetOrMapValueType<KeyType, MappedTypeOrVoid>>>> {
125 
126  using Key = KeyType;
127  using Mapped = MappedTypeOrVoid;
128  using Value = SetOrMapValueType<Key, Mapped>;
129  using Item = ItemType;
130  using Hasher = Defaulted<HasherOrVoid, DefaultHasher<Key>>;
131  using KeyEqual = Defaulted<KeyEqualOrVoid, DefaultKeyEqual<Key>>;
132  using Alloc = Defaulted<AllocOrVoid, DefaultAlloc<Value>>;
133  using AllocTraits = std::allocator_traits<Alloc>;
134 
135  using ByteAlloc = typename AllocTraits::template rebind_alloc<uint8_t>;
136  using ByteAllocTraits = typename std::allocator_traits<ByteAlloc>;
137  using BytePtr = typename ByteAllocTraits::pointer;
138 
140 
141  static_assert(
143  "wrong allocator value_type");
144 
145  private:
146  using HasherHolder = ObjectHolder<'H', Hasher>;
147  using KeyEqualHolder = ObjectHolder<'E', KeyEqual>;
148  using AllocHolder = ObjectHolder<'A', Alloc>;
149 
150  // emulate c++17's std::allocator_traits<A>::is_always_equal
151 
152  template <typename A, typename = void>
153  struct AllocIsAlwaysEqual : std::is_empty<A> {};
154 
155  template <typename A>
156  struct AllocIsAlwaysEqual<A, typename A::is_always_equal>
157  : A::is_always_equal {};
158 
159  // emulate c++17 has std::is_nothrow_swappable
160  template <typename T>
161  static constexpr bool isNothrowSwap() {
162  using std::swap;
163  return noexcept(swap(std::declval<T&>(), std::declval<T&>()));
164  }
165 
166  public:
167  static constexpr bool kAllocIsAlwaysEqual = AllocIsAlwaysEqual<Alloc>::value;
168 
169  static constexpr bool kDefaultConstructIsNoexcept =
173 
174  static constexpr bool kSwapIsNoexcept = kAllocIsAlwaysEqual &&
175  isNothrowSwap<Hasher>() && isNothrowSwap<KeyEqual>();
176 
177  static constexpr bool isAvalanchingHasher() {
179  }
180 
182 
183  using InternalSizeType = std::size_t;
184 
185  // if false, F14Table will be smaller but F14Table::begin() won't work
186  static constexpr bool kEnableItemIteration = true;
187 
188  using Chunk = F14Chunk<Item>;
189  using ChunkPtr = typename std::pointer_traits<
190  typename AllocTraits::pointer>::template rebind<Chunk>;
191  using ItemIter = F14ItemIter<ChunkPtr>;
192 
193  static constexpr bool kIsMap = !std::is_same<Key, Value>::value;
194  static_assert(
196  "Assumption for the kIsMap check violated.");
197 
198  using MappedOrBool = std::conditional_t<kIsMap, Mapped, bool>;
199 
201 
202  BasePolicy(Hasher const& hasher, KeyEqual const& keyEqual, Alloc const& alloc)
203  : HasherHolder{hasher}, KeyEqualHolder{keyEqual}, AllocHolder{alloc} {}
204 
205  BasePolicy(BasePolicy const& rhs)
206  : HasherHolder{rhs.hasher()},
207  KeyEqualHolder{rhs.keyEqual()},
208  AllocHolder{
209  AllocTraits::select_on_container_copy_construction(rhs.alloc())} {}
210 
211  BasePolicy(BasePolicy const& rhs, Alloc const& alloc)
212  : HasherHolder{rhs.hasher()},
213  KeyEqualHolder{rhs.keyEqual()},
214  AllocHolder{alloc} {}
215 
216  BasePolicy(BasePolicy&& rhs) noexcept
217  : HasherHolder{std::move(rhs.hasher())},
218  KeyEqualHolder{std::move(rhs.keyEqual())},
219  AllocHolder{std::move(rhs.alloc())} {}
220 
221  BasePolicy(BasePolicy&& rhs, Alloc const& alloc) noexcept
222  : HasherHolder{std::move(rhs.hasher())},
223  KeyEqualHolder{std::move(rhs.keyEqual())},
224  AllocHolder{alloc} {}
225 
226  BasePolicy& operator=(BasePolicy const& rhs) {
227  hasher() = rhs.hasher();
228  keyEqual() = rhs.keyEqual();
230  alloc() = rhs.alloc();
231  }
232  return *this;
233  }
234 
235  BasePolicy& operator=(BasePolicy&& rhs) noexcept {
236  hasher() = std::move(rhs.hasher());
237  keyEqual() = std::move(rhs.keyEqual());
239  alloc() = std::move(rhs.alloc());
240  }
241  return *this;
242  }
243 
244  void swapBasePolicy(BasePolicy& rhs) {
245  using std::swap;
246  swap(hasher(), rhs.hasher());
247  swap(keyEqual(), rhs.keyEqual());
249  swap(alloc(), rhs.alloc());
250  }
251  }
252 
253  Hasher& hasher() {
254  return *static_cast<HasherHolder&>(*this);
255  }
256  Hasher const& hasher() const {
257  return *static_cast<HasherHolder const&>(*this);
258  }
259  KeyEqual& keyEqual() {
260  return *static_cast<KeyEqualHolder&>(*this);
261  }
262  KeyEqual const& keyEqual() const {
263  return *static_cast<KeyEqualHolder const&>(*this);
264  }
265  Alloc& alloc() {
266  return *static_cast<AllocHolder&>(*this);
267  }
268  Alloc const& alloc() const {
269  return *static_cast<AllocHolder const&>(*this);
270  }
271 
272  template <typename K>
273  std::size_t computeKeyHash(K const& key) const {
274  static_assert(
275  isAvalanchingHasher() == IsAvalanchingHasher<Hasher, K>::value, "");
276  static_assert(
277  !isAvalanchingHasher() ||
278  sizeof(decltype(hasher()(key))) >= sizeof(std::size_t),
279  "hasher is not avalanching if it doesn't return enough bits");
280  return hasher()(key);
281  }
282 
283  Key const& keyForValue(Key const& v) const {
284  return v;
285  }
286  Key const& keyForValue(std::pair<Key const, MappedOrBool> const& p) const {
287  return p.first;
288  }
289  Key const& keyForValue(std::pair<Key&&, MappedOrBool&&> const& p) const {
290  return p.first;
291  }
292 
293  // map's choice of pair<K const, T> as value_type is unfortunate,
294  // because it means we either need a proxy iterator, a pointless key
295  // copy when moving items during rehash, or some sort of UB hack.
296  //
297  // This code implements the hack. Use moveValue(v) instead of
298  // std::move(v) as the source of a move construction. enable_if_t is
299  // used so that this works for maps while being a no-op for sets.
300  template <typename Dummy = int>
301  static std::pair<Key&&, MappedOrBool&&> moveValue(
302  std::pair<Key const, MappedOrBool>& value,
303  std::enable_if_t<kIsMap, Dummy> = 0) {
304  return {std::move(const_cast<Key&>(value.first)), std::move(value.second)};
305  }
306 
307  template <typename Dummy = int>
308  static Value&& moveValue(Value& value, std::enable_if_t<!kIsMap, Dummy> = 0) {
309  return std::move(value);
310  }
311 
312  template <typename P>
313  bool
314  beforeBuild(std::size_t /*size*/, std::size_t /*capacity*/, P&& /*rhs*/) {
315  return false;
316  }
317 
318  template <typename P>
319  void afterBuild(
320  bool /*undoState*/,
321  bool /*success*/,
322  std::size_t /*size*/,
323  std::size_t /*capacity*/,
324  P&& /*rhs*/) {}
325 
326  std::size_t alignedAllocSize(std::size_t n) const {
327  if (kRequiredVectorAlignment <= alignof(max_align_t) ||
328  std::is_same<ByteAlloc, std::allocator<uint8_t>>::value) {
329  return n;
330  } else {
331  return n + kRequiredVectorAlignment;
332  }
333  }
334 
335  bool beforeRehash(
336  std::size_t /*size*/,
337  std::size_t /*oldCapacity*/,
338  std::size_t /*newCapacity*/,
339  std::size_t chunkAllocSize,
340  BytePtr& outChunkAllocation) {
341  outChunkAllocation =
342  allocateOverAligned<ByteAlloc, kRequiredVectorAlignment>(
343  ByteAlloc{alloc()}, chunkAllocSize);
344  return false;
345  }
346 
347  void afterRehash(
348  bool /*undoState*/,
349  bool /*success*/,
350  std::size_t /*size*/,
351  std::size_t /*oldCapacity*/,
352  std::size_t /*newCapacity*/,
353  BytePtr chunkAllocation,
354  std::size_t chunkAllocSize) {
355  // on success, this will be the old allocation, on failure the new one
356  if (chunkAllocation != nullptr) {
357  deallocateOverAligned<ByteAlloc, kRequiredVectorAlignment>(
358  ByteAlloc{alloc()}, chunkAllocation, chunkAllocSize);
359  }
360  }
361 
362  void beforeClear(std::size_t /*size*/, std::size_t /*capacity*/) {}
363 
364  void afterClear(std::size_t /*size*/, std::size_t /*capacity*/) {}
365 
366  void beforeReset(std::size_t /*size*/, std::size_t /*capacity*/) {}
367 
368  void afterReset(
369  std::size_t /*size*/,
370  std::size_t /*capacity*/,
371  BytePtr chunkAllocation,
372  std::size_t chunkAllocSize) {
373  deallocateOverAligned<ByteAlloc, kRequiredVectorAlignment>(
374  ByteAlloc{alloc()}, chunkAllocation, chunkAllocSize);
375  }
376 
377  void prefetchValue(Item const&) const {
378  // Subclass should disable with prefetchBeforeRehash(),
379  // prefetchBeforeCopy(), and prefetchBeforeDestroy(). if they don't
380  // override this method, because neither gcc nor clang can figure
381  // out that DenseMaskIter with an empty body can be elided.
382  FOLLY_SAFE_DCHECK(false, "should be disabled");
383  }
384 
385  void afterDestroyWithoutDeallocate(Value* addr, std::size_t n) {
386  if (kIsSanitizeAddress) {
387  memset(static_cast<void*>(addr), 0x66, sizeof(Value) * n);
388  }
389  }
390 };
391 
392 // BaseIter is a convenience for concrete set and map implementations
393 template <typename ValuePtr, typename Item>
394 class BaseIter : public std::iterator<
395  std::forward_iterator_tag,
396  std::remove_const_t<
397  typename std::pointer_traits<ValuePtr>::element_type>,
398  std::ptrdiff_t,
399  ValuePtr,
400  decltype(*std::declval<ValuePtr>())> {
401  protected:
402  using Chunk = F14Chunk<Item>;
403  using ChunkPtr =
404  typename std::pointer_traits<ValuePtr>::template rebind<Chunk>;
405  using ItemIter = F14ItemIter<ChunkPtr>;
406 
407  using ValueConstPtr = typename std::pointer_traits<ValuePtr>::template rebind<
408  std::add_const_t<typename std::pointer_traits<ValuePtr>::element_type>>;
409 };
410 
412 
413 template <
414  typename Key,
415  typename Mapped,
416  typename HasherOrVoid,
417  typename KeyEqualOrVoid,
418  typename AllocOrVoid>
419 class ValueContainerPolicy;
420 
421 template <typename ValuePtr>
422 using ValueContainerIteratorBase = BaseIter<
423  ValuePtr,
424  std::remove_const_t<typename std::pointer_traits<ValuePtr>::element_type>>;
425 
426 template <typename ValuePtr>
427 class ValueContainerIterator : public ValueContainerIteratorBase<ValuePtr> {
428  using Super = ValueContainerIteratorBase<ValuePtr>;
429  using ItemIter = typename Super::ItemIter;
430  using ValueConstPtr = typename Super::ValueConstPtr;
431 
432  public:
433  using pointer = typename Super::pointer;
434  using reference = typename Super::reference;
435  using value_type = typename Super::value_type;
436 
437  ValueContainerIterator() = default;
438  ValueContainerIterator(ValueContainerIterator const&) = default;
439  ValueContainerIterator(ValueContainerIterator&&) = default;
440  ValueContainerIterator& operator=(ValueContainerIterator const&) = default;
441  ValueContainerIterator& operator=(ValueContainerIterator&&) = default;
442  ~ValueContainerIterator() = default;
443 
444  /*implicit*/ operator ValueContainerIterator<ValueConstPtr>() const {
445  return ValueContainerIterator<ValueConstPtr>{underlying_};
446  }
447 
448  reference operator*() const {
449  return underlying_.item();
450  }
451 
452  pointer operator->() const {
453  return std::pointer_traits<pointer>::pointer_to(**this);
454  }
455 
456  ValueContainerIterator& operator++() {
457  underlying_.advance();
458  return *this;
459  }
460 
461  ValueContainerIterator operator++(int) {
462  auto cur = *this;
463  ++*this;
464  return cur;
465  }
466 
467  bool operator==(ValueContainerIterator<ValueConstPtr> const& rhs) const {
468  return underlying_ == rhs.underlying_;
469  }
470  bool operator!=(ValueContainerIterator<ValueConstPtr> const& rhs) const {
471  return !(*this == rhs);
472  }
473 
474  private:
475  ItemIter underlying_;
476 
477  explicit ValueContainerIterator(ItemIter const& underlying)
478  : underlying_{underlying} {}
479 
480  template <typename K, typename M, typename H, typename E, typename A>
481  friend class ValueContainerPolicy;
482 
483  template <typename P>
484  friend class ValueContainerIterator;
485 };
486 
487 template <
488  typename Key,
489  typename MappedTypeOrVoid,
490  typename HasherOrVoid,
491  typename KeyEqualOrVoid,
492  typename AllocOrVoid>
493 class ValueContainerPolicy : public BasePolicy<
494  Key,
495  MappedTypeOrVoid,
496  HasherOrVoid,
497  KeyEqualOrVoid,
498  AllocOrVoid,
499  SetOrMapValueType<Key, MappedTypeOrVoid>> {
500  public:
501  using Super = BasePolicy<
502  Key,
503  MappedTypeOrVoid,
504  HasherOrVoid,
505  KeyEqualOrVoid,
506  AllocOrVoid,
507  SetOrMapValueType<Key, MappedTypeOrVoid>>;
508  using Alloc = typename Super::Alloc;
509  using AllocTraits = typename Super::AllocTraits;
510  using Item = typename Super::Item;
511  using ItemIter = typename Super::ItemIter;
512  using Value = typename Super::Value;
513 
514  private:
515  using ByteAlloc = typename Super::ByteAlloc;
516 
517  using Super::kIsMap;
518 
519  public:
520  using ConstIter = ValueContainerIterator<typename AllocTraits::const_pointer>;
521  using Iter = std::conditional_t<
522  kIsMap,
523  ValueContainerIterator<typename AllocTraits::pointer>,
524  ConstIter>;
525 
527 
528  static constexpr bool prefetchBeforeRehash() {
529  return false;
530  }
531 
532  static constexpr bool prefetchBeforeCopy() {
533  return false;
534  }
535 
536  static constexpr bool prefetchBeforeDestroy() {
537  return false;
538  }
539 
540  static constexpr bool destroyItemOnClear() {
543  }
544 
545  // inherit constructors
546  using Super::Super;
547 
548  void swapPolicy(ValueContainerPolicy& rhs) {
549  this->swapBasePolicy(rhs);
550  }
551 
552  using Super::keyForValue;
553  static_assert(
555  "Item and Value should be the same type for ValueContainerPolicy.");
556 
557  std::size_t computeItemHash(Item const& item) const {
558  return this->computeKeyHash(keyForValue(item));
559  }
560 
561  template <typename K>
562  bool keyMatchesItem(K const& key, Item const& item) const {
563  return this->keyEqual()(key, keyForValue(item));
564  }
565 
566  Value const& buildArgForItem(Item const& item) const& {
567  return item;
568  }
569 
570  // buildArgForItem(Item&)&& is used when moving between unequal allocators
571  decltype(auto) buildArgForItem(Item& item) && {
572  return Super::moveValue(item);
573  }
574 
575  Value&& valueAtItemForExtract(Item& item) {
576  return std::move(item);
577  }
578 
579  template <typename... Args>
580  void
581  constructValueAtItem(std::size_t /*size*/, Item* itemAddr, Args&&... args) {
582  Alloc& a = this->alloc();
583  // GCC < 6 doesn't use the fact that itemAddr came from a reference
584  // to avoid a null-check in the placement new. folly::assume-ing it
585  // here gets rid of that branch. The branch is very predictable,
586  // but spoils some further optimizations. All clang versions that
587  // compile folly seem to be okay.
588  //
589  // TODO(T31574848): clean up assume-s used to optimize placement new
590  assume(itemAddr != nullptr);
591  AllocTraits::construct(a, itemAddr, std::forward<Args>(args)...);
592  }
593 
594  template <typename T>
596  complainUnlessNothrowMove() {}
597 
598  template <typename T>
599  [[deprecated(
600  "use F14NodeMap/Set or mark key and mapped type move constructor nothrow")]] std::
602  complainUnlessNothrowMove() {}
603 
604  void moveItemDuringRehash(Item* itemAddr, Item& src) {
605  complainUnlessNothrowMove<Key>();
606  complainUnlessNothrowMove<lift_unit_t<MappedTypeOrVoid>>();
607 
608  constructValueAtItem(0, itemAddr, Super::moveValue(src));
609  if (destroyItemOnClear()) {
610  if (kIsMap) {
611  // Laundering in the standard is only described as a solution
612  // for changes to const fields due to the creation of a new
613  // object lifetime (destroy and then placement new in the same
614  // location), but it seems highly likely that it will also cause
615  // the compiler to drop such assumptions that are violated due
616  // to our UB const_cast in moveValue.
617  destroyItem(*launder(std::addressof(src)));
618  } else {
619  destroyItem(src);
620  }
621  }
622  }
623 
624  void destroyItem(Item& item) {
625  Alloc& a = this->alloc();
626  auto ptr = std::addressof(item);
628  this->afterDestroyWithoutDeallocate(ptr, 1);
629  }
630 
631  template <typename V>
632  void visitPolicyAllocationClasses(
633  std::size_t chunkAllocSize,
634  std::size_t /*size*/,
635  std::size_t /*capacity*/,
636  V&& visitor) const {
637  if (chunkAllocSize > 0) {
638  visitor(
639  allocationBytesForOverAligned<ByteAlloc, kRequiredVectorAlignment>(
640  chunkAllocSize),
641  1);
642  }
643  }
644 
646 
647  Iter makeIter(ItemIter const& underlying) const {
648  return Iter{underlying};
649  }
650  ConstIter makeConstIter(ItemIter const& underlying) const {
651  return ConstIter{underlying};
652  }
653  ItemIter const& unwrapIter(ConstIter const& iter) const {
654  return iter.underlying_;
655  }
656 };
657 
659 
660 template <
661  typename Key,
662  typename Mapped,
663  typename HasherOrVoid,
664  typename KeyEqualOrVoid,
665  typename AllocOrVoid>
666 class NodeContainerPolicy;
667 
668 template <typename ValuePtr>
669 class NodeContainerIterator : public BaseIter<ValuePtr, NonConstPtr<ValuePtr>> {
670  using Super = BaseIter<ValuePtr, NonConstPtr<ValuePtr>>;
671  using ItemIter = typename Super::ItemIter;
672  using ValueConstPtr = typename Super::ValueConstPtr;
673 
674  public:
675  using pointer = typename Super::pointer;
676  using reference = typename Super::reference;
677  using value_type = typename Super::value_type;
678 
679  NodeContainerIterator() = default;
680  NodeContainerIterator(NodeContainerIterator const&) = default;
681  NodeContainerIterator(NodeContainerIterator&&) = default;
682  NodeContainerIterator& operator=(NodeContainerIterator const&) = default;
683  NodeContainerIterator& operator=(NodeContainerIterator&&) = default;
684  ~NodeContainerIterator() = default;
685 
686  /*implicit*/ operator NodeContainerIterator<ValueConstPtr>() const {
687  return NodeContainerIterator<ValueConstPtr>{underlying_};
688  }
689 
690  reference operator*() const {
691  return *underlying_.item();
692  }
693 
694  pointer operator->() const {
695  return std::pointer_traits<pointer>::pointer_to(**this);
696  }
697 
698  NodeContainerIterator& operator++() {
699  underlying_.advance();
700  return *this;
701  }
702 
703  NodeContainerIterator operator++(int) {
704  auto cur = *this;
705  ++*this;
706  return cur;
707  }
708 
709  bool operator==(NodeContainerIterator<ValueConstPtr> const& rhs) const {
710  return underlying_ == rhs.underlying_;
711  }
712  bool operator!=(NodeContainerIterator<ValueConstPtr> const& rhs) const {
713  return !(*this == rhs);
714  }
715 
716  private:
717  ItemIter underlying_;
718 
719  explicit NodeContainerIterator(ItemIter const& underlying)
720  : underlying_{underlying} {}
721 
722  template <typename K, typename M, typename H, typename E, typename A>
723  friend class NodeContainerPolicy;
724 
725  template <typename P>
726  friend class NodeContainerIterator;
727 };
728 
729 template <
730  typename Key,
731  typename MappedTypeOrVoid,
732  typename HasherOrVoid,
733  typename KeyEqualOrVoid,
734  typename AllocOrVoid>
735 class NodeContainerPolicy
736  : public BasePolicy<
737  Key,
738  MappedTypeOrVoid,
739  HasherOrVoid,
740  KeyEqualOrVoid,
741  AllocOrVoid,
742  typename std::allocator_traits<Defaulted<
743  AllocOrVoid,
744  DefaultAlloc<std::conditional_t<
745  std::is_void<MappedTypeOrVoid>::value,
746  Key,
747  MapValueType<Key, MappedTypeOrVoid>>>>>::pointer> {
748  public:
749  using Super = BasePolicy<
750  Key,
751  MappedTypeOrVoid,
752  HasherOrVoid,
753  KeyEqualOrVoid,
754  AllocOrVoid,
755  typename std::allocator_traits<Defaulted<
756  AllocOrVoid,
757  DefaultAlloc<std::conditional_t<
759  Key,
760  MapValueType<Key, MappedTypeOrVoid>>>>>::pointer>;
761  using Alloc = typename Super::Alloc;
762  using AllocTraits = typename Super::AllocTraits;
763  using Item = typename Super::Item;
764  using ItemIter = typename Super::ItemIter;
765  using Value = typename Super::Value;
766 
767  private:
768  using ByteAlloc = typename Super::ByteAlloc;
769 
770  using Super::kIsMap;
771 
772  public:
773  using ConstIter = NodeContainerIterator<typename AllocTraits::const_pointer>;
774  using Iter = std::conditional_t<
775  kIsMap,
776  NodeContainerIterator<typename AllocTraits::pointer>,
777  ConstIter>;
778 
780 
781  static constexpr bool prefetchBeforeRehash() {
782  return true;
783  }
784 
785  static constexpr bool prefetchBeforeCopy() {
786  return true;
787  }
788 
789  static constexpr bool prefetchBeforeDestroy() {
791  }
792 
793  static constexpr bool destroyItemOnClear() {
794  return true;
795  }
796 
797  // inherit constructors
798  using Super::Super;
799 
800  void swapPolicy(NodeContainerPolicy& rhs) {
801  this->swapBasePolicy(rhs);
802  }
803 
804  using Super::keyForValue;
805 
806  std::size_t computeItemHash(Item const& item) const {
807  return this->computeKeyHash(keyForValue(*item));
808  }
809 
810  template <typename K>
811  bool keyMatchesItem(K const& key, Item const& item) const {
812  return this->keyEqual()(key, keyForValue(*item));
813  }
814 
815  Value const& buildArgForItem(Item const& item) const& {
816  return *item;
817  }
818 
819  // buildArgForItem(Item&)&& is used when moving between unequal allocators
820  decltype(auto) buildArgForItem(Item& item) && {
821  return Super::moveValue(*item);
822  }
823 
824  Value&& valueAtItemForExtract(Item& item) {
825  return std::move(*item);
826  }
827 
828  template <typename... Args>
829  void
830  constructValueAtItem(std::size_t /*size*/, Item* itemAddr, Args&&... args) {
831  Alloc& a = this->alloc();
832  // TODO(T31574848): clean up assume-s used to optimize placement new
833  assume(itemAddr != nullptr);
834  new (itemAddr) Item{AllocTraits::allocate(a, 1)};
835  auto p = std::addressof(**itemAddr);
836  // TODO(T31574848): clean up assume-s used to optimize placement new
837  assume(p != nullptr);
838  AllocTraits::construct(a, p, std::forward<Args>(args)...);
839  }
840 
841  void moveItemDuringRehash(Item* itemAddr, Item& src) {
842  // This is basically *itemAddr = src; src = nullptr, but allowing
843  // for fancy pointers.
844  // TODO(T31574848): clean up assume-s used to optimize placement new
845  assume(itemAddr != nullptr);
846  new (itemAddr) Item{std::move(src)};
847  src = nullptr;
848  src.~Item();
849  }
850 
851  void prefetchValue(Item const& item) const {
852  prefetchAddr(std::addressof(*item));
853  }
854 
855  void destroyItem(Item& item) {
856  if (item != nullptr) {
857  Alloc& a = this->alloc();
858  AllocTraits::destroy(a, std::addressof(*item));
859  AllocTraits::deallocate(a, item, 1);
860  }
861  item.~Item();
862  }
863 
864  template <typename V>
865  void visitPolicyAllocationClasses(
866  std::size_t chunkAllocSize,
867  std::size_t size,
868  std::size_t /*capacity*/,
869  V&& visitor) const {
870  if (chunkAllocSize > 0) {
871  visitor(
872  allocationBytesForOverAligned<ByteAlloc, kRequiredVectorAlignment>(
873  chunkAllocSize),
874  1);
875  }
876  if (size > 0) {
877  visitor(sizeof(Value), size);
878  }
879  }
880 
882 
883  Iter makeIter(ItemIter const& underlying) const {
884  return Iter{underlying};
885  }
886  ConstIter makeConstIter(ItemIter const& underlying) const {
887  return Iter{underlying};
888  }
889  ItemIter const& unwrapIter(ConstIter const& iter) const {
890  return iter.underlying_;
891  }
892 };
893 
895 
896 template <
897  typename Key,
898  typename MappedTypeOrVoid,
899  typename HasherOrVoid,
900  typename KeyEqualOrVoid,
901  typename AllocOrVoid>
902 class VectorContainerPolicy;
903 
904 template <typename ValuePtr>
905 class VectorContainerIterator : public BaseIter<ValuePtr, uint32_t> {
906  using Super = BaseIter<ValuePtr, uint32_t>;
907  using ValueConstPtr = typename Super::ValueConstPtr;
908 
909  public:
910  using pointer = typename Super::pointer;
911  using reference = typename Super::reference;
912  using value_type = typename Super::value_type;
913 
914  VectorContainerIterator() = default;
915  VectorContainerIterator(VectorContainerIterator const&) = default;
916  VectorContainerIterator(VectorContainerIterator&&) = default;
917  VectorContainerIterator& operator=(VectorContainerIterator const&) = default;
918  VectorContainerIterator& operator=(VectorContainerIterator&&) = default;
919  ~VectorContainerIterator() = default;
920 
921  /*implicit*/ operator VectorContainerIterator<ValueConstPtr>() const {
922  return VectorContainerIterator<ValueConstPtr>{current_, lowest_};
923  }
924 
925  reference operator*() const {
926  return *current_;
927  }
928 
929  pointer operator->() const {
930  return current_;
931  }
932 
933  VectorContainerIterator& operator++() {
934  if (UNLIKELY(current_ == lowest_)) {
935  current_ = nullptr;
936  } else {
937  --current_;
938  }
939  return *this;
940  }
941 
942  VectorContainerIterator operator++(int) {
943  auto cur = *this;
944  ++*this;
945  return cur;
946  }
947 
948  bool operator==(VectorContainerIterator<ValueConstPtr> const& rhs) const {
949  return current_ == rhs.current_;
950  }
951  bool operator!=(VectorContainerIterator<ValueConstPtr> const& rhs) const {
952  return !(*this == rhs);
953  }
954 
955  private:
956  ValuePtr current_;
957  ValuePtr lowest_;
958 
959  explicit VectorContainerIterator(ValuePtr current, ValuePtr lowest)
960  : current_(current), lowest_(lowest) {}
961 
962  std::size_t index() const {
963  return current_ - lowest_;
964  }
965 
966  template <typename K, typename M, typename H, typename E, typename A>
967  friend class VectorContainerPolicy;
968 
969  template <typename P>
970  friend class VectorContainerIterator;
971 };
972 
973 struct VectorContainerIndexSearch {
974  uint32_t index_;
975 };
976 
977 template <
978  typename Key,
979  typename MappedTypeOrVoid,
980  typename HasherOrVoid,
981  typename KeyEqualOrVoid,
982  typename AllocOrVoid>
983 class VectorContainerPolicy : public BasePolicy<
984  Key,
985  MappedTypeOrVoid,
986  HasherOrVoid,
987  KeyEqualOrVoid,
988  AllocOrVoid,
989  uint32_t> {
990  public:
991  using Super = BasePolicy<
992  Key,
993  MappedTypeOrVoid,
994  HasherOrVoid,
995  KeyEqualOrVoid,
996  AllocOrVoid,
997  uint32_t>;
998  using Alloc = typename Super::Alloc;
999  using AllocTraits = typename Super::AllocTraits;
1000  using ByteAlloc = typename Super::ByteAlloc;
1001  using ByteAllocTraits = typename Super::ByteAllocTraits;
1002  using BytePtr = typename Super::BytePtr;
1003  using Hasher = typename Super::Hasher;
1004  using Item = typename Super::Item;
1005  using ItemIter = typename Super::ItemIter;
1006  using KeyEqual = typename Super::KeyEqual;
1007  using Value = typename Super::Value;
1008 
1009  using Super::kAllocIsAlwaysEqual;
1010 
1011  private:
1012  using Super::kIsMap;
1013 
1014  public:
1015  static constexpr bool kEnableItemIteration = false;
1016 
1017  using InternalSizeType = Item;
1018 
1019  using ConstIter =
1020  VectorContainerIterator<typename AllocTraits::const_pointer>;
1021  using Iter = std::conditional_t<
1022  kIsMap,
1023  VectorContainerIterator<typename AllocTraits::pointer>,
1024  ConstIter>;
1025  using ConstReverseIter = typename AllocTraits::const_pointer;
1026  using ReverseIter = std::
1027  conditional_t<kIsMap, typename AllocTraits::pointer, ConstReverseIter>;
1028 
1029  using ValuePtr = typename AllocTraits::pointer;
1030 
1032 
1033  static constexpr bool prefetchBeforeRehash() {
1034  return true;
1035  }
1036 
1037  static constexpr bool prefetchBeforeCopy() {
1038  return false;
1039  }
1040 
1041  static constexpr bool prefetchBeforeDestroy() {
1042  return false;
1043  }
1044 
1045  static constexpr bool destroyItemOnClear() {
1046  return false;
1047  }
1048 
1049  private:
1050  static constexpr bool valueIsTriviallyCopyable() {
1054  }
1055 
1056  public:
1057  VectorContainerPolicy(
1058  Hasher const& hasher,
1059  KeyEqual const& keyEqual,
1060  Alloc const& alloc)
1061  : Super{hasher, keyEqual, alloc} {}
1062 
1063  VectorContainerPolicy(VectorContainerPolicy const& rhs) : Super{rhs} {
1064  // values_ will get allocated later to do the copy
1065  }
1066 
1067  VectorContainerPolicy(VectorContainerPolicy const& rhs, Alloc const& alloc)
1068  : Super{rhs, alloc} {
1069  // values_ will get allocated later to do the copy
1070  }
1071 
1072  VectorContainerPolicy(VectorContainerPolicy&& rhs) noexcept
1073  : Super{std::move(rhs)}, values_{rhs.values_} {
1074  rhs.values_ = nullptr;
1075  }
1076 
1077  VectorContainerPolicy(
1078  VectorContainerPolicy&& rhs,
1079  Alloc const& alloc) noexcept
1080  : Super{std::move(rhs), alloc} {
1081  if (kAllocIsAlwaysEqual || this->alloc() == rhs.alloc()) {
1082  // common case
1083  values_ = rhs.values_;
1084  rhs.values_ = nullptr;
1085  } else {
1086  // table must be constructed in new memory
1087  values_ = nullptr;
1088  }
1089  }
1090 
1091  VectorContainerPolicy& operator=(VectorContainerPolicy const& rhs) {
1092  if (this != &rhs) {
1093  FOLLY_SAFE_DCHECK(values_ == nullptr, "");
1094  Super::operator=(rhs);
1095  }
1096  return *this;
1097  }
1098 
1099  VectorContainerPolicy& operator=(VectorContainerPolicy&& rhs) noexcept {
1100  if (this != &rhs) {
1101  FOLLY_SAFE_DCHECK(values_ == nullptr, "");
1102  bool transfer =
1104  kAllocIsAlwaysEqual || this->alloc() == rhs.alloc();
1105  Super::operator=(std::move(rhs));
1106  if (transfer) {
1107  values_ = rhs.values_;
1108  rhs.values_ = nullptr;
1109  }
1110  }
1111  return *this;
1112  }
1113 
1114  void swapPolicy(VectorContainerPolicy& rhs) {
1115  using std::swap;
1116  this->swapBasePolicy(rhs);
1117  swap(values_, rhs.values_);
1118  }
1119 
1120  template <typename K>
1121  std::size_t computeKeyHash(K const& key) const {
1122  static_assert(
1123  Super::isAvalanchingHasher() == IsAvalanchingHasher<Hasher, K>::value,
1124  "");
1125  return this->hasher()(key);
1126  }
1127 
1128  std::size_t computeKeyHash(VectorContainerIndexSearch const& key) const {
1129  return computeItemHash(key.index_);
1130  }
1131 
1132  using Super::keyForValue;
1133 
1134  std::size_t computeItemHash(Item const& item) const {
1135  return this->computeKeyHash(keyForValue(values_[item]));
1136  }
1137 
1138  bool keyMatchesItem(VectorContainerIndexSearch const& key, Item const& item)
1139  const {
1140  return key.index_ == item;
1141  }
1142 
1143  template <typename K>
1144  bool keyMatchesItem(K const& key, Item const& item) const {
1145  return this->keyEqual()(key, keyForValue(values_[item]));
1146  }
1147 
1148  Key const& keyForValue(VectorContainerIndexSearch const& arg) const {
1149  return keyForValue(values_[arg.index_]);
1150  }
1151 
1152  VectorContainerIndexSearch buildArgForItem(Item const& item) const {
1153  return {item};
1154  }
1155 
1156  Value&& valueAtItemForExtract(Item& item) {
1157  return std::move(values_[item]);
1158  }
1159 
1160  void constructValueAtItem(
1161  std::size_t /*size*/,
1162  Item* itemAddr,
1163  VectorContainerIndexSearch arg) {
1164  *itemAddr = arg.index_;
1165  }
1166 
1167  template <typename... Args>
1168  void constructValueAtItem(std::size_t size, Item* itemAddr, Args&&... args) {
1169  Alloc& a = this->alloc();
1171  *itemAddr = static_cast<InternalSizeType>(size);
1172  auto dst = std::addressof(values_[size]);
1173  // TODO(T31574848): clean up assume-s used to optimize placement new
1174  assume(dst != nullptr);
1175  AllocTraits::construct(a, dst, std::forward<Args>(args)...);
1176  }
1177 
1178  void moveItemDuringRehash(Item* itemAddr, Item& src) {
1179  *itemAddr = src;
1180  }
1181 
1182  void prefetchValue(Item const& item) const {
1183  prefetchAddr(std::addressof(values_[item]));
1184  }
1185 
1186  void destroyItem(Item&) {}
1187 
1188  template <typename T>
1190  complainUnlessNothrowMove() {}
1191 
1192  template <typename T>
1193  [[deprecated(
1194  "use F14NodeMap/Set or mark key and mapped type move constructor nothrow")]] std::
1196  complainUnlessNothrowMove() {}
1197 
1198  void transfer(Alloc& a, Value* src, Value* dst, std::size_t n) {
1199  complainUnlessNothrowMove<Key>();
1200  complainUnlessNothrowMove<lift_unit_t<MappedTypeOrVoid>>();
1201 
1202  auto origSrc = src;
1203  if (valueIsTriviallyCopyable()) {
1204  std::memcpy(static_cast<void*>(dst), src, n * sizeof(Value));
1205  } else {
1206  for (std::size_t i = 0; i < n; ++i, ++src, ++dst) {
1207  // TODO(T31574848): clean up assume-s used to optimize placement new
1208  assume(dst != nullptr);
1209  AllocTraits::construct(a, dst, Super::moveValue(*src));
1210  if (kIsMap) {
1211  AllocTraits::destroy(a, launder(src));
1212  } else {
1213  AllocTraits::destroy(a, src);
1214  }
1215  }
1216  }
1217  this->afterDestroyWithoutDeallocate(origSrc, n);
1218  }
1219 
1220  template <typename P, typename V>
1221  bool beforeBuildImpl(std::size_t size, P&& rhs, V const& constructorArgFor) {
1222  Alloc& a = this->alloc();
1223 
1224  FOLLY_SAFE_DCHECK(values_ != nullptr, "");
1225 
1226  auto src = std::addressof(rhs.values_[0]);
1227  Value* dst = std::addressof(values_[0]);
1228 
1229  if (valueIsTriviallyCopyable()) {
1230  std::memcpy(dst, src, size * sizeof(Value));
1231  } else {
1232  for (std::size_t i = 0; i < size; ++i, ++src, ++dst) {
1233  try {
1234  // TODO(T31574848): clean up assume-s used to optimize placement new
1235  assume(dst != nullptr);
1236  AllocTraits::construct(a, dst, constructorArgFor(*src));
1237  } catch (...) {
1238  for (Value* cleanup = std::addressof(values_[0]); cleanup != dst;
1239  ++cleanup) {
1241  }
1242  throw;
1243  }
1244  }
1245  }
1246  return true;
1247  }
1248 
1249  bool beforeBuild(
1250  std::size_t size,
1251  std::size_t /*capacity*/,
1252  VectorContainerPolicy const& rhs) {
1253  return beforeBuildImpl(size, rhs, [](Value const& v) { return v; });
1254  }
1255 
1256  bool beforeBuild(
1257  std::size_t size,
1258  std::size_t /*capacity*/,
1259  VectorContainerPolicy&& rhs) {
1260  return beforeBuildImpl(
1261  size, rhs, [](Value& v) { return Super::moveValue(v); });
1262  }
1263 
1264  template <typename P>
1265  void afterBuild(
1266  bool /*undoState*/,
1267  bool success,
1268  std::size_t /*size*/,
1269  std::size_t /*capacity*/,
1270  P&& /*rhs*/) {
1271  // buildArgForItem can be used to construct a new item trivially,
1272  // so no failure between beforeBuild and afterBuild should be possible
1273  FOLLY_SAFE_DCHECK(success, "");
1274  }
1275 
1276  private:
1277  // Returns the byte offset of the first Value in a unified allocation
1278  // that first holds prefixBytes of data, where prefixBytes comes from
1279  // Chunk storage and hence must be at least 8-byte aligned (sub-Chunk
1280  // allocations always have an even capacity and sizeof(Item) == 4).
1281  static std::size_t valuesOffset(std::size_t prefixBytes) {
1282  FOLLY_SAFE_DCHECK((prefixBytes % 8) == 0, "");
1283  if (alignof(Value) > 8) {
1284  prefixBytes = -(-prefixBytes & ~(alignof(Value) - 1));
1285  }
1286  FOLLY_SAFE_DCHECK((prefixBytes % alignof(Value)) == 0, "");
1287  return prefixBytes;
1288  }
1289 
1290  // Returns the total number of bytes that should be allocated to store
1291  // prefixBytes of Chunks and valueCapacity values.
1292  static std::size_t allocSize(
1293  std::size_t prefixBytes,
1294  std::size_t valueCapacity) {
1295  return valuesOffset(prefixBytes) + sizeof(Value) * valueCapacity;
1296  }
1297 
1298  public:
1299  ValuePtr beforeRehash(
1300  std::size_t size,
1301  std::size_t oldCapacity,
1302  std::size_t newCapacity,
1303  std::size_t chunkAllocSize,
1304  BytePtr& outChunkAllocation) {
1306  size <= oldCapacity && ((values_ == nullptr) == (oldCapacity == 0)) &&
1307  newCapacity > 0 &&
1308  newCapacity <= (std::numeric_limits<Item>::max)(),
1309  "");
1310 
1311  outChunkAllocation =
1312  allocateOverAligned<ByteAlloc, kRequiredVectorAlignment>(
1313  ByteAlloc{Super::alloc()}, allocSize(chunkAllocSize, newCapacity));
1314 
1315  ValuePtr before = values_;
1316  ValuePtr after = std::pointer_traits<ValuePtr>::pointer_to(
1317  *static_cast<Value*>(static_cast<void*>(
1318  &*outChunkAllocation + valuesOffset(chunkAllocSize))));
1319 
1320  if (size > 0) {
1321  Alloc& a{this->alloc()};
1322  transfer(a, std::addressof(before[0]), std::addressof(after[0]), size);
1323  }
1324 
1325  values_ = after;
1326  return before;
1327  }
1328 
1329  FOLLY_NOINLINE void afterFailedRehash(ValuePtr state, std::size_t size) {
1330  // state holds the old storage
1331  Alloc& a = this->alloc();
1332  if (size > 0) {
1333  transfer(a, std::addressof(values_[0]), std::addressof(state[0]), size);
1334  }
1335  values_ = state;
1336  }
1337 
1338  void afterRehash(
1339  ValuePtr state,
1340  bool success,
1341  std::size_t size,
1342  std::size_t oldCapacity,
1343  std::size_t newCapacity,
1344  BytePtr chunkAllocation,
1345  std::size_t chunkAllocSize) {
1346  if (!success) {
1347  afterFailedRehash(state, size);
1348  }
1349 
1350  // on success, chunkAllocation is the old allocation, on failure it is the
1351  // new one
1352  if (chunkAllocation != nullptr) {
1353  deallocateOverAligned<ByteAlloc, kRequiredVectorAlignment>(
1354  ByteAlloc{Super::alloc()},
1355  chunkAllocation,
1356  allocSize(chunkAllocSize, (success ? oldCapacity : newCapacity)));
1357  }
1358  }
1359 
1360  void beforeClear(std::size_t size, std::size_t capacity) {
1362  size <= capacity && ((values_ == nullptr) == (capacity == 0)), "");
1363  Alloc& a = this->alloc();
1364  for (std::size_t i = 0; i < size; ++i) {
1365  AllocTraits::destroy(a, std::addressof(values_[i]));
1366  }
1367  }
1368 
1369  void beforeReset(std::size_t size, std::size_t capacity) {
1370  beforeClear(size, capacity);
1371  }
1372 
1373  void afterReset(
1374  std::size_t /*size*/,
1375  std::size_t capacity,
1376  BytePtr chunkAllocation,
1377  std::size_t chunkAllocSize) {
1378  if (chunkAllocation != nullptr) {
1379  deallocateOverAligned<ByteAlloc, kRequiredVectorAlignment>(
1380  ByteAlloc{Super::alloc()},
1381  chunkAllocation,
1382  allocSize(chunkAllocSize, capacity));
1383  values_ = nullptr;
1384  }
1385  }
1386 
1387  template <typename V>
1388  void visitPolicyAllocationClasses(
1389  std::size_t chunkAllocSize,
1390  std::size_t /*size*/,
1391  std::size_t capacity,
1392  V&& visitor) const {
1393  FOLLY_SAFE_DCHECK((chunkAllocSize == 0) == (capacity == 0), "");
1394  if (chunkAllocSize > 0) {
1395  visitor(
1396  allocationBytesForOverAligned<ByteAlloc, kRequiredVectorAlignment>(
1397  allocSize(chunkAllocSize, capacity)),
1398  1);
1399  }
1400  }
1401 
1402  // Iterator stuff
1403 
1404  Iter linearBegin(std::size_t size) const {
1405  return Iter{(size > 0 ? values_ + size - 1 : nullptr), values_};
1406  }
1407 
1408  Iter linearEnd() const {
1409  return Iter{nullptr, nullptr};
1410  }
1411 
1413 
1414  Iter makeIter(ItemIter const& underlying) const {
1415  if (underlying.atEnd()) {
1416  return linearEnd();
1417  } else {
1418  assume(values_ + underlying.item() != nullptr);
1419  assume(values_ != nullptr);
1420  return Iter{values_ + underlying.item(), values_};
1421  }
1422  }
1423 
1424  ConstIter makeConstIter(ItemIter const& underlying) const {
1425  return makeIter(underlying);
1426  }
1427 
1428  Item iterToIndex(ConstIter const& iter) const {
1429  auto n = iter.index();
1431  return static_cast<Item>(n);
1432  }
1433 
1434  Iter indexToIter(Item index) const {
1435  return Iter{values_ + index, values_};
1436  }
1437 
1438  Iter iter(ReverseIter it) {
1439  return Iter{it, values_};
1440  }
1441 
1442  ConstIter iter(ConstReverseIter it) const {
1443  return ConstIter{it, values_};
1444  }
1445 
1446  ReverseIter riter(Iter it) {
1447  return it.current_;
1448  }
1449 
1450  ConstReverseIter riter(ConstIter it) const {
1451  return it.current_;
1452  }
1453 
1454  ValuePtr values_{nullptr};
1455 };
1456 
1457 template <
1458  template <typename, typename, typename, typename, typename> class Policy,
1459  typename Key,
1460  typename Mapped,
1461  typename Hasher,
1462  typename KeyEqual,
1463  typename Alloc>
1464 using MapPolicyWithDefaults = Policy<
1465  Key,
1466  Mapped,
1467  VoidDefault<Hasher, DefaultHasher<Key>>,
1468  VoidDefault<KeyEqual, DefaultKeyEqual<Key>>,
1469  VoidDefault<Alloc, DefaultAlloc<std::pair<Key const, Mapped>>>>;
1470 
1471 template <
1472  template <typename, typename, typename, typename, typename> class Policy,
1473  typename Key,
1474  typename Hasher,
1475  typename KeyEqual,
1476  typename Alloc>
1477 using SetPolicyWithDefaults = Policy<
1478  Key,
1479  void,
1480  VoidDefault<Hasher, DefaultHasher<Key>>,
1481  VoidDefault<KeyEqual, DefaultKeyEqual<Key>>,
1482  VoidDefault<Alloc, DefaultAlloc<Key>>>;
1483 
1484 } // namespace detail
1485 } // namespace f14
1486 } // namespace folly
1487 
1488 #endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
impl::Inherit_< List > Inherit
Definition: TypeList.h:438
void * ptr
auto v
std::unique_ptr< int > A
int current_
Definition: json.cpp:327
LogLevel max
Definition: LogLevel.cpp:31
bool operator!=(SwapTrackingAlloc< T1 > const &, SwapTrackingAlloc< T2 > const &)
Definition: F14TestUtil.h:427
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
internal::KeyMatcher< M > Key(M inner_matcher)
constexpr bool kIsSanitizeAddress
Definition: Portability.h:118
folly::std T
internal::ArgsMatcher< InnerMatcher > Args(const InnerMatcher &matcher)
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
KeyType
Definition: Signature.h:17
requires E e noexcept(noexcept(s.error(std::move(e))))
std::allocator< T > DefaultAlloc
Definition: F14Defaults.h:32
FOLLY_PUSH_WARNING RHS rhs
Definition: Traits.h:649
static void destroy()
int current
#define FOLLY_NOINLINE
Definition: CPortability.h:142
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
def Iter(n, format, sep='')
bool Value(const T &value, M matcher)
char a
static const char *const value
Definition: Conv.cpp:50
const
Definition: upload.py:398
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
FOLLY_NODISCARD T * launder(T *in) noexcept
Definition: Launder.h:48
void swap(SwapTrackingAlloc< T > &, SwapTrackingAlloc< T > &)
Definition: F14TestUtil.h:414
#define UNLIKELY(x)
Definition: Likely.h:48
FOLLY_ALWAYS_INLINE void assume(bool cond)
Definition: Assume.h:41
ThreadPoolListHook * addr
#define FOLLY_SAFE_DCHECK(expr, msg)
Definition: SafeAssert.h:42
void cleanup()
Definition: Init.cpp:59
state
Definition: http_parser.c:272
bool operator==(SwapTrackingAlloc< T1 > const &, SwapTrackingAlloc< T2 > const &)
Definition: F14TestUtil.h:422