mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 23:20:20 +00:00
AK: Add template specializations for Optional<{,Fly}String>
Slice the size of `Optional<{,Fly}String>` in half by introducing `UINTPTR_MAX` as an invalid bit pattern for these values.
This commit is contained in:
parent
fcdf3014f1
commit
2457118024
Notes:
github-actions[bot]
2024-10-31 22:27:23 +00:00
Author: https://github.com/yyny Commit: https://github.com/LadybirdBrowser/ladybird/commit/2457118024a Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2032 Reviewed-by: https://github.com/Hendiadyoin1 Reviewed-by: https://github.com/awesomekling
8 changed files with 427 additions and 2 deletions
|
@ -50,6 +50,8 @@ FlyString FlyString::from_utf8_without_validation(ReadonlyBytes string)
|
||||||
|
|
||||||
FlyString::FlyString(String const& string)
|
FlyString::FlyString(String const& string)
|
||||||
{
|
{
|
||||||
|
ASSERT(!string.is_invalid());
|
||||||
|
|
||||||
if (string.is_short_string()) {
|
if (string.is_short_string()) {
|
||||||
m_data = string;
|
m_data = string;
|
||||||
return;
|
return;
|
||||||
|
|
118
AK/FlyString.h
118
AK/FlyString.h
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <AK/Error.h>
|
#include <AK/Error.h>
|
||||||
#include <AK/Format.h>
|
#include <AK/Format.h>
|
||||||
|
#include <AK/Optional.h>
|
||||||
#include <AK/Platform.h>
|
#include <AK/Platform.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/Traits.h>
|
#include <AK/Traits.h>
|
||||||
|
@ -80,12 +81,129 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class Optional<FlyString>;
|
||||||
|
|
||||||
|
explicit FlyString(nullptr_t)
|
||||||
|
: m_data(Detail::StringBase(nullptr))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
explicit FlyString(Detail::StringBase data)
|
explicit FlyString(Detail::StringBase data)
|
||||||
: m_data(move(data))
|
: m_data(move(data))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Detail::StringBase m_data;
|
Detail::StringBase m_data;
|
||||||
|
|
||||||
|
bool is_invalid() const { return m_data.is_invalid(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
class Optional<FlyString> : public OptionalBase<FlyString> {
|
||||||
|
template<typename U>
|
||||||
|
friend class Optional;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using ValueType = FlyString;
|
||||||
|
|
||||||
|
Optional() = default;
|
||||||
|
|
||||||
|
template<SameAs<OptionalNone> V>
|
||||||
|
Optional(V) { }
|
||||||
|
|
||||||
|
Optional(Optional<FlyString> const& other)
|
||||||
|
{
|
||||||
|
if (other.has_value())
|
||||||
|
m_value = other.m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional(Optional&& other)
|
||||||
|
: m_value(other.m_value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename U = FlyString>
|
||||||
|
requires(!IsSame<OptionalNone, RemoveCVReference<U>>)
|
||||||
|
explicit(!IsConvertible<U&&, FlyString>) Optional(U&& value)
|
||||||
|
requires(!IsSame<RemoveCVReference<U>, Optional<FlyString>> && IsConstructible<FlyString, U &&>)
|
||||||
|
: m_value(forward<U>(value))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<SameAs<OptionalNone> V>
|
||||||
|
Optional& operator=(V)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional& operator=(Optional const& other)
|
||||||
|
{
|
||||||
|
if (this != &other) {
|
||||||
|
clear();
|
||||||
|
m_value = other.m_value;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional& operator=(Optional&& other)
|
||||||
|
{
|
||||||
|
if (this != &other) {
|
||||||
|
clear();
|
||||||
|
m_value = other.m_value;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename O>
|
||||||
|
ALWAYS_INLINE bool operator==(Optional<O> const& other) const
|
||||||
|
{
|
||||||
|
return has_value() == other.has_value() && (!has_value() || value() == other.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename O>
|
||||||
|
ALWAYS_INLINE bool operator==(O const& other) const
|
||||||
|
{
|
||||||
|
return has_value() && value() == other;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_value = FlyString(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool has_value() const
|
||||||
|
{
|
||||||
|
return !m_value.is_invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] FlyString& value() &
|
||||||
|
{
|
||||||
|
VERIFY(has_value());
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] FlyString const& value() const&
|
||||||
|
{
|
||||||
|
VERIFY(has_value());
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] FlyString value() &&
|
||||||
|
{
|
||||||
|
return release_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] FlyString release_value()
|
||||||
|
{
|
||||||
|
VERIFY(has_value());
|
||||||
|
FlyString released_value = m_value;
|
||||||
|
clear();
|
||||||
|
return released_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FlyString m_value = FlyString(nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
|
|
@ -125,6 +125,12 @@ class NonnullOwnPtr;
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class Optional;
|
class Optional;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
class Optional<String>;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
class Optional<FlyString>;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class RefPtr;
|
class RefPtr;
|
||||||
|
|
||||||
|
|
|
@ -211,7 +211,7 @@ public:
|
||||||
|
|
||||||
template<typename U>
|
template<typename U>
|
||||||
requires(IsConstructible<T, U const&> && !IsSpecializationOf<T, Optional> && !IsSpecializationOf<U, Optional>) ALWAYS_INLINE explicit Optional(Optional<U> const& other)
|
requires(IsConstructible<T, U const&> && !IsSpecializationOf<T, Optional> && !IsSpecializationOf<U, Optional>) ALWAYS_INLINE explicit Optional(Optional<U> const& other)
|
||||||
: m_has_value(other.m_has_value)
|
: m_has_value(other.has_value())
|
||||||
{
|
{
|
||||||
if (other.has_value())
|
if (other.has_value())
|
||||||
new (&m_storage) T(other.value());
|
new (&m_storage) T(other.value());
|
||||||
|
@ -219,7 +219,7 @@ public:
|
||||||
|
|
||||||
template<typename U>
|
template<typename U>
|
||||||
requires(IsConstructible<T, U &&> && !IsSpecializationOf<T, Optional> && !IsSpecializationOf<U, Optional>) ALWAYS_INLINE explicit Optional(Optional<U>&& other)
|
requires(IsConstructible<T, U &&> && !IsSpecializationOf<T, Optional> && !IsSpecializationOf<U, Optional>) ALWAYS_INLINE explicit Optional(Optional<U>&& other)
|
||||||
: m_has_value(other.m_has_value)
|
: m_has_value(other.has_value())
|
||||||
{
|
{
|
||||||
if (other.has_value())
|
if (other.has_value())
|
||||||
new (&m_storage) T(other.release_value());
|
new (&m_storage) T(other.release_value());
|
||||||
|
|
112
AK/String.h
112
AK/String.h
|
@ -211,6 +211,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class ::AK::FlyString;
|
friend class ::AK::FlyString;
|
||||||
|
friend class Optional<String>;
|
||||||
|
|
||||||
using ShortString = Detail::ShortString;
|
using ShortString = Detail::ShortString;
|
||||||
|
|
||||||
|
@ -218,6 +219,117 @@ private:
|
||||||
: StringBase(move(base))
|
: StringBase(move(base))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
explicit constexpr String(nullptr_t)
|
||||||
|
: StringBase(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
class Optional<String> : public OptionalBase<String> {
|
||||||
|
template<typename U>
|
||||||
|
friend class Optional;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using ValueType = String;
|
||||||
|
|
||||||
|
Optional() = default;
|
||||||
|
|
||||||
|
template<SameAs<OptionalNone> V>
|
||||||
|
Optional(V) { }
|
||||||
|
|
||||||
|
Optional(Optional<String> const& other)
|
||||||
|
{
|
||||||
|
if (other.has_value())
|
||||||
|
m_value = other.m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional(Optional&& other)
|
||||||
|
: m_value(move(other.m_value))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename U = String>
|
||||||
|
requires(!IsSame<OptionalNone, RemoveCVReference<U>>)
|
||||||
|
explicit(!IsConvertible<U&&, String>) Optional(U&& value)
|
||||||
|
requires(!IsSame<RemoveCVReference<U>, Optional<String>> && IsConstructible<String, U &&>)
|
||||||
|
: m_value(forward<U>(value))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<SameAs<OptionalNone> V>
|
||||||
|
Optional& operator=(V)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional& operator=(Optional const& other)
|
||||||
|
{
|
||||||
|
if (this != &other) {
|
||||||
|
m_value = other.m_value;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional& operator=(Optional&& other)
|
||||||
|
{
|
||||||
|
if (this != &other) {
|
||||||
|
m_value = move(other.m_value);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename O>
|
||||||
|
ALWAYS_INLINE bool operator==(Optional<O> const& other) const
|
||||||
|
{
|
||||||
|
return has_value() == other.has_value() && (!has_value() || value() == other.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename O>
|
||||||
|
ALWAYS_INLINE bool operator==(O const& other) const
|
||||||
|
{
|
||||||
|
return has_value() && value() == other;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_value = String(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool has_value() const
|
||||||
|
{
|
||||||
|
return !m_value.is_invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] String& value() &
|
||||||
|
{
|
||||||
|
VERIFY(has_value());
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] String const& value() const&
|
||||||
|
{
|
||||||
|
VERIFY(has_value());
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] String value() &&
|
||||||
|
{
|
||||||
|
return release_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] String release_value()
|
||||||
|
{
|
||||||
|
VERIFY(has_value());
|
||||||
|
String released_value = m_value;
|
||||||
|
clear();
|
||||||
|
return released_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
String m_value { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
|
|
@ -58,6 +58,7 @@ StringBase& StringBase::operator=(StringBase const& other)
|
||||||
|
|
||||||
ReadonlyBytes StringBase::bytes() const
|
ReadonlyBytes StringBase::bytes() const
|
||||||
{
|
{
|
||||||
|
ASSERT(!is_invalid());
|
||||||
if (is_short_string())
|
if (is_short_string())
|
||||||
return m_short_string.bytes();
|
return m_short_string.bytes();
|
||||||
return m_data->bytes();
|
return m_data->bytes();
|
||||||
|
@ -65,6 +66,7 @@ ReadonlyBytes StringBase::bytes() const
|
||||||
|
|
||||||
u32 StringBase::hash() const
|
u32 StringBase::hash() const
|
||||||
{
|
{
|
||||||
|
ASSERT(!is_invalid());
|
||||||
if (is_short_string()) {
|
if (is_short_string()) {
|
||||||
auto bytes = this->bytes();
|
auto bytes = this->bytes();
|
||||||
return string_hash(reinterpret_cast<char const*>(bytes.data()), bytes.size());
|
return string_hash(reinterpret_cast<char const*>(bytes.data()), bytes.size());
|
||||||
|
@ -74,6 +76,7 @@ u32 StringBase::hash() const
|
||||||
|
|
||||||
size_t StringBase::byte_count() const
|
size_t StringBase::byte_count() const
|
||||||
{
|
{
|
||||||
|
ASSERT(!is_invalid());
|
||||||
if (is_short_string())
|
if (is_short_string())
|
||||||
return m_short_string.byte_count_and_short_string_flag >> 1;
|
return m_short_string.byte_count_and_short_string_flag >> 1;
|
||||||
return m_data->byte_count();
|
return m_data->byte_count();
|
||||||
|
@ -81,6 +84,7 @@ size_t StringBase::byte_count() const
|
||||||
|
|
||||||
bool StringBase::operator==(StringBase const& other) const
|
bool StringBase::operator==(StringBase const& other) const
|
||||||
{
|
{
|
||||||
|
ASSERT(!is_invalid());
|
||||||
if (is_short_string())
|
if (is_short_string())
|
||||||
return m_data == other.m_data;
|
return m_data == other.m_data;
|
||||||
if (other.is_short_string())
|
if (other.is_short_string())
|
||||||
|
@ -92,6 +96,7 @@ bool StringBase::operator==(StringBase const& other) const
|
||||||
|
|
||||||
void StringBase::replace_with_string_builder(StringBuilder& builder)
|
void StringBase::replace_with_string_builder(StringBuilder& builder)
|
||||||
{
|
{
|
||||||
|
ASSERT(!is_invalid());
|
||||||
if (builder.length() <= MAX_SHORT_STRING_BYTE_COUNT) {
|
if (builder.length() <= MAX_SHORT_STRING_BYTE_COUNT) {
|
||||||
return replace_with_new_short_string(builder.length(), [&](Bytes buffer) {
|
return replace_with_new_short_string(builder.length(), [&](Bytes buffer) {
|
||||||
builder.string_view().bytes().copy_to(buffer);
|
builder.string_view().bytes().copy_to(buffer);
|
||||||
|
@ -105,6 +110,7 @@ void StringBase::replace_with_string_builder(StringBuilder& builder)
|
||||||
|
|
||||||
ErrorOr<Bytes> StringBase::replace_with_uninitialized_buffer(size_t byte_count)
|
ErrorOr<Bytes> StringBase::replace_with_uninitialized_buffer(size_t byte_count)
|
||||||
{
|
{
|
||||||
|
ASSERT(!is_invalid());
|
||||||
if (byte_count <= MAX_SHORT_STRING_BYTE_COUNT)
|
if (byte_count <= MAX_SHORT_STRING_BYTE_COUNT)
|
||||||
return replace_with_uninitialized_short_string(byte_count);
|
return replace_with_uninitialized_short_string(byte_count);
|
||||||
|
|
||||||
|
@ -116,6 +122,7 @@ ErrorOr<Bytes> StringBase::replace_with_uninitialized_buffer(size_t byte_count)
|
||||||
|
|
||||||
ErrorOr<StringBase> StringBase::substring_from_byte_offset_with_shared_superstring(size_t start, size_t length) const
|
ErrorOr<StringBase> StringBase::substring_from_byte_offset_with_shared_superstring(size_t start, size_t length) const
|
||||||
{
|
{
|
||||||
|
ASSERT(!is_invalid());
|
||||||
VERIFY(start + length <= byte_count());
|
VERIFY(start + length <= byte_count());
|
||||||
|
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
|
|
|
@ -75,6 +75,8 @@ public:
|
||||||
[[nodiscard]] ALWAYS_INLINE FlatPtr raw(Badge<FlyString>) const { return bit_cast<FlatPtr>(m_data); }
|
[[nodiscard]] ALWAYS_INLINE FlatPtr raw(Badge<FlyString>) const { return bit_cast<FlatPtr>(m_data); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
bool is_invalid() const { return m_invalid_tag == UINTPTR_MAX; }
|
||||||
|
|
||||||
template<typename Func>
|
template<typename Func>
|
||||||
ErrorOr<void> replace_with_new_string(size_t byte_count, Func&& callback)
|
ErrorOr<void> replace_with_new_string(size_t byte_count, Func&& callback)
|
||||||
{
|
{
|
||||||
|
@ -107,6 +109,11 @@ private:
|
||||||
|
|
||||||
explicit StringBase(NonnullRefPtr<Detail::StringData const>);
|
explicit StringBase(NonnullRefPtr<Detail::StringData const>);
|
||||||
|
|
||||||
|
explicit constexpr StringBase(nullptr_t)
|
||||||
|
: m_invalid_tag(UINTPTR_MAX)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
explicit constexpr StringBase(ShortString short_string)
|
explicit constexpr StringBase(ShortString short_string)
|
||||||
: m_short_string(short_string)
|
: m_short_string(short_string)
|
||||||
{
|
{
|
||||||
|
@ -129,6 +136,7 @@ private:
|
||||||
union {
|
union {
|
||||||
ShortString m_short_string;
|
ShortString m_short_string;
|
||||||
Detail::StringData const* m_data { nullptr };
|
Detail::StringData const* m_data { nullptr };
|
||||||
|
uintptr_t m_invalid_tag;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
#include <LibTest/TestCase.h>
|
#include <LibTest/TestCase.h>
|
||||||
|
|
||||||
#include <AK/ByteString.h>
|
#include <AK/ByteString.h>
|
||||||
|
#include <AK/FlyString.h>
|
||||||
#include <AK/Optional.h>
|
#include <AK/Optional.h>
|
||||||
|
#include <AK/String.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
|
|
||||||
TEST_CASE(basic_optional)
|
TEST_CASE(basic_optional)
|
||||||
|
@ -269,3 +271,173 @@ TEST_CASE(comparison_reference)
|
||||||
EXPECT_EQ(opt1, opt2);
|
EXPECT_EQ(opt1, opt2);
|
||||||
EXPECT_NE(opt1, opt3);
|
EXPECT_NE(opt1, opt3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE(string_specialization)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(sizeof(Optional<String>), sizeof(String));
|
||||||
|
|
||||||
|
{
|
||||||
|
Optional<String> foo;
|
||||||
|
|
||||||
|
EXPECT(!foo.has_value());
|
||||||
|
|
||||||
|
foo = "long_enough_to_be_allocated"_string;
|
||||||
|
|
||||||
|
EXPECT(foo.has_value());
|
||||||
|
EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Optional<String> foo = "initial_value"_string;
|
||||||
|
|
||||||
|
EXPECT(foo.has_value());
|
||||||
|
EXPECT_EQ(foo.value(), "initial_value"sv);
|
||||||
|
|
||||||
|
foo = "long_enough_to_be_allocated"_string;
|
||||||
|
|
||||||
|
EXPECT(foo.has_value());
|
||||||
|
EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Optional<String> foo;
|
||||||
|
|
||||||
|
EXPECT(!foo.has_value());
|
||||||
|
|
||||||
|
String bar = "long_enough_to_be_allocated"_string;
|
||||||
|
foo = bar;
|
||||||
|
|
||||||
|
EXPECT(foo.has_value());
|
||||||
|
EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Optional<String> foo;
|
||||||
|
|
||||||
|
EXPECT(!foo.has_value());
|
||||||
|
|
||||||
|
Optional<String> bar = "long_enough_to_be_allocated"_string;
|
||||||
|
foo = bar;
|
||||||
|
|
||||||
|
EXPECT(foo.has_value());
|
||||||
|
EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv);
|
||||||
|
EXPECT(bar.has_value());
|
||||||
|
EXPECT_EQ(bar.value(), "long_enough_to_be_allocated"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Optional<String> foo;
|
||||||
|
|
||||||
|
EXPECT(!foo.has_value());
|
||||||
|
|
||||||
|
foo = Optional<String> { "long_enough_to_be_allocated"_string };
|
||||||
|
|
||||||
|
EXPECT(foo.has_value());
|
||||||
|
EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Optional<String> foo = "long_enough_to_be_allocated"_string;
|
||||||
|
|
||||||
|
EXPECT_EQ(foo.value_or("fallback_value"_string), "long_enough_to_be_allocated"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Optional<String> foo;
|
||||||
|
|
||||||
|
EXPECT_EQ(foo.value_or("fallback_value"_string), "fallback_value"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
EXPECT_EQ((Optional<String> { "long_enough_to_be_allocated"_string }).value_or("fallback_value"_string), "long_enough_to_be_allocated"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
EXPECT_EQ((Optional<String> {}).value_or("fallback_value"_string), "fallback_value"sv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE(flystring_specialization)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(sizeof(Optional<FlyString>), sizeof(FlyString));
|
||||||
|
|
||||||
|
{
|
||||||
|
Optional<FlyString> foo;
|
||||||
|
|
||||||
|
EXPECT(!foo.has_value());
|
||||||
|
|
||||||
|
foo = "long_enough_to_be_allocated"_fly_string;
|
||||||
|
|
||||||
|
EXPECT(foo.has_value());
|
||||||
|
EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Optional<FlyString> foo = "initial_value"_fly_string;
|
||||||
|
|
||||||
|
EXPECT(foo.has_value());
|
||||||
|
EXPECT_EQ(foo.value(), "initial_value"sv);
|
||||||
|
|
||||||
|
foo = "long_enough_to_be_allocated"_fly_string;
|
||||||
|
|
||||||
|
EXPECT(foo.has_value());
|
||||||
|
EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Optional<FlyString> foo;
|
||||||
|
|
||||||
|
EXPECT(!foo.has_value());
|
||||||
|
|
||||||
|
FlyString bar = "long_enough_to_be_allocated"_fly_string;
|
||||||
|
foo = bar;
|
||||||
|
|
||||||
|
EXPECT(foo.has_value());
|
||||||
|
EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Optional<FlyString> foo;
|
||||||
|
|
||||||
|
EXPECT(!foo.has_value());
|
||||||
|
|
||||||
|
Optional<FlyString> bar = "long_enough_to_be_allocated"_fly_string;
|
||||||
|
foo = bar;
|
||||||
|
|
||||||
|
EXPECT(bar.has_value());
|
||||||
|
EXPECT_EQ(bar.value(), "long_enough_to_be_allocated"sv);
|
||||||
|
EXPECT(foo.has_value());
|
||||||
|
EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Optional<FlyString> foo;
|
||||||
|
|
||||||
|
EXPECT(!foo.has_value());
|
||||||
|
|
||||||
|
foo = Optional<FlyString> { "long_enough_to_be_allocated"_fly_string };
|
||||||
|
|
||||||
|
EXPECT(foo.has_value());
|
||||||
|
EXPECT_EQ(foo.value(), "long_enough_to_be_allocated"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Optional<FlyString> foo = "long_enough_to_be_allocated"_fly_string;
|
||||||
|
|
||||||
|
EXPECT_EQ(foo.value_or("fallback_value"_fly_string), "long_enough_to_be_allocated"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Optional<FlyString> foo;
|
||||||
|
|
||||||
|
EXPECT_EQ(foo.value_or("fallback_value"_fly_string), "fallback_value"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
EXPECT_EQ((Optional<FlyString> { "long_enough_to_be_allocated"_fly_string }).value_or("fallback_value"_fly_string), "long_enough_to_be_allocated"sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
EXPECT_EQ((Optional<FlyString> {}).value_or("fallback_value"_fly_string), "fallback_value"sv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue