Skip to content

Commit cec43d2

Browse files
Nikolai Tillmannfacebook-github-bot
authored andcommitted
UnorderedSet
Summary: This introduces a new UnorderedSet<> type, and various global helper functions. Reviewed By: agampe Differential Revision: D71938099 fbshipit-source-id: 6973da4b84dafac678d987cccef4dfdbc57bf0d0
1 parent 631efa0 commit cec43d2

File tree

2 files changed

+342
-7
lines changed

2 files changed

+342
-7
lines changed

libredex/DeterministicContainers.h

Lines changed: 303 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
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
*
@@ -79,6 +79,7 @@
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+
391649
template <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>
515776
std::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+
526804
template <
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+
553844
template <class Collection, class T>
554845
T 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+
633932
template <class Collection>
634933
struct UnorderedMergeContainers {
635934
void operator()(const Collection& addend, Collection* accumulator) {

0 commit comments

Comments
 (0)