utils: Add more functionality to EnumSet
Add equality operators for EnumSet <-> Enum. Add unidirectional iterator. Add ostream support. Change-Id: I8ea9e905bf17e618c6b12004200d37f65ccfb68c Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/68402 Reviewed-by: Antonio Maiorano <amaiorano@google.com> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
85170d76bc
commit
307eff0da4
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <ostream>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
|
@ -62,16 +63,79 @@ struct EnumSet {
|
||||||
/// Equality operator
|
/// Equality operator
|
||||||
/// @param rhs the other EnumSet to compare this to
|
/// @param rhs the other EnumSet to compare this to
|
||||||
/// @return true if this EnumSet is equal to rhs
|
/// @return true if this EnumSet is equal to rhs
|
||||||
inline bool operator==(const EnumSet& rhs) const { return set == rhs.set; }
|
inline bool operator==(EnumSet rhs) const { return set == rhs.set; }
|
||||||
|
|
||||||
/// Inequality operator
|
/// Inequality operator
|
||||||
/// @param rhs the other EnumSet to compare this to
|
/// @param rhs the other EnumSet to compare this to
|
||||||
/// @return true if this EnumSet is not equal to rhs
|
/// @return true if this EnumSet is not equal to rhs
|
||||||
inline bool operator!=(const EnumSet& rhs) const { return set != rhs.set; }
|
inline bool operator!=(EnumSet rhs) const { return set != rhs.set; }
|
||||||
|
|
||||||
|
/// Equality operator
|
||||||
|
/// @param rhs the enum to compare this to
|
||||||
|
/// @return true if this EnumSet only contains `rhs`
|
||||||
|
inline bool operator==(Enum rhs) const { return set == Bit(rhs); }
|
||||||
|
|
||||||
|
/// Inequality operator
|
||||||
|
/// @param rhs the enum to compare this to
|
||||||
|
/// @return false if this EnumSet only contains `rhs`
|
||||||
|
inline bool operator!=(Enum rhs) const { return set != Bit(rhs); }
|
||||||
|
|
||||||
/// @return the underlying value for the EnumSet
|
/// @return the underlying value for the EnumSet
|
||||||
inline uint64_t Value() const { return set; }
|
inline uint64_t Value() const { return set; }
|
||||||
|
|
||||||
|
/// Iterator provides read-only, unidirectional iterator over the enums of an
|
||||||
|
/// EnumSet.
|
||||||
|
class Iterator {
|
||||||
|
static constexpr int8_t kEnd = 63;
|
||||||
|
|
||||||
|
Iterator(uint64_t s, int8_t b) : set(s), pos(b) {}
|
||||||
|
|
||||||
|
/// Make the constructor accessible to the EnumSet.
|
||||||
|
friend struct EnumSet;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// @return the Enum value at this point in the iterator
|
||||||
|
Enum operator*() const { return static_cast<Enum>(pos); }
|
||||||
|
|
||||||
|
/// Increments the iterator
|
||||||
|
/// @returns this iterator
|
||||||
|
Iterator& operator++() {
|
||||||
|
while (pos < kEnd) {
|
||||||
|
pos++;
|
||||||
|
if (set & (static_cast<uint64_t>(1) << static_cast<uint64_t>(pos))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Equality operator
|
||||||
|
/// @param rhs the Iterator to compare this to
|
||||||
|
/// @return true if the two iterators are equal
|
||||||
|
bool operator==(const Iterator& rhs) const {
|
||||||
|
return set == rhs.set && pos == rhs.pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inequality operator
|
||||||
|
/// @param rhs the Iterator to compare this to
|
||||||
|
/// @return true if the two iterators are different
|
||||||
|
bool operator!=(const Iterator& rhs) const { return !(*this == rhs); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const uint64_t set;
|
||||||
|
int8_t pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns an read-only iterator to the beginning of the set
|
||||||
|
Iterator begin() {
|
||||||
|
auto it = Iterator{set, -1};
|
||||||
|
++it; // Move to first set bit
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @returns an iterator to the beginning of the set
|
||||||
|
Iterator end() { return Iterator{set, Iterator::kEnd}; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr uint64_t Bit(Enum value) {
|
static constexpr uint64_t Bit(Enum value) {
|
||||||
return static_cast<uint64_t>(1) << static_cast<uint64_t>(value);
|
return static_cast<uint64_t>(1) << static_cast<uint64_t>(value);
|
||||||
|
@ -87,6 +151,24 @@ struct EnumSet {
|
||||||
uint64_t set = 0;
|
uint64_t set = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Writes the EnumSet to the std::ostream.
|
||||||
|
/// @param out the std::ostream to write to
|
||||||
|
/// @param set the EnumSet to write
|
||||||
|
/// @returns out so calls can be chained
|
||||||
|
template <typename ENUM>
|
||||||
|
inline std::ostream& operator<<(std::ostream& out, EnumSet<ENUM> set) {
|
||||||
|
out << "{";
|
||||||
|
bool first = true;
|
||||||
|
for (auto e : set) {
|
||||||
|
if (!first) {
|
||||||
|
out << ", ";
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
out << e;
|
||||||
|
}
|
||||||
|
return out << "}";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace utils
|
} // namespace utils
|
||||||
} // namespace tint
|
} // namespace tint
|
||||||
|
|
||||||
|
|
|
@ -14,13 +14,30 @@
|
||||||
|
|
||||||
#include "src/utils/enum_set.h"
|
#include "src/utils/enum_set.h"
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
namespace utils {
|
namespace utils {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
enum class E { A, B, C };
|
using ::testing::ElementsAre;
|
||||||
|
|
||||||
|
enum class E { A = 0, B = 3, C = 7 };
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, E e) {
|
||||||
|
switch (e) {
|
||||||
|
case E::A:
|
||||||
|
return out << "A";
|
||||||
|
case E::B:
|
||||||
|
return out << "B";
|
||||||
|
case E::C:
|
||||||
|
return out << "C";
|
||||||
|
}
|
||||||
|
return out << "E(" << static_cast<uint32_t>(e) << ")";
|
||||||
|
}
|
||||||
|
|
||||||
TEST(EnumSetTest, ConstructEmpty) {
|
TEST(EnumSetTest, ConstructEmpty) {
|
||||||
EnumSet<E> set;
|
EnumSet<E> set;
|
||||||
|
@ -59,16 +76,34 @@ TEST(EnumSetTest, Remove) {
|
||||||
EXPECT_FALSE(set.Contains(E::C));
|
EXPECT_FALSE(set.Contains(E::C));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(EnumSetTest, Equality) {
|
TEST(EnumSetTest, EqualitySet) {
|
||||||
EXPECT_TRUE(EnumSet<E>(E::A, E::B) == EnumSet<E>(E::A, E::B));
|
EXPECT_TRUE(EnumSet<E>(E::A, E::B) == EnumSet<E>(E::A, E::B));
|
||||||
EXPECT_FALSE(EnumSet<E>(E::A, E::B) == EnumSet<E>(E::A, E::C));
|
EXPECT_FALSE(EnumSet<E>(E::A, E::B) == EnumSet<E>(E::A, E::C));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(EnumSetTest, Inequality) {
|
TEST(EnumSetTest, InequalitySet) {
|
||||||
EXPECT_FALSE(EnumSet<E>(E::A, E::B) != EnumSet<E>(E::A, E::B));
|
EXPECT_FALSE(EnumSet<E>(E::A, E::B) != EnumSet<E>(E::A, E::B));
|
||||||
EXPECT_TRUE(EnumSet<E>(E::A, E::B) != EnumSet<E>(E::A, E::C));
|
EXPECT_TRUE(EnumSet<E>(E::A, E::B) != EnumSet<E>(E::A, E::C));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(EnumSetTest, EqualityEnum) {
|
||||||
|
EXPECT_TRUE(EnumSet<E>(E::A) == E::A);
|
||||||
|
EXPECT_FALSE(EnumSet<E>(E::B) == E::A);
|
||||||
|
EXPECT_FALSE(EnumSet<E>(E::B) == E::C);
|
||||||
|
EXPECT_FALSE(EnumSet<E>(E::A, E::B) == E::A);
|
||||||
|
EXPECT_FALSE(EnumSet<E>(E::A, E::B) == E::B);
|
||||||
|
EXPECT_FALSE(EnumSet<E>(E::A, E::B) == E::C);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EnumSetTest, InequalityEnum) {
|
||||||
|
EXPECT_FALSE(EnumSet<E>(E::A) != E::A);
|
||||||
|
EXPECT_TRUE(EnumSet<E>(E::B) != E::A);
|
||||||
|
EXPECT_TRUE(EnumSet<E>(E::B) != E::C);
|
||||||
|
EXPECT_TRUE(EnumSet<E>(E::A, E::B) != E::A);
|
||||||
|
EXPECT_TRUE(EnumSet<E>(E::A, E::B) != E::B);
|
||||||
|
EXPECT_TRUE(EnumSet<E>(E::A, E::B) != E::C);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(EnumSetTest, Hash) {
|
TEST(EnumSetTest, Hash) {
|
||||||
auto hash = [&](EnumSet<E> s) { return std::hash<EnumSet<E>>()(s); };
|
auto hash = [&](EnumSet<E> s) { return std::hash<EnumSet<E>>()(s); };
|
||||||
EXPECT_EQ(hash(EnumSet<E>(E::A, E::B)), hash(EnumSet<E>(E::A, E::B)));
|
EXPECT_EQ(hash(EnumSet<E>(E::A, E::B)), hash(EnumSet<E>(E::A, E::B)));
|
||||||
|
@ -78,9 +113,44 @@ TEST(EnumSetTest, Hash) {
|
||||||
TEST(EnumSetTest, Value) {
|
TEST(EnumSetTest, Value) {
|
||||||
EXPECT_EQ(EnumSet<E>().Value(), 0u);
|
EXPECT_EQ(EnumSet<E>().Value(), 0u);
|
||||||
EXPECT_EQ(EnumSet<E>(E::A).Value(), 1u);
|
EXPECT_EQ(EnumSet<E>(E::A).Value(), 1u);
|
||||||
EXPECT_EQ(EnumSet<E>(E::B).Value(), 2u);
|
EXPECT_EQ(EnumSet<E>(E::B).Value(), 8u);
|
||||||
EXPECT_EQ(EnumSet<E>(E::C).Value(), 4u);
|
EXPECT_EQ(EnumSet<E>(E::C).Value(), 128u);
|
||||||
EXPECT_EQ(EnumSet<E>(E::A, E::C).Value(), 5u);
|
EXPECT_EQ(EnumSet<E>(E::A, E::C).Value(), 129u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EnumSetTest, Iterator) {
|
||||||
|
auto set = EnumSet<E>(E::C, E::A);
|
||||||
|
|
||||||
|
auto it = set.begin();
|
||||||
|
EXPECT_EQ(*it, E::A);
|
||||||
|
EXPECT_NE(it, set.end());
|
||||||
|
++it;
|
||||||
|
EXPECT_EQ(*it, E::C);
|
||||||
|
EXPECT_NE(it, set.end());
|
||||||
|
++it;
|
||||||
|
EXPECT_EQ(it, set.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EnumSetTest, IteratorEmpty) {
|
||||||
|
auto set = EnumSet<E>();
|
||||||
|
EXPECT_EQ(set.begin(), set.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EnumSetTest, Loop) {
|
||||||
|
auto set = EnumSet<E>(E::C, E::A);
|
||||||
|
|
||||||
|
std::vector<E> seen;
|
||||||
|
for (auto e : set) {
|
||||||
|
seen.emplace_back(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_THAT(seen, ElementsAre(E::A, E::C));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EnumSetTest, Ostream) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << EnumSet<E>(E::A, E::C);
|
||||||
|
EXPECT_EQ(ss.str(), "{A, C}");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
Loading…
Reference in New Issue