diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2026-04-13 16:26:45 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2026-04-13 16:26:45 +0200 |
| commit | 6253705095086de8c6585acc9400942e6a267a5d (patch) | |
| tree | 8dd4baafe46561d64fd72ae086ebc2ddaa719816 /libs/kstd/include | |
| parent | 4d2a1d028f8ba28b655026b93124e71a12562619 (diff) | |
| parent | 3795115641bf5c1d1a3d60313408ba462057ba18 (diff) | |
| download | teachos-6253705095086de8c6585acc9400942e6a267a5d.tar.xz teachos-6253705095086de8c6585acc9400942e6a267a5d.zip | |
Merge branch 'fmorgner/develop-BA-FS26/string-format' into 'develop-BA-FS26'
Implement `kstd::format` and `kstd::format_to`
See merge request teachos/kernel!24
Diffstat (limited to 'libs/kstd/include')
| -rw-r--r-- | libs/kstd/include/kstd/bits/format/context.hpp | 17 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/format/formatter/bool.hpp | 4 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/format/formatter/char.hpp | 94 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/format/formatter/integral.hpp | 4 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/format/output_buffer.hpp | 32 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/format/specifiers.hpp | 6 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/format/vformat.hpp | 99 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/format | 3 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/string | 20 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/vector | 2 |
10 files changed, 267 insertions, 14 deletions
diff --git a/libs/kstd/include/kstd/bits/format/context.hpp b/libs/kstd/include/kstd/bits/format/context.hpp index 478a48f..7f392a0 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 <kstd/format> #include "arg.hpp" +#include "kstd/bits/format/output_buffer.hpp" #include <kstd/os/error.hpp> @@ -29,13 +30,14 @@ namespace kstd struct format_context { - using writer_function = void(void *, std::string_view); using format_args = std::span<format_arg const>; - - writer_function * writer{}; - void * user_data{}; 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()) @@ -47,13 +49,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/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/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 <iterator> + +namespace kstd +{ + + template<> + struct formatter<char> : formatter<unsigned char> + { + 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<unsigned char>::format(static_cast<unsigned char>(value), context); + } + } + }; + +} // namespace kstd + +#endif
\ No newline at end of file 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/output_buffer.hpp b/libs/kstd/include/kstd/bits/format/output_buffer.hpp new file mode 100644 index 0000000..fd7a2b4 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/output_buffer.hpp @@ -0,0 +1,32 @@ +#ifndef KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP +#define KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP + +// IWYU pragma: private, include <kstd/format> + +#include <string_view> + +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 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<std::size_t>(*it - '0'); 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..4fec7dd --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/vformat.hpp @@ -0,0 +1,99 @@ +#ifndef KSTD_BITS_FORMAT_VFORMAT_HPP +#define KSTD_BITS_FORMAT_VFORMAT_HPP + +// IWYU pragma: private, include <kstd/format> + +#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 <iterator> +#include <string_view> +#include <type_traits> + +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{}; + }; + + template<std::output_iterator<char> Output> + struct iterator_writer : output_buffer + { + explicit iterator_writer(Output iterator) + : m_output{iterator} + {} + + auto iterator() const -> Output + { + return m_output; + } + + auto push(std::string_view text) -> void override + { + m_output = std::ranges::copy(text, m_output).out; + } + + auto push(char character) -> void override + { + *m_output++ = character; + } + + private: + Output m_output{}; + }; + + } // 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<typename... ArgumentTypes> + auto format(format_string<std::type_identity_t<ArgumentTypes>...> format, ArgumentTypes &&... args) -> string + { + auto buffer = bits::format::string_writer{}; + bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward<ArgumentTypes>(args)...).args); + 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<std::output_iterator<char> Output, typename... ArgumentTypes> + auto format_to(Output iterator, format_string<std::type_identity_t<ArgumentTypes>...> format, + ArgumentTypes &&... args) -> Output + { + auto buffer = bits::format::iterator_writer{iterator}; + bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward<ArgumentTypes>(args)...).args); + return buffer.iterator(); + } + +} // 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..047ea5c 100644 --- a/libs/kstd/include/kstd/format +++ b/libs/kstd/include/kstd/format @@ -7,13 +7,16 @@ #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 #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 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<string> : formatter<std::string_view> { 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<allocator_type>) : m_allocator{allocator} - , m_size{std::ranges::distance(first, last)} + , m_size{static_cast<std::size_t>(std::ranges::distance(first, last))} , m_capacity{m_size} , m_data{allocate_n(m_capacity)} { |
