diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2025-12-18 14:00:03 +0100 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2025-12-18 14:00:03 +0100 |
| commit | 8042003647b50e9fddfe500677a132130449d69e (patch) | |
| tree | 82e89f225b8324c91a84d61008c790136d681b5a | |
| parent | 0c8e77b367ca617beb2c30cd7e032cd9b24aeea4 (diff) | |
| download | teachos-8042003647b50e9fddfe500677a132130449d69e.tar.xz teachos-8042003647b50e9fddfe500677a132130449d69e.zip | |
kapi/cio: implement formatted printing
| -rw-r--r-- | kapi/include/kapi/cio.hpp | 55 | ||||
| -rw-r--r-- | kernel/src/kapi/cio.cpp | 155 |
2 files changed, 210 insertions, 0 deletions
diff --git a/kapi/include/kapi/cio.hpp b/kapi/include/kapi/cio.hpp index 071e6cb..98c715b 100644 --- a/kapi/include/kapi/cio.hpp +++ b/kapi/include/kapi/cio.hpp @@ -3,8 +3,12 @@ #include "kapi/cio/output_device.hpp" // IWYU pragma: export +#include <kstd/format> + +#include <array> #include <optional> #include <string_view> +#include <type_traits> namespace teachos::cio { @@ -46,6 +50,57 @@ namespace teachos::cio //! //! @param text The error text to print. auto println_error(std::string_view text) -> void; + + template<typename... Args> + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + auto print(kstd::format_string<std::type_identity_t<Args>...> format, Args &&... args) -> void + { + auto vprint(std::string_view format, kstd::format_args args) -> void; + auto arguments = std::array{ + kstd::format_arg{&args, kstd::format_dispatcher<std::remove_cvref_t<Args>>} + ... + }; + vprint(format.str, kstd::format_args{arguments.data(), sizeof...(Args)}); + } + + template<typename... Args> + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + auto println(kstd::format_string<std::type_identity_t<Args>...> format, Args &&... args) -> void + { + auto vprint(std::string_view format, kstd::format_args args) -> void; + auto arguments = std::array{ + kstd::format_arg{&args, kstd::format_dispatcher<std::remove_cvref_t<Args>>} + ... + }; + vprint(format.str, kstd::format_args{arguments.data(), sizeof...(Args)}); + println(""); + } + + template<typename... Args> + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + auto print_error(kstd::format_string<std::type_identity_t<Args>...> format, Args &&... args) -> void + { + auto vprint_error(std::string_view format, kstd::format_args args) -> void; + auto arguments = std::array{ + kstd::format_arg{&args, kstd::format_dispatcher<std::remove_cvref_t<Args>>} + ... + }; + vprint_error(format.str, kstd::format_args{arguments.data(), sizeof...(Args)}); + } + + template<typename... Args> + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + auto println_error(kstd::format_string<std::type_identity_t<Args>...> format, Args &&... args) -> void + { + auto vprint_error(std::string_view format, kstd::format_args args) -> void; + auto arguments = std::array{ + kstd::format_arg{&args, kstd::format_dispatcher<std::remove_cvref_t<Args>>} + ... + }; + vprint_error(format.str, kstd::format_args{arguments.data(), sizeof...(Args)}); + println_error(""); + } + } // namespace teachos::cio #endif diff --git a/kernel/src/kapi/cio.cpp b/kernel/src/kapi/cio.cpp index 66493b6..500d61e 100644 --- a/kernel/src/kapi/cio.cpp +++ b/kernel/src/kapi/cio.cpp @@ -1,5 +1,10 @@ #include "kapi/cio.hpp" +#include <kstd/format> + +#include <array> +#include <cstddef> +#include <iterator> #include <optional> #include <string_view> #include <utility> @@ -20,6 +25,63 @@ namespace teachos::cio }; constinit null_device null_device::instance; + + struct write_buffer + { + constexpr auto static size = 128uz; + + write_buffer(write_buffer const &) = delete; + write_buffer(write_buffer &&) = delete; + auto operator=(write_buffer const &) -> write_buffer & = delete; + auto operator=(write_buffer &&) -> write_buffer & = delete; + + explicit write_buffer(output_device * device, bool write_to_error) + : m_device{device} + , m_write_to_error{write_to_error} + {} + + ~write_buffer() noexcept + { + flush(); + } + + auto flush() noexcept -> void + { + if (m_position > 0) + { + std::string_view chunk{m_buffer.data(), m_position}; + if (m_write_to_error) + { + m_device->write_error(chunk); + } + else + { + m_device->write(chunk); + } + m_position = 0; + } + } + + auto static callback(void * object, std::string_view text) -> void + { + auto * self = static_cast<write_buffer *>(object); + for (char const character : text) + { + if (self->m_position >= size) + { + self->flush(); + } + self->m_buffer.at(self->m_position++) = character; + } + } + + private: + output_device * m_device; + bool m_write_to_error; + std::array<char, size> m_buffer{}; + std::size_t m_position{}; + }; + } // namespace // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) @@ -54,4 +116,97 @@ namespace teachos::cio active_device->writeln_error(text); } + namespace + { + auto static vprint_impl(std::string_view fmt, kstd::format_args args, bool write_to_error) -> void + { + if (!active_device) + return; + + auto writer = write_buffer{active_device, write_to_error}; + auto context = kstd::format_context{.writer = write_buffer::callback, .user_data = &writer}; + + auto current = fmt.begin(); + auto end = fmt.end(); + auto next_automatic_index = 0uz; + + while (current != end) + { + if (*current != '{') + { + auto start = current; + while (current != end && *current != '{') + { + std::advance(current, 1); + } + context.push(std::string_view(start, current - start)); + continue; + } + + if (std::next(current) != end && *(std::next(current)) == '{') + { + context.push('{'); + std::advance(current, 2); + continue; + } + + std::advance(current, 1); + + auto index = 0uz; + if (current != end && *current >= '0' && *current <= '9') + { + while (current != end && *current >= '0' && *current <= '9') + { + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers) + index = index * 10 + static_cast<std::size_t>(*current - '0'); + std::advance(current, 1); + } + } + else + { + index = next_automatic_index++; + } + + auto remaining_fmt = std::string_view{current, static_cast<std::size_t>(std::distance(current, end))}; + + auto const arg = args.get(index); + if (arg.format) + { + auto const after_specs = arg.format(arg.value, remaining_fmt, context); + auto const consumed = remaining_fmt.size() - after_specs.size(); + std::advance(current, consumed); + } + else + { + context.push("{?}"); + while (current != end && *current != '}') + std::advance(current, 1); + } + + if (current != end && *current == '}') + { + std::advance(current, 1); + } + else + { + context.push("{fmt-err}"); + while (current != end && *current != '}') + std::advance(current, 1); + if (current != end) + std::advance(current, 1); + } + } + } + } // namespace + + auto vprint(std::string_view fmt, kstd::format_args args) -> void + { + vprint_impl(fmt, args, false); + } + + auto vprint_error(std::string_view fmt, kstd::format_args args) -> void + { + vprint_impl(fmt, args, true); + } + } // namespace teachos::cio
\ No newline at end of file |
