66 */
77
88/*
9- * This file defines a zero-cost wrapper around std::unordered_map that prevents
10- * accidental non-deterministic iteration.
9+ * This file defines zero-cost wrappers around std::unordered_map and
10+ * std::unordered_set that prevent accidental non-deterministic iteration.
1111 *
12- * TODO: Define UnorderedSet, UnorderedMultiMap.
12+ * TODO: Define UnorderedMultiMap.
1313 * TODO: Replace (virtually) all usages of std::unordered_x with UnorderedX in
1414 * the codebase.
1515 *
7979#include < numeric>
8080#include < type_traits>
8181#include < unordered_map>
82+ #include < unordered_set>
8283
8384#include " TemplateUtil.h"
8485
@@ -388,6 +389,263 @@ bool operator!=(const UnorderedMap<Key, Value, Hash, KeyEqual>& lhs,
388389 return lhs._internal_unsafe_unwrap () != rhs._internal_unsafe_unwrap ();
389390}
390391
392+ template <class Key ,
393+ class Hash = std::hash<Key>,
394+ class KeyEqual = std::equal_to<Key>>
395+ class UnorderedSet : UnorderedBase<UnorderedSet<Key, Hash, KeyEqual>> {
396+ using Type = std::unordered_set<Key, Hash, KeyEqual>;
397+ Type m_data;
398+
399+ public:
400+ using key_type = typename Type::key_type;
401+ using value_type = typename Type::value_type;
402+ using size_type = typename Type::size_type;
403+ using difference_type = typename Type::difference_type;
404+ using hasher = typename Type::hasher;
405+ using key_equal = typename Type::key_equal;
406+ using reference = typename Type::reference;
407+ using const_reference = typename Type::const_reference;
408+ using pointer = typename Type::pointer;
409+
410+ class FixedIterator ;
411+
412+ class ConstFixedIterator {
413+ typename Type::const_iterator m_entry;
414+
415+ public:
416+ const Key* operator ->() const { return &*m_entry; }
417+
418+ const Key& operator *() const { return *m_entry; }
419+
420+ bool operator ==(const FixedIterator& other) const {
421+ return m_entry == other._internal_unsafe_unwrap ();
422+ }
423+
424+ bool operator !=(const FixedIterator& other) const {
425+ return m_entry != other._internal_unsafe_unwrap ();
426+ }
427+
428+ bool operator ==(const ConstFixedIterator& other) const {
429+ return m_entry == other.m_entry ;
430+ }
431+
432+ bool operator !=(const ConstFixedIterator& other) const {
433+ return m_entry != other.m_entry ;
434+ }
435+
436+ explicit ConstFixedIterator (typename Type::const_iterator entry)
437+ : m_entry(entry) {}
438+
439+ typename Type::const_iterator _internal_unsafe_unwrap () const {
440+ return m_entry;
441+ }
442+ };
443+
444+ class FixedIterator {
445+ typename Type::iterator m_entry;
446+
447+ public:
448+ // Note that the Set iterator doesn't expose mutable values at all.
449+
450+ const Key* operator ->() const { return &*m_entry; }
451+
452+ const Key& operator *() const { return *m_entry; }
453+
454+ bool operator ==(const FixedIterator& other) const {
455+ return m_entry == other.m_entry ;
456+ }
457+
458+ bool operator !=(const FixedIterator& other) const {
459+ return m_entry != other.m_entry ;
460+ }
461+
462+ bool operator ==(const ConstFixedIterator& other) const {
463+ return m_entry == other._internal_unsafe_unwrap ();
464+ }
465+
466+ bool operator !=(const ConstFixedIterator& other) const {
467+ return m_entry != other._internal_unsafe_unwrap ();
468+ }
469+
470+ explicit FixedIterator (typename Type::iterator entry) : m_entry(entry) {}
471+
472+ typename Type::iterator _internal_unsafe_unwrap () const { return m_entry; }
473+ };
474+
475+ // TODO: Make extra non-deterministic in debug builds
476+ class UnorderedIterable {
477+ Type& m_data;
478+
479+ public:
480+ using iterator = typename Type::iterator;
481+ using const_iterator = typename Type::const_iterator;
482+
483+ explicit UnorderedIterable (Type& data) : m_data(data) {}
484+
485+ iterator begin () { return m_data.begin (); }
486+
487+ iterator end () { return m_data.end (); }
488+
489+ const_iterator begin () const { return m_data.begin (); }
490+
491+ const_iterator end () const { return m_data.end (); }
492+
493+ const_iterator cbegin () const { return m_data.cbegin (); }
494+
495+ const_iterator cend () const { return m_data.cend (); }
496+
497+ iterator find (const Key& key) { return m_data.find (key); }
498+
499+ const_iterator find (const Key& key) const { return m_data.find (key); }
500+
501+ iterator erase (iterator position) { return m_data.erase (position); }
502+
503+ iterator erase (const_iterator position) { return m_data.erase (position); }
504+ };
505+
506+ // TODO: Make extra non-deterministic in debug builds
507+ class ConstUnorderedIterable {
508+ const Type& m_data;
509+
510+ public:
511+ using const_iterator = typename Type::const_iterator;
512+
513+ explicit ConstUnorderedIterable (const Type& data) : m_data(data) {}
514+
515+ const_iterator begin () const { return m_data.begin (); }
516+
517+ const_iterator end () const { return m_data.end (); }
518+
519+ const_iterator cbegin () const { return m_data.cbegin (); }
520+
521+ const_iterator cend () const { return m_data.cend (); }
522+
523+ const_iterator find (const Key& key) const { return m_data.find (key); }
524+ };
525+
526+ UnorderedSet () : m_data() {}
527+
528+ explicit UnorderedSet (size_t bucket_count) : m_data(bucket_count) {}
529+
530+ UnorderedSet (size_t bucket_count, const Hash& hash)
531+ : m_data(bucket_count, hash) {}
532+
533+ UnorderedSet (size_t bucket_count, const Hash& hash, const KeyEqual& equal)
534+ : m_data(bucket_count, hash, equal) {}
535+
536+ UnorderedSet (const UnorderedSet& other) = default ;
537+
538+ UnorderedSet (UnorderedSet&& other) noexcept = default ;
539+
540+ // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
541+ /* implicit */ UnorderedSet(std::initializer_list<Key> init) : m_data(init) {}
542+
543+ template <class InputIt >
544+ UnorderedSet (InputIt first, InputIt last) : m_data(first, last) {}
545+
546+ UnorderedSet& operator =(const UnorderedSet& other) = default ;
547+
548+ UnorderedSet& operator =(UnorderedSet&& other) noexcept = default ;
549+
550+ template <typename ... Args>
551+ std::pair<FixedIterator, bool > emplace (Args&&... args) {
552+ auto [it, success] = m_data.emplace (std::forward<Args>(args)...);
553+ return std::pair<FixedIterator, bool >(FixedIterator (it), success);
554+ }
555+
556+ std::pair<FixedIterator, bool > insert (const Key& value) {
557+ auto [it, success] = m_data.insert (value);
558+ return std::pair<FixedIterator, bool >(FixedIterator (it), success);
559+ }
560+
561+ std::pair<FixedIterator, bool > insert (Key&& value) {
562+ auto [it, success] = m_data.insert (std::forward<Key>(value));
563+ return std::pair<FixedIterator, bool >(FixedIterator (it), success);
564+ }
565+
566+ FixedIterator insert (FixedIterator hint, const Key& value) {
567+ auto it = m_data.insert (hint._internal_unsafe_unwrap (), value);
568+ return FixedIterator (it);
569+ }
570+
571+ FixedIterator insert (FixedIterator hint, Key&& value) {
572+ auto it =
573+ m_data.insert (hint._internal_unsafe_unwrap (), std::forward<Key>(value));
574+ return FixedIterator (it);
575+ }
576+
577+ template <class InputIt >
578+ void insert (InputIt first, InputIt last) {
579+ m_data.insert (first, last);
580+ }
581+
582+ void insert (std::initializer_list<Key> ilist) { m_data.insert (ilist); }
583+
584+ FixedIterator _internal_unordered_any () {
585+ return FixedIterator (m_data.begin ());
586+ }
587+
588+ ConstFixedIterator _internal_unordered_any () const {
589+ return ConstFixedIterator (m_data.begin ());
590+ }
591+
592+ FixedIterator find (const Key& key) { return FixedIterator (m_data.find (key)); }
593+
594+ ConstFixedIterator find (const Key& key) const {
595+ return ConstFixedIterator (m_data.find (key));
596+ }
597+
598+ ConstFixedIterator end () const { return ConstFixedIterator (m_data.end ()); }
599+
600+ FixedIterator end () { return FixedIterator (m_data.end ()); }
601+
602+ ConstFixedIterator cend () const { return ConstFixedIterator (m_data.end ()); }
603+
604+ size_t erase (const Key& key) { return m_data.erase (key); }
605+
606+ void erase (FixedIterator position) {
607+ m_data.erase (position._internal_unsafe_unwrap ());
608+ }
609+
610+ void erase (ConstFixedIterator position) {
611+ m_data.erase (position._internal_unsafe_unwrap ());
612+ }
613+
614+ void clear () { m_data.clear (); }
615+
616+ size_t count (const Key& key) const { return m_data.count (key); }
617+
618+ void reserve (size_t size) { m_data.reserve (size); }
619+
620+ size_t size () const { return m_data.size (); }
621+
622+ bool empty () const { return m_data.empty (); }
623+
624+ UnorderedIterable _internal_unordered_iterable () {
625+ return UnorderedIterable (m_data);
626+ }
627+
628+ ConstUnorderedIterable _internal_unordered_iterable () const {
629+ return ConstUnorderedIterable (m_data);
630+ }
631+
632+ const Type& _internal_unsafe_unwrap () const { return m_data; }
633+
634+ Type& _internal_unsafe_unwrap () { return m_data; }
635+ };
636+
637+ template <class Key , class Hash , class KeyEqual >
638+ bool operator ==(const UnorderedSet<Key, Hash, KeyEqual>& lhs,
639+ const UnorderedSet<Key, Hash, KeyEqual>& rhs) {
640+ return lhs._internal_unsafe_unwrap () == rhs._internal_unsafe_unwrap ();
641+ }
642+
643+ template <class Key , class Hash , class KeyEqual >
644+ bool operator !=(const UnorderedSet<Key, Hash, KeyEqual>& lhs,
645+ const UnorderedSet<Key, Hash, KeyEqual>& rhs) {
646+ return lhs._internal_unsafe_unwrap () != rhs._internal_unsafe_unwrap ();
647+ }
648+
391649template <class UnorderedCollection ,
392650 std::enable_if_t <std::is_base_of_v<UnorderedBase<UnorderedCollection>,
393651 UnorderedCollection>,
@@ -511,7 +769,10 @@ template <
511769 class Compare ,
512770 class Key = typename std::remove_const<typename Collection::key_type>::type,
513771 class Value =
514- typename std::remove_const<typename Collection::mapped_type>::type>
772+ typename std::remove_const<typename Collection::mapped_type>::type,
773+ std::enable_if_t <!std::is_same_v<typename Collection::key_type,
774+ typename Collection::value_type>,
775+ bool > = true >
515776std::vector<std::pair<Key, Value>> unordered_order (Collection& collection,
516777 Compare comp) {
517778 std::vector<std::pair<Key, Value>> result;
@@ -523,6 +784,23 @@ std::vector<std::pair<Key, Value>> unordered_order(Collection& collection,
523784 return result;
524785}
525786
787+ template <class Collection ,
788+ class Compare ,
789+ class Value =
790+ typename std::remove_const<typename Collection::value_type>::type,
791+ std::enable_if_t <std::is_same_v<typename Collection::key_type,
792+ typename Collection::value_type>,
793+ bool > = true >
794+ std::vector<Value> unordered_order (Collection& collection, Compare comp) {
795+ std::vector<Value> result;
796+ result.reserve (collection.size ());
797+ for (auto & entry : UnorderedIterable (collection)) {
798+ result.emplace_back (entry);
799+ }
800+ std::sort (result.begin (), result.end (), std::move (comp));
801+ return result;
802+ }
803+
526804template <
527805 class Collection ,
528806 class Key = typename std::remove_const<typename Collection::key_type>::type>
@@ -550,6 +828,19 @@ std::vector<Key> unordered_order_keys(Collection& collection, Compare comp) {
550828 return result;
551829}
552830
831+ template <
832+ class Collection ,
833+ class Key = typename std::remove_const<typename Collection::key_type>::type>
834+ auto unordered_keys (Collection& collection) {
835+ UnorderedSet<Key, typename Collection::hasher, typename Collection::key_equal>
836+ result;
837+ result.reserve (collection.size ());
838+ for (auto & entry : UnorderedIterable (collection)) {
839+ result.insert (entry.first );
840+ }
841+ return result;
842+ }
843+
553844template <class Collection , class T >
554845T unordered_accumulate (const Collection& collection, T init) {
555846 auto ui = UnorderedIterable (collection);
@@ -630,6 +921,14 @@ void insert_unordered_iterable(Target& target, const Source& source) {
630921 target.insert (ui.begin (), ui.end ());
631922}
632923
924+ template <class Target , class TargetIt , class Source >
925+ void insert_unordered_iterable (Target& target,
926+ const TargetIt& target_it,
927+ const Source& source) {
928+ auto ui = UnorderedIterable (source);
929+ target.insert (target_it, ui.begin (), ui.end ());
930+ }
931+
633932template <class Collection >
634933struct UnorderedMergeContainers {
635934 void operator ()(const Collection& addend, Collection* accumulator) {
0 commit comments