diff options
| -rw-r--r-- | .nvim.lua | 2 | ||||
| -rw-r--r-- | CMakePresets.json | 8 | ||||
| -rw-r--r-- | libs/kstd/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | libs/kstd/kstd/bits/basic_string.hpp | 234 | ||||
| -rw-r--r-- | libs/kstd/kstd/bits/format/string.hpp | 8 | ||||
| -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.hpp | 41 | ||||
| -rw-r--r-- | libs/kstd/kstd/format | 36 | ||||
| -rw-r--r-- | libs/kstd/kstd/format.test.cpp | 68 | ||||
| -rw-r--r-- | libs/kstd/kstd/string | 47 | ||||
| -rw-r--r-- | libs/kstd/kstd/string.test.cpp | 831 |
11 files changed, 1231 insertions, 61 deletions
@@ -10,7 +10,7 @@ vim.g.load_doxygen_syntax = true -- C++ vim.filetype.add({ pattern = { - [".*/include/kstd/.*"] = function(path, _) + [".*/libs/kstd/kstd/.*"] = function(path, _) if not path:match("%.[^/]+$") then return "cpp" end diff --git a/CMakePresets.json b/CMakePresets.json index 0e5dd88c..0e70d1a9 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -47,7 +47,13 @@ "testPresets": [ { "name": "bht-dbg", - "configurePreset": "bht" + "configurePreset": "bht", + "output": { + "outputOnFailure": true + }, + "execution": { + "jobs": 0 + } } ] }
\ No newline at end of file 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"}); + } + } + } +} |
