aboutsummaryrefslogtreecommitdiff
path: root/libs/kstd
diff options
context:
space:
mode:
Diffstat (limited to 'libs/kstd')
-rw-r--r--libs/kstd/CMakeLists.txt2
-rw-r--r--libs/kstd/kstd/bits/basic_string.hpp234
-rw-r--r--libs/kstd/kstd/bits/format/string.hpp8
-rw-r--r--libs/kstd/kstd/bits/format/vformat.cpp (renamed from libs/kstd/kstd/vformat.cpp)15
-rw-r--r--libs/kstd/kstd/bits/format/vformat.hpp41
-rw-r--r--libs/kstd/kstd/format36
-rw-r--r--libs/kstd/kstd/format.test.cpp68
-rw-r--r--libs/kstd/kstd/string47
-rw-r--r--libs/kstd/kstd/string.test.cpp831
9 files changed, 1223 insertions, 59 deletions
diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt
index 5a957573..002fb548 100644
--- a/libs/kstd/CMakeLists.txt
+++ b/libs/kstd/CMakeLists.txt
@@ -18,7 +18,7 @@ add_library("kstd::lib" ALIAS "kstd")
target_sources("kstd" PRIVATE
"kstd/os/error.cpp"
"kstd/mutex.cpp"
- "kstd/vformat.cpp"
+ "kstd/bits/format/vformat.cpp"
)
file(GLOB_RECURSE KSTD_HEADERS
diff --git a/libs/kstd/kstd/bits/basic_string.hpp b/libs/kstd/kstd/bits/basic_string.hpp
index 5d2eecd7..1675434c 100644
--- a/libs/kstd/kstd/bits/basic_string.hpp
+++ b/libs/kstd/kstd/bits/basic_string.hpp
@@ -10,6 +10,7 @@
#include <array>
#include <compare>
#include <cstddef>
+#include <functional>
#include <initializer_list>
#include <iterator>
#include <limits>
@@ -407,6 +408,36 @@ namespace kstd
//! Assignment from nullptr is prohibited.
constexpr auto operator=(std::nullptr_t) = delete;
+ //! Assign a new value to this string.
+ //!
+ //! @param string The string to use to replace this strings value.
+ //! @return A reference to this string after assignment.
+ constexpr auto assign(basic_string const & string) -> basic_string &
+ {
+ return *this = string;
+ }
+
+ //! Assign a new value to this string.
+ //!
+ //! @param string The string to use to replace this strings value.
+ //! @return A reference to this string after assignment.
+ constexpr auto assign(basic_string && string) noexcept(noexcept(*this = std::move(string))) -> basic_string &
+ {
+ return *this = std::move(string);
+ }
+
+ //! Replace the contents of this string with a given number of copies of a given character.
+ //!
+ //! @param count The number of copies.
+ //! @param character The character to use to fill this string.
+ //! @return A reference to this string after assignment.
+ constexpr auto assign(size_type count, CharacterType character) -> basic_string &
+ {
+ clear();
+ resize(count, character);
+ return *this;
+ }
+
//! Get the allocator used by this string.
//!
//! @return A copy of the allocator used by this string.
@@ -734,6 +765,7 @@ namespace kstd
//!
//! @param index The position to insert the data at.
//! @param string The string to insert.
+ //! @return A reference to this string.
constexpr auto insert(size_type index, CharacterType const * string) -> basic_string &
{
if (string == nullptr)
@@ -744,6 +776,48 @@ namespace kstd
return do_insert(index, std::basic_string_view<value_type, traits_type>{string});
}
+ //! Insert the characters in the range [string, string + length) into this string at a given position.
+ //!
+ //! The inserted range may contain null characters.
+ //!
+ //! @param index The position to insert the data at.
+ //! @param range The pointer to the first character of the range.
+ //! @param length The length of the range.
+ //! @return A reference to this string.
+ constexpr auto insert(size_type index, CharacterType const * range, size_type length) -> basic_string &
+ {
+ if (range == nullptr)
+ {
+ os::panic("Tried to insert nullptr range");
+ }
+
+ return do_insert(index, std::basic_string_view<value_type, traits_type>{range, length});
+ }
+
+ //! Insert a given string into this string at a given position.
+ //!
+ //! @param index The position to insert the data at.
+ //! @param string The string to insert.
+ //! @return A reference to this string.
+ constexpr auto insert(size_type index, basic_string const & string) -> basic_string &
+ {
+ return do_insert(index, static_cast<std::basic_string_view<value_type, traits_type>>(string));
+ }
+
+ //! Insert the elements of a string view like object at a given position.
+ //!
+ //! @param index The position to insert the data at.
+ //! @param view_like The string view like object.
+ //! @return A reference to this string.
+ template<typename StringViewLike>
+ requires(std::is_convertible_v<StringViewLike const &, std::basic_string_view<CharacterType, Traits>> &&
+ !std::is_convertible_v<StringViewLike const &, CharacterType const *>)
+ constexpr auto insert(size_type index, StringViewLike const & view_like) -> basic_string &
+ {
+ auto view = static_cast<std::basic_string_view<CharacterType, Traits>>(view_like);
+ return do_insert(index, view);
+ }
+
//! Append a character to this string.
//!
//! @param character The character to append.
@@ -882,6 +956,36 @@ namespace kstd
return *this;
}
+ //! Resize this string to contain a given number of characters, filling with null-bytes if the size increases.
+ //!
+ //! @param count The new size of the string.
+ constexpr auto resize(size_type count) -> void
+ {
+ resize(count, CharacterType{});
+ }
+
+ //! Resize this string to contain a given number of characters, filling with the given character if the size
+ //! increases.
+ //!
+ //! @param count The new size of the string.
+ //! @param character The character to use to fill up empty space.
+ constexpr auto resize(size_type count, CharacterType character) -> void
+ {
+ if (count > size() && count < capacity())
+ {
+ traits_type::assign(m_data + size(), count - size(), character);
+ }
+ else if (count > capacity())
+ {
+ auto new_buffer = allocate_buffer(count);
+ traits_type::copy(new_buffer.data(), data(), size());
+ traits_type::assign(new_buffer.data() + size(), count - size(), character);
+ update_data(new_buffer);
+ }
+
+ update_length(count);
+ }
+
//! Swap the contents of this string with another one.
//!
//! @param other The string to swap contents with.
@@ -938,6 +1042,87 @@ namespace kstd
swap(m_length, other.m_length);
}
+ //! Lexicographically compare this string to another string
+ //!
+ //! @param other The string to compare this string with.
+ //! @return a negative value if this string is ordered lexicographically before the given string, zero if both
+ //! strings are equal, or a positive value if this string is ordered lexicographically before the given string.
+ [[nodiscard]] constexpr auto compare(basic_string const & other) const noexcept -> int
+ {
+ return do_compare(*this, other);
+ }
+
+ //! Lexicographically compare a substring of this string with a given string.
+ //!
+ //! @param position The start position of this substring.
+ //! @param count The length of this substring.
+ //! @param other The string to compare to the substring of this string.
+ //! @return a negative value if this string is ordered lexicographically before the given string, zero if both
+ //! strings are equal, or a positive value if this string is ordered lexicographically before the given string.
+ [[nodiscard]] constexpr auto compare(size_type position, size_type count, basic_string const & other) const noexcept
+ -> int
+ {
+ auto view = static_cast<std::basic_string_view<value_type, traits_type>>(*this);
+ return do_compare(view.substr(position, count), other);
+ }
+
+ //! Lexicographically compare a substring of this string with a substring of another string.
+ //!
+ //! @param position The start position of this substring.
+ //! @param count The length of this substring.
+ //! @param other The string, a substring of which to compare to the substring of this string.
+ //! @param other_position The start position of the substring of @p other.
+ //! @param other_count The length of the substring of @p other.
+ //! @return a negative value if this string is ordered lexicographically before the given string, zero if both
+ //! strings are equal, or a positive value if this string is ordered lexicographically before the given string.
+ [[nodiscard]] constexpr auto compare(size_type position, size_type count, basic_string const & other,
+ size_type other_position, size_type other_count = npos) const noexcept -> int
+ {
+ auto view = static_cast<std::basic_string_view<value_type, traits_type>>(*this);
+ auto other_view = static_cast<std::basic_string_view<value_type, traits_type>>(other);
+ return do_compare(view.substr(position, count), other_view.substr(other_position, other_count));
+ }
+
+ //! Lexicographically compare this string to a C-style string.
+ //!
+ //! @param string The C-style string to compare this string with.
+ //! @return a negative value if this string is ordered lexicographically before the given string, zero if both
+ //! strings are equal, or a positive value if this string is ordered lexicographically before the given string.
+ [[nodiscard]] constexpr auto compare(CharacterType const * string) const noexcept -> int
+ {
+ return do_compare(*this, {string, traits_type::length(string)});
+ }
+
+ //! Lexicographically compare a substring of this string with a given C-style string.
+ //!
+ //! @param position The start position of this substring.
+ //! @param count The length of this substring.
+ //! @param string The C-style string to compare to the substring of this string.
+ //! @return a negative value if this string is ordered lexicographically before the given string, zero if both
+ //! strings are equal, or a positive value if this string is ordered lexicographically before the given string.
+ [[nodiscard]] constexpr auto compare(size_type position, size_type count,
+ CharacterType const * string) const noexcept -> int
+ {
+ auto view = static_cast<std::basic_string_view<value_type, traits_type>>(*this);
+ return do_compare(view.substr(position, count), {string, traits_type::length(string)});
+ }
+
+ //! Lexicographically compare a substring of this string with a substring of a C-style string.
+ //!
+ //! @param position The start position of this substring.
+ //! @param count The length of this substring.
+ //! @param string The C-style string, a substring of which to compare to the substring of this string.
+ //! @param string_count The length of the substring of @p other.
+ //! @return a negative value if this string is ordered lexicographically before the given string, zero if both
+ //! strings are equal, or a positive value if this string is ordered lexicographically before the given string.
+ [[nodiscard]] constexpr auto compare(size_type position, size_type count, CharacterType const * string,
+ size_type string_count) const noexcept -> int
+ {
+ auto view = static_cast<std::basic_string_view<value_type, traits_type>>(*this);
+ auto other_view = static_cast<std::basic_string_view<value_type, traits_type>>(string, string_count);
+ return do_compare(view.substr(position, count), other_view);
+ }
+
//! Concatenate two string.
//!
//! @param lhs The left hand side string.
@@ -974,7 +1159,7 @@ namespace kstd
return result;
}
- //! Contcatenate a C-style string and a string.
+ //! Concatenate a C-style string and a string.
//!
//! @param lhs The left hand side C-style string.
//! @param rhs The right hand side string.
@@ -998,7 +1183,7 @@ namespace kstd
return result;
}
- //! Contcatenate two strings.
+ //! Concatenate two strings.
//!
//! @param lhs The left hand side string.
//! @param rhs The right hand side string.
@@ -1009,7 +1194,7 @@ namespace kstd
return std::move(lhs);
}
- //! Contcatenate two strings.
+ //! Concatenate two strings.
//!
//! @param lhs The left hand side string.
//! @param rhs The right hand side string.
@@ -1020,7 +1205,7 @@ namespace kstd
return std::move(lhs);
}
- //! Contcatenate a string and a C-style string.
+ //! Concatenate a string and a C-style string.
//!
//! @param lhs The left hand side string.
//! @param rhs The right hand side C-style string.
@@ -1257,6 +1442,27 @@ namespace kstd
return *this;
}
+ [[nodiscard]] constexpr auto static do_compare(std::basic_string_view<value_type, traits_type> lhs,
+ std::basic_string_view<value_type, traits_type> rhs) noexcept
+ {
+ // clang-tidy is not smart enough to see that whe are, in essence, passing the correct length.
+ // NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
+ if (auto result = traits_type::compare(lhs.data(), rhs.data(), std::min(lhs.size(), rhs.size())))
+ {
+ return result;
+ }
+ else if (lhs.size() < rhs.size())
+ {
+ return -1;
+ }
+ else if (lhs.size() > rhs.size())
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
//! Copy elements from a range described by an input iterator pair.
//!
//! @param first An iterator pointing to the first element to copy.
@@ -1401,4 +1607,24 @@ namespace kstd
} // namespace kstd
+//! A specialization of std::hash for kstd::string.
+//!
+//! This specialization allows for the use of kstd::string in hashed containers.
+//!
+//! @note Under the hood, this specialization delegates to the specialization for std::string_view. This saves
+//! implementation work and automatically guarantees that the hash for a kstd::string with some contents X is equal to
+//! the hash of a std::string_view of the same X. This is a standard requirement for the specialization for std::string,
+//! so providing the same for kstd::string yields the same interoperability.
+template<typename Allocator>
+struct std::hash<kstd::basic_string<char, kstd::char_traits<char>, Allocator>>
+{
+ constexpr auto static implementation = std::hash<std::string_view>{};
+
+ constexpr auto operator()(kstd::basic_string<char, kstd::char_traits<char>, Allocator> string) const noexcept
+ -> std::size_t
+ {
+ return implementation(string);
+ };
+};
+
#endif
diff --git a/libs/kstd/kstd/bits/format/string.hpp b/libs/kstd/kstd/bits/format/string.hpp
index e7e40886..a2b298d4 100644
--- a/libs/kstd/kstd/bits/format/string.hpp
+++ b/libs/kstd/kstd/bits/format/string.hpp
@@ -72,8 +72,7 @@ namespace kstd
template<typename... Args>
struct format_string
{
- template<std::size_t Size>
- consteval format_string(char const (&str)[Size]) noexcept(false) // NOLINT
+ consteval format_string(std::string_view const & str) noexcept(false)
: str_view{str}
{
using namespace bits::format;
@@ -140,6 +139,11 @@ namespace kstd
}
}
+ template<std::size_t Size>
+ consteval format_string(char const (&str)[Size]) noexcept(false) // NOLINT
+ : format_string{std::string_view{str}}
+ {}
+
std::string_view str_view;
};
diff --git a/libs/kstd/kstd/vformat.cpp b/libs/kstd/kstd/bits/format/vformat.cpp
index b7c51218..1bf293a4 100644
--- a/libs/kstd/kstd/vformat.cpp
+++ b/libs/kstd/kstd/bits/format/vformat.cpp
@@ -206,4 +206,19 @@ namespace kstd::bits::format
return std::move(m_result);
}
+ auto size_recorder::push(std::string_view text) -> void
+ {
+ m_size += text.size();
+ }
+
+ auto size_recorder::push(char) -> void
+ {
+ ++m_size;
+ }
+
+ auto size_recorder::size() const noexcept -> std::size_t
+ {
+ return m_size;
+ }
+
} // namespace kstd::bits::format \ No newline at end of file
diff --git a/libs/kstd/kstd/bits/format/vformat.hpp b/libs/kstd/kstd/bits/format/vformat.hpp
index 994fae50..2db56d17 100644
--- a/libs/kstd/kstd/bits/format/vformat.hpp
+++ b/libs/kstd/kstd/bits/format/vformat.hpp
@@ -3,12 +3,13 @@
// IWYU pragma: private, include <kstd/format>
+#include <kstd/bits/basic_string.hpp>
#include <kstd/bits/format/args.hpp>
#include <kstd/bits/format/output_buffer.hpp>
#include <kstd/bits/format/string.hpp>
-#include <kstd/string>
#include <algorithm>
+#include <cstddef>
#include <iterator>
#include <string_view>
#include <type_traits>
@@ -29,13 +30,25 @@ namespace kstd
struct string_writer : output_buffer
{
- auto push(std::string_view text) -> void override;
- auto push(char character) -> void override;
+ auto push(std::string_view text) -> void final;
+ auto push(char character) -> void final;
- auto release() -> string &&;
+ auto release() -> basic_string<char> &&;
private:
- string m_result{};
+ basic_string<char> m_result{};
+ };
+
+ //! A buffer that merely records the size of the output written to it.
+ struct size_recorder : output_buffer
+ {
+ auto push(std::string_view text) -> void final;
+ auto push(char character) -> void final;
+
+ [[nodiscard]] auto size() const noexcept -> std::size_t;
+
+ private:
+ std::size_t m_size{};
};
template<std::output_iterator<char> Output>
@@ -72,7 +85,8 @@ namespace kstd
//! @param args The arguments for the format string.
//! @return A new string containing the result of the format operation.
template<typename... ArgumentTypes>
- auto format(format_string<std::type_identity_t<ArgumentTypes>...> format, ArgumentTypes &&... args) -> string
+ auto format(format_string<std::type_identity_t<ArgumentTypes>...> format, ArgumentTypes &&... args)
+ -> basic_string<char>
{
auto buffer = bits::format::string_writer{};
bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward<ArgumentTypes>(args)...).args);
@@ -94,6 +108,21 @@ namespace kstd
return buffer.iterator();
}
+ //! Determine the number of characters required to format the format string with the given arguments.
+ //!
+ //! @param format The format string.
+ //! @param args The arguments for the format string.
+ //! @tparam ArgumentTypes The type of the format string arguments.
+ //! @return The number of characters required to format @p format with @p args
+ template<typename... ArgumentTypes>
+ auto formatted_size(format_string<std::type_identity_t<ArgumentTypes>...> format, ArgumentTypes &&... args)
+ -> std::size_t
+ {
+ auto buffer = bits::format::size_recorder{};
+ bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward<ArgumentTypes>(args)...).args);
+ return buffer.size();
+ }
+
} // namespace kstd
#endif \ No newline at end of file
diff --git a/libs/kstd/kstd/format b/libs/kstd/kstd/format
index e04b79a9..5ed798db 100644
--- a/libs/kstd/kstd/format
+++ b/libs/kstd/kstd/format
@@ -1,22 +1,24 @@
#ifndef KSTD_FORMAT_HPP
#define KSTD_FORMAT_HPP
-#include <kstd/bits/format/arg.hpp> // IWYU pragma: export
-#include <kstd/bits/format/args.hpp> // IWYU pragma: export
-#include <kstd/bits/format/context.hpp> // IWYU pragma: export
-#include <kstd/bits/format/formatter.hpp> // IWYU pragma: export
-#include <kstd/bits/format/formatter/bool.hpp> // IWYU pragma: export
-#include <kstd/bits/format/formatter/byte.hpp> // IWYU pragma: export
-#include <kstd/bits/format/formatter/char.hpp> // IWYU pragma: export
-#include <kstd/bits/format/formatter/cstring.hpp> // IWYU pragma: export
-#include <kstd/bits/format/formatter/integral.hpp> // IWYU pragma: export
-#include <kstd/bits/format/formatter/ordering.hpp> // IWYU pragma: export
-#include <kstd/bits/format/formatter/pointer.hpp> // IWYU pragma: export
-#include <kstd/bits/format/formatter/range.hpp> // IWYU pragma: export
-#include <kstd/bits/format/formatter/string_view.hpp> // IWYU pragma: export
-#include <kstd/bits/format/output_buffer.hpp> // IWYU pragma: export
-#include <kstd/bits/format/parse_context.hpp> // IWYU pragma: export
-#include <kstd/bits/format/string.hpp> // IWYU pragma: export
-#include <kstd/bits/format/vformat.hpp> // IWYU pragma: export
+// IWYU pragma: begin_exports
+#include <kstd/bits/format/arg.hpp>
+#include <kstd/bits/format/args.hpp>
+#include <kstd/bits/format/context.hpp>
+#include <kstd/bits/format/formatter.hpp>
+#include <kstd/bits/format/formatter/bool.hpp>
+#include <kstd/bits/format/formatter/byte.hpp>
+#include <kstd/bits/format/formatter/char.hpp>
+#include <kstd/bits/format/formatter/cstring.hpp>
+#include <kstd/bits/format/formatter/integral.hpp>
+#include <kstd/bits/format/formatter/ordering.hpp>
+#include <kstd/bits/format/formatter/pointer.hpp>
+#include <kstd/bits/format/formatter/range.hpp>
+#include <kstd/bits/format/formatter/string_view.hpp>
+#include <kstd/bits/format/output_buffer.hpp>
+#include <kstd/bits/format/parse_context.hpp>
+#include <kstd/bits/format/string.hpp>
+#include <kstd/bits/format/vformat.hpp>
+// IWYU pragma: end_exports
#endif \ No newline at end of file
diff --git a/libs/kstd/kstd/format.test.cpp b/libs/kstd/kstd/format.test.cpp
index 624779af..6126605f 100644
--- a/libs/kstd/kstd/format.test.cpp
+++ b/libs/kstd/kstd/format.test.cpp
@@ -4,14 +4,17 @@
#include <iterator>
#include <sstream>
+#include <string_view>
+
+using namespace std::string_view_literals;
SCENARIO("Formatting to a new string", "[format]")
{
GIVEN("a format string without any placeholders")
{
- auto const & fmt = "This is a test";
+ constexpr auto fmt = "This is a test"sv;
- WHEN("calling format with without any arguments.")
+ WHEN("calling format without any arguments.")
{
auto result = kstd::format(fmt);
@@ -34,7 +37,7 @@ SCENARIO("Formatting to a new string", "[format]")
GIVEN("a format string with placeholders")
{
- auto const & fmt = "Here are some placeholders: {} {} {}";
+ constexpr auto fmt = "Here are some placeholders: {} {} {}"sv;
WHEN("calling format with the same number of arguments as there are placeholders")
{
@@ -64,9 +67,9 @@ SCENARIO("Formatting to an output iterator", "[format]")
GIVEN("a format string without any placeholders")
{
- auto const & fmt = "This is a test";
+ constexpr auto fmt = "This is a test"sv;
- WHEN("calling format with without any arguments.")
+ WHEN("calling format without any arguments.")
{
kstd::format_to(std::ostream_iterator<char>{buffer}, fmt);
@@ -89,7 +92,7 @@ SCENARIO("Formatting to an output iterator", "[format]")
GIVEN("a format string with placeholders")
{
- auto const & fmt = "Here are some placeholders: {} {} {}";
+ constexpr auto fmt = "Here are some placeholders: {} {} {}"sv;
WHEN("calling format with the same number of arguments as there are placeholders")
{
@@ -111,4 +114,57 @@ SCENARIO("Formatting to an output iterator", "[format]")
}
}
}
+}
+
+SCENARIO("Determining formatted size")
+{
+ GIVEN("a format string without any placeholders")
+ {
+ constexpr auto fmt = "This is a test"sv;
+
+ WHEN("calling formatted_size without any arguments")
+ {
+ auto const size = kstd::formatted_size(fmt);
+
+ THEN("the result is equal to the length of the format string")
+ {
+ REQUIRE(size == fmt.size());
+ }
+ }
+
+ WHEN("calling format with additional arguments")
+ {
+ auto const size = kstd::formatted_size(fmt, 1, 2, 3);
+
+ THEN("the result is equal to the length of the format string")
+ {
+ REQUIRE(size == fmt.size());
+ }
+ }
+ }
+
+ GIVEN("a format string with placeholders")
+ {
+ constexpr auto fmt = "Here are some placeholders: {} {} {}"sv;
+
+ WHEN("calling format with the same number of arguments as there are placeholders")
+ {
+ auto result = kstd::formatted_size(fmt, 1, true, 'a');
+
+ THEN("the result is the formatted length")
+ {
+ REQUIRE(result == std::string_view{"Here are some placeholders: 1 true a"}.size());
+ }
+ }
+
+ WHEN("calling format with too many arguments")
+ {
+ auto result = kstd::formatted_size(fmt, 2, false, 'b', 4, 5, 6);
+
+ THEN("the result is the formatted length")
+ {
+ REQUIRE(result == std::string_view{"Here are some placeholders: 2 false b"}.size());
+ }
+ }
+ }
} \ No newline at end of file
diff --git a/libs/kstd/kstd/string b/libs/kstd/kstd/string
index de254a1d..4a67a3a4 100644
--- a/libs/kstd/kstd/string
+++ b/libs/kstd/kstd/string
@@ -5,44 +5,29 @@
#include <kstd/bits/format/context.hpp>
#include <kstd/bits/format/formatter.hpp>
#include <kstd/bits/format/formatter/cstring.hpp>
+#include <kstd/bits/format/formatter/integral.hpp>
+#include <kstd/bits/format/vformat.hpp>
#include <kstd/cstring>
#include <kstd/os/error.hpp>
#include <kstd/vector>
-#include <algorithm>
#include <concepts>
+#include <cstddef>
namespace kstd
{
using string = basic_string<char>;
/**
- * @brief Converts an unsigned integer to a string by converting each digit to the corresponding character and
- * concatenating them.
- * @tparam N The type of the unsigned integer to convert.
+ * @brief Converts an integer to a string as if by kstd::format("{}", value).
+ * @tparam IntegralType The type of the unsigned integer to convert.
* @param value The unsigned integer to convert.
* @return A string representation of the given unsigned integer.
*/
- template<typename N>
- requires std::unsigned_integral<N>
- [[nodiscard]] constexpr auto inline to_string(N value) -> string
+ template<std::integral IntegralType>
+ [[nodiscard]] constexpr auto inline to_string(IntegralType value) -> string
{
- if (value == 0)
- {
- return "0";
- }
-
- string result;
-
- while (value > 0)
- {
- char const digit = '0' + (value % 10);
- result.push_back(digit);
- value /= 10;
- }
-
- std::reverse(result.begin(), result.end());
- return result;
+ return kstd::format("{}", value);
}
template<>
@@ -54,6 +39,22 @@ namespace kstd
}
};
+ inline namespace literals
+ {
+ inline namespace string_literals
+ {
+ //! Create a string new string from a literal C-style string.
+ //!
+ //! @param string The C-style string to use as a source for the new string.
+ //! @param length The length of the supplied C-style string.
+ //! @return a new string object, containing the same value as the provided C-style string.
+ constexpr auto operator""_s(char const * string, std::size_t length) -> kstd::string
+ {
+ return {string, length};
+ }
+ } // namespace string_literals
+ } // namespace literals
+
} // namespace kstd
#endif \ No newline at end of file
diff --git a/libs/kstd/kstd/string.test.cpp b/libs/kstd/kstd/string.test.cpp
index 19345e09..e11877e5 100644
--- a/libs/kstd/kstd/string.test.cpp
+++ b/libs/kstd/kstd/string.test.cpp
@@ -8,9 +8,11 @@
#include <algorithm>
#include <cstring>
#include <forward_list>
+#include <functional>
#include <iterator>
#include <memory>
#include <sstream>
+#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
@@ -1952,6 +1954,112 @@ SCENARIO("String modifiers", "[string]")
REQUIRE(s.data() != old_data);
}
}
+
+ WHEN("resizing to a greater size within the capacity")
+ {
+ s.resize(10);
+
+ THEN("the capacity stays the same")
+ {
+ REQUIRE(s.capacity() == old_capacity);
+ }
+
+ THEN("the size increases")
+ {
+ CHECK(s.size() > old_size);
+ REQUIRE(s.size() == 10);
+ }
+
+ THEN("the string get filled with null characters")
+ {
+ REQUIRE(kstd::string::traits_type::compare(s.data(), "abcd\0\0\0\0\0\0", s.size() + 1) == 0);
+ }
+ }
+
+ WHEN("resizing to a greater size with a given character within the capacity")
+ {
+ s.resize(10, 'x');
+
+ THEN("the capacity stays the same")
+ {
+ REQUIRE(s.capacity() == old_capacity);
+ }
+
+ THEN("the size increases")
+ {
+ CHECK(s.size() > old_size);
+ REQUIRE(s.size() == 10);
+ }
+
+ THEN("the string get filled with the given character")
+ {
+ REQUIRE(kstd::string::traits_type::compare(s.data(), "abcdxxxxxx", s.size() + 1) == 0);
+ }
+ }
+
+ WHEN("resizing to a greater size outside the capacity")
+ {
+ s.resize(20);
+
+ THEN("the capacity increases")
+ {
+ REQUIRE(s.capacity() > old_capacity);
+ }
+
+ THEN("the size increases")
+ {
+ CHECK(s.size() > old_size);
+ REQUIRE(s.size() == 20);
+ }
+
+ THEN("the string get filled with null characters")
+ {
+ REQUIRE(kstd::string::traits_type::compare(s.data(), "abcd\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", s.size() + 1) ==
+ 0);
+ }
+ }
+
+ WHEN("resizing to a greater size with a given character within the capacity")
+ {
+ s.resize(20, 'x');
+
+ THEN("the capacity increases")
+ {
+ REQUIRE(s.capacity() > old_capacity);
+ }
+
+ THEN("the size increases")
+ {
+ CHECK(s.size() > old_size);
+ REQUIRE(s.size() == 20);
+ }
+
+ THEN("the string get filled with the given character")
+ {
+ REQUIRE(kstd::string::traits_type::compare(s.data(), "abcdxxxxxxxxxxxxxxxx", s.size() + 1) == 0);
+ }
+ }
+
+ WHEN("resizing to a lesser size")
+ {
+ s.resize(2);
+
+ THEN("the capacity stays the same")
+ {
+ REQUIRE(s.capacity() == old_capacity);
+ }
+
+ THEN("the size decreases")
+ {
+ CHECK(s.size() < old_size);
+ REQUIRE(s.size() == 2);
+ }
+
+ THEN("the string get filled with the given character")
+ {
+ REQUIRE(kstd::string::traits_type::compare(s.data(), "ab\0", s.size() + 1) == 0);
+ }
+ }
}
GIVEN("A long string")
@@ -2035,6 +2143,69 @@ SCENARIO("String modifiers", "[string]")
REQUIRE(s.data() != old_data);
}
}
+
+ WHEN("resizing to a greater size")
+ {
+ s.resize(22); // NOLINT
+
+ THEN("the capacity increases")
+ {
+ REQUIRE(s.capacity() > old_capacity);
+ }
+
+ THEN("the size increases")
+ {
+ CHECK(s.size() > old_size);
+ REQUIRE(s.size() == 22);
+ }
+
+ THEN("the string get filled with null characters")
+ {
+ REQUIRE(kstd::string::traits_type::compare(s.data(), "abcdefghijABCDEFGHIJ\0\0", s.size() + 1) == 0);
+ }
+ }
+
+ WHEN("resizing to a greater size with a given character within the capacity")
+ {
+ s.resize(22, 'x'); // NOLINT
+
+ THEN("the capacity increases")
+ {
+ REQUIRE(s.capacity() > old_capacity);
+ }
+
+ THEN("the size increases")
+ {
+ CHECK(s.size() > old_size);
+ REQUIRE(s.size() == 22);
+ }
+
+ THEN("the string get filled with the given character")
+ {
+ REQUIRE(kstd::string::traits_type::compare(s.data(), "abcdefghijABCDEFGHIJxx", s.size() + 1) == 0);
+ }
+ }
+
+ WHEN("resizing to a lesser size")
+ {
+ s.resize(2);
+
+ THEN("the capacity stays the same")
+ {
+ REQUIRE(s.capacity() == old_capacity);
+ }
+
+ THEN("the size decreases")
+ {
+ CHECK(s.size() < old_size);
+ REQUIRE(s.size() == 2);
+ }
+
+ THEN("the string get filled with the given character")
+ {
+ REQUIRE(kstd::string::traits_type::compare(s.data(), "ab\0", s.size() + 1) == 0);
+ }
+ }
}
GIVEN("Two strings")
@@ -2152,6 +2323,310 @@ SCENARIO("String modifiers", "[string]")
}
}
+SCENARIO("String insertion", "[string]")
+{
+ GIVEN("A short string")
+ {
+ auto s = kstd::string{"abcd"};
+ auto old_capacity = s.capacity();
+ auto old_size = s.size();
+
+ WHEN("inserting five copies of a character at the beginning")
+ {
+ s.insert(0, 5, 'A');
+
+ THEN("the size of the string is increased by 5")
+ {
+ REQUIRE(s.size() == old_size + 5);
+ }
+
+ THEN("The capacity of the string stays the same")
+ {
+ REQUIRE(s.capacity() == old_capacity);
+ }
+
+ THEN("The string is equal to the old string with the new characters prepended")
+ {
+ REQUIRE(s == kstd::string{"AAAAAabcd"});
+ }
+ }
+
+ WHEN("inserting five copies of a character in the middle")
+ {
+ s.insert(2, 5, 'A');
+
+ THEN("the size of the string is increased by 5")
+ {
+ REQUIRE(s.size() == old_size + 5);
+ }
+
+ THEN("The capacity of the string stays the same")
+ {
+ REQUIRE(s.capacity() == old_capacity);
+ }
+
+ THEN("The string is equal to the old string with the new characters inserted")
+ {
+ REQUIRE(s == kstd::string{"abAAAAAcd"});
+ }
+ }
+
+ WHEN("inserting five copies of a character at the end")
+ {
+ s.insert(4, 5, 'A');
+
+ THEN("the size of the string is increased by 5")
+ {
+ REQUIRE(s.size() == old_size + 5);
+ }
+
+ THEN("The capacity of the string stays the same")
+ {
+ REQUIRE(s.capacity() == old_capacity);
+ }
+
+ THEN("The string is equal to the old string with the new characters appended")
+ {
+ REQUIRE(s == kstd::string{"abcdAAAAA"});
+ }
+ }
+
+ WHEN("inserting five copies of a character behind the end")
+ {
+ THEN("an OS panic is triggered")
+ {
+ REQUIRE_THROWS_AS(s.insert(5, 5, 'A'), kstd::tests::os_panic);
+ }
+ }
+
+ WHEN("inserting 20 copies of a character")
+ {
+ s.insert(2, 20, 'A');
+
+ THEN("the capacity of the string grows")
+ {
+ REQUIRE(s.capacity() > old_capacity);
+ }
+
+ THEN("the string is equal t o the old string with the new characters inserted")
+ {
+ REQUIRE(s == kstd::string{"abAAAAAAAAAAAAAAAAAAAAcd"});
+ }
+ }
+
+ WHEN("inserting a C-style string of length 5 at the beginning")
+ {
+ s.insert(0, "AAAAA");
+
+ THEN("the size of the string is increased by 5")
+ {
+ REQUIRE(s.size() == old_size + 5);
+ }
+
+ THEN("The capacity of the string stays the same")
+ {
+ REQUIRE(s.capacity() == old_capacity);
+ }
+
+ THEN("The string is equal to the old string with the new characters prepended")
+ {
+ REQUIRE(s == kstd::string{"AAAAAabcd"});
+ }
+ }
+
+ WHEN("inserting a C-style string of length 5 in the middle")
+ {
+ s.insert(2, "AAAAA");
+
+ THEN("the size of the string is increased by 5")
+ {
+ REQUIRE(s.size() == old_size + 5);
+ }
+
+ THEN("The capacity of the string stays the same")
+ {
+ REQUIRE(s.capacity() == old_capacity);
+ }
+
+ THEN("The string is equal to the old string with the new characters inserted")
+ {
+ REQUIRE(s == kstd::string{"abAAAAAcd"});
+ }
+ }
+
+ WHEN("inserting a C-style string of length 5 at the end")
+ {
+ s.insert(4, "AAAAA");
+
+ THEN("the size of the string is increased by 5")
+ {
+ REQUIRE(s.size() == old_size + 5);
+ }
+
+ THEN("The capacity of the string stays the same")
+ {
+ REQUIRE(s.capacity() == old_capacity);
+ }
+
+ THEN("The string is equal to the old string with the new characters appended")
+ {
+ REQUIRE(s == kstd::string{"abcdAAAAA"});
+ }
+ }
+
+ WHEN("inserting a C-style string of length 5 behind the end")
+ {
+ THEN("an OS panic is triggered")
+ {
+ REQUIRE_THROWS_AS(s.insert(5, "AAAAA"), kstd::tests::os_panic);
+ }
+ }
+ }
+
+ GIVEN("A long string")
+ {
+ auto s = kstd::string{"abcdefghijABCDEFGHIJ"};
+ auto old_capacity = s.capacity();
+ auto old_size = s.size();
+
+ WHEN("inserting five copies of a character at the beginning")
+ {
+ s.insert(0, 5, 'A');
+
+ THEN("the size of the string is increased by 5")
+ {
+ REQUIRE(s.size() == old_size + 5);
+ }
+
+ THEN("The capacity of the string increases")
+ {
+ REQUIRE(s.capacity() >= old_capacity + 5);
+ }
+
+ THEN("The string is equal to the old string with the new characters prepended")
+ {
+ REQUIRE(s == kstd::string{"AAAAAabcdefghijABCDEFGHIJ"});
+ }
+ }
+
+ WHEN("inserting five copies of a character in the middle")
+ {
+ s.insert(10, 5, 'A');
+
+ THEN("the size of the string is increased by 5")
+ {
+ REQUIRE(s.size() == old_size + 5);
+ }
+
+ THEN("The capacity of the string increases")
+ {
+ REQUIRE(s.capacity() >= old_capacity + 5);
+ }
+
+ THEN("The string is equal to the old string with the new characters inserted")
+ {
+ REQUIRE(s == kstd::string{"abcdefghijAAAAAABCDEFGHIJ"});
+ }
+ }
+
+ WHEN("inserting five copies of a character at the end")
+ {
+ s.insert(20, 5, 'A');
+
+ THEN("the size of the string is increased by 5")
+ {
+ REQUIRE(s.size() == old_size + 5);
+ }
+
+ THEN("The capacity of the string increases")
+ {
+ REQUIRE(s.capacity() >= old_capacity + 5);
+ }
+
+ THEN("The string is equal to the old string with the new characters appended")
+ {
+ REQUIRE(s == kstd::string{"abcdefghijABCDEFGHIJAAAAA"});
+ }
+ }
+
+ WHEN("inserting five copies of a character behind the end")
+ {
+ THEN("an OS panic is triggered")
+ {
+ REQUIRE_THROWS_AS(s.insert(21, 5, 'A'), kstd::tests::os_panic);
+ }
+ }
+
+ WHEN("inserting a C-style string of length 5 at the beginning")
+ {
+ s.insert(0, "AAAAA");
+
+ THEN("the size of the string is increased by 5")
+ {
+ REQUIRE(s.size() == old_size + 5);
+ }
+
+ THEN("The capacity of the string increases")
+ {
+ REQUIRE(s.capacity() >= old_capacity + 5);
+ }
+
+ THEN("The string is equal to the old string with the new characters prepended")
+ {
+ REQUIRE(s == kstd::string{"AAAAAabcdefghijABCDEFGHIJ"});
+ }
+ }
+
+ WHEN("inserting a C-style string of length 5 in the middle")
+ {
+ s.insert(10, "AAAAA");
+
+ THEN("the size of the string is increased by 5")
+ {
+ REQUIRE(s.size() == old_size + 5);
+ }
+
+ THEN("The capacity of the string increases")
+ {
+ REQUIRE(s.capacity() >= old_capacity + 5);
+ }
+
+ THEN("The string is equal to the old string with the new characters inserted")
+ {
+ REQUIRE(s == kstd::string{"abcdefghijAAAAAABCDEFGHIJ"});
+ }
+ }
+
+ WHEN("inserting a C-style string of length 5 at the end")
+ {
+ s.insert(20, "AAAAA");
+
+ THEN("the size of the string is increased by 5")
+ {
+ REQUIRE(s.size() == old_size + 5);
+ }
+
+ THEN("The capacity of the increases")
+ {
+ REQUIRE(s.capacity() >= old_capacity + 5);
+ }
+
+ THEN("The string is equal to the old string with the new characters appended")
+ {
+ REQUIRE(s == kstd::string{"abcdefghijABCDEFGHIJAAAAA"});
+ }
+ }
+
+ WHEN("inserting a C-style string of length 5 behind the end")
+ {
+ THEN("an OS panic is triggered")
+ {
+ REQUIRE_THROWS_AS(s.insert(21, "AAAAA"), kstd::tests::os_panic);
+ }
+ }
+ }
+}
+
SCENARIO("String concatenation", "[string]")
{
GIVEN("Two strings")
@@ -2229,6 +2704,311 @@ SCENARIO("String conversion and comparison", "[string]")
REQUIRE_FALSE(view != str);
}
}
+
+ GIVEN("The string 'abc'")
+ {
+ auto s1 = kstd::string{"abc"};
+
+ WHEN("comparing with the string 'def'")
+ {
+ auto result = s1.compare(kstd::string{"def"});
+
+ THEN("the result is negative")
+ {
+ REQUIRE(result < 0);
+ }
+ }
+
+ WHEN("comparing with the string 'abb'")
+ {
+ auto result = s1.compare(kstd::string{"abb"});
+
+ THEN("the result is positive")
+ {
+ REQUIRE(result > 0);
+ }
+ }
+
+ WHEN("comparing with the string 'abc'")
+ {
+ auto result = s1.compare(kstd::string{"abc"});
+
+ THEN("the result is zero")
+ {
+ REQUIRE(result == 0);
+ }
+ }
+
+ WHEN("comparing substring [1,2] with the string 'def'")
+ {
+ auto result = s1.compare(1, 2, kstd::string{"def"});
+
+ THEN("the result is negative")
+ {
+ REQUIRE(result < 0);
+ }
+ }
+
+ WHEN("comparing substring [1,2] with the string 'abb'")
+ {
+ auto result = s1.compare(1, 2, kstd::string{"abb"});
+
+ THEN("the result is positive")
+ {
+ REQUIRE(result > 0);
+ }
+ }
+
+ WHEN("comparing substring [1,2] with the string 'cc'")
+ {
+ auto result = s1.compare(1, 2, kstd::string{"bc"});
+
+ THEN("the result is zero")
+ {
+ REQUIRE(result == 0);
+ }
+ }
+
+ WHEN("comparing substring [1,npos] with the string 'ef'")
+ {
+ auto result = s1.compare(1, kstd::string::npos, kstd::string{"ef"});
+
+ THEN("the result is negative")
+ {
+ REQUIRE(result < 0);
+ }
+ }
+
+ WHEN("comparing substring [1,npos] with the string 'bb'")
+ {
+ auto result = s1.compare(1, kstd::string::npos, kstd::string{"bb"});
+
+ THEN("the result is positive")
+ {
+ REQUIRE(result > 0);
+ }
+ }
+
+ WHEN("comparing substring [1,npos] with the string 'bc'")
+ {
+ auto result = s1.compare(1, kstd::string::npos, kstd::string{"bc"});
+
+ THEN("the result is zero")
+ {
+ REQUIRE(result == 0);
+ }
+ }
+
+ WHEN("comparing substring [1,2] with the substring [1,2] of 'def'")
+ {
+ auto result = s1.compare(1, 2, kstd::string{"def"}, 1, 2);
+
+ THEN("the result is negative")
+ {
+ REQUIRE(result < 0);
+ }
+ }
+
+ WHEN("comparing substring [1,2] with the substring [1,2] of 'abb'")
+ {
+ auto result = s1.compare(1, 2, kstd::string{"abb"}, 1, 2);
+
+ THEN("the result is positive")
+ {
+ REQUIRE(result > 0);
+ }
+ }
+
+ WHEN("comparing substring [1,2] with the substring [1,2] of 'abc'")
+ {
+ auto result = s1.compare(1, 2, kstd::string{"abc"}, 1, 2);
+
+ THEN("the result is zero")
+ {
+ REQUIRE(result == 0);
+ }
+ }
+
+ WHEN("comparing substring [1,npos] with the substring [1,2] of 'def'")
+ {
+ auto result = s1.compare(1, kstd::string::npos, kstd::string{"def"}, 1, 2);
+
+ THEN("the result is negative")
+ {
+ REQUIRE(result < 0);
+ }
+ }
+
+ WHEN("comparing substring [1,npos] with the substring [1,2] of 'abb'")
+ {
+ auto result = s1.compare(1, kstd::string::npos, kstd::string{"abb"}, 1, 2);
+
+ THEN("the result is positive")
+ {
+ REQUIRE(result > 0);
+ }
+ }
+
+ WHEN("comparing substring [1,npos] with the substring [1,2] of 'abc'")
+ {
+ auto result = s1.compare(1, kstd::string::npos, kstd::string{"abc"}, 1, 2);
+
+ THEN("the result is zero")
+ {
+ REQUIRE(result == 0);
+ }
+ }
+
+ WHEN("comparing substring [1,2] with the substring [1,npos] of 'def'")
+ {
+ auto result = s1.compare(1, 2, kstd::string{"def"}, 1, kstd::string::npos);
+
+ THEN("the result is negative")
+ {
+ REQUIRE(result < 0);
+ }
+ }
+
+ WHEN("comparing substring [1,2] with the substring [1,npos] of 'abb'")
+ {
+ auto result = s1.compare(1, 2, kstd::string{"abb"}, 1, kstd::string::npos);
+
+ THEN("the result is positive")
+ {
+ REQUIRE(result > 0);
+ }
+ }
+
+ WHEN("comparing substring [1,2] with the substring [1,npos] of 'abc'")
+ {
+ auto result = s1.compare(1, 2, kstd::string{"abc"}, 1, kstd::string::npos);
+
+ THEN("the result is zero")
+ {
+ REQUIRE(result == 0);
+ }
+ }
+
+ WHEN("comparing substring [1,npos] with the substring [1,npos] of 'def'")
+ {
+ auto result = s1.compare(1, kstd::string::npos, kstd::string{"def"}, 1, kstd::string::npos);
+
+ THEN("the result is negative")
+ {
+ REQUIRE(result < 0);
+ }
+ }
+
+ WHEN("comparing substring [1,npos] with the substring [1,npos] of 'abb'")
+ {
+ auto result = s1.compare(1, kstd::string::npos, kstd::string{"abb"}, 1, kstd::string::npos);
+
+ THEN("the result is positive")
+ {
+ REQUIRE(result > 0);
+ }
+ }
+
+ WHEN("comparing substring [1,npos] with the substring [1,npos] of 'abc'")
+ {
+ auto result = s1.compare(1, kstd::string::npos, kstd::string{"abc"}, 1, kstd::string::npos);
+
+ THEN("the result is zero")
+ {
+ REQUIRE(result == 0);
+ }
+ }
+
+ WHEN("comparing with the C-style string 'def'")
+ {
+ auto result = s1.compare("def");
+
+ THEN("the result is negative")
+ {
+ REQUIRE(result < 0);
+ }
+ }
+
+ WHEN("comparing with the C-style string 'abb'")
+ {
+ auto result = s1.compare("abb");
+
+ THEN("the result is positive")
+ {
+ REQUIRE(result > 0);
+ }
+ }
+
+ WHEN("comparing with the C-style string 'abc'")
+ {
+ auto result = s1.compare("abc");
+
+ THEN("the result is zero")
+ {
+ REQUIRE(result == 0);
+ }
+ }
+
+ WHEN("comparing substring [1,2] with the C-style string 'def'")
+ {
+ auto result = s1.compare(1, 2, "def");
+
+ THEN("the result is negative")
+ {
+ REQUIRE(result < 0);
+ }
+ }
+
+ WHEN("comparing substring [1,2] with the C-style string 'abb'")
+ {
+ auto result = s1.compare(1, 2, "abb");
+
+ THEN("the result is positive")
+ {
+ REQUIRE(result > 0);
+ }
+ }
+
+ WHEN("comparing substring [1,2] with the C-style string 'bc'")
+ {
+ auto result = s1.compare(1, 2, "bc");
+
+ THEN("the result is zero")
+ {
+ REQUIRE(result == 0);
+ }
+ }
+
+ WHEN("comparing substring [1,npos] with the C-style string 'def'")
+ {
+ auto result = s1.compare(1, 2, "ef");
+
+ THEN("the result is negative")
+ {
+ REQUIRE(result < 0);
+ }
+ }
+
+ WHEN("comparing substring [1,npos] with the C-style string 'abb'")
+ {
+ auto result = s1.compare(1, 2, "abb");
+
+ THEN("the result is positive")
+ {
+ REQUIRE(result > 0);
+ }
+ }
+
+ WHEN("comparing substring [1,npos] with the C-style string 'bc'")
+ {
+ auto result = s1.compare(1, 2, "bc");
+
+ THEN("the result is zero")
+ {
+ REQUIRE(result == 0);
+ }
+ }
+ }
}
SCENARIO("String iteration", "[string]")
@@ -2333,3 +3113,54 @@ SCENARIO("String iteration", "[string]")
}
}
}
+
+SCENARIO("String STL integration", "[string]")
+{
+ GIVEN("An empty string")
+ {
+ auto s = kstd::string{""};
+
+ WHEN("hashing the string")
+ {
+ auto hash_result = std::hash<kstd::string>{}(s);
+
+ THEN("the result is equal to hashing an empty std::string")
+ {
+ REQUIRE(hash_result == std::hash<std::string>{}(""));
+ }
+ }
+ }
+
+ GIVEN("A non-empty string")
+ {
+ auto s = kstd::string{"abcd"};
+
+ WHEN("hashing the string")
+ {
+ auto hash_result = std::hash<kstd::string>{}(s);
+
+ THEN("the result is equal to hashing the same std::string")
+ {
+ REQUIRE(hash_result == std::hash<std::string>{}("abcd"));
+ }
+ }
+ }
+}
+
+SCENARIO("String user-defined literals")
+{
+ GIVEN("the user defined literals are in scope")
+ {
+ using namespace kstd::string_literals;
+
+ WHEN("creating a string using the _s literal suffix")
+ {
+ auto s = "abcd"_s;
+
+ THEN("the result is equal to the string created with the same literal")
+ {
+ REQUIRE(s == kstd::string{"abcd"});
+ }
+ }
+ }
+}