diff --git a/AK/Utf16FlyString.h b/AK/Utf16FlyString.h index b7bbe63665a..eeb43320baf 100644 --- a/AK/Utf16FlyString.h +++ b/AK/Utf16FlyString.h @@ -136,6 +136,13 @@ public: [[nodiscard]] ALWAYS_INLINE size_t code_unit_offset_of(size_t code_point_offset) const { return m_data.code_unit_offset_of(code_point_offset); } [[nodiscard]] ALWAYS_INLINE size_t code_point_offset_of(size_t code_unit_offset) const { return m_data.code_point_offset_of(code_unit_offset); } + constexpr Utf16FlyString(Badge>, nullptr_t) + : m_data(Badge {}, nullptr) + { + } + + [[nodiscard]] constexpr bool is_invalid(Badge>) const { return m_data.raw({}) == 0; } + // This is primarily interesting to unit tests. [[nodiscard]] static size_t number_of_utf16_fly_strings(); @@ -151,6 +158,96 @@ private: Detail::Utf16StringBase m_data; }; +template<> +class Optional : public OptionalBase { + template + friend class Optional; + +public: + using ValueType = Utf16FlyString; + + constexpr Optional() = default; + + template V> + constexpr Optional(V) { } + + constexpr Optional(Optional const& other) + : m_value(other.m_value) + { + } + + constexpr Optional(Optional&& other) + : m_value(move(other.m_value)) + { + } + + template + requires(!IsSame>) + explicit(!IsConvertible) constexpr Optional(U&& value) + requires(!IsSame, Optional> && IsConstructible) + : m_value(forward(value)) + { + } + + template V> + constexpr Optional& operator=(V) + { + clear(); + return *this; + } + + constexpr Optional& operator=(Optional const& other) + { + if (this != &other) + m_value = other.m_value; + return *this; + } + + constexpr Optional& operator=(Optional&& other) + { + if (this != &other) + m_value = other.m_value; + return *this; + } + + constexpr void clear() + { + m_value = empty_value; + } + + [[nodiscard]] constexpr bool has_value() const + { + return !m_value.is_invalid({}); + } + + [[nodiscard]] constexpr Utf16FlyString& value() & + { + VERIFY(has_value()); + return m_value; + } + + [[nodiscard]] constexpr Utf16FlyString const& value() const& + { + VERIFY(has_value()); + return m_value; + } + + [[nodiscard]] constexpr Utf16FlyString value() && + { + return release_value(); + } + + [[nodiscard]] constexpr Utf16FlyString release_value() + { + VERIFY(has_value()); + return exchange(m_value, empty_value); + } + +private: + static constexpr Utf16FlyString empty_value { {}, nullptr }; + Utf16FlyString m_value { empty_value }; +}; + template<> struct Traits : public DefaultTraits { static unsigned hash(Utf16FlyString const& string) { return string.hash(); } diff --git a/AK/Utf16String.h b/AK/Utf16String.h index 12c2ffdf218..5d3b52f79a9 100644 --- a/AK/Utf16String.h +++ b/AK/Utf16String.h @@ -204,6 +204,13 @@ public: static Utf16String from_string_builder(Badge, StringBuilder& builder); static ErrorOr from_ipc_stream(Stream&, size_t length_in_code_units, bool is_ascii); + constexpr Utf16String(Badge>, nullptr_t) + : Detail::Utf16StringBase(Badge {}, nullptr) + { + } + + [[nodiscard]] constexpr bool is_invalid(Badge>) const { return raw() == 0; } + private: ALWAYS_INLINE explicit Utf16String(NonnullRefPtr value) : Utf16StringBase(move(value)) @@ -211,6 +218,96 @@ private: } }; +template<> +class [[nodiscard]] Optional : public OptionalBase { + template + friend class Optional; + +public: + using ValueType = Utf16String; + + constexpr Optional() = default; + + template V> + constexpr Optional(V) { } + + constexpr Optional(Optional const& other) + : m_value(other.m_value) + { + } + + constexpr Optional(Optional&& other) + : m_value(move(other.m_value)) + { + } + + template + requires(!IsSame>) + explicit(!IsConvertible) constexpr Optional(U&& value) + requires(!IsSame, Optional> && IsConstructible) + : m_value(forward(value)) + { + } + + template V> + constexpr Optional& operator=(V) + { + clear(); + return *this; + } + + constexpr Optional& operator=(Optional const& other) + { + if (this != &other) + m_value = other.m_value; + return *this; + } + + constexpr Optional& operator=(Optional&& other) + { + if (this != &other) + m_value = move(other.m_value); + return *this; + } + + constexpr void clear() + { + m_value = empty_value; + } + + [[nodiscard]] constexpr bool has_value() const + { + return !m_value.is_invalid({}); + } + + [[nodiscard]] constexpr Utf16String& value() & + { + VERIFY(has_value()); + return m_value; + } + + [[nodiscard]] constexpr Utf16String const& value() const& + { + VERIFY(has_value()); + return m_value; + } + + [[nodiscard]] constexpr Utf16String value() && + { + return release_value(); + } + + [[nodiscard]] constexpr Utf16String release_value() + { + VERIFY(has_value()); + return exchange(m_value, empty_value); + } + +private: + static constexpr Utf16String empty_value { Badge> {}, nullptr }; + Utf16String m_value { empty_value }; +}; + template<> struct Formatter : Formatter { ErrorOr format(FormatBuilder&, Utf16String const&); diff --git a/AK/Utf16StringBase.h b/AK/Utf16StringBase.h index b2e5a37f108..5957bdb919f 100644 --- a/AK/Utf16StringBase.h +++ b/AK/Utf16StringBase.h @@ -330,9 +330,17 @@ public: (*this_data)->ref(); } - [[nodiscard]] constexpr FlatPtr raw(Badge) const { return bit_cast(m_value); } + template T> + constexpr Utf16StringBase(Badge, nullptr_t) + : m_value { .data = nullptr } + { + } + + [[nodiscard]] constexpr FlatPtr raw(Badge) const { return raw(); } protected: + [[nodiscard]] constexpr FlatPtr raw() const { return bit_cast(m_value); } + ALWAYS_INLINE void destroy_string() const { if (has_long_storage()) { diff --git a/Tests/AK/TestUtf16FlyString.cpp b/Tests/AK/TestUtf16FlyString.cpp index 770619b6ed1..1f7d6ccb8d8 100644 --- a/Tests/AK/TestUtf16FlyString.cpp +++ b/Tests/AK/TestUtf16FlyString.cpp @@ -146,3 +146,27 @@ TEST_CASE(is_one_of) EXPECT(bar.is_one_of("bar"sv, "foo"sv)); EXPECT(bar.is_one_of("bar"sv)); } + +TEST_CASE(optional) +{ + static_assert(AssertSize, sizeof(Utf16FlyString)>()); + + Optional string; + EXPECT(!string.has_value()); + + string = "ascii"_utf16_fly_string; + EXPECT(string.has_value()); + EXPECT_EQ(string.value(), "ascii"sv); + + auto released = string.release_value(); + EXPECT(!string.has_value()); + EXPECT_EQ(released, "ascii"sv); + + string = u"well 😀 hello"_utf16_fly_string; + EXPECT(string.has_value()); + EXPECT_EQ(string.value(), u"well 😀 hello"sv); + + released = string.release_value(); + EXPECT(!string.has_value()); + EXPECT_EQ(released, u"well 😀 hello"sv); +} diff --git a/Tests/AK/TestUtf16String.cpp b/Tests/AK/TestUtf16String.cpp index 3d21098c859..66e1cdcd59d 100644 --- a/Tests/AK/TestUtf16String.cpp +++ b/Tests/AK/TestUtf16String.cpp @@ -1271,3 +1271,27 @@ TEST_CASE(code_point_at) test(u"😀"sv, 1); test(u"hello 😀 there!"sv, 14); } + +TEST_CASE(optional) +{ + static_assert(AssertSize, sizeof(Utf16String)>()); + + Optional string; + EXPECT(!string.has_value()); + + string = "ascii"_utf16; + EXPECT(string.has_value()); + EXPECT_EQ(string.value(), "ascii"sv); + + auto released = string.release_value(); + EXPECT(!string.has_value()); + EXPECT_EQ(released, "ascii"sv); + + string = u"well 😀 hello"_utf16; + EXPECT(string.has_value()); + EXPECT_EQ(string.value(), u"well 😀 hello"sv); + + released = string.release_value(); + EXPECT(!string.has_value()); + EXPECT_EQ(released, u"well 😀 hello"sv); +}