diff options
Diffstat (limited to 'kernel/kstd/print.cpp')
| -rw-r--r-- | kernel/kstd/print.cpp | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/kernel/kstd/print.cpp b/kernel/kstd/print.cpp new file mode 100644 index 0000000..c7d26ba --- /dev/null +++ b/kernel/kstd/print.cpp @@ -0,0 +1,145 @@ +#include "kapi/cio.hpp" + +#include <kstd/format> +#include <kstd/os/print.hpp> +#include <kstd/print> + +#include <array> +#include <cstddef> +#include <iterator> +#include <string_view> + +namespace kstd::os +{ + + namespace + { + struct write_buffer + { + using output_stream = kapi::cio::output_stream; + + 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_stream stream) + : m_stream{stream} + {} + + ~write_buffer() noexcept + { + flush(); + } + + auto flush() noexcept -> void + { + if (m_position > 0) + { + std::string_view chunk{m_buffer.data(), m_position}; + kapi::cio::write(m_stream, 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_stream m_stream; + std::array<char, size> m_buffer{}; + std::size_t m_position{}; + }; + + } // namespace + + auto vprint(print_sink sink, std::string_view format, kstd::format_args args) -> void + { + auto writer = write_buffer{(sink == print_sink::stderr) ? kapi::cio::output_stream::stderr + : kapi::cio::output_stream::stdout}; + auto context = kstd::format_context{.writer = write_buffer::callback, .user_data = &writer}; + + auto current = format.begin(); + auto end = format.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') + { + 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 kstd::os |
