aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2025-12-18 14:00:03 +0100
committerFelix Morgner <felix.morgner@ost.ch>2025-12-18 14:00:03 +0100
commit8042003647b50e9fddfe500677a132130449d69e (patch)
tree82e89f225b8324c91a84d61008c790136d681b5a
parent0c8e77b367ca617beb2c30cd7e032cd9b24aeea4 (diff)
downloadteachos-8042003647b50e9fddfe500677a132130449d69e.tar.xz
teachos-8042003647b50e9fddfe500677a132130449d69e.zip
kapi/cio: implement formatted printing
-rw-r--r--kapi/include/kapi/cio.hpp55
-rw-r--r--kernel/src/kapi/cio.cpp155
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