From 8042003647b50e9fddfe500677a132130449d69e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 18 Dec 2025 14:00:03 +0100 Subject: kapi/cio: implement formatted printing --- kernel/src/kapi/cio.cpp | 155 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) (limited to 'kernel') 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 + +#include +#include +#include #include #include #include @@ -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(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 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(*current - '0'); + std::advance(current, 1); + } + } + else + { + index = next_automatic_index++; + } + + auto remaining_fmt = std::string_view{current, static_cast(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 -- cgit v1.2.3