From e38fd2056a8cab7a3c6f3da8150fed69530bde6c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Apr 2026 14:14:08 +0200 Subject: kstd: introduce output buffer for formatting --- libs/kstd/include/kstd/bits/format/context.hpp | 12 ++++----- .../include/kstd/bits/format/output_buffer.hpp | 30 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 libs/kstd/include/kstd/bits/format/output_buffer.hpp (limited to 'libs/kstd/include') diff --git a/libs/kstd/include/kstd/bits/format/context.hpp b/libs/kstd/include/kstd/bits/format/context.hpp index 478a48f..e8d0302 100644 --- a/libs/kstd/include/kstd/bits/format/context.hpp +++ b/libs/kstd/include/kstd/bits/format/context.hpp @@ -4,6 +4,7 @@ // IWYU pragma: private, include #include "arg.hpp" +#include "kstd/bits/format/output_buffer.hpp" #include @@ -29,11 +30,7 @@ namespace kstd struct format_context { - using writer_function = void(void *, std::string_view); using format_args = std::span; - - writer_function * writer{}; - void * user_data{}; format_args args{}; [[nodiscard]] auto arg(std::size_t const id) const -> format_arg const & @@ -47,13 +44,16 @@ namespace kstd constexpr auto push(std::string_view string) -> void { - writer(user_data, string); + m_buffer->push(string); } constexpr auto push(char character) -> void { - writer(user_data, std::string_view(&character, 1)); + m_buffer->push(character); } + + private: + bits::format::output_buffer * m_buffer; }; } // namespace kstd diff --git a/libs/kstd/include/kstd/bits/format/output_buffer.hpp b/libs/kstd/include/kstd/bits/format/output_buffer.hpp new file mode 100644 index 0000000..be2034f --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/output_buffer.hpp @@ -0,0 +1,30 @@ +#ifndef KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP +#define KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP + +#include + +namespace kstd::bits::format +{ + + //! An abstract interface for formatted output buffers. + //! + //! This interface is intended to be use for functions dealing with string formatting, like the print and the format + //! family. + struct output_buffer + { + virtual ~output_buffer() = default; + + //! Push a text segment into the buffer. + //! + //! @param text The text segment to push. + virtual auto push(std::string_view text) -> void = 0; + + //! Push a single character into the buffer. + //! + //! @param character The character to push into the buffer. + virtual auto push(char character) -> void = 0; + }; + +} // namespace kstd::bits::format + +#endif \ No newline at end of file -- cgit v1.2.3 From 98fc294deb6f7c27eda3f9ed3b616572a1ab2009 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Apr 2026 14:37:20 +0200 Subject: kstd/format: hook up vformat_to --- .../include/kstd/bits/format/output_buffer.hpp | 2 + libs/kstd/include/kstd/bits/format/vformat.hpp | 65 ++++++++++++++++++++++ libs/kstd/include/kstd/format | 2 + 3 files changed, 69 insertions(+) create mode 100644 libs/kstd/include/kstd/bits/format/vformat.hpp (limited to 'libs/kstd/include') diff --git a/libs/kstd/include/kstd/bits/format/output_buffer.hpp b/libs/kstd/include/kstd/bits/format/output_buffer.hpp index be2034f..fd7a2b4 100644 --- a/libs/kstd/include/kstd/bits/format/output_buffer.hpp +++ b/libs/kstd/include/kstd/bits/format/output_buffer.hpp @@ -1,6 +1,8 @@ #ifndef KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP #define KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP +// IWYU pragma: private, include + #include namespace kstd::bits::format diff --git a/libs/kstd/include/kstd/bits/format/vformat.hpp b/libs/kstd/include/kstd/bits/format/vformat.hpp new file mode 100644 index 0000000..90b74a9 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/vformat.hpp @@ -0,0 +1,65 @@ +#ifndef KSTD_BITS_FORMAT_VFORMAT_HPP +#define KSTD_BITS_FORMAT_VFORMAT_HPP + +// IWYU pragma: private, include + +#include +#include +#include +#include + +#include +#include + +namespace kstd +{ + + namespace bits::format + { + //! Format a string with the given arguments into the given buffer. + //! + //! External implementations of `vprint` may call this function. + //! + //! @param buffer The buffer to format into. + //! @param format The format string to use. + //! @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 + { + auto push(std::string_view text) -> void override; + auto push(char character) -> void override; + + auto release() -> string &&; + + private: + string m_result{}; + }; + + struct string_iterator_writer : output_buffer + { + auto push(std::string_view text) -> void override; + auto push(char character) -> void override; + + private: + string::iterator m_iter{}; + }; + + } // namespace bits::format + + //! Format a given string with the provided arguments. + //! + //! @param format The format string. + //! @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 buffer = bits::format::string_writer{}; + bits::format::vformat_to(buffer, format.str_view, std::forward(args)...); + return buffer.release(); + } + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/format b/libs/kstd/include/kstd/format index adee5f8..d11c221 100644 --- a/libs/kstd/include/kstd/format +++ b/libs/kstd/include/kstd/format @@ -13,7 +13,9 @@ #include "bits/format/formatter/pointer.hpp" // IWYU pragma: export #include "bits/format/formatter/range.hpp" // IWYU pragma: export #include "bits/format/formatter/string_view.hpp" // IWYU pragma: export +#include "bits/format/output_buffer.hpp" // IWYU pragma: export #include "bits/format/parse_context.hpp" // IWYU pragma: export #include "bits/format/string.hpp" // IWYU pragma: export +#include "bits/format/vformat.hpp" // IWYU pragma: export #endif \ No newline at end of file -- cgit v1.2.3 From 45091789eb9e49856ea6c2f3606639d43f4584e7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Apr 2026 14:56:39 +0200 Subject: kstd: move formatting implementation to kstd --- libs/kstd/include/kstd/bits/format/context.hpp | 5 +++++ libs/kstd/include/kstd/bits/format/formatter/bool.hpp | 4 ++-- libs/kstd/include/kstd/bits/format/formatter/integral.hpp | 4 ++-- libs/kstd/include/kstd/bits/format/specifiers.hpp | 6 +++--- libs/kstd/include/kstd/bits/format/vformat.hpp | 2 ++ libs/kstd/include/kstd/vector | 2 +- 6 files changed, 15 insertions(+), 8 deletions(-) (limited to 'libs/kstd/include') diff --git a/libs/kstd/include/kstd/bits/format/context.hpp b/libs/kstd/include/kstd/bits/format/context.hpp index e8d0302..7f392a0 100644 --- a/libs/kstd/include/kstd/bits/format/context.hpp +++ b/libs/kstd/include/kstd/bits/format/context.hpp @@ -33,6 +33,11 @@ namespace kstd using format_args = std::span; format_args args{}; + format_context(bits::format::output_buffer & buffer, format_args args) + : args{args} + , m_buffer{&buffer} + {} + [[nodiscard]] auto arg(std::size_t const id) const -> format_arg const & { if (id >= args.size()) diff --git a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp index 336e1b0..e371cec 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp @@ -51,11 +51,11 @@ namespace kstd auto const text = value ? std::string_view{"true"} : std::string_view{"false"}; auto final_width = 0uz; - if (specifiers.width_mode == bits::format::width_mode::static_value) + if (specifiers.mode == bits::format::width_mode::static_value) { final_width = specifiers.width_value; } - else if (specifiers.width_mode == bits::format::width_mode::dynamic_argument_id) + else if (specifiers.mode == bits::format::width_mode::dynamic_argument_id) { auto const & arg = context.arg(specifiers.width_value); final_width = bits::format::extrat_dynamic_width(arg); diff --git a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp index 4912a44..e5a234a 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp @@ -64,11 +64,11 @@ namespace kstd auto format(T value, format_context & context) const -> void { auto final_width = 0uz; - if (specifiers.width_mode == bits::format::width_mode::static_value) + if (specifiers.mode == bits::format::width_mode::static_value) { final_width = specifiers.width_value; } - else if (specifiers.width_mode == bits::format::width_mode::dynamic_argument_id) + else if (specifiers.mode == bits::format::width_mode::dynamic_argument_id) { auto const & arg = context.arg(specifiers.width_value); final_width = bits::format::extrat_dynamic_width(arg); diff --git a/libs/kstd/include/kstd/bits/format/specifiers.hpp b/libs/kstd/include/kstd/bits/format/specifiers.hpp index 9bc66c7..18c6f66 100644 --- a/libs/kstd/include/kstd/bits/format/specifiers.hpp +++ b/libs/kstd/include/kstd/bits/format/specifiers.hpp @@ -43,7 +43,7 @@ namespace kstd::bits::format bool alternative_form{}; bool zero_pad{}; - width_mode width_mode{}; + width_mode mode{}; std::size_t width_value{}; char type{}; }; @@ -133,7 +133,7 @@ namespace kstd::bits::format if (it != end && *it == '{') { - specs.width_mode = width_mode::dynamic_argument_id; + specs.mode = width_mode::dynamic_argument_id; std::advance(it, 1); auto argument_id = 0uz; @@ -160,7 +160,7 @@ namespace kstd::bits::format } else if (it != end && *it >= '0' && *it <= '9') { - specs.width_mode = width_mode::static_value; + specs.mode = width_mode::static_value; while (it != end && *it >= '0' && *it <= '9') { specs.width_value = specs.width_value * 10 + static_cast(*it - '0'); diff --git a/libs/kstd/include/kstd/bits/format/vformat.hpp b/libs/kstd/include/kstd/bits/format/vformat.hpp index 90b74a9..d032c3a 100644 --- a/libs/kstd/include/kstd/bits/format/vformat.hpp +++ b/libs/kstd/include/kstd/bits/format/vformat.hpp @@ -38,6 +38,8 @@ namespace kstd struct string_iterator_writer : output_buffer { + explicit string_iterator_writer(string::iterator iterator); + auto push(std::string_view text) -> void override; auto push(char character) -> void override; diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index e51cbac..79593c6 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -111,7 +111,7 @@ namespace kstd allocator_type const & allocator = allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) : m_allocator{allocator} - , m_size{std::ranges::distance(first, last)} + , m_size{static_cast(std::ranges::distance(first, last))} , m_capacity{m_size} , m_data{allocate_n(m_capacity)} { -- cgit v1.2.3 From a0720bbe3cdfe3174e3d064356b21f0fcd37832e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Apr 2026 15:05:38 +0200 Subject: kstd/format: add kstd::format_to --- libs/kstd/include/kstd/bits/format/vformat.hpp | 42 +++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) (limited to 'libs/kstd/include') diff --git a/libs/kstd/include/kstd/bits/format/vformat.hpp b/libs/kstd/include/kstd/bits/format/vformat.hpp index d032c3a..69c7f33 100644 --- a/libs/kstd/include/kstd/bits/format/vformat.hpp +++ b/libs/kstd/include/kstd/bits/format/vformat.hpp @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include @@ -36,15 +38,30 @@ namespace kstd string m_result{}; }; - struct string_iterator_writer : output_buffer + template Output> + struct iterator_writer : output_buffer { - explicit string_iterator_writer(string::iterator iterator); + explicit iterator_writer(Output iterator) + : m_output{iterator} + {} - auto push(std::string_view text) -> void override; - auto push(char character) -> void override; + auto iterator() const -> Output + { + return m_output; + } + + auto push(std::string_view text) -> void override + { + m_output = std::ranges::copy(text, m_output); + } + + auto push(char character) -> void override + { + *m_output++ = character; + } private: - string::iterator m_iter{}; + Output m_output{}; }; } // namespace bits::format @@ -62,6 +79,21 @@ namespace kstd return buffer.release(); } + //! Format a given string with the provided arguments. + //! + //! @param iterator The iterator to write to. + //! @param format The format string. + //! @param args The arguments for the format string. + //! @return An iterator past the last element written. + template Output, typename... ArgumentTypes> + auto format_to(Output iterator, format_string...> format, + ArgumentTypes &&... args) -> Output + { + auto buffer = bits::format::iterator_writer{iterator}; + bits::format::vformat_to(buffer, format.str_view, std::forward(args)...); + return buffer.iterator(); + } + } // namespace kstd #endif \ No newline at end of file -- cgit v1.2.3 From cd1dd2037cbe1d5f1362202d3127640406b468b8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Apr 2026 15:38:08 +0200 Subject: kstd: add basic format and format_to tests --- libs/kstd/include/kstd/bits/format/vformat.hpp | 6 +++--- libs/kstd/include/kstd/string | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'libs/kstd/include') diff --git a/libs/kstd/include/kstd/bits/format/vformat.hpp b/libs/kstd/include/kstd/bits/format/vformat.hpp index 69c7f33..4fec7dd 100644 --- a/libs/kstd/include/kstd/bits/format/vformat.hpp +++ b/libs/kstd/include/kstd/bits/format/vformat.hpp @@ -52,7 +52,7 @@ namespace kstd auto push(std::string_view text) -> void override { - m_output = std::ranges::copy(text, m_output); + m_output = std::ranges::copy(text, m_output).out; } auto push(char character) -> void override @@ -75,7 +75,7 @@ namespace kstd auto format(format_string...> format, ArgumentTypes &&... args) -> string { auto buffer = bits::format::string_writer{}; - bits::format::vformat_to(buffer, format.str_view, std::forward(args)...); + bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward(args)...).args); return buffer.release(); } @@ -90,7 +90,7 @@ namespace kstd ArgumentTypes &&... args) -> Output { auto buffer = bits::format::iterator_writer{iterator}; - bits::format::vformat_to(buffer, format.str_view, std::forward(args)...); + bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward(args)...).args); return buffer.iterator(); } diff --git a/libs/kstd/include/kstd/string b/libs/kstd/include/kstd/string index 4ce19ce..58c4a08 100644 --- a/libs/kstd/include/kstd/string +++ b/libs/kstd/include/kstd/string @@ -347,6 +347,26 @@ namespace kstd return !(lhs == rhs); } + [[nodiscard]] constexpr auto inline operator==(string const & lhs, char const * rhs) -> bool + { + return lhs.view() == std::string_view{rhs}; + } + + [[nodiscard]] constexpr auto inline operator!=(string const & lhs, char const * rhs) -> bool + { + return !(lhs == rhs); + } + + [[nodiscard]] constexpr auto inline operator==(char const * lhs, string const & rhs) -> bool + { + return std::string_view{lhs} == rhs.view(); + } + + [[nodiscard]] constexpr auto inline operator!=(char const * lhs, string const & rhs) -> bool + { + return !(lhs == rhs); + } + template<> struct formatter : formatter { -- cgit v1.2.3 From 3795115641bf5c1d1a3d60313408ba462057ba18 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Apr 2026 16:18:00 +0200 Subject: kstd/format: add support for char formatting --- .../include/kstd/bits/format/formatter/char.hpp | 94 ++++++++++++++++++++++ libs/kstd/include/kstd/format | 1 + 2 files changed, 95 insertions(+) create mode 100644 libs/kstd/include/kstd/bits/format/formatter/char.hpp (limited to 'libs/kstd/include') diff --git a/libs/kstd/include/kstd/bits/format/formatter/char.hpp b/libs/kstd/include/kstd/bits/format/formatter/char.hpp new file mode 100644 index 0000000..ddfefe5 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/char.hpp @@ -0,0 +1,94 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_CHAR_HPP +#define KSTD_BITS_FORMAT_FORMATTER_CHAR_HPP + +#include "../context.hpp" +#include "../error.hpp" +#include "../formatter.hpp" +#include "../parse_context.hpp" +#include "../specifiers.hpp" +#include "integral.hpp" + +#include + +namespace kstd +{ + + template<> + struct formatter : formatter + { + bits::format::specifiers specifiers{}; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + + auto it = context.begin(); + auto const end = context.end(); + + if (specifiers.alternative_form || specifiers.zero_pad || specifiers.sign != bits::format::sign_mode::none) + { + bits::format::error("Invalid format specifiers for 'char'"); + } + + if (it != end && *it != '}') + { + if (*it == 'c' || *it == 'b' || *it == 'B' || *it == 'd' || *it == 'o' || *it == 'x' || *it == 'X') + { + specifiers.type = *it; + std::advance(it, 1); + } + else + { + bits::format::error("Invalid type specifier for char."); + } + } + + if (it != end && *it != '}') + { + bits::format::error("Missing terminating '}' in format string."); + } + + return it; + } + + auto format(char value, format_context & context) const -> void + { + if (specifiers.type == '\0' || specifiers.type == 'c') + { + auto final_width = 0uz; + + if (specifiers.mode == bits::format::width_mode::static_value) + { + final_width = specifiers.width_value; + } + else if (specifiers.mode == bits::format::width_mode::dynamic_argument_id) + { + auto const & arg = context.arg(specifiers.width_value); + final_width = bits::format::extrat_dynamic_width(arg); + } + + auto padding = + bits::format::calculate_format_padding(final_width, 1, specifiers.align, bits::format::alignment::left); + + for (auto i = 0uz; i < padding.left; ++i) + { + context.push(specifiers.fill); + } + + context.push(value); + + for (auto i = 0uz; i < padding.right; ++i) + { + context.push(specifiers.fill); + } + } + else + { + formatter::format(static_cast(value), context); + } + } + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/format b/libs/kstd/include/kstd/format index d11c221..047ea5c 100644 --- a/libs/kstd/include/kstd/format +++ b/libs/kstd/include/kstd/format @@ -7,6 +7,7 @@ #include "bits/format/formatter.hpp" // IWYU pragma: export #include "bits/format/formatter/bool.hpp" // IWYU pragma: export #include "bits/format/formatter/byte.hpp" // IWYU pragma: export +#include "bits/format/formatter/char.hpp" // IWYU pragma: export #include "bits/format/formatter/cstring.hpp" // IWYU pragma: export #include "bits/format/formatter/integral.hpp" // IWYU pragma: export #include "bits/format/formatter/ordering.hpp" // IWYU pragma: export -- cgit v1.2.3