diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/kstd/kstd/bits/format/formatter/cstring.hpp | 6 | ||||
| -rw-r--r-- | libs/kstd/kstd/bits/format/vformat.cpp | 20 | ||||
| -rw-r--r-- | libs/kstd/kstd/bits/format/vformat.hpp | 44 | ||||
| -rw-r--r-- | libs/kstd/kstd/format.tests.cpp | 90 |
4 files changed, 156 insertions, 4 deletions
diff --git a/libs/kstd/kstd/bits/format/formatter/cstring.hpp b/libs/kstd/kstd/bits/format/formatter/cstring.hpp index 553c8caf..12cb9c6f 100644 --- a/libs/kstd/kstd/bits/format/formatter/cstring.hpp +++ b/libs/kstd/kstd/bits/format/formatter/cstring.hpp @@ -5,6 +5,7 @@ #include <kstd/bits/format/formatter.hpp> #include <kstd/bits/format/formatter/string_view.hpp> +#include <cstddef> #include <string_view> namespace kstd @@ -24,6 +25,11 @@ namespace kstd { }; + template<std::size_t Size> + struct formatter<char[Size]> : formatter<char const *> // NOLINT + { + }; + } // namespace kstd #endif
\ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/vformat.cpp b/libs/kstd/kstd/bits/format/vformat.cpp index 495f0a10..7210a0b6 100644 --- a/libs/kstd/kstd/bits/format/vformat.cpp +++ b/libs/kstd/kstd/bits/format/vformat.cpp @@ -1,6 +1,7 @@ #include <kstd/format.hpp> #include <kstd/string.hpp> +#include <algorithm> #include <cstddef> #include <iterator> #include <string_view> @@ -221,4 +222,23 @@ namespace kstd::bits::format return m_size; } + auto span_writer::push(std::string_view text) -> void + { + if (m_position < m_span.size() - 1) + { + auto space_left = m_span.size() - m_position; + auto to_copy = std::min(space_left, text.size()); + std::ranges::copy_n(text.begin(), to_copy, m_span.subspan(m_position).begin()); + m_position += to_copy; + } + } + + auto span_writer::push(char character) -> void + { + if (m_position < m_span.size() - 1) + { + m_span[m_position++] = character; + } + } + } // 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 829e0ea4..63b41ab6 100644 --- a/libs/kstd/kstd/bits/format/vformat.hpp +++ b/libs/kstd/kstd/bits/format/vformat.hpp @@ -11,8 +11,10 @@ #include <algorithm> #include <cstddef> #include <iterator> +#include <span> #include <string_view> #include <type_traits> +#include <utility> namespace kstd { @@ -28,7 +30,7 @@ namespace kstd //! @param args The arguments for the format string. auto vformat_to(output_buffer & buffer, std::string_view format, format_args args) -> void; - struct string_writer : output_buffer + struct string_writer final : output_buffer { auto push(std::string_view text) -> void final; auto push(char character) -> void final; @@ -40,7 +42,7 @@ namespace kstd }; //! A buffer that merely records the size of the output written to it. - struct size_recorder : output_buffer + struct size_recorder final : output_buffer { auto push(std::string_view text) -> void final; auto push(char character) -> void final; @@ -52,9 +54,9 @@ namespace kstd }; template<std::output_iterator<char> Output> - struct iterator_writer : output_buffer + struct iterator_writer final : output_buffer { - explicit iterator_writer(Output iterator) + constexpr explicit iterator_writer(Output iterator) : m_output{iterator} {} @@ -77,6 +79,25 @@ namespace kstd Output m_output{}; }; + struct span_writer final : output_buffer + { + constexpr explicit span_writer(std::span<char> span) + : m_span{span} + {} + + auto push(std::string_view text) -> void override; + auto push(char character) -> void override; + + [[nodiscard]] constexpr auto position() const noexcept -> std::size_t + { + return m_position; + } + + private: + std::size_t m_position{}; + std::span<char> m_span{}; + }; + } // namespace bits::format //! Format a given string with the provided arguments. @@ -108,6 +129,21 @@ namespace kstd return buffer.iterator(); } + //! Format a given string with the provided arguments. + //! + //! @param span The span to write to. + //! @param format The format string. + //! @param args The arguments for the format string. + //! @return A pair of the span written to and the next position to write to. + template<typename... ArgumentTypes> + auto format_to(std::span<char> span, format_string<std::type_identity_t<ArgumentTypes>...> format, + ArgumentTypes &&... args) -> std::pair<std::span<char>, std::size_t> + { + auto buffer = bits::format::span_writer{span}; + bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward<ArgumentTypes>(args)...).args); + return {span, buffer.position()}; + } + //! Determine the number of characters required to format the format string with the given arguments. //! //! @param format The format string. diff --git a/libs/kstd/kstd/format.tests.cpp b/libs/kstd/kstd/format.tests.cpp index d38a8107..a34cf063 100644 --- a/libs/kstd/kstd/format.tests.cpp +++ b/libs/kstd/kstd/format.tests.cpp @@ -2,6 +2,7 @@ #include <catch2/catch_test_macros.hpp> +#include <array> #include <iterator> #include <sstream> #include <string_view> @@ -116,6 +117,95 @@ SCENARIO("Formatting to an output iterator", "[format]") } } +SCENARIO("Formatting to a span", "[format]") +{ + GIVEN("a format string without any placeholders") + { + constexpr auto fmt = "This is a test"sv; + + WHEN("calling format without any arguments.") + { + auto buffer = std::array<char, 100>{}; + auto [span, length] = kstd::format_to(buffer, fmt); + + THEN("the unmodified string is written to the span") + { + REQUIRE(std::string_view{span.data(), length} == "This is a test"); + } + } + + WHEN("calling format with additional arguments") + { + auto buffer = std::array<char, 100>{}; + auto [span, length] = kstd::format_to(buffer, fmt, 1, 2, 3); + + THEN("the unmodified string is written to the span") + { + REQUIRE(std::string_view(span.data(), length) == "This is a test"); + } + } + + WHEN("the span is too small") + { + auto buffer = std::array<char, 4>{}; + auto [span, length] = kstd::format_to(buffer, fmt, 1, 2, 3); + + THEN("the written length is equal to the span size") + { + REQUIRE(length == span.size()); + } + + THEN("only part of the unmodified string is written to the span") + { + REQUIRE(std::string_view(span.data(), length) == "This"); + } + } + } + + 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 buffer = std::array<char, 100>{}; + auto [span, length] = kstd::format_to(buffer, fmt, 1, true, -100); + + THEN("the formatted string is written to the span") + { + REQUIRE(std::string_view{span.data(), length} == "Here are some placeholders: 1 true -100"); + } + } + + WHEN("calling format with too many arguments") + { + auto buffer = std::array<char, 100>{}; + auto [span, length] = kstd::format_to(buffer, fmt, 2, false, -200, 4, 5, 6); + + THEN("the formatted string is written to the span") + { + REQUIRE(std::string_view{span.data(), length} == "Here are some placeholders: 2 false -200"); + } + } + + WHEN("the span is too small") + { + auto buffer = std::array<char, 4>{}; + auto [span, length] = kstd::format_to(buffer, fmt, 2, false, -200, 4, 5, 6); + + THEN("the written length is equal to the span size") + { + REQUIRE(length == span.size()); + } + + THEN("only part of the unmodified string is written to the span") + { + REQUIRE(std::string_view(span.data(), length) == "Here"); + } + } + } +} + SCENARIO("Determining formatted size") { GIVEN("a format string without any placeholders") |
