Commit f27a2019 authored by Christoph Schwering's avatar Christoph Schwering Committed by Chromium LUCI CQ

[Autofill] Added union, difference, subset operations to DenseSet.

This CL adds set operations to DenseSet for the union and difference
and for subset and disjointness of two DenseSets.

The set's size parameter (formerly kEnd, now kMaxValue) is now implicit
and defaults to the T::kMaxValue (which according to the chromium-style
is the highest value in the enum.

Bug: 1007974
Change-Id: Ia8fd96481bf28d361a7736032ea4d65b3dbbab83
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2630706
Commit-Queue: Christoph Schwering <schwering@google.com>
Reviewed-by: default avatarMatthias Körber <koerber@google.com>
Cr-Commit-Position: refs/heads/master@{#844523}
parent 484f3956
...@@ -23,18 +23,18 @@ namespace autofill { ...@@ -23,18 +23,18 @@ namespace autofill {
// representation. // representation.
// //
// The lower and upper bounds of elements storable in a container are // The lower and upper bounds of elements storable in a container are
// [T(0), kEnd). // [T(0), kMaxValue]. By default, kMaxValue is T::kMaxValue.
// //
// Internally, the set is represented as a std::bitset. // Internally, the set is represented as a std::bitset.
// //
// Time and space complexity depend on std::bitset: // Time and space complexity depend on std::bitset:
// - insert(), erase(), contains() should run in time O(1) // - insert(), erase(), contains() should run in time O(1)
// - empty(), size(), iteration should run in time O(kEnd) // - empty(), size(), iteration should run in time O(kMaxValue)
// - sizeof(DenseSet) should be ceil(kEnd / 8) bytes. // - sizeof(DenseSet) should be ceil(kMaxValue / 8) bytes.
// //
// Iterators are invalidated when the owning container is destructed or moved, // Iterators are invalidated when the owning container is destructed or moved,
// or when the element the iterator points to is erased from the container. // or when the element the iterator points to is erased from the container.
template <typename T, T kEnd> template <typename T, T kMaxValue = T::kMaxValue>
class DenseSet { class DenseSet {
private: private:
using Index = std::make_unsigned_t<T>; using Index = std::make_unsigned_t<T>;
...@@ -194,6 +194,9 @@ class DenseSet { ...@@ -194,6 +194,9 @@ class DenseSet {
return {find(x), !contained}; return {find(x), !contained};
} }
// Inserts all values of |xs| into the present set.
void insert_all(const DenseSet& xs) { bitset_ |= xs.bitset_; }
// Erases the element whose index matches the index of |x| and returns the // Erases the element whose index matches the index of |x| and returns the
// number of erased elements (0 or 1). // number of erased elements (0 or 1).
size_t erase(T x) { size_t erase(T x) {
...@@ -220,6 +223,9 @@ class DenseSet { ...@@ -220,6 +223,9 @@ class DenseSet {
return last; return last;
} }
// Erases all values of |xs| into the present set.
void erase_all(const DenseSet& xs) { bitset_ &= ~xs.bitset_; }
// Lookup. // Lookup.
// Returns 1 if |x| is an element, otherwise 0. // Returns 1 if |x| is an element, otherwise 0.
...@@ -233,6 +239,21 @@ class DenseSet { ...@@ -233,6 +239,21 @@ class DenseSet {
// Returns true if |x| is an element, else |false|. // Returns true if |x| is an element, else |false|.
bool contains(T x) const { return bitset_.test(value_to_index(x)); } bool contains(T x) const { return bitset_.test(value_to_index(x)); }
// Returns true if some element of |xs| is an element, else |false|.
bool contains_none(const DenseSet& xs) const {
return (bitset_ & xs.bitset_).none();
}
// Returns true if some element of |xs| is an element, else |false|.
bool contains_any(const DenseSet& xs) const {
return (bitset_ & xs.bitset_).any();
}
// Returns true if every elements of |xs| is an element, else |false|.
bool contains_all(const DenseSet& xs) const {
return (bitset_ & xs.bitset_) == xs.bitset_;
}
// Returns an iterator to the first element not less than the |x|, or end(). // Returns an iterator to the first element not less than the |x|, or end().
const_iterator lower_bound(T x) const { const_iterator lower_bound(T x) const {
const_iterator it(this, value_to_index(x)); const_iterator it(this, value_to_index(x));
...@@ -255,12 +276,12 @@ class DenseSet { ...@@ -255,12 +276,12 @@ class DenseSet {
}; };
static constexpr Index value_to_index(T x) { static constexpr Index value_to_index(T x) {
DCHECK(index_to_value(0) <= x && x < kEnd); DCHECK(index_to_value(0) <= x && x <= kMaxValue);
return base::checked_cast<Index>(x); return base::checked_cast<Index>(x);
} }
static constexpr T index_to_value(Index i) { static constexpr T index_to_value(Index i) {
DCHECK_LT(i, base::checked_cast<Index>(kEnd)); DCHECK_LE(i, base::checked_cast<Index>(kMaxValue));
using UnderlyingType = using UnderlyingType =
typename std::conditional_t<std::is_enum<T>::value, typename std::conditional_t<std::is_enum<T>::value,
std::underlying_type<T>, Wrapper>::type; std::underlying_type<T>, Wrapper>::type;
...@@ -268,9 +289,9 @@ class DenseSet { ...@@ -268,9 +289,9 @@ class DenseSet {
} }
static_assert(std::is_integral<T>::value || std::is_enum<T>::value, ""); static_assert(std::is_integral<T>::value || std::is_enum<T>::value, "");
static_assert(index_to_value(0) <= kEnd, ""); static_assert(0 <= base::checked_cast<Index>(kMaxValue) + 1, "");
std::bitset<base::checked_cast<Index>(kEnd)> bitset_{}; std::bitset<base::checked_cast<Index>(kMaxValue) + 1> bitset_{};
}; };
} // namespace autofill } // namespace autofill
......
...@@ -19,9 +19,9 @@ TEST(DenseSet, initialization) { ...@@ -19,9 +19,9 @@ TEST(DenseSet, initialization) {
Three = 3, Three = 3,
Four = 4, Four = 4,
Five = 5, Five = 5,
kEnd = 6 kMaxValue = Five,
}; };
using DS = DenseSet<T, T::kEnd>; using DS = DenseSet<T>;
DS s; DS s;
EXPECT_TRUE(s.empty()); EXPECT_TRUE(s.empty());
...@@ -45,9 +45,9 @@ TEST(DenseSet, iterators_begin_end) { ...@@ -45,9 +45,9 @@ TEST(DenseSet, iterators_begin_end) {
Three = 3, Three = 3,
Four = 4, Four = 4,
Five = 5, Five = 5,
kEnd = 6 kMaxValue = Five,
}; };
using DS = DenseSet<T, T::kEnd>; using DS = DenseSet<T, T::kMaxValue>;
DS s; DS s;
s.insert(T::Two); s.insert(T::Two);
...@@ -88,9 +88,9 @@ TEST(DenseSet, iterators_begin_end_reverse) { ...@@ -88,9 +88,9 @@ TEST(DenseSet, iterators_begin_end_reverse) {
Three = 3, Three = 3,
Four = 4, Four = 4,
Five = 5, Five = 5,
kEnd = 6 kMaxValue = Five,
}; };
using DS = DenseSet<T, T::kEnd>; using DS = DenseSet<T>;
DS s; DS s;
s.insert(T::Two); s.insert(T::Two);
...@@ -123,8 +123,15 @@ TEST(DenseSet, iterators_begin_end_reverse) { ...@@ -123,8 +123,15 @@ TEST(DenseSet, iterators_begin_end_reverse) {
} }
TEST(DenseSet, iterators_rbegin_rend) { TEST(DenseSet, iterators_rbegin_rend) {
enum class T { One = 1, Two = 2, Three = 3, Four = 4, Five = 5, kEnd = 6 }; enum class T {
using DS = DenseSet<T, T::kEnd>; One = 1,
Two = 2,
Three = 3,
Four = 4,
Five = 5,
kMaxValue = Five
};
using DS = DenseSet<T, T::kMaxValue>;
DS s; DS s;
s.insert(T::Two); s.insert(T::Two);
...@@ -160,8 +167,15 @@ TEST(DenseSet, iterators_rbegin_rend) { ...@@ -160,8 +167,15 @@ TEST(DenseSet, iterators_rbegin_rend) {
} }
TEST(DenseSet, lookup) { TEST(DenseSet, lookup) {
enum class T { One = 1, Two = 2, Three = 3, Four = 4, Five = 5, kEnd = 6 }; enum class T {
using DS = DenseSet<T, T::kEnd>; One = 1,
Two = 2,
Three = 3,
Four = 4,
Five = 5,
kMaxValue = Five
};
using DS = DenseSet<T, T::kMaxValue>;
DS s; DS s;
s.insert(T::Two); s.insert(T::Two);
...@@ -199,11 +213,37 @@ TEST(DenseSet, lookup) { ...@@ -199,11 +213,37 @@ TEST(DenseSet, lookup) {
EXPECT_NE(s.find(T::Three), s.lower_bound(T::Three)); EXPECT_NE(s.find(T::Three), s.lower_bound(T::Three));
EXPECT_EQ(s.find(T::Four), s.lower_bound(T::Four)); EXPECT_EQ(s.find(T::Four), s.lower_bound(T::Four));
EXPECT_EQ(s.find(T::Five), s.lower_bound(T::Five)); EXPECT_EQ(s.find(T::Five), s.lower_bound(T::Five));
DS t;
EXPECT_TRUE(t.empty());
EXPECT_TRUE(t.contains_none({}));
EXPECT_FALSE(t.contains_any({}));
EXPECT_TRUE(t.contains_all({}));
t.insert_all(s);
EXPECT_EQ(s, t);
EXPECT_FALSE(s.contains_none(t));
EXPECT_TRUE(s.contains_any(t));
EXPECT_TRUE(s.contains_all(t));
EXPECT_TRUE(s.contains_none({}));
EXPECT_FALSE(s.contains_any({}));
EXPECT_TRUE(s.contains_all({}));
t.erase(t.begin());
EXPECT_FALSE(s.contains_none(t));
EXPECT_TRUE(s.contains_any(t));
EXPECT_TRUE(s.contains_all(t));
EXPECT_FALSE(t.contains_none(s));
EXPECT_FALSE(t.contains_all(s));
EXPECT_TRUE(t.contains_any(s));
EXPECT_TRUE(s.contains_none({}));
EXPECT_FALSE(s.contains_any({}));
EXPECT_TRUE(s.contains_all({}));
} }
TEST(DenseSet, iterators_lower_upper_bound) { TEST(DenseSet, iterators_lower_upper_bound) {
enum class T { One = 1, Two = 2, Three = 3, Four = 4, Five = 5, kEnd = 6 }; enum class T { One = 1, Two = 2, Three = 3, Four = 4, Five = 5 };
using DS = DenseSet<T, T::kEnd>; using DS = DenseSet<T, T::Five>;
DS s; DS s;
s.insert(T::Two); s.insert(T::Two);
...@@ -276,8 +316,8 @@ TEST(DenseSet, max_size) { ...@@ -276,8 +316,8 @@ TEST(DenseSet, max_size) {
// const int Three = 3; // const int Three = 3;
const int Four = 4; const int Four = 4;
// const int Five = 5; // const int Five = 5;
const int kEnd = 6; const int kMaxValue = 5;
using DS = DenseSet<int, kEnd>; using DS = DenseSet<int, kMaxValue>;
DS s; DS s;
EXPECT_TRUE(s.empty()); EXPECT_TRUE(s.empty());
...@@ -301,8 +341,8 @@ TEST(DenseSet, modifiers) { ...@@ -301,8 +341,8 @@ TEST(DenseSet, modifiers) {
const size_t Three = 3; const size_t Three = 3;
const size_t Four = 4; const size_t Four = 4;
// const size_t Five = 5; // const size_t Five = 5;
const size_t kEnd = 6; const size_t kMaxValue = 5;
using DS = DenseSet<size_t, kEnd>; using DS = DenseSet<size_t, kMaxValue>;
DS s; DS s;
s.insert(Two); s.insert(Two);
...@@ -375,7 +415,35 @@ TEST(DenseSet, modifiers) { ...@@ -375,7 +415,35 @@ TEST(DenseSet, modifiers) {
s.clear(); s.clear();
EXPECT_EQ(s, DS()); EXPECT_EQ(s, DS());
EXPECT_EQ(s.size(), 0u); EXPECT_TRUE(s.empty());
s.insert(Three);
s.insert_all(t);
EXPECT_EQ(s.size(), 4u);
EXPECT_EQ(t.size(), 3u);
EXPECT_NE(s, t);
EXPECT_FALSE(s.contains_none(t));
EXPECT_TRUE(s.contains_any(t));
EXPECT_TRUE(s.contains_all(t));
EXPECT_TRUE(s.contains(Three));
EXPECT_FALSE(t.contains_none(s));
EXPECT_TRUE(t.contains_any(s));
EXPECT_FALSE(t.contains_all(s));
s.erase_all(t);
EXPECT_EQ(s.size(), 1u);
EXPECT_TRUE(s.contains(Three));
EXPECT_TRUE(s.contains_none(t));
EXPECT_FALSE(s.contains_any(t));
EXPECT_FALSE(s.contains_all(t));
s.insert_all(t);
s.erase(Three);
EXPECT_EQ(s.size(), 3u);
EXPECT_EQ(s, t);
s.erase_all(t);
EXPECT_TRUE(s.empty());
EXPECT_INSERTION(s, *t.begin(), true); EXPECT_INSERTION(s, *t.begin(), true);
EXPECT_TRUE(s.contains(One)); EXPECT_TRUE(s.contains(One));
...@@ -389,8 +457,8 @@ TEST(DenseSet, modifiers) { ...@@ -389,8 +457,8 @@ TEST(DenseSet, modifiers) {
} }
TEST(DenseSet, std_set) { TEST(DenseSet, std_set) {
constexpr size_t kEnd = 50; constexpr size_t kMaxValue = 50;
DenseSet<size_t, kEnd> dense_set; DenseSet<size_t, kMaxValue> dense_set;
std::set<size_t> std_set; std::set<size_t> std_set;
auto expect_equivalence = [&] { auto expect_equivalence = [&] {
...@@ -401,7 +469,7 @@ TEST(DenseSet, std_set) { ...@@ -401,7 +469,7 @@ TEST(DenseSet, std_set) {
auto random_insert = [&] { auto random_insert = [&] {
expect_equivalence(); expect_equivalence();
size_t value = base::RandUint64() % kEnd; size_t value = base::RandUint64() % kMaxValue;
auto p = dense_set.insert(value); auto p = dense_set.insert(value);
auto q = std_set.insert(value); auto q = std_set.insert(value);
EXPECT_EQ(p.second, q.second); EXPECT_EQ(p.second, q.second);
...@@ -412,13 +480,13 @@ TEST(DenseSet, std_set) { ...@@ -412,13 +480,13 @@ TEST(DenseSet, std_set) {
auto random_erase = [&] { auto random_erase = [&] {
expect_equivalence(); expect_equivalence();
size_t value = base::RandUint64() % kEnd; size_t value = base::RandUint64() % kMaxValue;
EXPECT_EQ(dense_set.erase(value), std_set.erase(value)); EXPECT_EQ(dense_set.erase(value), std_set.erase(value));
}; };
auto random_erase_iterator = [&] { auto random_erase_iterator = [&] {
expect_equivalence(); expect_equivalence();
size_t value = base::RandUint64() % kEnd; size_t value = base::RandUint64() % kMaxValue;
auto it = dense_set.find(value); auto it = dense_set.find(value);
auto jt = std_set.find(value); auto jt = std_set.find(value);
EXPECT_EQ(it == dense_set.end(), jt == std_set.end()); EXPECT_EQ(it == dense_set.end(), jt == std_set.end());
...@@ -434,8 +502,8 @@ TEST(DenseSet, std_set) { ...@@ -434,8 +502,8 @@ TEST(DenseSet, std_set) {
auto random_erase_range = [&] { auto random_erase_range = [&] {
expect_equivalence(); expect_equivalence();
size_t min_value = base::RandUint64() % kEnd; size_t min_value = base::RandUint64() % kMaxValue;
size_t max_value = base::RandUint64() % kEnd; size_t max_value = base::RandUint64() % kMaxValue;
min_value = std::min(min_value, max_value); min_value = std::min(min_value, max_value);
max_value = std::max(min_value, max_value); max_value = std::max(min_value, max_value);
dense_set.erase(dense_set.lower_bound(min_value), dense_set.erase(dense_set.lower_bound(min_value),
...@@ -444,11 +512,11 @@ TEST(DenseSet, std_set) { ...@@ -444,11 +512,11 @@ TEST(DenseSet, std_set) {
std_set.upper_bound(max_value)); std_set.upper_bound(max_value));
}; };
for (size_t i = 0; i < kEnd; ++i) { for (size_t i = 0; i < kMaxValue; ++i) {
random_insert(); random_insert();
} }
for (size_t i = 0; i < kEnd / 2; ++i) { for (size_t i = 0; i < kMaxValue / 2; ++i) {
random_erase(); random_erase();
} }
...@@ -457,11 +525,11 @@ TEST(DenseSet, std_set) { ...@@ -457,11 +525,11 @@ TEST(DenseSet, std_set) {
std_set.clear(); std_set.clear();
expect_equivalence(); expect_equivalence();
for (size_t i = 0; i < kEnd; ++i) { for (size_t i = 0; i < kMaxValue; ++i) {
random_insert(); random_insert();
} }
for (size_t i = 0; i < kEnd; ++i) { for (size_t i = 0; i < kMaxValue; ++i) {
random_erase_iterator(); random_erase_iterator();
} }
...@@ -470,11 +538,11 @@ TEST(DenseSet, std_set) { ...@@ -470,11 +538,11 @@ TEST(DenseSet, std_set) {
std_set.clear(); std_set.clear();
expect_equivalence(); expect_equivalence();
for (size_t i = 0; i < kEnd; ++i) { for (size_t i = 0; i < kMaxValue; ++i) {
random_insert(); random_insert();
} }
for (size_t i = 0; i < kEnd; ++i) { for (size_t i = 0; i < kMaxValue; ++i) {
random_erase_range(); random_erase_range();
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment