فهرست منبع

AK: Add an ArbitrarySizedEnum template

This is an enum-like type that works with arbitrary sized storage > u64,
which is the limit for a regular enum class - which limits it to 64
members when needing bit field behavior.

Co-authored-by: Ali Mohammad Pur <mpfard@serenityos.org>
Linus Groh 3 سال پیش
والد
کامیت
22308e52cf
3فایلهای تغییر یافته به همراه248 افزوده شده و 0 حذف شده
  1. 127 0
      AK/ArbitrarySizedEnum.h
  2. 1 0
      Tests/AK/CMakeLists.txt
  3. 120 0
      Tests/AK/TestArbitrarySizedEnum.cpp

+ 127 - 0
AK/ArbitrarySizedEnum.h

@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
+ * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/DistinctNumeric.h>
+
+namespace AK {
+
+template<typename T>
+struct ArbitrarySizedEnum : public T {
+    using T::T;
+
+    consteval ArbitrarySizedEnum(T v)
+        : T(v)
+    {
+    }
+
+    constexpr ArbitrarySizedEnum(T v, Badge<ArbitrarySizedEnum<T>>)
+        : T(v)
+    {
+    }
+
+    template<Integral X>
+    [[nodiscard]] consteval ArbitrarySizedEnum<T> operator<<(X other) const
+    {
+        return T(this->value() << other);
+    }
+
+    template<Integral X>
+    constexpr ArbitrarySizedEnum<T>& operator<<=(X other)
+    {
+        this->value() <<= other;
+        return *this;
+    }
+
+    template<Integral X>
+    [[nodiscard]] consteval ArbitrarySizedEnum<T> operator>>(X other) const
+    {
+        return T(this->value() >> other);
+    }
+
+    template<Integral X>
+    constexpr ArbitrarySizedEnum<T>& operator>>=(X other)
+    {
+        this->value() >>= other;
+        return *this;
+    }
+
+    template<Integral X>
+    [[nodiscard]] constexpr bool operator==(X other) const
+    {
+        return this->value() == T(other);
+    }
+
+    [[nodiscard]] constexpr bool operator==(ArbitrarySizedEnum<T> const& other) const
+    {
+        return this->value() == other.value();
+    }
+
+    // NOTE: The following operators mirror AK_ENUM_BITWISE_OPERATORS.
+
+    [[nodiscard]] constexpr ArbitrarySizedEnum<T> operator|(ArbitrarySizedEnum<T> const& other) const
+    {
+        return { T(this->value() | other.value()), {} };
+    }
+
+    [[nodiscard]] constexpr ArbitrarySizedEnum<T> operator&(ArbitrarySizedEnum<T> const& other) const
+    {
+        return { T(this->value() & other.value()), {} };
+    }
+
+    [[nodiscard]] constexpr ArbitrarySizedEnum<T> operator^(ArbitrarySizedEnum<T> const& other) const
+    {
+        return { T(this->value() ^ other.value()), {} };
+    }
+
+    [[nodiscard]] constexpr ArbitrarySizedEnum<T> operator~() const
+    {
+        return { T(~this->value()), {} };
+    }
+
+    constexpr ArbitrarySizedEnum<T>& operator|=(ArbitrarySizedEnum<T> const& other)
+    {
+        this->value() |= other.value();
+        return *this;
+    }
+
+    constexpr ArbitrarySizedEnum<T>& operator&=(ArbitrarySizedEnum<T> const& other)
+    {
+        this->value() &= other.value();
+        return *this;
+    }
+
+    constexpr ArbitrarySizedEnum<T>& operator^=(ArbitrarySizedEnum<T> const& other)
+    {
+        this->value() ^= other.value();
+        return *this;
+    }
+
+    [[nodiscard]] constexpr bool has_flag(ArbitrarySizedEnum<T> const& mask) const
+    {
+        return (*this & mask) == mask;
+    }
+
+    [[nodiscard]] constexpr bool has_any_flag(ArbitrarySizedEnum<T> const& mask) const
+    {
+        return (*this & mask) != 0u;
+    }
+};
+
+#define AK_MAKE_ARBITRARY_SIZED_ENUM(EnumName, T, ...)                               \
+    namespace EnumName {                                                             \
+    using EnumName = ArbitrarySizedEnum<DistinctNumeric<T, struct __##EnumName##Tag, \
+        false, true, false, false, false, false>>;                                   \
+    using Type = EnumName;                                                           \
+    using UnderlyingType = T;                                                        \
+    inline constexpr static EnumName __VA_ARGS__;                                    \
+    }
+
+}
+
+using AK::ArbitrarySizedEnum;

+ 1 - 0
Tests/AK/CMakeLists.txt

@@ -2,6 +2,7 @@ set(AK_TEST_SOURCES
     TestFixedPoint.cpp
     TestFixedPoint.cpp
     TestAllOf.cpp
     TestAllOf.cpp
     TestAnyOf.cpp
     TestAnyOf.cpp
+    TestArbitrarySizedEnum.cpp
     TestArray.cpp
     TestArray.cpp
     TestAtomic.cpp
     TestAtomic.cpp
     TestBadge.cpp
     TestBadge.cpp

+ 120 - 0
Tests/AK/TestArbitrarySizedEnum.cpp

@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibTest/TestCase.h>
+
+#include <AK/ArbitrarySizedEnum.h>
+#include <AK/UFixedBigInt.h>
+
+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));
+    }
+}