proxygen
F14Set.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 
30 #include <tuple>
31 
32 #include <folly/lang/SafeAssert.h>
33 
37 
38 #if !FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
39 
41 
42 #include <unordered_set>
43 
44 namespace folly {
45 
46 namespace f14 {
47 namespace detail {
48 template <typename K, typename H, typename E, typename A>
49 class F14BasicSet : public std::unordered_set<K, H, E, A> {
50  using Super = std::unordered_set<K, H, E, A>;
51 
52  public:
53  using typename Super::pointer;
54  using typename Super::value_type;
55 
56  F14BasicSet() = default;
57 
58  using Super::Super;
59 
61 
62  // exact for libstdc++, approximate for others
63  std::size_t getAllocatedMemorySize() const {
64  std::size_t rv = 0;
66  [&](std::size_t bytes, std::size_t n) { rv += bytes * n; });
67  return rv;
68  }
69 
70  // exact for libstdc++, approximate for others
71  template <typename V>
72  void visitAllocationClasses(V&& visitor) const {
73  auto bc = this->bucket_count();
74  if (bc > 1) {
75  visitor(bc * sizeof(pointer), 1);
76  }
77  if (this->size() > 0) {
78  visitor(sizeof(StdNodeReplica<K, value_type, H>), this->size());
79  }
80  }
81 
82  template <typename V>
83  void visitContiguousRanges(V&& visitor) const {
84  for (value_type const& entry : *this) {
85  value_type const* b = std::addressof(entry);
86  visitor(b, b + 1);
87  }
88  }
89 };
90 } // namespace detail
91 } // namespace f14
92 
93 template <typename Key, typename Hasher, typename KeyEqual, typename Alloc>
94 class F14NodeSet
95  : public f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc> {
97 
98  public:
99  using typename Super::value_type;
100 
101  F14NodeSet() = default;
102 
103  using Super::Super;
104 
105  F14NodeSet& operator=(std::initializer_list<value_type> ilist) {
106  Super::operator=(ilist);
107  return *this;
108  }
109 };
110 
111 template <typename Key, typename Hasher, typename KeyEqual, typename Alloc>
112 class F14ValueSet
113  : public f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc> {
115 
116  public:
117  using typename Super::value_type;
118 
120 
121  using Super::Super;
122 
123  F14ValueSet& operator=(std::initializer_list<value_type> ilist) {
124  Super::operator=(ilist);
125  return *this;
126  }
127 };
128 
129 template <typename Key, typename Hasher, typename KeyEqual, typename Alloc>
130 class F14VectorSet
131  : public f14::detail::F14BasicSet<Key, Hasher, KeyEqual, Alloc> {
133 
134  public:
135  using typename Super::value_type;
136 
137  F14VectorSet() = default;
138 
139  using Super::Super;
140 
141  F14VectorSet& operator=(std::initializer_list<value_type> ilist) {
142  Super::operator=(ilist);
143  return *this;
144  }
145 };
146 
147 } // namespace folly
148 
149 #else // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
150 
152 
153 namespace folly {
154 namespace f14 {
155 namespace detail {
156 
157 template <typename Policy>
158 class F14BasicSet {
159  template <typename K, typename T>
160  using EnableHeterogeneousFind = std::enable_if_t<
161  EligibleForHeterogeneousFind<
162  typename Policy::Value,
163  typename Policy::Hasher,
164  typename Policy::KeyEqual,
165  K>::value,
166  T>;
167 
168  template <typename K, typename T>
169  using EnableHeterogeneousInsert = std::enable_if_t<
170  EligibleForHeterogeneousInsert<
171  typename Policy::Value,
172  typename Policy::Hasher,
173  typename Policy::KeyEqual,
174  K>::value,
175  T>;
176 
177  template <typename K, typename T>
178  using EnableHeterogeneousErase = std::enable_if_t<
179  EligibleForHeterogeneousFind<
180  typename Policy::Value,
181  typename Policy::Hasher,
182  typename Policy::KeyEqual,
183  K>::value &&
184  !std::is_same<typename Policy::Iter, remove_cvref_t<K>>::value,
185  T>;
186 
187  public:
189 
190  using key_type = typename Policy::Value;
191  using value_type = key_type;
192  using size_type = std::size_t;
193  using difference_type = std::ptrdiff_t;
194  using hasher = typename Policy::Hasher;
195  using key_equal = typename Policy::KeyEqual;
196  using allocator_type = typename Policy::Alloc;
197  using reference = value_type&;
198  using const_reference = value_type const&;
199  using pointer = typename Policy::AllocTraits::pointer;
200  using const_pointer = typename Policy::AllocTraits::const_pointer;
201  using iterator = typename Policy::Iter;
202  using const_iterator = iterator;
203 
204  private:
205  using ItemIter = typename Policy::ItemIter;
206 
207  public:
209 
210  F14BasicSet() noexcept(Policy::kDefaultConstructIsNoexcept)
211  : F14BasicSet(0) {}
212 
213  explicit F14BasicSet(
214  std::size_t initialCapacity,
215  hasher const& hash = hasher{},
216  key_equal const& eq = key_equal{},
217  allocator_type const& alloc = allocator_type{})
218  : table_{initialCapacity, hash, eq, alloc} {}
219 
220  explicit F14BasicSet(std::size_t initialCapacity, allocator_type const& alloc)
221  : F14BasicSet(initialCapacity, hasher{}, key_equal{}, alloc) {}
222 
223  explicit F14BasicSet(
224  std::size_t initialCapacity,
225  hasher const& hash,
226  allocator_type const& alloc)
227  : F14BasicSet(initialCapacity, hash, key_equal{}, alloc) {}
228 
229  explicit F14BasicSet(allocator_type const& alloc)
230  : F14BasicSet(0, hasher{}, key_equal{}, alloc) {}
231 
232  template <typename InputIt>
233  F14BasicSet(
234  InputIt first,
235  InputIt last,
236  std::size_t initialCapacity = 0,
237  hasher const& hash = hasher{},
238  key_equal const& eq = key_equal{},
239  allocator_type const& alloc = allocator_type{})
240  : table_{initialCapacity, hash, eq, alloc} {
241  initialInsert(first, last, initialCapacity);
242  }
243 
244  template <typename InputIt>
245  F14BasicSet(
246  InputIt first,
247  InputIt last,
248  std::size_t initialCapacity,
249  allocator_type const& alloc)
250  : table_{initialCapacity, hasher{}, key_equal{}, alloc} {
251  initialInsert(first, last, initialCapacity);
252  }
253 
254  template <typename InputIt>
255  F14BasicSet(
256  InputIt first,
257  InputIt last,
258  std::size_t initialCapacity,
259  hasher const& hash,
260  allocator_type const& alloc)
261  : table_{initialCapacity, hash, key_equal{}, alloc} {
262  initialInsert(first, last, initialCapacity);
263  }
264 
265  F14BasicSet(F14BasicSet const& rhs) = default;
266 
267  F14BasicSet(F14BasicSet const& rhs, allocator_type const& alloc)
268  : table_(rhs.table_, alloc) {}
269 
270  F14BasicSet(F14BasicSet&& rhs) = default;
271 
272  F14BasicSet(F14BasicSet&& rhs, allocator_type const& alloc) noexcept(
273  Policy::kAllocIsAlwaysEqual)
274  : table_{std::move(rhs.table_), alloc} {}
275 
276  F14BasicSet(
277  std::initializer_list<value_type> init,
278  std::size_t initialCapacity = 0,
279  hasher const& hash = hasher{},
280  key_equal const& eq = key_equal{},
281  allocator_type const& alloc = allocator_type{})
282  : table_{initialCapacity, hash, eq, alloc} {
283  initialInsert(init.begin(), init.end(), initialCapacity);
284  }
285 
286  F14BasicSet(
287  std::initializer_list<value_type> init,
288  std::size_t initialCapacity,
289  allocator_type const& alloc)
290  : table_{initialCapacity, hasher{}, key_equal{}, alloc} {
291  initialInsert(init.begin(), init.end(), initialCapacity);
292  }
293 
294  F14BasicSet(
295  std::initializer_list<value_type> init,
296  std::size_t initialCapacity,
297  hasher const& hash,
298  allocator_type const& alloc)
299  : table_{initialCapacity, hash, key_equal{}, alloc} {
300  initialInsert(init.begin(), init.end(), initialCapacity);
301  }
302 
303  F14BasicSet& operator=(F14BasicSet const&) = default;
304 
305  F14BasicSet& operator=(F14BasicSet&&) = default;
306 
307  F14BasicSet& operator=(std::initializer_list<value_type> ilist) {
308  clear();
309  bulkInsert(ilist.begin(), ilist.end(), false);
310  return *this;
311  }
312 
313  allocator_type get_allocator() const noexcept {
314  return table_.alloc();
315  }
316 
318 
319  iterator begin() noexcept {
320  return cbegin();
321  }
322  const_iterator begin() const noexcept {
323  return cbegin();
324  }
325  const_iterator cbegin() const noexcept {
326  return table_.makeIter(table_.begin());
327  }
328 
329  iterator end() noexcept {
330  return cend();
331  }
332  const_iterator end() const noexcept {
333  return cend();
334  }
335  const_iterator cend() const noexcept {
336  return table_.makeIter(table_.end());
337  }
338 
340 
341  bool empty() const noexcept {
342  return table_.empty();
343  }
344 
345  std::size_t size() const noexcept {
346  return table_.size();
347  }
348 
349  std::size_t max_size() const noexcept {
350  return table_.max_size();
351  }
352 
354 
355  void clear() noexcept {
356  table_.clear();
357  }
358 
359  std::pair<iterator, bool> insert(value_type const& value) {
360  return emplace(value);
361  }
362 
363  std::pair<iterator, bool> insert(value_type&& value) {
364  return emplace(std::move(value));
365  }
366 
367  // std::unordered_set's hinted insertion API is misleading. No
368  // implementation I've seen actually uses the hint. Code restructuring
369  // by the caller to use the hinted API is at best unnecessary, and at
370  // worst a pessimization. It is used, however, so we provide it.
371 
372  iterator insert(const_iterator /*hint*/, value_type const& value) {
373  return insert(value).first;
374  }
375 
376  iterator insert(const_iterator /*hint*/, value_type&& value) {
377  return insert(std::move(value)).first;
378  }
379 
380  template <typename K>
381  EnableHeterogeneousInsert<K, std::pair<iterator, bool>> insert(K&& value) {
382  return emplace(std::forward<K>(value));
383  }
384 
385  private:
386  template <class InputIt>
388  bulkInsert(InputIt first, InputIt last, bool autoReserve) {
389  if (autoReserve) {
390  table_.reserveForInsert(std::distance(first, last));
391  }
392  while (first != last) {
393  insert(*first);
394  ++first;
395  }
396  }
397 
398  template <class InputIt>
399  void initialInsert(InputIt first, InputIt last, std::size_t initialCapacity) {
400  FOLLY_SAFE_DCHECK(empty() && bucket_count() >= initialCapacity, "");
401 
402  // It's possible that there are a lot of duplicates in first..last and
403  // so we will oversize ourself. The common case, however, is that
404  // we can avoid a lot of rehashing if we pre-expand. The behavior
405  // is easy to disable at a particular call site by asking for an
406  // initialCapacity of 1.
407  bool autoReserve =
408  std::is_same<
409  typename std::iterator_traits<InputIt>::iterator_category,
410  std::random_access_iterator_tag>::value &&
411  initialCapacity == 0;
412  bulkInsert(first, last, autoReserve);
413  }
414 
415  public:
416  template <class InputIt>
417  void insert(InputIt first, InputIt last) {
418  // Bulk reserve is a heuristic choice, so it can backfire. We restrict
419  // ourself to situations that mimic bulk construction without an
420  // explicit initialCapacity.
421  bool autoReserve =
422  std::is_same<
423  typename std::iterator_traits<InputIt>::iterator_category,
424  std::random_access_iterator_tag>::value &&
425  bucket_count() == 0;
426  bulkInsert(first, last, autoReserve);
427  }
428 
429  void insert(std::initializer_list<value_type> ilist) {
430  insert(ilist.begin(), ilist.end());
431  }
432 
433  template <class... Args>
434  std::pair<iterator, bool> emplace(Args&&... args) {
435  using K = KeyTypeForEmplace<key_type, hasher, key_equal, Args...>;
436 
437  // If args is a single arg that can be emplaced directly (either
438  // key_type or a heterogeneous find + conversion to key_type) key will
439  // just be a reference to that arg, otherwise this will construct an
440  // intermediate key.
441  K key(std::forward<Args>(args)...);
442 
443  auto rv = table_.tryEmplaceValue(key, std::forward<K>(key));
444 
445  return std::make_pair(table_.makeIter(rv.first), rv.second);
446  }
447 
448  template <class... Args>
449  iterator emplace_hint(const_iterator /*hint*/, Args&&... args) {
450  return emplace(std::forward<Args>(args)...).first;
451  }
452 
453  FOLLY_ALWAYS_INLINE iterator erase(const_iterator pos) {
454  return eraseInto(pos, [](value_type&&) {});
455  }
456 
457  iterator erase(const_iterator first, const_iterator last) {
458  return eraseInto(first, last, [](value_type&&) {});
459  }
460 
461  size_type erase(key_type const& key) {
462  return eraseInto(key, [](value_type&&) {});
463  }
464 
465  template <typename K>
466  EnableHeterogeneousErase<K, size_type> erase(K const& key) {
467  return eraseInto(key, [](value_type&&) {});
468  }
469 
470  // eraseInto contains the same overloads as erase but provides
471  // an additional callback argument which is called with an rvalue
472  // reference to the item directly before it is destroyed. This can be
473  // used to extract an item out of a F14Set while avoiding a copy.
474  template <typename BeforeDestroy>
475  FOLLY_ALWAYS_INLINE iterator
476  eraseInto(const_iterator pos, BeforeDestroy&& beforeDestroy) {
477  auto itemPos = table_.unwrapIter(pos);
478  table_.eraseIterInto(itemPos, beforeDestroy);
479 
480  // If we are inlined then gcc and clang can optimize away all of the
481  // work of ++pos if the caller discards it.
482  itemPos.advanceLikelyDead();
483  return table_.makeIter(itemPos);
484  }
485 
486  template <typename BeforeDestroy>
487  iterator eraseInto(
488  const_iterator first,
489  const_iterator last,
490  BeforeDestroy&& beforeDestroy) {
491  while (first != last) {
492  first = eraseInto(first, beforeDestroy);
493  }
494  return first;
495  }
496 
497  template <typename BeforeDestroy>
498  size_type eraseInto(key_type const& key, BeforeDestroy&& beforeDestroy) {
499  return table_.eraseKeyInto(key, beforeDestroy);
500  }
501 
502  template <typename K, typename BeforeDestroy>
503  EnableHeterogeneousErase<K, size_type> eraseInto(
504  K const& key,
505  BeforeDestroy&& beforeDestroy) {
506  return table_.eraseKeyInto(key, beforeDestroy);
507  }
508 
510 
511  FOLLY_ALWAYS_INLINE std::size_t count(key_type const& key) const {
512  return table_.find(key).atEnd() ? 0 : 1;
513  }
514 
515  template <typename K>
516  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, size_type> count(
517  K const& key) const {
518  return table_.find(key).atEnd() ? 0 : 1;
519  }
520 
521  // prehash(key) does the work of evaluating hash_function()(key)
522  // (including additional bit-mixing for non-avalanching hash functions),
523  // wraps the result of that work in a token for later reuse, and
524  // begins prefetching of the first steps of looking for key into the
525  // local CPU cache.
526  //
527  // The returned token may be used at any time, may be used more than
528  // once, and may be used in other F14 sets and maps. Tokens are
529  // transferrable between any F14 containers (maps and sets) with the
530  // same key_type and equal hash_function()s.
531  //
532  // Hash tokens are not hints -- it is a bug to call any method on this
533  // class with a token t and key k where t isn't the result of a call
534  // to prehash(k2) with k2 == k.
535  F14HashToken prehash(key_type const& key) const {
536  return table_.prehash(key);
537  }
538 
539  template <typename K>
540  EnableHeterogeneousFind<K, F14HashToken> prehash(K const& key) const {
541  return table_.prehash(key);
542  }
543 
544  FOLLY_ALWAYS_INLINE iterator find(key_type const& key) {
545  return const_cast<F14BasicSet const*>(this)->find(key);
546  }
547 
548  FOLLY_ALWAYS_INLINE const_iterator find(key_type const& key) const {
549  return table_.makeIter(table_.find(key));
550  }
551 
552  FOLLY_ALWAYS_INLINE iterator
553  find(F14HashToken const& token, key_type const& key) {
554  return const_cast<F14BasicSet const*>(this)->find(token, key);
555  }
556 
557  FOLLY_ALWAYS_INLINE const_iterator
558  find(F14HashToken const& token, key_type const& key) const {
559  return table_.makeIter(table_.find(token, key));
560  }
561 
562  template <typename K>
563  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, iterator> find(K const& key) {
564  return const_cast<F14BasicSet const*>(this)->find(key);
565  }
566 
567  template <typename K>
568  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, const_iterator> find(
569  K const& key) const {
570  return table_.makeIter(table_.find(key));
571  }
572 
573  template <typename K>
574  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, iterator> find(
575  F14HashToken const& token,
576  K const& key) {
577  return const_cast<F14BasicSet const*>(this)->find(token, key);
578  }
579 
580  template <typename K>
581  FOLLY_ALWAYS_INLINE EnableHeterogeneousFind<K, const_iterator> find(
582  F14HashToken const& token,
583  K const& key) const {
584  return table_.makeIter(table_.find(token, key));
585  }
586 
587  std::pair<iterator, iterator> equal_range(key_type const& key) {
588  return equal_range(*this, key);
589  }
590 
591  std::pair<const_iterator, const_iterator> equal_range(
592  key_type const& key) const {
593  return equal_range(*this, key);
594  }
595 
596  template <typename K>
597  EnableHeterogeneousFind<K, std::pair<iterator, iterator>> equal_range(
598  K const& key) {
599  return equal_range(*this, key);
600  }
601 
602  template <typename K>
603  EnableHeterogeneousFind<K, std::pair<const_iterator, const_iterator>>
604  equal_range(K const& key) const {
605  return equal_range(*this, key);
606  }
607 
609 
610  std::size_t bucket_count() const noexcept {
611  return table_.bucket_count();
612  }
613 
614  std::size_t max_bucket_count() const noexcept {
615  return table_.max_bucket_count();
616  }
617 
619 
620  float load_factor() const noexcept {
621  return table_.load_factor();
622  }
623 
624  float max_load_factor() const noexcept {
625  return table_.max_load_factor();
626  }
627 
628  void max_load_factor(float v) {
629  table_.max_load_factor(v);
630  }
631 
632  void rehash(std::size_t bucketCapacity) {
633  // The standard's rehash() requires understanding the max load factor,
634  // which is easy to get wrong. Since we don't actually allow adjustment
635  // of max_load_factor there is no difference.
636  reserve(bucketCapacity);
637  }
638 
639  void reserve(std::size_t capacity) {
640  table_.reserve(capacity);
641  }
642 
644 
645  hasher hash_function() const {
646  return table_.hasher();
647  }
648 
649  key_equal key_eq() const {
650  return table_.keyEqual();
651  }
652 
654 
655  // Get memory footprint, not including sizeof(*this).
656  std::size_t getAllocatedMemorySize() const {
657  return table_.getAllocatedMemorySize();
658  }
659 
660  // Enumerates classes of allocated memory blocks currently owned
661  // by this table, calling visitor(allocationSize, allocationCount).
662  // This can be used to get a more accurate indication of memory footprint
663  // than getAllocatedMemorySize() if you have some way of computing the
664  // internal fragmentation of the allocator, such as JEMalloc's nallocx.
665  // The visitor might be called twice with the same allocationSize. The
666  // visitor's computation should produce the same result for visitor(8,
667  // 2) as for two calls to visitor(8, 1), for example. The visitor may
668  // be called with a zero allocationCount.
669  template <typename V>
670  void visitAllocationClasses(V&& visitor) const {
671  return table_.visitAllocationClasses(visitor);
672  }
673 
674  // Calls visitor with two value_type const*, b and e, such that every
675  // entry in the table is included in exactly one of the ranges [b,e).
676  // This can be used to efficiently iterate elements in bulk when crossing
677  // an API boundary that supports contiguous blocks of items.
678  template <typename V>
679  void visitContiguousRanges(V&& visitor) const;
680 
681  F14TableStats computeStats() const noexcept {
682  return table_.computeStats();
683  }
684 
685  private:
686  template <typename Self, typename K>
687  static auto equal_range(Self& self, K const& key) {
688  auto first = self.find(key);
689  auto last = first;
690  if (last != self.end()) {
691  ++last;
692  }
693  return std::make_pair(first, last);
694  }
695 
696  protected:
697  F14Table<Policy> table_;
698 };
699 
700 template <typename S>
701 bool setsEqual(S const& lhs, S const& rhs) {
702  if (lhs.size() != rhs.size()) {
703  return false;
704  }
705  for (auto& k : lhs) {
706  auto iter = rhs.find(k);
707  if (iter == rhs.end()) {
708  return false;
709  }
710  if (!std::is_same<
711  typename S::key_equal,
712  std::equal_to<typename S::value_type>>::value) {
713  // spec says we compare key with == as well as with key_eq()
714  if (!(k == *iter)) {
715  return false;
716  }
717  }
718  }
719  return true;
720 }
721 } // namespace detail
722 } // namespace f14
723 
724 template <typename Key, typename Hasher, typename KeyEqual, typename Alloc>
725 class F14ValueSet
726  : public f14::detail::F14BasicSet<f14::detail::SetPolicyWithDefaults<
727  f14::detail::ValueContainerPolicy,
728  Key,
729  Hasher,
730  KeyEqual,
731  Alloc>> {
732  using Policy = f14::detail::SetPolicyWithDefaults<
733  f14::detail::ValueContainerPolicy,
734  Key,
735  Hasher,
736  KeyEqual,
737  Alloc>;
739 
740  public:
741  using typename Super::value_type;
742 
743  F14ValueSet() = default;
744 
745  using Super::Super;
746 
747  F14ValueSet& operator=(std::initializer_list<value_type> ilist) {
748  Super::operator=(ilist);
749  return *this;
750  }
751 
752  void swap(F14ValueSet& rhs) noexcept(Policy::kSwapIsNoexcept) {
753  this->table_.swap(rhs.table_);
754  }
755 
756  template <typename V>
757  void visitContiguousRanges(V&& visitor) const {
758  this->table_.visitContiguousItemRanges(std::forward<V>(visitor));
759  }
760 };
761 
762 template <typename K, typename H, typename E, typename A>
763 bool operator==(
764  F14ValueSet<K, H, E, A> const& lhs,
765  F14ValueSet<K, H, E, A> const& rhs) {
766  return setsEqual(lhs, rhs);
767 }
768 
769 template <typename K, typename H, typename E, typename A>
770 bool operator!=(
771  F14ValueSet<K, H, E, A> const& lhs,
772  F14ValueSet<K, H, E, A> const& rhs) {
773  return !(lhs == rhs);
774 }
775 
776 template <typename Key, typename Hasher, typename KeyEqual, typename Alloc>
777 class F14NodeSet
778  : public f14::detail::F14BasicSet<f14::detail::SetPolicyWithDefaults<
779  f14::detail::NodeContainerPolicy,
780  Key,
781  Hasher,
782  KeyEqual,
783  Alloc>> {
784  using Policy = f14::detail::SetPolicyWithDefaults<
785  f14::detail::NodeContainerPolicy,
786  Key,
787  Hasher,
788  KeyEqual,
789  Alloc>;
791 
792  public:
793  using typename Super::value_type;
794 
795  F14NodeSet() = default;
796 
797  using Super::Super;
798 
799  F14NodeSet& operator=(std::initializer_list<value_type> ilist) {
800  Super::operator=(ilist);
801  return *this;
802  }
803 
804  void swap(F14NodeSet& rhs) noexcept(Policy::kSwapIsNoexcept) {
805  this->table_.swap(rhs.table_);
806  }
807 
808  template <typename V>
809  void visitContiguousRanges(V&& visitor) const {
810  this->table_.visitItems([&](typename Policy::Item ptr) {
811  value_type const* b = std::addressof(*ptr);
812  visitor(b, b + 1);
813  });
814  }
815 };
816 
817 template <typename K, typename H, typename E, typename A>
818 bool operator==(
819  F14NodeSet<K, H, E, A> const& lhs,
820  F14NodeSet<K, H, E, A> const& rhs) {
821  return setsEqual(lhs, rhs);
822 }
823 
824 template <typename K, typename H, typename E, typename A>
825 bool operator!=(
826  F14NodeSet<K, H, E, A> const& lhs,
827  F14NodeSet<K, H, E, A> const& rhs) {
828  return !(lhs == rhs);
829 }
830 
831 template <typename Key, typename Hasher, typename KeyEqual, typename Alloc>
832 class F14VectorSet
833  : public f14::detail::F14BasicSet<f14::detail::SetPolicyWithDefaults<
834  f14::detail::VectorContainerPolicy,
835  Key,
836  Hasher,
837  KeyEqual,
838  Alloc>> {
839  using Policy = f14::detail::SetPolicyWithDefaults<
840  f14::detail::VectorContainerPolicy,
841  Key,
842  Hasher,
843  KeyEqual,
844  Alloc>;
846 
847  template <typename K, typename T>
848  using EnableHeterogeneousVectorErase = std::enable_if_t<
849  f14::detail::EligibleForHeterogeneousFind<
850  typename Policy::Value,
851  typename Policy::Hasher,
852  typename Policy::KeyEqual,
853  K>::value &&
854  !std::is_same<typename Policy::Iter, remove_cvref_t<K>>::value &&
855  !std::is_same<typename Policy::ReverseIter, remove_cvref_t<K>>::value,
856  T>;
857 
858  public:
859  using typename Super::const_iterator;
860  using typename Super::iterator;
861  using typename Super::key_type;
862  using typename Super::value_type;
863  using reverse_iterator = typename Policy::ReverseIter;
864  using const_reverse_iterator = reverse_iterator;
865 
866  F14VectorSet() = default;
867 
868  using Super::Super;
869 
870  F14VectorSet& operator=(std::initializer_list<value_type> ilist) {
871  Super::operator=(ilist);
872  return *this;
873  }
874 
875  void swap(F14VectorSet& rhs) noexcept(Policy::kSwapIsNoexcept) {
876  this->table_.swap(rhs.table_);
877  }
878 
879  // ITERATION ORDER
880  //
881  // Deterministic iteration order for insert-only workloads is part of
882  // F14VectorSet's supported API: iterator is LIFO and reverse_iterator
883  // is FIFO.
884  //
885  // If there have been no calls to erase() then iterator and
886  // const_iterator enumerate entries in the opposite of insertion order.
887  // begin()->first is the key most recently inserted. reverse_iterator
888  // and reverse_const_iterator, therefore, enumerate in LIFO (insertion)
889  // order for insert-only workloads. Deterministic iteration order is
890  // only guaranteed if no keys were removed since the last time the
891  // set was empty. Iteration order is preserved across rehashes and
892  // F14VectorSet copies and moves.
893  //
894  // iterator uses LIFO order so that erasing while iterating with begin()
895  // and end() is safe using the erase(it++) idiom, which is supported
896  // by std::set and std::unordered_set. erase(iter) invalidates iter
897  // and all iterators before iter in the non-reverse iteration order.
898  // Every successful erase invalidates all reverse iterators.
899 
900  iterator begin() {
901  return cbegin();
902  }
903  const_iterator begin() const {
904  return cbegin();
905  }
906  const_iterator cbegin() const {
907  return this->table_.linearBegin(this->size());
908  }
909 
910  iterator end() {
911  return cend();
912  }
913  const_iterator end() const {
914  return cend();
915  }
916  const_iterator cend() const {
917  return this->table_.linearEnd();
918  }
919 
920  reverse_iterator rbegin() {
921  return this->table_.values_;
922  }
923  const_reverse_iterator rbegin() const {
924  return crbegin();
925  }
926  const_reverse_iterator crbegin() const {
927  return this->table_.values_;
928  }
929 
930  reverse_iterator rend() {
931  return this->table_.values_ + this->table_.size();
932  }
933  const_reverse_iterator rend() const {
934  return crend();
935  }
936  const_reverse_iterator crend() const {
937  return this->table_.values_ + this->table_.size();
938  }
939 
940  // explicit conversions between iterator and reverse_iterator
941  iterator iter(reverse_iterator riter) {
942  return this->table_.iter(riter);
943  }
944  const_iterator iter(const_reverse_iterator riter) const {
945  return this->table_.iter(riter);
946  }
947 
948  reverse_iterator riter(iterator it) {
949  return this->table_.riter(it);
950  }
951  const_reverse_iterator riter(const_iterator it) const {
952  return this->table_.riter(it);
953  }
954 
955  private:
956  template <typename BeforeDestroy>
957  void eraseUnderlying(
958  typename Policy::ItemIter underlying,
959  BeforeDestroy&& beforeDestroy) {
960  Alloc& a = this->table_.alloc();
961  auto values = this->table_.values_;
962 
963  // destroy the value and remove the ptr from the base table
964  auto index = underlying.item();
965  this->table_.eraseIterInto(underlying, beforeDestroy);
966  Policy::AllocTraits::destroy(a, std::addressof(values[index]));
967 
968  // move the last element in values_ down and fix up the inbound index
969  auto tailIndex = this->size();
970  if (tailIndex != index) {
971  auto tail = this->table_.find(f14::detail::VectorContainerIndexSearch{
972  static_cast<uint32_t>(tailIndex)});
973  tail.item() = index;
974  auto p = std::addressof(values[index]);
975  assume(p != nullptr);
976  this->table_.transfer(a, std::addressof(values[tailIndex]), p, 1);
977  }
978  }
979 
980  template <typename K, typename BeforeDestroy>
981  std::size_t eraseUnderlyingKey(K const& key, BeforeDestroy&& beforeDestroy) {
982  auto underlying = this->table_.find(key);
983  if (underlying.atEnd()) {
984  return 0;
985  } else {
986  eraseUnderlying(underlying, beforeDestroy);
987  return 1;
988  }
989  }
990 
991  public:
992  FOLLY_ALWAYS_INLINE iterator erase(const_iterator pos) {
993  return eraseInto(pos, [](value_type&&) {});
994  }
995 
996  iterator erase(const_iterator first, const_iterator last) {
997  return eraseInto(first, last, [](value_type&&) {});
998  }
999 
1000  // No erase is provided for reverse_iterator (AKA const_reverse_iterator)
1001  // to make it harder to shoot yourself in the foot by erasing while
1002  // reverse-iterating. You can write that as set.erase(set.iter(riter)).
1003 
1004  std::size_t erase(key_type const& key) {
1005  return eraseInto(key, [](value_type&&) {});
1006  }
1007 
1008  template <typename K>
1009  EnableHeterogeneousVectorErase<K, std::size_t> erase(K const& key) {
1010  return eraseInto(key, [](value_type&&) {});
1011  }
1012 
1013  template <typename BeforeDestroy>
1014  FOLLY_ALWAYS_INLINE iterator
1015  eraseInto(const_iterator pos, BeforeDestroy&& beforeDestroy) {
1016  auto underlying = this->table_.find(
1017  f14::detail::VectorContainerIndexSearch{this->table_.iterToIndex(pos)});
1018  eraseUnderlying(underlying, beforeDestroy);
1019  return ++pos;
1020  }
1021 
1022  template <typename BeforeDestroy>
1023  iterator eraseInto(
1024  const_iterator first,
1025  const_iterator last,
1026  BeforeDestroy&& beforeDestroy) {
1027  while (first != last) {
1028  first = eraseInto(first, beforeDestroy);
1029  }
1030  return first;
1031  }
1032 
1033  template <typename BeforeDestroy>
1034  std::size_t eraseInto(key_type const& key, BeforeDestroy&& beforeDestroy) {
1035  return eraseUnderlyingKey(key, beforeDestroy);
1036  }
1037 
1038  template <typename K, typename BeforeDestroy>
1039  EnableHeterogeneousVectorErase<K, std::size_t> eraseInto(
1040  K const& key,
1041  BeforeDestroy&& beforeDestroy) {
1042  return eraseUnderlyingKey(key, beforeDestroy);
1043  }
1044 
1045  template <typename V>
1046  void visitContiguousRanges(V&& visitor) const {
1047  auto n = this->table_.size();
1048  if (n > 0) {
1049  value_type const* b = std::addressof(this->table_.values_[0]);
1050  visitor(b, b + n);
1051  }
1052  }
1053 };
1054 
1055 template <typename K, typename H, typename E, typename A>
1056 bool operator==(
1057  F14VectorSet<K, H, E, A> const& lhs,
1058  F14VectorSet<K, H, E, A> const& rhs) {
1059  return setsEqual(lhs, rhs);
1060 }
1061 
1062 template <typename K, typename H, typename E, typename A>
1063 bool operator!=(
1064  F14VectorSet<K, H, E, A> const& lhs,
1065  F14VectorSet<K, H, E, A> const& rhs) {
1066  return !(lhs == rhs);
1067 }
1068 } // namespace folly
1069 
1070 #endif // FOLLY_F14_VECTOR_INTRINSICS_AVAILABLE
1071 
1072 namespace folly {
1073 template <typename Key, typename Hasher, typename KeyEqual, typename Alloc>
1074 class F14FastSet : public std::conditional_t<
1075  sizeof(Key) < 24,
1076  F14ValueSet<Key, Hasher, KeyEqual, Alloc>,
1077  F14VectorSet<Key, Hasher, KeyEqual, Alloc>> {
1078  using Super = std::conditional_t<
1079  sizeof(Key) < 24,
1080  F14ValueSet<Key, Hasher, KeyEqual, Alloc>,
1081  F14VectorSet<Key, Hasher, KeyEqual, Alloc>>;
1082 
1083  public:
1084  using typename Super::value_type;
1085 
1086  F14FastSet() = default;
1087 
1088  using Super::Super;
1089 
1090  F14FastSet& operator=(std::initializer_list<value_type> ilist) {
1091  Super::operator=(ilist);
1092  return *this;
1093  }
1094 };
1095 
1096 template <typename K, typename H, typename E, typename A>
1097 void swap(F14ValueSet<K, H, E, A>& lhs, F14ValueSet<K, H, E, A>& rhs) noexcept(
1098  noexcept(lhs.swap(rhs))) {
1099  lhs.swap(rhs);
1100 }
1101 
1102 template <typename K, typename H, typename E, typename A>
1103 void swap(F14NodeSet<K, H, E, A>& lhs, F14NodeSet<K, H, E, A>& rhs) noexcept(
1104  noexcept(lhs.swap(rhs))) {
1105  lhs.swap(rhs);
1106 }
1107 
1108 template <typename K, typename H, typename E, typename A>
1109 void swap(
1110  F14VectorSet<K, H, E, A>& lhs,
1111  F14VectorSet<K, H, E, A>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
1112  lhs.swap(rhs);
1113 }
1114 
1115 template <typename K, typename H, typename E, typename A>
1116 void swap(F14FastSet<K, H, E, A>& lhs, F14FastSet<K, H, E, A>& rhs) noexcept(
1117  noexcept(lhs.swap(rhs))) {
1118  lhs.swap(rhs);
1119 }
1120 
1121 } // namespace folly
std::unordered_set< Key, Hasher, KeyEqual, Alloc > Super
Definition: F14Set.h:50
void * ptr
F14VectorSet & operator=(std::initializer_list< value_type > ilist)
Definition: F14Set.h:141
#define FOLLY_ALWAYS_INLINE
Definition: CPortability.h:151
char b
std::size_t getAllocatedMemorySize() const
Definition: F14Set.h:63
bool operator!=(SwapTrackingAlloc< T1 > const &, SwapTrackingAlloc< T2 > const &)
Definition: F14TestUtil.h:427
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
F14NodeSet & operator=(std::initializer_list< value_type > ilist)
Definition: F14Set.h:105
internal::KeyMatcher< M > Key(M inner_matcher)
auto begin(TestAdlIterable &instance)
Definition: ForeachTest.cpp:56
folly::std T
internal::ArgsMatcher< InnerMatcher > Args(const InnerMatcher &matcher)
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
requires E e noexcept(noexcept(s.error(std::move(e))))
FOLLY_PUSH_WARNING RHS rhs
Definition: Traits.h:649
static void destroy()
void visitContiguousRanges(V &&visitor) const
Definition: F14Set.h:83
void init(int *argc, char ***argv, bool removeFlags)
Definition: Init.cpp:34
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
constexpr auto empty(C const &c) -> decltype(c.empty())
Definition: Access.h:55
def Iter(n, format, sep='')
bool Value(const T &value, M matcher)
auto end(TestAdlIterable &instance)
Definition: ForeachTest.cpp:62
std::enable_if< std::is_integral< Src >::value &&IsSomeString< Tgt >::value &&sizeof(Src)< 4 >::typetoAppend(Src value, Tgt *result){typedef typename std::conditional< std::is_signed< Src >::value, int64_t, uint64_t >::type Intermediate;toAppend< Tgt >static_cast< Intermediate >value), result);}template< class Src >typename std::enable_if< std::is_integral< Src >::value &&sizeof(Src)< 4 &&!std::is_same< Src, char >::value, size_t >::typeestimateSpaceNeeded(Src value){typedef typename std::conditional< std::is_signed< Src >::value, int64_t, uint64_t >::type Intermediate;return estimateSpaceNeeded(static_cast< Intermediate >value));}template< class Tgt, class Src >typename std::enable_if< std::is_enum< Src >::value &&IsSomeString< Tgt >::value >::typetoAppend(Src value, Tgt *result){toAppend(static_cast< typename std::underlying_type< Src >::type >value), result);}template< class Src >typename std::enable_if< std::is_enum< Src >::value, size_t >::typeestimateSpaceNeeded(Src value){return estimateSpaceNeeded(static_cast< typename std::underlying_type< Src >::type >value));}namespace detail{constexpr int kConvMaxDecimalInShortestLow=-6;constexpr int kConvMaxDecimalInShortestHigh=21;}template< class Tgt, class Src >typename std::enable_if< std::is_floating_point< Src >::value &&IsSomeString< Tgt >::value >::typetoAppend(Src value, Tgt *result, double_conversion::DoubleToStringConverter::DtoaMode mode, unsigned int numDigits){using namespace double_conversion;DoubleToStringConverter conv(DoubleToStringConverter::NO_FLAGS,"Infinity","NaN", 'E', detail::kConvMaxDecimalInShortestLow, detail::kConvMaxDecimalInShortestHigh, 6, 1);char buffer[256];StringBuilder builder(buffer, sizeof(buffer));switch(mode){case DoubleToStringConverter::SHORTEST:conv.ToShortest(value,&builder);break;case DoubleToStringConverter::SHORTEST_SINGLE:conv.ToShortestSingle(static_cast< float >value),&builder);break;case DoubleToStringConverter::FIXED:conv.ToFixed(value, int(numDigits),&builder);break;default:CHECK(mode==DoubleToStringConverter::PRECISION);conv.ToPrecision(value, int(numDigits),&builder);break;}const size_t length=size_t(builder.position());builder.Finalize();result->append(buffer, length);}template< class Tgt, class Src >typename std::enable_if< std::is_floating_point< Src >::value &&IsSomeString< Tgt >::value >::typetoAppend(Src value, Tgt *result){toAppend(value, result, double_conversion::DoubleToStringConverter::SHORTEST, 0);}template< class Src >typename std::enable_if< std::is_floating_point< Src >::value, size_t >::typeestimateSpaceNeeded(Src value){constexpr int kMaxMantissaSpace=double_conversion::DoubleToStringConverter::kBase10MaximalLength+1;constexpr int kMaxExponentSpace=2+3;static const int kMaxPositiveSpace=std::max({kMaxMantissaSpace+kMaxExponentSpace, kMaxMantissaSpace-detail::kConvMaxDecimalInShortestLow, detail::kConvMaxDecimalInShortestHigh,});return size_t(kMaxPositiveSpace+(value< 0?1:0));}template< class Src >struct HasLengthEstimator:std::false_type{};template< class Src >constexpr typename std::enable_if< !std::is_fundamental< Src >::value &&!IsSomeString< Src >::value &&!std::is_convertible< Src, const char * >::value &&!std::is_convertible< Src, StringPiece >::value &&!std::is_enum< Src >::value &&!HasLengthEstimator< Src >::value, size_t >::typeestimateSpaceNeeded(const Src &){return sizeof(Src)+1;}namespace detail{template< class Tgt >typename std::enable_if< IsSomeString< Tgt >::value, size_t >::typeestimateSpaceToReserve(size_t sofar, Tgt *){return sofar;}template< class T, class...Ts >size_t estimateSpaceToReserve(size_t sofar, const T &v, const Ts &...vs){return estimateSpaceToReserve(sofar+estimateSpaceNeeded(v), vs...);}template< class...Ts >void reserveInTarget(const Ts &...vs){getLastElement(vs...) -> reserve(estimateSpaceToReserve(0, vs...))
Definition: Conv.h:792
char a
int * count
F14ValueSet & operator=(std::initializer_list< value_type > ilist)
Definition: F14Set.h:123
const
Definition: upload.py:398
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
void swap(SwapTrackingAlloc< T > &, SwapTrackingAlloc< T > &)
Definition: F14TestUtil.h:414
FOLLY_ALWAYS_INLINE void assume(bool cond)
Definition: Assume.h:41
void visitAllocationClasses(V &&visitor) const
Definition: F14Set.h:72
KeyT k
#define FOLLY_SAFE_DCHECK(expr, msg)
Definition: SafeAssert.h:42
Iterator< typename Container::const_iterator > cbegin(const Container &c)
Definition: Padded.h:319
Iterator< typename Container::const_iterator > cend(const Container &c)
Definition: Padded.h:324
std::vector< int > values(1'000)
constexpr detail::First first
Definition: Base-inl.h:2553
bool operator==(SwapTrackingAlloc< T1 > const &, SwapTrackingAlloc< T2 > const &)
Definition: F14TestUtil.h:422