/* * Copyright (c) 2023, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include namespace AK { class FlyString { AK_MAKE_DEFAULT_MOVABLE(FlyString); AK_MAKE_DEFAULT_COPYABLE(FlyString); public: FlyString() = default; static ErrorOr from_utf8(StringView); static FlyString from_utf8_without_validation(ReadonlyBytes); template requires(IsOneOf, ByteString, FlyString, String>) static ErrorOr from_utf8(T&&) = delete; FlyString(String const&); FlyString& operator=(String const&); [[nodiscard]] bool is_empty() const { return m_data.byte_count() == 0; } [[nodiscard]] unsigned hash() const { return m_data.hash(); } [[nodiscard]] u32 ascii_case_insensitive_hash() const; explicit operator String() const; String to_string() const; [[nodiscard]] Utf8View code_points() const; [[nodiscard]] ReadonlyBytes bytes() const LIFETIME_BOUND { return m_data.bytes(); } [[nodiscard]] StringView bytes_as_string_view() const LIFETIME_BOUND { return m_data.bytes(); } [[nodiscard]] ALWAYS_INLINE bool operator==(FlyString const& other) const { return m_data.raw(Badge {}) == other.m_data.raw(Badge {}); } [[nodiscard]] bool operator==(String const& other) const { return m_data == other; } [[nodiscard]] bool operator==(StringView) const; [[nodiscard]] bool operator==(char const*) const; [[nodiscard]] int operator<=>(FlyString const& other) const; [[nodiscard]] Detail::StringBase data(Badge) const; // This is primarily interesting to unit tests. [[nodiscard]] static size_t number_of_fly_strings(); template requires(IsSame, StringView>) static ErrorOr from_deprecated_fly_string(T&&) = delete; // Compare this FlyString against another string with ASCII caseless matching. [[nodiscard]] bool equals_ignoring_ascii_case(FlyString const&) const; [[nodiscard]] bool equals_ignoring_ascii_case(StringView) const; [[nodiscard]] FlyString to_ascii_lowercase() const; [[nodiscard]] FlyString to_ascii_uppercase() const; [[nodiscard]] bool is_ascii() const { return bytes_as_string_view().is_ascii(); } [[nodiscard]] bool starts_with_bytes(StringView, CaseSensitivity = CaseSensitivity::CaseSensitive) const; [[nodiscard]] bool ends_with_bytes(StringView, CaseSensitivity = CaseSensitivity::CaseSensitive) const; template [[nodiscard]] ALWAYS_INLINE constexpr bool is_one_of(Ts&&... strings) const { return (... || this->operator==(forward(strings))); } template [[nodiscard]] ALWAYS_INLINE constexpr bool is_one_of_ignoring_ascii_case(Ts&&... strings) const { return (... || this->equals_ignoring_ascii_case(forward(strings))); } private: friend struct SentinelOptionalTraits; explicit constexpr FlyString(nullptr_t) : m_data(nullptr) { } explicit constexpr FlyString(Detail::StringBase data) : m_data(move(data)) { } Detail::StringBase m_data; constexpr bool is_invalid() const { return m_data.raw(Badge {}) == 0; } }; template<> struct SentinelOptionalTraits { static constexpr FlyString sentinel_value() { return FlyString(nullptr); } static constexpr bool is_sentinel(FlyString const& value) { return value.is_invalid(); } }; template<> class Optional : public SentinelOptional { public: using SentinelOptional::SentinelOptional; }; template<> struct Traits : public DefaultTraits { static unsigned hash(FlyString const&); static constexpr bool may_have_slow_equality_check() { return false; } }; template<> struct Formatter : Formatter { ErrorOr format(FormatBuilder&, FlyString const&); }; struct ASCIICaseInsensitiveFlyStringTraits : public Traits { static unsigned hash(FlyString const& s) { return s.ascii_case_insensitive_hash(); } static bool equals(FlyString const& a, FlyString const& b) { return a.equals_ignoring_ascii_case(b); } }; } [[nodiscard]] ALWAYS_INLINE AK::FlyString operator""_fly_string(char const* cstring, size_t length) { ASSERT(Utf8View(AK::StringView(cstring, length)).validate()); return AK::FlyString::from_utf8_without_validation({ cstring, length }); } #if USING_AK_GLOBALLY using AK::FlyString; #endif