From 08bfe2af099f6acc78772e7b315a6cf922626b76 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 24 Jun 2026 13:03:29 +0200 Subject: kstd: implement kstd::formatted_size --- libs/kstd/CMakeLists.txt | 2 +- libs/kstd/kstd/bits/format/string.hpp | 8 +- libs/kstd/kstd/bits/format/vformat.cpp | 224 +++++++++++++++++++++++++++++++++ libs/kstd/kstd/bits/format/vformat.hpp | 41 +++++- libs/kstd/kstd/format | 36 +++--- libs/kstd/kstd/format.test.cpp | 68 +++++++++- libs/kstd/kstd/vformat.cpp | 209 ------------------------------ 7 files changed, 347 insertions(+), 241 deletions(-) create mode 100644 libs/kstd/kstd/bits/format/vformat.cpp delete mode 100644 libs/kstd/kstd/vformat.cpp (limited to 'libs') 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/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 struct format_string { - template - 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 + 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/bits/format/vformat.cpp b/libs/kstd/kstd/bits/format/vformat.cpp new file mode 100644 index 00000000..1bf293a4 --- /dev/null +++ b/libs/kstd/kstd/bits/format/vformat.cpp @@ -0,0 +1,224 @@ +#include +#include + +#include +#include +#include +#include + +namespace kstd::bits::format +{ + + auto vformat_to(output_buffer & buffer, std::string_view format, format_args args) -> void + { + auto context = kstd::format_context{buffer, args}; + auto parse_context = kstd::format_parse_context{format, args.size()}; + + auto it = parse_context.begin(); + auto end = parse_context.end(); + + while (it != end) + { + if (*it != '{' && *it != '}') + { + auto start = it; + while (it != end && *it != '{' && *it != '}') + { + std::advance(it, 1); + } + parse_context.advance_to(it); + context.push(std::string_view(start, it - start)); + continue; + } + + if (*it == '{') + { + std::advance(it, 1); + if (it != end && *it == '{') + { + context.push('{'); + std::advance(it, 1); + parse_context.advance_to(it); + continue; + } + + parse_context.advance_to(it); + auto index = 0uz; + + if (it != end && *it >= '0' && *it <= '9') + { + while (it != end && *it >= '0' && *it <= '9') + { + index = index * 10 + static_cast(*it - '0'); + std::advance(it, 1); + } + parse_context.check_arg_id(index); + } + else + { + index = parse_context.next_arg_id(); + } + + if (it != end && *it == ':') + { + std::advance(it, 1); + } + + parse_context.advance_to(it); + + if (index < args.size()) + { + auto const & arg = args[index]; + switch (arg.type) + { + case kstd::bits::format::arg_type::boolean: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.boolean, context); + break; + } + case kstd::bits::format::arg_type::character: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.character, context); + break; + } + case kstd::bits::format::arg_type::integer: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.integer, context); + break; + } + case kstd::bits::format::arg_type::unsigned_integer: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.unsigned_integer, context); + break; + } + case kstd::bits::format::arg_type::string_view: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.string_view, context); + break; + } + case kstd::bits::format::arg_type::c_string: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.c_string, context); + break; + } + case kstd::bits::format::arg_type::pointer: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.pointer, context); + break; + } + case kstd::bits::format::arg_type::user_defined: + { + if (arg.value.user_defined.format) + { + arg.value.user_defined.format(arg.value.user_defined.pointer, parse_context, context); + } + else + { + context.push("{?}"); + } + break; + } + default: + { + context.push("{fmt-err: unknown-type}"); + break; + } + } + } + else + { + context.push("{fmt-err: bound}"); + } + + it = parse_context.begin(); + + if (it != end && *it == '}') + { + std::advance(it, 1); + parse_context.advance_to(it); + } + else + { + context.push("{fmt-err: unconsumed}"); + while (it != end && *it != '}') + { + std::advance(it, 1); + } + + if (it != end) + { + std::advance(it, 1); + parse_context.advance_to(it); + } + } + } + else if (*it == '}') + { + std::advance(it, 1); + if (it != end && *it == '}') + { + context.push('}'); + std::advance(it, 1); + parse_context.advance_to(it); + } + else + { + context.push("{fmt-err: unescaped}"); + parse_context.advance_to(it); + } + } + } + } + + auto string_writer::push(std::string_view text) -> void + { + m_result.append(text); + } + + auto string_writer::push(char character) -> void + { + m_result.push_back(character); + } + + auto string_writer::release() -> string && + { + 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 +#include #include #include #include -#include #include +#include #include #include #include @@ -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 &&; private: - string m_result{}; + basic_string 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 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 - auto format(format_string...> format, ArgumentTypes &&... args) -> string + auto format(format_string...> format, ArgumentTypes &&... args) + -> basic_string { auto buffer = bits::format::string_writer{}; bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward(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 + auto formatted_size(format_string...> 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(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 // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export +// IWYU pragma: begin_exports +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// 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 #include +#include + +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{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/vformat.cpp b/libs/kstd/kstd/vformat.cpp deleted file mode 100644 index b7c51218..00000000 --- a/libs/kstd/kstd/vformat.cpp +++ /dev/null @@ -1,209 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -namespace kstd::bits::format -{ - - auto vformat_to(output_buffer & buffer, std::string_view format, format_args args) -> void - { - auto context = kstd::format_context{buffer, args}; - auto parse_context = kstd::format_parse_context{format, args.size()}; - - auto it = parse_context.begin(); - auto end = parse_context.end(); - - while (it != end) - { - if (*it != '{' && *it != '}') - { - auto start = it; - while (it != end && *it != '{' && *it != '}') - { - std::advance(it, 1); - } - parse_context.advance_to(it); - context.push(std::string_view(start, it - start)); - continue; - } - - if (*it == '{') - { - std::advance(it, 1); - if (it != end && *it == '{') - { - context.push('{'); - std::advance(it, 1); - parse_context.advance_to(it); - continue; - } - - parse_context.advance_to(it); - auto index = 0uz; - - if (it != end && *it >= '0' && *it <= '9') - { - while (it != end && *it >= '0' && *it <= '9') - { - index = index * 10 + static_cast(*it - '0'); - std::advance(it, 1); - } - parse_context.check_arg_id(index); - } - else - { - index = parse_context.next_arg_id(); - } - - if (it != end && *it == ':') - { - std::advance(it, 1); - } - - parse_context.advance_to(it); - - if (index < args.size()) - { - auto const & arg = args[index]; - switch (arg.type) - { - case kstd::bits::format::arg_type::boolean: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.boolean, context); - break; - } - case kstd::bits::format::arg_type::character: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.character, context); - break; - } - case kstd::bits::format::arg_type::integer: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.integer, context); - break; - } - case kstd::bits::format::arg_type::unsigned_integer: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.unsigned_integer, context); - break; - } - case kstd::bits::format::arg_type::string_view: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.string_view, context); - break; - } - case kstd::bits::format::arg_type::c_string: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.c_string, context); - break; - } - case kstd::bits::format::arg_type::pointer: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.pointer, context); - break; - } - case kstd::bits::format::arg_type::user_defined: - { - if (arg.value.user_defined.format) - { - arg.value.user_defined.format(arg.value.user_defined.pointer, parse_context, context); - } - else - { - context.push("{?}"); - } - break; - } - default: - { - context.push("{fmt-err: unknown-type}"); - break; - } - } - } - else - { - context.push("{fmt-err: bound}"); - } - - it = parse_context.begin(); - - if (it != end && *it == '}') - { - std::advance(it, 1); - parse_context.advance_to(it); - } - else - { - context.push("{fmt-err: unconsumed}"); - while (it != end && *it != '}') - { - std::advance(it, 1); - } - - if (it != end) - { - std::advance(it, 1); - parse_context.advance_to(it); - } - } - } - else if (*it == '}') - { - std::advance(it, 1); - if (it != end && *it == '}') - { - context.push('}'); - std::advance(it, 1); - parse_context.advance_to(it); - } - else - { - context.push("{fmt-err: unescaped}"); - parse_context.advance_to(it); - } - } - } - } - - auto string_writer::push(std::string_view text) -> void - { - m_result.append(text); - } - - auto string_writer::push(char character) -> void - { - m_result.push_back(character); - } - - auto string_writer::release() -> string && - { - return std::move(m_result); - } - -} // namespace kstd::bits::format \ No newline at end of file -- cgit v1.2.3 From b41581ed656dde74b7a7b961b8a5c08bc61fb7d4 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 24 Jun 2026 13:10:40 +0200 Subject: kstd: simplify kstd::to_string --- libs/kstd/kstd/string | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) (limited to 'libs') diff --git a/libs/kstd/kstd/string b/libs/kstd/kstd/string index de254a1d..682b8404 100644 --- a/libs/kstd/kstd/string +++ b/libs/kstd/kstd/string @@ -5,11 +5,12 @@ #include #include #include +#include +#include #include #include #include -#include #include namespace kstd @@ -17,32 +18,15 @@ namespace kstd using string = basic_string; /** - * @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 - requires std::unsigned_integral - [[nodiscard]] constexpr auto inline to_string(N value) -> string + template + [[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<> -- cgit v1.2.3 From 8583e53229e7b6cbe8b28d615ba8a02e4732d65b Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 25 Jun 2026 10:10:34 +0200 Subject: kstd: add more basic_string::insert overloads --- libs/kstd/kstd/bits/basic_string.hpp | 43 +++++ libs/kstd/kstd/string.test.cpp | 304 +++++++++++++++++++++++++++++++++++ 2 files changed, 347 insertions(+) (limited to 'libs') diff --git a/libs/kstd/kstd/bits/basic_string.hpp b/libs/kstd/kstd/bits/basic_string.hpp index 5d2eecd7..0509e33a 100644 --- a/libs/kstd/kstd/bits/basic_string.hpp +++ b/libs/kstd/kstd/bits/basic_string.hpp @@ -734,6 +734,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 +745,48 @@ namespace kstd return do_insert(index, std::basic_string_view{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{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>(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 + requires(std::is_convertible_v> && + !std::is_convertible_v) + constexpr auto insert(size_type index, StringViewLike const & view_like) -> basic_string & + { + auto view = static_cast>(view_like); + return do_insert(index, view); + } + //! Append a character to this string. //! //! @param character The character to append. diff --git a/libs/kstd/kstd/string.test.cpp b/libs/kstd/kstd/string.test.cpp index 19345e09..74be1f0b 100644 --- a/libs/kstd/kstd/string.test.cpp +++ b/libs/kstd/kstd/string.test.cpp @@ -2152,6 +2152,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") -- cgit v1.2.3 From d0d7b1253d6c93a2a0bda57bc52fa40169dfad50 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 26 Jun 2026 16:56:46 +0200 Subject: kstd: add some basic_string::compare overloads --- libs/kstd/kstd/bits/basic_string.hpp | 110 ++++++++++++- libs/kstd/kstd/string.test.cpp | 305 +++++++++++++++++++++++++++++++++++ 2 files changed, 411 insertions(+), 4 deletions(-) (limited to 'libs') diff --git a/libs/kstd/kstd/bits/basic_string.hpp b/libs/kstd/kstd/bits/basic_string.hpp index 0509e33a..212aa8ac 100644 --- a/libs/kstd/kstd/bits/basic_string.hpp +++ b/libs/kstd/kstd/bits/basic_string.hpp @@ -981,6 +981,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>(*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>(*this); + auto other_view = static_cast>(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>(*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>(*this); + auto other_view = static_cast>(string, string_count); + return do_compare(view.substr(position, count), other_view); + } + //! Concatenate two string. //! //! @param lhs The left hand side string. @@ -1017,7 +1098,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. @@ -1041,7 +1122,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. @@ -1052,7 +1133,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. @@ -1063,7 +1144,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. @@ -1300,6 +1381,27 @@ namespace kstd return *this; } + [[nodiscard]] constexpr auto static do_compare(std::basic_string_view lhs, + std::basic_string_view 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. diff --git a/libs/kstd/kstd/string.test.cpp b/libs/kstd/kstd/string.test.cpp index 74be1f0b..3fbf6c0c 100644 --- a/libs/kstd/kstd/string.test.cpp +++ b/libs/kstd/kstd/string.test.cpp @@ -2533,6 +2533,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]") -- cgit v1.2.3 From 278256683f3be86d21e008670ab1bc1b76cdc3b1 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Jul 2026 09:12:09 +0200 Subject: kstd: implement string hash support --- libs/kstd/kstd/bits/basic_string.hpp | 21 +++++++++++++++++++++ libs/kstd/kstd/string.test.cpp | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) (limited to 'libs') diff --git a/libs/kstd/kstd/bits/basic_string.hpp b/libs/kstd/kstd/bits/basic_string.hpp index 212aa8ac..4d690ac3 100644 --- a/libs/kstd/kstd/bits/basic_string.hpp +++ b/libs/kstd/kstd/bits/basic_string.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -1546,4 +1547,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 +struct std::hash, Allocator>> +{ + constexpr auto static implementation = std::hash{}; + + constexpr auto operator()(kstd::basic_string, Allocator> string) const noexcept + -> std::size_t + { + return implementation(string); + }; +}; + #endif diff --git a/libs/kstd/kstd/string.test.cpp b/libs/kstd/kstd/string.test.cpp index 3fbf6c0c..2de8b513 100644 --- a/libs/kstd/kstd/string.test.cpp +++ b/libs/kstd/kstd/string.test.cpp @@ -8,9 +8,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -2942,3 +2944,36 @@ 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{}(s); + + THEN("the result is equal to hashing an empty std::string") + { + REQUIRE(hash_result == std::hash{}("")); + } + } + } + + GIVEN("A non-empty string") + { + auto s = kstd::string{"abcd"}; + + WHEN("hashing the string") + { + auto hash_result = std::hash{}(s); + + THEN("the result is equal to hashing the same std::string") + { + REQUIRE(hash_result == std::hash{}("abcd")); + } + } + } +} -- cgit v1.2.3 From ab8c37673ca247c190d60147dff66c958ec960de Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Jul 2026 10:20:12 +0200 Subject: kstd: implement _s UDL operator for string. --- libs/kstd/kstd/string | 17 +++++++++++++++++ libs/kstd/kstd/string.test.cpp | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'libs') diff --git a/libs/kstd/kstd/string b/libs/kstd/kstd/string index 682b8404..4a67a3a4 100644 --- a/libs/kstd/kstd/string +++ b/libs/kstd/kstd/string @@ -12,6 +12,7 @@ #include #include +#include namespace kstd { @@ -38,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 2de8b513..27ca95b3 100644 --- a/libs/kstd/kstd/string.test.cpp +++ b/libs/kstd/kstd/string.test.cpp @@ -2977,3 +2977,21 @@ SCENARIO("String STL integration", "[string]") } } } + +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"}); + } + } + } +} -- cgit v1.2.3 From a24e133b7b90afae64b3edf0d8dd29b6e98a250b Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Jul 2026 19:11:26 +0200 Subject: kstd: implement resize for string --- libs/kstd/kstd/bits/basic_string.hpp | 30 +++++++ libs/kstd/kstd/string.test.cpp | 169 +++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) (limited to 'libs') diff --git a/libs/kstd/kstd/bits/basic_string.hpp b/libs/kstd/kstd/bits/basic_string.hpp index 4d690ac3..c780a083 100644 --- a/libs/kstd/kstd/bits/basic_string.hpp +++ b/libs/kstd/kstd/bits/basic_string.hpp @@ -926,6 +926,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. diff --git a/libs/kstd/kstd/string.test.cpp b/libs/kstd/kstd/string.test.cpp index 27ca95b3..e11877e5 100644 --- a/libs/kstd/kstd/string.test.cpp +++ b/libs/kstd/kstd/string.test.cpp @@ -1954,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") @@ -2037,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") -- cgit v1.2.3 From 3b2e03acb3fdf19e5546c29363fa538f59da22df Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Jul 2026 19:20:39 +0200 Subject: kstd: implement some assign overloads for string --- libs/kstd/kstd/bits/basic_string.hpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'libs') diff --git a/libs/kstd/kstd/bits/basic_string.hpp b/libs/kstd/kstd/bits/basic_string.hpp index c780a083..1675434c 100644 --- a/libs/kstd/kstd/bits/basic_string.hpp +++ b/libs/kstd/kstd/bits/basic_string.hpp @@ -408,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. -- cgit v1.2.3