diff --git a/AK/ArbitrarySizedEnum.h b/AK/ArbitrarySizedEnum.h new file mode 100644 index 00000000000..3e740866124 --- /dev/null +++ b/AK/ArbitrarySizedEnum.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2022, Ali Mohammad Pur + * Copyright (c) 2022, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace AK { + +template +struct ArbitrarySizedEnum : public T { + using T::T; + + consteval ArbitrarySizedEnum(T v) + : T(v) + { + } + + constexpr ArbitrarySizedEnum(T v, Badge>) + : T(v) + { + } + + template + [[nodiscard]] consteval ArbitrarySizedEnum operator<<(X other) const + { + return T(this->value() << other); + } + + template + constexpr ArbitrarySizedEnum& operator<<=(X other) + { + this->value() <<= other; + return *this; + } + + template + [[nodiscard]] consteval ArbitrarySizedEnum operator>>(X other) const + { + return T(this->value() >> other); + } + + template + constexpr ArbitrarySizedEnum& operator>>=(X other) + { + this->value() >>= other; + return *this; + } + + template + [[nodiscard]] constexpr bool operator==(X other) const + { + return this->value() == T(other); + } + + [[nodiscard]] constexpr bool operator==(ArbitrarySizedEnum const& other) const + { + return this->value() == other.value(); + } + + // NOTE: The following operators mirror AK_ENUM_BITWISE_OPERATORS. + + [[nodiscard]] constexpr ArbitrarySizedEnum operator|(ArbitrarySizedEnum const& other) const + { + return { T(this->value() | other.value()), {} }; + } + + [[nodiscard]] constexpr ArbitrarySizedEnum operator&(ArbitrarySizedEnum const& other) const + { + return { T(this->value() & other.value()), {} }; + } + + [[nodiscard]] constexpr ArbitrarySizedEnum operator^(ArbitrarySizedEnum const& other) const + { + return { T(this->value() ^ other.value()), {} }; + } + + [[nodiscard]] constexpr ArbitrarySizedEnum operator~() const + { + return { T(~this->value()), {} }; + } + + constexpr ArbitrarySizedEnum& operator|=(ArbitrarySizedEnum const& other) + { + this->value() |= other.value(); + return *this; + } + + constexpr ArbitrarySizedEnum& operator&=(ArbitrarySizedEnum const& other) + { + this->value() &= other.value(); + return *this; + } + + constexpr ArbitrarySizedEnum& operator^=(ArbitrarySizedEnum const& other) + { + this->value() ^= other.value(); + return *this; + } + + [[nodiscard]] constexpr bool has_flag(ArbitrarySizedEnum const& mask) const + { + return (*this & mask) == mask; + } + + [[nodiscard]] constexpr bool has_any_flag(ArbitrarySizedEnum const& mask) const + { + return (*this & mask) != 0u; + } +}; + +#define AK_MAKE_ARBITRARY_SIZED_ENUM(EnumName, T, ...) \ + namespace EnumName { \ + using EnumName = ArbitrarySizedEnum>; \ + using Type = EnumName; \ + using UnderlyingType = T; \ + inline constexpr static EnumName __VA_ARGS__; \ + } + +} + +using AK::ArbitrarySizedEnum; diff --git a/Tests/AK/CMakeLists.txt b/Tests/AK/CMakeLists.txt index a7314da56db..fac063f6fd0 100644 --- a/Tests/AK/CMakeLists.txt +++ b/Tests/AK/CMakeLists.txt @@ -2,6 +2,7 @@ set(AK_TEST_SOURCES TestFixedPoint.cpp TestAllOf.cpp TestAnyOf.cpp + TestArbitrarySizedEnum.cpp TestArray.cpp TestAtomic.cpp TestBadge.cpp diff --git a/Tests/AK/TestArbitrarySizedEnum.cpp b/Tests/AK/TestArbitrarySizedEnum.cpp new file mode 100644 index 00000000000..1abda962e87 --- /dev/null +++ b/Tests/AK/TestArbitrarySizedEnum.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2022, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#include +#include + +AK_MAKE_ARBITRARY_SIZED_ENUM(TestEnum, u8, + Foo = TestEnum(1) << 0, + Bar = TestEnum(1) << 1, + Baz = TestEnum(1) << 2); + +AK_MAKE_ARBITRARY_SIZED_ENUM(BigIntTestEnum, u128, + Foo = BigIntTestEnum(1u) << 127u); + +TEST_CASE(constructor) +{ + { + constexpr TestEnum::Type test; + static_assert(test.value() == 0); + } + { + constexpr TestEnum::Type test { TestEnum::Foo | TestEnum::Baz }; + static_assert(test.value() == 0b101); + } + { + constexpr BigIntTestEnum::Type test { BigIntTestEnum::Foo }; + static_assert(test.value() == u128(1u) << 127u); + } +} + +TEST_CASE(bitwise_or) +{ + { + TestEnum::Type test; + EXPECT_EQ(test.value(), 0); + test |= TestEnum::Foo; + EXPECT_EQ(test.value(), 0b001); + test |= TestEnum::Bar; + EXPECT_EQ(test.value(), 0b011); + test |= TestEnum::Baz; + EXPECT_EQ(test.value(), 0b111); + } + { + BigIntTestEnum::Type test; + EXPECT_EQ(test.value(), 0u); + test |= BigIntTestEnum::Foo; + EXPECT_EQ(test.value(), u128(1u) << 127u); + } +} + +TEST_CASE(bitwise_and) +{ + { + TestEnum::Type test { 0b111 }; + EXPECT_EQ(test.value(), 0b111); + test &= TestEnum::Foo; + EXPECT_EQ(test.value(), 0b001); + } + { + BigIntTestEnum::Type test { u128(1u) << 127u | u128(1u) << 126u }; + EXPECT_EQ(test.value(), u128(1u) << 127u | u128(1u) << 126u); + test &= BigIntTestEnum::Foo; + EXPECT_EQ(test.value(), u128(1u) << 127u); + } +} + +TEST_CASE(bitwise_xor) +{ + { + TestEnum::Type test { 0b111 }; + EXPECT_EQ(test.value(), 0b111); + test ^= TestEnum::Foo; + EXPECT_EQ(test.value(), 0b110); + } + { + BigIntTestEnum::Type test { u128(1u) << 127u | 1u }; + EXPECT_EQ(test.value(), u128(1u) << 127u | 1u); + test ^= BigIntTestEnum::Foo; + EXPECT_EQ(test.value(), 1u); + } +} + +TEST_CASE(has_flag) +{ + { + TestEnum::Type test; + test |= TestEnum::Foo; + EXPECT(test.has_flag(TestEnum::Foo)); + EXPECT(!test.has_flag(TestEnum::Bar)); + EXPECT(!test.has_flag(TestEnum::Baz)); + EXPECT(!test.has_flag(TestEnum::Foo | TestEnum::Bar | TestEnum::Baz)); + } + { + BigIntTestEnum::Type test; + test |= BigIntTestEnum::Foo; + EXPECT(test.has_flag(BigIntTestEnum::Foo)); + } +} + +TEST_CASE(has_any_flag) +{ + { + TestEnum::Type test; + test |= TestEnum::Foo; + EXPECT(test.has_any_flag(TestEnum::Foo)); + EXPECT(!test.has_any_flag(TestEnum::Bar)); + EXPECT(!test.has_any_flag(TestEnum::Baz)); + EXPECT(test.has_any_flag(TestEnum::Foo | TestEnum::Bar | TestEnum::Baz)); + } + { + BigIntTestEnum::Type test; + test |= BigIntTestEnum::Foo; + EXPECT(test.has_any_flag(BigIntTestEnum::Foo)); + } +}